xref: /openbmc/linux/sound/soc/codecs/zl38060.c (revision da1f2282)
152e8a94bSSven Van Asbroeck // SPDX-License-Identifier: GPL-2.0-only
252e8a94bSSven Van Asbroeck //
352e8a94bSSven Van Asbroeck // Codec driver for Microsemi ZL38060 Connected Home Audio Processor.
452e8a94bSSven Van Asbroeck //
552e8a94bSSven Van Asbroeck // Copyright(c) 2020 Sven Van Asbroeck
652e8a94bSSven Van Asbroeck 
752e8a94bSSven Van Asbroeck // The ZL38060 is very flexible and configurable. This driver implements only a
852e8a94bSSven Van Asbroeck // tiny subset of the chip's possible configurations:
952e8a94bSSven Van Asbroeck //
1052e8a94bSSven Van Asbroeck // - DSP block bypassed: DAI        routed straight to DACs
1152e8a94bSSven Van Asbroeck //                       microphone routed straight to DAI
1252e8a94bSSven Van Asbroeck // - chip's internal clock is driven by a 12 MHz external crystal
1352e8a94bSSven Van Asbroeck // - chip's DAI connected to CPU is I2S, and bit + frame clock master
1452e8a94bSSven Van Asbroeck // - chip must be strapped for "host boot": in this mode, firmware will be
1552e8a94bSSven Van Asbroeck //   provided by this driver.
1652e8a94bSSven Van Asbroeck 
1752e8a94bSSven Van Asbroeck #include <linux/gpio/consumer.h>
1852e8a94bSSven Van Asbroeck #include <linux/gpio/driver.h>
1952e8a94bSSven Van Asbroeck #include <linux/property.h>
2052e8a94bSSven Van Asbroeck #include <linux/spi/spi.h>
2152e8a94bSSven Van Asbroeck #include <linux/regmap.h>
2252e8a94bSSven Van Asbroeck #include <linux/module.h>
2352e8a94bSSven Van Asbroeck #include <linux/ihex.h>
2452e8a94bSSven Van Asbroeck 
2552e8a94bSSven Van Asbroeck #include <sound/pcm_params.h>
2652e8a94bSSven Van Asbroeck #include <sound/core.h>
2752e8a94bSSven Van Asbroeck #include <sound/pcm.h>
2852e8a94bSSven Van Asbroeck #include <sound/soc.h>
2952e8a94bSSven Van Asbroeck 
3052e8a94bSSven Van Asbroeck #define DRV_NAME		"zl38060"
3152e8a94bSSven Van Asbroeck 
3252e8a94bSSven Van Asbroeck #define ZL38_RATES		(SNDRV_PCM_RATE_8000  |\
3352e8a94bSSven Van Asbroeck 				SNDRV_PCM_RATE_16000 |\
3452e8a94bSSven Van Asbroeck 				SNDRV_PCM_RATE_48000)
3552e8a94bSSven Van Asbroeck #define ZL38_FORMATS		SNDRV_PCM_FMTBIT_S16_LE
3652e8a94bSSven Van Asbroeck 
3752e8a94bSSven Van Asbroeck #define HBI_FIRMWARE_PAGE	0xFF
3852e8a94bSSven Van Asbroeck #define ZL38_MAX_RAW_XFER	0x100
3952e8a94bSSven Van Asbroeck 
4052e8a94bSSven Van Asbroeck #define REG_TDMA_CFG_CLK	0x0262
4152e8a94bSSven Van Asbroeck #define CFG_CLK_PCLK_SHIFT	4
4252e8a94bSSven Van Asbroeck #define CFG_CLK_PCLK_MASK	(0x7ff << CFG_CLK_PCLK_SHIFT)
4352e8a94bSSven Van Asbroeck #define CFG_CLK_PCLK(bits)	((bits - 1) << CFG_CLK_PCLK_SHIFT)
4452e8a94bSSven Van Asbroeck #define CFG_CLK_MASTER		BIT(15)
4552e8a94bSSven Van Asbroeck #define CFG_CLK_FSRATE_MASK	0x7
4652e8a94bSSven Van Asbroeck #define CFG_CLK_FSRATE_8KHZ	0x1
4752e8a94bSSven Van Asbroeck #define CFG_CLK_FSRATE_16KHZ	0x2
4852e8a94bSSven Van Asbroeck #define CFG_CLK_FSRATE_48KHZ	0x6
4952e8a94bSSven Van Asbroeck 
5052e8a94bSSven Van Asbroeck #define REG_CLK_CFG		0x0016
5152e8a94bSSven Van Asbroeck #define CLK_CFG_SOURCE_XTAL	BIT(15)
5252e8a94bSSven Van Asbroeck 
5352e8a94bSSven Van Asbroeck #define REG_CLK_STATUS		0x0014
5452e8a94bSSven Van Asbroeck #define CLK_STATUS_HWRST	BIT(0)
5552e8a94bSSven Van Asbroeck 
5652e8a94bSSven Van Asbroeck #define REG_PARAM_RESULT	0x0034
5752e8a94bSSven Van Asbroeck #define PARAM_RESULT_READY	0xD3D3
5852e8a94bSSven Van Asbroeck 
5952e8a94bSSven Van Asbroeck #define REG_PG255_BASE_HI	0x000C
6052e8a94bSSven Van Asbroeck #define REG_PG255_OFFS(addr)	((HBI_FIRMWARE_PAGE << 8) | (addr & 0xFF))
6152e8a94bSSven Van Asbroeck #define REG_FWR_EXEC		0x012C
6252e8a94bSSven Van Asbroeck 
6352e8a94bSSven Van Asbroeck #define REG_CMD			0x0032
6452e8a94bSSven Van Asbroeck #define REG_HW_REV		0x0020
6552e8a94bSSven Van Asbroeck #define REG_FW_PROD		0x0022
6652e8a94bSSven Van Asbroeck #define REG_FW_REV		0x0024
6752e8a94bSSven Van Asbroeck 
6852e8a94bSSven Van Asbroeck #define REG_SEMA_FLAGS		0x0006
6952e8a94bSSven Van Asbroeck #define SEMA_FLAGS_BOOT_CMD	BIT(0)
7052e8a94bSSven Van Asbroeck #define SEMA_FLAGS_APP_REBOOT	BIT(1)
7152e8a94bSSven Van Asbroeck 
7252e8a94bSSven Van Asbroeck #define REG_HW_REV		0x0020
7352e8a94bSSven Van Asbroeck #define REG_FW_PROD		0x0022
7452e8a94bSSven Van Asbroeck #define REG_FW_REV		0x0024
7552e8a94bSSven Van Asbroeck #define REG_GPIO_DIR		0x02DC
7652e8a94bSSven Van Asbroeck #define REG_GPIO_DAT		0x02DA
7752e8a94bSSven Van Asbroeck 
7852e8a94bSSven Van Asbroeck #define BOOTCMD_LOAD_COMPLETE	0x000D
7952e8a94bSSven Van Asbroeck #define BOOTCMD_FW_GO		0x0008
8052e8a94bSSven Van Asbroeck 
8152e8a94bSSven Van Asbroeck #define FIRMWARE_MAJOR		2
8252e8a94bSSven Van Asbroeck #define FIRMWARE_MINOR		2
8352e8a94bSSven Van Asbroeck 
8452e8a94bSSven Van Asbroeck struct zl38_codec_priv {
8552e8a94bSSven Van Asbroeck 	struct device *dev;
8652e8a94bSSven Van Asbroeck 	struct regmap *regmap;
8752e8a94bSSven Van Asbroeck 	bool is_stream_in_use[2];
8852e8a94bSSven Van Asbroeck 	struct gpio_chip *gpio_chip;
8952e8a94bSSven Van Asbroeck };
9052e8a94bSSven Van Asbroeck 
zl38_fw_issue_command(struct regmap * regmap,u16 cmd)9152e8a94bSSven Van Asbroeck static int zl38_fw_issue_command(struct regmap *regmap, u16 cmd)
9252e8a94bSSven Van Asbroeck {
9352e8a94bSSven Van Asbroeck 	unsigned int val;
9452e8a94bSSven Van Asbroeck 	int err;
9552e8a94bSSven Van Asbroeck 
9652e8a94bSSven Van Asbroeck 	err = regmap_read_poll_timeout(regmap, REG_SEMA_FLAGS, val,
9752e8a94bSSven Van Asbroeck 				       !(val & SEMA_FLAGS_BOOT_CMD), 10000,
9852e8a94bSSven Van Asbroeck 				       10000 * 100);
9952e8a94bSSven Van Asbroeck 	if (err)
10052e8a94bSSven Van Asbroeck 		return err;
10152e8a94bSSven Van Asbroeck 	err = regmap_write(regmap, REG_CMD, cmd);
10252e8a94bSSven Van Asbroeck 	if (err)
10352e8a94bSSven Van Asbroeck 		return err;
10452e8a94bSSven Van Asbroeck 	err = regmap_update_bits(regmap, REG_SEMA_FLAGS, SEMA_FLAGS_BOOT_CMD,
10552e8a94bSSven Van Asbroeck 				 SEMA_FLAGS_BOOT_CMD);
10652e8a94bSSven Van Asbroeck 	if (err)
10752e8a94bSSven Van Asbroeck 		return err;
10852e8a94bSSven Van Asbroeck 
10952e8a94bSSven Van Asbroeck 	return regmap_read_poll_timeout(regmap, REG_CMD, val, !val, 10000,
11052e8a94bSSven Van Asbroeck 					10000 * 100);
11152e8a94bSSven Van Asbroeck }
11252e8a94bSSven Van Asbroeck 
zl38_fw_go(struct regmap * regmap)11352e8a94bSSven Van Asbroeck static int zl38_fw_go(struct regmap *regmap)
11452e8a94bSSven Van Asbroeck {
11552e8a94bSSven Van Asbroeck 	int err;
11652e8a94bSSven Van Asbroeck 
11752e8a94bSSven Van Asbroeck 	err = zl38_fw_issue_command(regmap, BOOTCMD_LOAD_COMPLETE);
11852e8a94bSSven Van Asbroeck 	if (err)
11952e8a94bSSven Van Asbroeck 		return err;
12052e8a94bSSven Van Asbroeck 
12152e8a94bSSven Van Asbroeck 	return zl38_fw_issue_command(regmap, BOOTCMD_FW_GO);
12252e8a94bSSven Van Asbroeck }
12352e8a94bSSven Van Asbroeck 
zl38_fw_enter_boot_mode(struct regmap * regmap)12452e8a94bSSven Van Asbroeck static int zl38_fw_enter_boot_mode(struct regmap *regmap)
12552e8a94bSSven Van Asbroeck {
12652e8a94bSSven Van Asbroeck 	unsigned int val;
12752e8a94bSSven Van Asbroeck 	int err;
12852e8a94bSSven Van Asbroeck 
12952e8a94bSSven Van Asbroeck 	err = regmap_update_bits(regmap, REG_CLK_STATUS, CLK_STATUS_HWRST,
13052e8a94bSSven Van Asbroeck 				 CLK_STATUS_HWRST);
13152e8a94bSSven Van Asbroeck 	if (err)
13252e8a94bSSven Van Asbroeck 		return err;
13352e8a94bSSven Van Asbroeck 
13452e8a94bSSven Van Asbroeck 	return regmap_read_poll_timeout(regmap, REG_PARAM_RESULT, val,
13552e8a94bSSven Van Asbroeck 					val == PARAM_RESULT_READY, 1000, 50000);
13652e8a94bSSven Van Asbroeck }
13752e8a94bSSven Van Asbroeck 
13852e8a94bSSven Van Asbroeck static int
zl38_fw_send_data(struct regmap * regmap,u32 addr,const void * data,u16 len)13952e8a94bSSven Van Asbroeck zl38_fw_send_data(struct regmap *regmap, u32 addr, const void *data, u16 len)
14052e8a94bSSven Van Asbroeck {
14152e8a94bSSven Van Asbroeck 	__be32 addr_base = cpu_to_be32(addr & ~0xFF);
14252e8a94bSSven Van Asbroeck 	int err;
14352e8a94bSSven Van Asbroeck 
14452e8a94bSSven Van Asbroeck 	err = regmap_raw_write(regmap, REG_PG255_BASE_HI, &addr_base,
14552e8a94bSSven Van Asbroeck 			       sizeof(addr_base));
14652e8a94bSSven Van Asbroeck 	if (err)
14752e8a94bSSven Van Asbroeck 		return err;
14852e8a94bSSven Van Asbroeck 	return regmap_raw_write(regmap, REG_PG255_OFFS(addr), data, len);
14952e8a94bSSven Van Asbroeck }
15052e8a94bSSven Van Asbroeck 
zl38_fw_send_xaddr(struct regmap * regmap,const void * data)15152e8a94bSSven Van Asbroeck static int zl38_fw_send_xaddr(struct regmap *regmap, const void *data)
15252e8a94bSSven Van Asbroeck {
15352e8a94bSSven Van Asbroeck 	/* execution address from ihex: 32-bit little endian.
15452e8a94bSSven Van Asbroeck 	 * device register expects 32-bit big endian.
15552e8a94bSSven Van Asbroeck 	 */
15652e8a94bSSven Van Asbroeck 	u32 addr = le32_to_cpup(data);
15752e8a94bSSven Van Asbroeck 	__be32 baddr = cpu_to_be32(addr);
15852e8a94bSSven Van Asbroeck 
15952e8a94bSSven Van Asbroeck 	return regmap_raw_write(regmap, REG_FWR_EXEC, &baddr, sizeof(baddr));
16052e8a94bSSven Van Asbroeck }
16152e8a94bSSven Van Asbroeck 
zl38_load_firmware(struct device * dev,struct regmap * regmap)16252e8a94bSSven Van Asbroeck static int zl38_load_firmware(struct device *dev, struct regmap *regmap)
16352e8a94bSSven Van Asbroeck {
16452e8a94bSSven Van Asbroeck 	const struct ihex_binrec *rec;
16552e8a94bSSven Van Asbroeck 	const struct firmware *fw;
16652e8a94bSSven Van Asbroeck 	u32 addr;
16752e8a94bSSven Van Asbroeck 	u16 len;
16852e8a94bSSven Van Asbroeck 	int err;
16952e8a94bSSven Van Asbroeck 
17052e8a94bSSven Van Asbroeck 	/* how to get this firmware:
17152e8a94bSSven Van Asbroeck 	 * 1. request and download chip firmware from Microsemi
17252e8a94bSSven Van Asbroeck 	 *    (provided by Microsemi in srec format)
17352e8a94bSSven Van Asbroeck 	 * 2. convert downloaded firmware from srec to ihex. Simple tool:
17452e8a94bSSven Van Asbroeck 	 *    https://gitlab.com/TheSven73/s3-to-irec
17552e8a94bSSven Van Asbroeck 	 * 3. convert ihex to binary (.fw) using ihex2fw tool which is included
17652e8a94bSSven Van Asbroeck 	 *    with the Linux kernel sources
17752e8a94bSSven Van Asbroeck 	 */
17852e8a94bSSven Van Asbroeck 	err = request_ihex_firmware(&fw, "zl38060.fw", dev);
17952e8a94bSSven Van Asbroeck 	if (err)
18052e8a94bSSven Van Asbroeck 		return err;
18152e8a94bSSven Van Asbroeck 	err = zl38_fw_enter_boot_mode(regmap);
18252e8a94bSSven Van Asbroeck 	if (err)
18352e8a94bSSven Van Asbroeck 		goto out;
18452e8a94bSSven Van Asbroeck 	rec = (const struct ihex_binrec *)fw->data;
18552e8a94bSSven Van Asbroeck 	while (rec) {
18652e8a94bSSven Van Asbroeck 		addr = be32_to_cpu(rec->addr);
18752e8a94bSSven Van Asbroeck 		len = be16_to_cpu(rec->len);
18852e8a94bSSven Van Asbroeck 		if (addr) {
18952e8a94bSSven Van Asbroeck 			/* regular data ihex record */
19052e8a94bSSven Van Asbroeck 			err = zl38_fw_send_data(regmap, addr, rec->data, len);
19152e8a94bSSven Van Asbroeck 		} else if (len == 4) {
19252e8a94bSSven Van Asbroeck 			/* execution address ihex record */
19352e8a94bSSven Van Asbroeck 			err = zl38_fw_send_xaddr(regmap, rec->data);
19452e8a94bSSven Van Asbroeck 		} else {
19552e8a94bSSven Van Asbroeck 			err = -EINVAL;
19652e8a94bSSven Van Asbroeck 		}
19752e8a94bSSven Van Asbroeck 		if (err)
19852e8a94bSSven Van Asbroeck 			goto out;
19952e8a94bSSven Van Asbroeck 		/* next ! */
20052e8a94bSSven Van Asbroeck 		rec = ihex_next_binrec(rec);
20152e8a94bSSven Van Asbroeck 	}
20252e8a94bSSven Van Asbroeck 	err = zl38_fw_go(regmap);
20352e8a94bSSven Van Asbroeck 
20452e8a94bSSven Van Asbroeck out:
20552e8a94bSSven Van Asbroeck 	release_firmware(fw);
20652e8a94bSSven Van Asbroeck 	return err;
20752e8a94bSSven Van Asbroeck }
20852e8a94bSSven Van Asbroeck 
20952e8a94bSSven Van Asbroeck 
zl38_software_reset(struct regmap * regmap)21052e8a94bSSven Van Asbroeck static int zl38_software_reset(struct regmap *regmap)
21152e8a94bSSven Van Asbroeck {
21252e8a94bSSven Van Asbroeck 	unsigned int val;
21352e8a94bSSven Van Asbroeck 	int err;
21452e8a94bSSven Van Asbroeck 
21552e8a94bSSven Van Asbroeck 	err = regmap_update_bits(regmap, REG_SEMA_FLAGS, SEMA_FLAGS_APP_REBOOT,
21652e8a94bSSven Van Asbroeck 				 SEMA_FLAGS_APP_REBOOT);
21752e8a94bSSven Van Asbroeck 	if (err)
21852e8a94bSSven Van Asbroeck 		return err;
21952e8a94bSSven Van Asbroeck 
22052e8a94bSSven Van Asbroeck 	/* wait for host bus interface to settle.
22152e8a94bSSven Van Asbroeck 	 * Not sure if this is required: Microsemi's vendor driver does this,
22252e8a94bSSven Van Asbroeck 	 * but the firmware manual does not mention it. Leave it in, there's
22352e8a94bSSven Van Asbroeck 	 * little downside, apart from a slower reset.
22452e8a94bSSven Van Asbroeck 	 */
22552e8a94bSSven Van Asbroeck 	msleep(50);
22652e8a94bSSven Van Asbroeck 
22752e8a94bSSven Van Asbroeck 	return regmap_read_poll_timeout(regmap, REG_SEMA_FLAGS, val,
22852e8a94bSSven Van Asbroeck 					!(val & SEMA_FLAGS_APP_REBOOT), 10000,
22952e8a94bSSven Van Asbroeck 					10000 * 100);
23052e8a94bSSven Van Asbroeck }
23152e8a94bSSven Van Asbroeck 
zl38_set_fmt(struct snd_soc_dai * dai,unsigned int fmt)23252e8a94bSSven Van Asbroeck static int zl38_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
23352e8a94bSSven Van Asbroeck {
23452e8a94bSSven Van Asbroeck 	struct zl38_codec_priv *priv = snd_soc_dai_get_drvdata(dai);
23552e8a94bSSven Van Asbroeck 	int err;
23652e8a94bSSven Van Asbroeck 
23752e8a94bSSven Van Asbroeck 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
23852e8a94bSSven Van Asbroeck 	case SND_SOC_DAIFMT_I2S:
23952e8a94bSSven Van Asbroeck 		/* firmware default is normal i2s */
24052e8a94bSSven Van Asbroeck 		break;
24152e8a94bSSven Van Asbroeck 	default:
24252e8a94bSSven Van Asbroeck 		return -EINVAL;
24352e8a94bSSven Van Asbroeck 	}
24452e8a94bSSven Van Asbroeck 
24552e8a94bSSven Van Asbroeck 	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
24652e8a94bSSven Van Asbroeck 	case SND_SOC_DAIFMT_NB_NF:
24752e8a94bSSven Van Asbroeck 		/* firmware default is normal bitclock and frame */
24852e8a94bSSven Van Asbroeck 		break;
24952e8a94bSSven Van Asbroeck 	default:
25052e8a94bSSven Van Asbroeck 		return -EINVAL;
25152e8a94bSSven Van Asbroeck 	}
25252e8a94bSSven Van Asbroeck 
253c5bc6275SMark Brown 	switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
254c5bc6275SMark Brown 	case SND_SOC_DAIFMT_CBP_CFP:
25552e8a94bSSven Van Asbroeck 		/* always 32 bits per frame (= 16 bits/channel, 2 channels) */
25652e8a94bSSven Van Asbroeck 		err = regmap_update_bits(priv->regmap, REG_TDMA_CFG_CLK,
25752e8a94bSSven Van Asbroeck 					 CFG_CLK_MASTER | CFG_CLK_PCLK_MASK,
25852e8a94bSSven Van Asbroeck 					 CFG_CLK_MASTER | CFG_CLK_PCLK(32));
25952e8a94bSSven Van Asbroeck 		if (err)
26052e8a94bSSven Van Asbroeck 			return err;
26152e8a94bSSven Van Asbroeck 		break;
26252e8a94bSSven Van Asbroeck 	default:
26352e8a94bSSven Van Asbroeck 		return -EINVAL;
26452e8a94bSSven Van Asbroeck 	}
26552e8a94bSSven Van Asbroeck 
26652e8a94bSSven Van Asbroeck 	return 0;
26752e8a94bSSven Van Asbroeck }
26852e8a94bSSven Van Asbroeck 
zl38_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)26952e8a94bSSven Van Asbroeck static int zl38_hw_params(struct snd_pcm_substream *substream,
27052e8a94bSSven Van Asbroeck 			  struct snd_pcm_hw_params *params,
27152e8a94bSSven Van Asbroeck 			  struct snd_soc_dai *dai)
27252e8a94bSSven Van Asbroeck {
27352e8a94bSSven Van Asbroeck 	struct zl38_codec_priv *priv = snd_soc_dai_get_drvdata(dai);
27452e8a94bSSven Van Asbroeck 	bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
27552e8a94bSSven Van Asbroeck 	unsigned int fsrate;
27652e8a94bSSven Van Asbroeck 	int err;
27752e8a94bSSven Van Asbroeck 
27852e8a94bSSven Van Asbroeck 	/* We cannot change hw_params while the dai is already in use - the
27952e8a94bSSven Van Asbroeck 	 * software reset will corrupt the audio. However, this is not required,
28052e8a94bSSven Van Asbroeck 	 * as the chip's TDM buses are fully symmetric, which mandates identical
28152e8a94bSSven Van Asbroeck 	 * rates, channels, and samplebits for record and playback.
28252e8a94bSSven Van Asbroeck 	 */
28352e8a94bSSven Van Asbroeck 	if (priv->is_stream_in_use[!tx])
28452e8a94bSSven Van Asbroeck 		goto skip_setup;
28552e8a94bSSven Van Asbroeck 
28652e8a94bSSven Van Asbroeck 	switch (params_rate(params)) {
28752e8a94bSSven Van Asbroeck 	case 8000:
28852e8a94bSSven Van Asbroeck 		fsrate = CFG_CLK_FSRATE_8KHZ;
28952e8a94bSSven Van Asbroeck 		break;
29052e8a94bSSven Van Asbroeck 	case 16000:
29152e8a94bSSven Van Asbroeck 		fsrate = CFG_CLK_FSRATE_16KHZ;
29252e8a94bSSven Van Asbroeck 		break;
29352e8a94bSSven Van Asbroeck 	case 48000:
29452e8a94bSSven Van Asbroeck 		fsrate = CFG_CLK_FSRATE_48KHZ;
29552e8a94bSSven Van Asbroeck 		break;
29652e8a94bSSven Van Asbroeck 	default:
29752e8a94bSSven Van Asbroeck 		return -EINVAL;
298edc475beSkbuild test robot 	}
29952e8a94bSSven Van Asbroeck 
30052e8a94bSSven Van Asbroeck 	err = regmap_update_bits(priv->regmap, REG_TDMA_CFG_CLK,
30152e8a94bSSven Van Asbroeck 				 CFG_CLK_FSRATE_MASK, fsrate);
30252e8a94bSSven Van Asbroeck 	if (err)
30352e8a94bSSven Van Asbroeck 		return err;
30452e8a94bSSven Van Asbroeck 
30552e8a94bSSven Van Asbroeck 	/* chip requires a software reset to apply audio register changes */
30652e8a94bSSven Van Asbroeck 	err = zl38_software_reset(priv->regmap);
30752e8a94bSSven Van Asbroeck 	if (err)
30852e8a94bSSven Van Asbroeck 		return err;
30952e8a94bSSven Van Asbroeck 
31052e8a94bSSven Van Asbroeck skip_setup:
31152e8a94bSSven Van Asbroeck 	priv->is_stream_in_use[tx] = true;
31252e8a94bSSven Van Asbroeck 
31352e8a94bSSven Van Asbroeck 	return 0;
31452e8a94bSSven Van Asbroeck }
31552e8a94bSSven Van Asbroeck 
zl38_hw_free(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)31652e8a94bSSven Van Asbroeck static int zl38_hw_free(struct snd_pcm_substream *substream,
31752e8a94bSSven Van Asbroeck 			struct snd_soc_dai *dai)
31852e8a94bSSven Van Asbroeck {
31952e8a94bSSven Van Asbroeck 	struct zl38_codec_priv *priv = snd_soc_dai_get_drvdata(dai);
32052e8a94bSSven Van Asbroeck 	bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
32152e8a94bSSven Van Asbroeck 
32252e8a94bSSven Van Asbroeck 	priv->is_stream_in_use[tx] = false;
32352e8a94bSSven Van Asbroeck 
32452e8a94bSSven Van Asbroeck 	return 0;
32552e8a94bSSven Van Asbroeck }
32652e8a94bSSven Van Asbroeck 
32752e8a94bSSven Van Asbroeck /* stereo bypass with no AEC */
32852e8a94bSSven Van Asbroeck static const struct reg_sequence cp_config_stereo_bypass[] = {
32952e8a94bSSven Van Asbroeck 	/* interconnects must be programmed first */
33052e8a94bSSven Van Asbroeck 	{ 0x0210, 0x0005 },	/* DAC1   in <= I2S1-L */
33152e8a94bSSven Van Asbroeck 	{ 0x0212, 0x0006 },	/* DAC2   in <= I2S1-R */
33252e8a94bSSven Van Asbroeck 	{ 0x0214, 0x0001 },	/* I2S1-L in <= MIC1   */
33352e8a94bSSven Van Asbroeck 	{ 0x0216, 0x0001 },	/* I2S1-R in <= MIC1   */
33452e8a94bSSven Van Asbroeck 	{ 0x0224, 0x0000 },	/* AEC-S  in <= n/a    */
33552e8a94bSSven Van Asbroeck 	{ 0x0226, 0x0000 },	/* AEC-R  in <= n/a    */
33652e8a94bSSven Van Asbroeck 	/* output enables must be programmed next */
33752e8a94bSSven Van Asbroeck 	{ 0x0202, 0x000F },	/* enable I2S1 + DAC   */
33852e8a94bSSven Van Asbroeck };
33952e8a94bSSven Van Asbroeck 
34052e8a94bSSven Van Asbroeck static const struct snd_soc_dai_ops zl38_dai_ops = {
34152e8a94bSSven Van Asbroeck 	.set_fmt = zl38_set_fmt,
34252e8a94bSSven Van Asbroeck 	.hw_params = zl38_hw_params,
34352e8a94bSSven Van Asbroeck 	.hw_free = zl38_hw_free,
34452e8a94bSSven Van Asbroeck };
34552e8a94bSSven Van Asbroeck 
34652e8a94bSSven Van Asbroeck static struct snd_soc_dai_driver zl38_dai = {
34752e8a94bSSven Van Asbroeck 	.name = "zl38060-tdma",
34852e8a94bSSven Van Asbroeck 	.playback = {
34952e8a94bSSven Van Asbroeck 		.stream_name = "Playback",
35052e8a94bSSven Van Asbroeck 		.channels_min = 2,
35152e8a94bSSven Van Asbroeck 		.channels_max = 2,
35252e8a94bSSven Van Asbroeck 		.rates = ZL38_RATES,
35352e8a94bSSven Van Asbroeck 		.formats = ZL38_FORMATS,
35452e8a94bSSven Van Asbroeck 	},
35552e8a94bSSven Van Asbroeck 	.capture = {
35652e8a94bSSven Van Asbroeck 		.stream_name = "Capture",
35752e8a94bSSven Van Asbroeck 		.channels_min = 2,
35852e8a94bSSven Van Asbroeck 		.channels_max = 2,
35952e8a94bSSven Van Asbroeck 		.rates = ZL38_RATES,
36052e8a94bSSven Van Asbroeck 		.formats = ZL38_FORMATS,
36152e8a94bSSven Van Asbroeck 	},
36252e8a94bSSven Van Asbroeck 	.ops = &zl38_dai_ops,
363b33c088aSKuninori Morimoto 	.symmetric_rate = 1,
364b33c088aSKuninori Morimoto 	.symmetric_sample_bits = 1,
36552e8a94bSSven Van Asbroeck 	.symmetric_channels = 1,
36652e8a94bSSven Van Asbroeck };
36752e8a94bSSven Van Asbroeck 
36852e8a94bSSven Van Asbroeck static const struct snd_soc_dapm_widget zl38_dapm_widgets[] = {
36952e8a94bSSven Van Asbroeck 	SND_SOC_DAPM_OUTPUT("DAC1"),
37052e8a94bSSven Van Asbroeck 	SND_SOC_DAPM_OUTPUT("DAC2"),
37152e8a94bSSven Van Asbroeck 
37252e8a94bSSven Van Asbroeck 	SND_SOC_DAPM_INPUT("DMICL"),
37352e8a94bSSven Van Asbroeck };
37452e8a94bSSven Van Asbroeck 
37552e8a94bSSven Van Asbroeck static const struct snd_soc_dapm_route zl38_dapm_routes[] = {
37652e8a94bSSven Van Asbroeck 	{ "DAC1",  NULL, "Playback" },
37752e8a94bSSven Van Asbroeck 	{ "DAC2",  NULL, "Playback" },
37852e8a94bSSven Van Asbroeck 
37952e8a94bSSven Van Asbroeck 	{ "Capture",  NULL, "DMICL" },
38052e8a94bSSven Van Asbroeck };
38152e8a94bSSven Van Asbroeck 
38252e8a94bSSven Van Asbroeck static const struct snd_soc_component_driver zl38_component_dev = {
38352e8a94bSSven Van Asbroeck 	.dapm_widgets		= zl38_dapm_widgets,
38452e8a94bSSven Van Asbroeck 	.num_dapm_widgets	= ARRAY_SIZE(zl38_dapm_widgets),
38552e8a94bSSven Van Asbroeck 	.dapm_routes		= zl38_dapm_routes,
38652e8a94bSSven Van Asbroeck 	.num_dapm_routes	= ARRAY_SIZE(zl38_dapm_routes),
38752e8a94bSSven Van Asbroeck 	.endianness		= 1,
38852e8a94bSSven Van Asbroeck };
38952e8a94bSSven Van Asbroeck 
chip_gpio_set(struct gpio_chip * c,unsigned int offset,int val)39052e8a94bSSven Van Asbroeck static void chip_gpio_set(struct gpio_chip *c, unsigned int offset, int val)
39152e8a94bSSven Van Asbroeck {
39252e8a94bSSven Van Asbroeck 	struct regmap *regmap = gpiochip_get_data(c);
39352e8a94bSSven Van Asbroeck 	unsigned int mask = BIT(offset);
39452e8a94bSSven Van Asbroeck 
39552e8a94bSSven Van Asbroeck 	regmap_update_bits(regmap, REG_GPIO_DAT, mask, val ? mask : 0);
39652e8a94bSSven Van Asbroeck }
39752e8a94bSSven Van Asbroeck 
chip_gpio_get(struct gpio_chip * c,unsigned int offset)39852e8a94bSSven Van Asbroeck static int chip_gpio_get(struct gpio_chip *c, unsigned int offset)
39952e8a94bSSven Van Asbroeck {
40052e8a94bSSven Van Asbroeck 	struct regmap *regmap = gpiochip_get_data(c);
40152e8a94bSSven Van Asbroeck 	unsigned int mask = BIT(offset);
40252e8a94bSSven Van Asbroeck 	unsigned int val;
40352e8a94bSSven Van Asbroeck 	int err;
40452e8a94bSSven Van Asbroeck 
40552e8a94bSSven Van Asbroeck 	err = regmap_read(regmap, REG_GPIO_DAT, &val);
40652e8a94bSSven Van Asbroeck 	if (err)
40752e8a94bSSven Van Asbroeck 		return err;
40852e8a94bSSven Van Asbroeck 
40952e8a94bSSven Van Asbroeck 	return !!(val & mask);
41052e8a94bSSven Van Asbroeck }
41152e8a94bSSven Van Asbroeck 
chip_direction_input(struct gpio_chip * c,unsigned int offset)41252e8a94bSSven Van Asbroeck static int chip_direction_input(struct gpio_chip *c, unsigned int offset)
41352e8a94bSSven Van Asbroeck {
41452e8a94bSSven Van Asbroeck 	struct regmap *regmap = gpiochip_get_data(c);
41552e8a94bSSven Van Asbroeck 	unsigned int mask = BIT(offset);
41652e8a94bSSven Van Asbroeck 
41752e8a94bSSven Van Asbroeck 	return regmap_update_bits(regmap, REG_GPIO_DIR, mask, 0);
41852e8a94bSSven Van Asbroeck }
41952e8a94bSSven Van Asbroeck 
42052e8a94bSSven Van Asbroeck static int
chip_direction_output(struct gpio_chip * c,unsigned int offset,int val)42152e8a94bSSven Van Asbroeck chip_direction_output(struct gpio_chip *c, unsigned int offset, int val)
42252e8a94bSSven Van Asbroeck {
42352e8a94bSSven Van Asbroeck 	struct regmap *regmap = gpiochip_get_data(c);
42452e8a94bSSven Van Asbroeck 	unsigned int mask = BIT(offset);
42552e8a94bSSven Van Asbroeck 
42652e8a94bSSven Van Asbroeck 	chip_gpio_set(c, offset, val);
42752e8a94bSSven Van Asbroeck 	return regmap_update_bits(regmap, REG_GPIO_DIR, mask, mask);
42852e8a94bSSven Van Asbroeck }
42952e8a94bSSven Van Asbroeck 
43052e8a94bSSven Van Asbroeck static const struct gpio_chip template_chip = {
43152e8a94bSSven Van Asbroeck 	.owner = THIS_MODULE,
43252e8a94bSSven Van Asbroeck 	.label = DRV_NAME,
43352e8a94bSSven Van Asbroeck 
43452e8a94bSSven Van Asbroeck 	.base = -1,
43552e8a94bSSven Van Asbroeck 	.ngpio = 14,
43652e8a94bSSven Van Asbroeck 	.direction_input = chip_direction_input,
43752e8a94bSSven Van Asbroeck 	.direction_output = chip_direction_output,
43852e8a94bSSven Van Asbroeck 	.get = chip_gpio_get,
43952e8a94bSSven Van Asbroeck 	.set = chip_gpio_set,
44052e8a94bSSven Van Asbroeck 
44152e8a94bSSven Van Asbroeck 	.can_sleep = true,
44252e8a94bSSven Van Asbroeck };
44352e8a94bSSven Van Asbroeck 
zl38_check_revision(struct device * dev,struct regmap * regmap)44452e8a94bSSven Van Asbroeck static int zl38_check_revision(struct device *dev, struct regmap *regmap)
44552e8a94bSSven Van Asbroeck {
44652e8a94bSSven Van Asbroeck 	unsigned int hwrev, fwprod, fwrev;
44752e8a94bSSven Van Asbroeck 	int fw_major, fw_minor, fw_micro;
44852e8a94bSSven Van Asbroeck 	int err;
44952e8a94bSSven Van Asbroeck 
45052e8a94bSSven Van Asbroeck 	err = regmap_read(regmap, REG_HW_REV, &hwrev);
45152e8a94bSSven Van Asbroeck 	if (err)
45252e8a94bSSven Van Asbroeck 		return err;
45352e8a94bSSven Van Asbroeck 	err = regmap_read(regmap, REG_FW_PROD, &fwprod);
45452e8a94bSSven Van Asbroeck 	if (err)
45552e8a94bSSven Van Asbroeck 		return err;
45652e8a94bSSven Van Asbroeck 	err = regmap_read(regmap, REG_FW_REV, &fwrev);
45752e8a94bSSven Van Asbroeck 	if (err)
45852e8a94bSSven Van Asbroeck 		return err;
45952e8a94bSSven Van Asbroeck 
46052e8a94bSSven Van Asbroeck 	fw_major = (fwrev >> 12) & 0xF;
46152e8a94bSSven Van Asbroeck 	fw_minor = (fwrev >>  8) & 0xF;
46252e8a94bSSven Van Asbroeck 	fw_micro = fwrev & 0xFF;
46352e8a94bSSven Van Asbroeck 	dev_info(dev, "hw rev 0x%x, fw product code %d, firmware rev %d.%d.%d",
46452e8a94bSSven Van Asbroeck 		 hwrev & 0x1F, fwprod, fw_major, fw_minor, fw_micro);
46552e8a94bSSven Van Asbroeck 
46652e8a94bSSven Van Asbroeck 	if (fw_major != FIRMWARE_MAJOR || fw_minor < FIRMWARE_MINOR) {
46752e8a94bSSven Van Asbroeck 		dev_err(dev, "unsupported firmware. driver supports %d.%d",
46852e8a94bSSven Van Asbroeck 			FIRMWARE_MAJOR, FIRMWARE_MINOR);
46952e8a94bSSven Van Asbroeck 		return -EINVAL;
47052e8a94bSSven Van Asbroeck 	}
47152e8a94bSSven Van Asbroeck 
47252e8a94bSSven Van Asbroeck 	return 0;
47352e8a94bSSven Van Asbroeck }
47452e8a94bSSven Van Asbroeck 
zl38_bus_read(void * context,const void * reg_buf,size_t reg_size,void * val_buf,size_t val_size)47552e8a94bSSven Van Asbroeck static int zl38_bus_read(void *context,
47652e8a94bSSven Van Asbroeck 			 const void *reg_buf, size_t reg_size,
47752e8a94bSSven Van Asbroeck 			 void *val_buf, size_t val_size)
47852e8a94bSSven Van Asbroeck {
47952e8a94bSSven Van Asbroeck 	struct spi_device *spi = context;
48052e8a94bSSven Van Asbroeck 	const u8 *reg_buf8 = reg_buf;
48152e8a94bSSven Van Asbroeck 	size_t len = 0;
48252e8a94bSSven Van Asbroeck 	u8 offs, page;
48352e8a94bSSven Van Asbroeck 	u8 txbuf[4];
48452e8a94bSSven Van Asbroeck 
48552e8a94bSSven Van Asbroeck 	if (reg_size != 2 || val_size > ZL38_MAX_RAW_XFER)
48652e8a94bSSven Van Asbroeck 		return -EINVAL;
48752e8a94bSSven Van Asbroeck 
48852e8a94bSSven Van Asbroeck 	offs = reg_buf8[1] >> 1;
48952e8a94bSSven Van Asbroeck 	page = reg_buf8[0];
49052e8a94bSSven Van Asbroeck 
49152e8a94bSSven Van Asbroeck 	if (page) {
49252e8a94bSSven Van Asbroeck 		txbuf[len++] = 0xFE;
49352e8a94bSSven Van Asbroeck 		txbuf[len++] = page == HBI_FIRMWARE_PAGE ? 0xFF : page - 1;
49452e8a94bSSven Van Asbroeck 		txbuf[len++] = offs;
49552e8a94bSSven Van Asbroeck 		txbuf[len++] = val_size / 2 - 1;
49652e8a94bSSven Van Asbroeck 	} else {
49752e8a94bSSven Van Asbroeck 		txbuf[len++] = offs | 0x80;
49852e8a94bSSven Van Asbroeck 		txbuf[len++] = val_size / 2 - 1;
49952e8a94bSSven Van Asbroeck 	}
50052e8a94bSSven Van Asbroeck 
50152e8a94bSSven Van Asbroeck 	return spi_write_then_read(spi, txbuf, len, val_buf, val_size);
50252e8a94bSSven Van Asbroeck }
50352e8a94bSSven Van Asbroeck 
zl38_bus_write(void * context,const void * data,size_t count)50452e8a94bSSven Van Asbroeck static int zl38_bus_write(void *context, const void *data, size_t count)
50552e8a94bSSven Van Asbroeck {
50652e8a94bSSven Van Asbroeck 	struct spi_device *spi = context;
50752e8a94bSSven Van Asbroeck 	u8 buf[4 + ZL38_MAX_RAW_XFER];
50852e8a94bSSven Van Asbroeck 	size_t val_len, len = 0;
50952e8a94bSSven Van Asbroeck 	const u8 *data8 = data;
51052e8a94bSSven Van Asbroeck 	u8 offs, page;
51152e8a94bSSven Van Asbroeck 
51252e8a94bSSven Van Asbroeck 	if (count > (2 + ZL38_MAX_RAW_XFER) || count < 4)
51352e8a94bSSven Van Asbroeck 		return -EINVAL;
51452e8a94bSSven Van Asbroeck 	val_len = count - 2;
51552e8a94bSSven Van Asbroeck 	offs = data8[1] >> 1;
51652e8a94bSSven Van Asbroeck 	page = data8[0];
51752e8a94bSSven Van Asbroeck 
51852e8a94bSSven Van Asbroeck 	if (page) {
51952e8a94bSSven Van Asbroeck 		buf[len++] = 0xFE;
52052e8a94bSSven Van Asbroeck 		buf[len++] = page == HBI_FIRMWARE_PAGE ? 0xFF : page - 1;
52152e8a94bSSven Van Asbroeck 		buf[len++] = offs;
52252e8a94bSSven Van Asbroeck 		buf[len++] = (val_len / 2 - 1) | 0x80;
52352e8a94bSSven Van Asbroeck 	} else {
52452e8a94bSSven Van Asbroeck 		buf[len++] = offs | 0x80;
52552e8a94bSSven Van Asbroeck 		buf[len++] = (val_len / 2 - 1) | 0x80;
52652e8a94bSSven Van Asbroeck 	}
52752e8a94bSSven Van Asbroeck 	memcpy(buf + len, data8 + 2, val_len);
52852e8a94bSSven Van Asbroeck 	len += val_len;
52952e8a94bSSven Van Asbroeck 
53052e8a94bSSven Van Asbroeck 	return spi_write(spi, buf, len);
53152e8a94bSSven Van Asbroeck }
53252e8a94bSSven Van Asbroeck 
53352e8a94bSSven Van Asbroeck static const struct regmap_bus zl38_regmap_bus = {
53452e8a94bSSven Van Asbroeck 	.read = zl38_bus_read,
53552e8a94bSSven Van Asbroeck 	.write = zl38_bus_write,
53652e8a94bSSven Van Asbroeck 	.max_raw_write = ZL38_MAX_RAW_XFER,
53752e8a94bSSven Van Asbroeck 	.max_raw_read = ZL38_MAX_RAW_XFER,
53852e8a94bSSven Van Asbroeck };
53952e8a94bSSven Van Asbroeck 
54052e8a94bSSven Van Asbroeck static const struct regmap_config zl38_regmap_conf = {
54152e8a94bSSven Van Asbroeck 	.reg_bits = 16,
54252e8a94bSSven Van Asbroeck 	.val_bits = 16,
54352e8a94bSSven Van Asbroeck 	.reg_stride = 2,
54452e8a94bSSven Van Asbroeck 	.use_single_read = true,
54552e8a94bSSven Van Asbroeck 	.use_single_write = true,
54652e8a94bSSven Van Asbroeck };
54752e8a94bSSven Van Asbroeck 
zl38_spi_probe(struct spi_device * spi)54852e8a94bSSven Van Asbroeck static int zl38_spi_probe(struct spi_device *spi)
54952e8a94bSSven Van Asbroeck {
55052e8a94bSSven Van Asbroeck 	struct device *dev = &spi->dev;
55152e8a94bSSven Van Asbroeck 	struct zl38_codec_priv *priv;
55252e8a94bSSven Van Asbroeck 	struct gpio_desc *reset_gpio;
55352e8a94bSSven Van Asbroeck 	int err;
55452e8a94bSSven Van Asbroeck 
55552e8a94bSSven Van Asbroeck 	/* get the chip to a known state by putting it in reset */
55652e8a94bSSven Van Asbroeck 	reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
55752e8a94bSSven Van Asbroeck 	if (IS_ERR(reset_gpio))
55852e8a94bSSven Van Asbroeck 		return PTR_ERR(reset_gpio);
55952e8a94bSSven Van Asbroeck 	if (reset_gpio) {
56052e8a94bSSven Van Asbroeck 		/* datasheet: need > 10us for a digital + analog reset */
56152e8a94bSSven Van Asbroeck 		usleep_range(15, 50);
56252e8a94bSSven Van Asbroeck 		/* take the chip out of reset */
56352e8a94bSSven Van Asbroeck 		gpiod_set_value_cansleep(reset_gpio, 0);
56452e8a94bSSven Van Asbroeck 		/* datasheet: need > 3ms for digital section to become stable */
56552e8a94bSSven Van Asbroeck 		usleep_range(3000, 10000);
56652e8a94bSSven Van Asbroeck 	}
56752e8a94bSSven Van Asbroeck 
56852e8a94bSSven Van Asbroeck 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
56952e8a94bSSven Van Asbroeck 	if (!priv)
57052e8a94bSSven Van Asbroeck 		return -ENOMEM;
57152e8a94bSSven Van Asbroeck 
57252e8a94bSSven Van Asbroeck 	priv->dev = dev;
57352e8a94bSSven Van Asbroeck 	dev_set_drvdata(dev, priv);
57452e8a94bSSven Van Asbroeck 	priv->regmap = devm_regmap_init(dev, &zl38_regmap_bus, spi,
57552e8a94bSSven Van Asbroeck 					&zl38_regmap_conf);
57652e8a94bSSven Van Asbroeck 	if (IS_ERR(priv->regmap))
57752e8a94bSSven Van Asbroeck 		return PTR_ERR(priv->regmap);
57852e8a94bSSven Van Asbroeck 
57952e8a94bSSven Van Asbroeck 	err = zl38_load_firmware(dev, priv->regmap);
58052e8a94bSSven Van Asbroeck 	if (err)
58152e8a94bSSven Van Asbroeck 		return err;
58252e8a94bSSven Van Asbroeck 
58352e8a94bSSven Van Asbroeck 	err = zl38_check_revision(dev, priv->regmap);
58452e8a94bSSven Van Asbroeck 	if (err)
58552e8a94bSSven Van Asbroeck 		return err;
58652e8a94bSSven Van Asbroeck 
58752e8a94bSSven Van Asbroeck 	priv->gpio_chip = devm_kmemdup(dev, &template_chip,
58852e8a94bSSven Van Asbroeck 				       sizeof(template_chip), GFP_KERNEL);
58952e8a94bSSven Van Asbroeck 	if (!priv->gpio_chip)
59052e8a94bSSven Van Asbroeck 		return -ENOMEM;
591766cc7f1SAndy Shevchenko 	priv->gpio_chip->parent = dev;
59252e8a94bSSven Van Asbroeck 	err = devm_gpiochip_add_data(dev, priv->gpio_chip, priv->regmap);
59352e8a94bSSven Van Asbroeck 	if (err)
59452e8a94bSSven Van Asbroeck 		return err;
59552e8a94bSSven Van Asbroeck 
59652e8a94bSSven Van Asbroeck 	/* setup the cross-point switch for stereo bypass */
59752e8a94bSSven Van Asbroeck 	err = regmap_multi_reg_write(priv->regmap, cp_config_stereo_bypass,
59852e8a94bSSven Van Asbroeck 				     ARRAY_SIZE(cp_config_stereo_bypass));
59952e8a94bSSven Van Asbroeck 	if (err)
60052e8a94bSSven Van Asbroeck 		return err;
60152e8a94bSSven Van Asbroeck 	/* setup for 12MHz crystal connected to the chip */
60252e8a94bSSven Van Asbroeck 	err = regmap_update_bits(priv->regmap, REG_CLK_CFG, CLK_CFG_SOURCE_XTAL,
60352e8a94bSSven Van Asbroeck 				 CLK_CFG_SOURCE_XTAL);
60452e8a94bSSven Van Asbroeck 	if (err)
60552e8a94bSSven Van Asbroeck 		return err;
60652e8a94bSSven Van Asbroeck 
60752e8a94bSSven Van Asbroeck 	return devm_snd_soc_register_component(dev, &zl38_component_dev,
60852e8a94bSSven Van Asbroeck 					       &zl38_dai, 1);
60952e8a94bSSven Van Asbroeck }
61052e8a94bSSven Van Asbroeck 
611*da1f2282SKrzysztof Kozlowski static const struct of_device_id zl38_dt_ids[] __maybe_unused = {
61252e8a94bSSven Van Asbroeck 	{ .compatible = "mscc,zl38060", },
61352e8a94bSSven Van Asbroeck 	{ /* sentinel */ }
61452e8a94bSSven Van Asbroeck };
61552e8a94bSSven Van Asbroeck MODULE_DEVICE_TABLE(of, zl38_dt_ids);
61652e8a94bSSven Van Asbroeck 
61752e8a94bSSven Van Asbroeck static const struct spi_device_id zl38_spi_ids[] = {
61852e8a94bSSven Van Asbroeck 	{ "zl38060", 0 },
61952e8a94bSSven Van Asbroeck 	{ /* sentinel */ }
62052e8a94bSSven Van Asbroeck };
62152e8a94bSSven Van Asbroeck MODULE_DEVICE_TABLE(spi, zl38_spi_ids);
62252e8a94bSSven Van Asbroeck 
62352e8a94bSSven Van Asbroeck static struct spi_driver zl38060_spi_driver = {
62452e8a94bSSven Van Asbroeck 	.driver	= {
62552e8a94bSSven Van Asbroeck 		.name = DRV_NAME,
62652e8a94bSSven Van Asbroeck 		.of_match_table = of_match_ptr(zl38_dt_ids),
62752e8a94bSSven Van Asbroeck 	},
62852e8a94bSSven Van Asbroeck 	.probe = zl38_spi_probe,
62952e8a94bSSven Van Asbroeck 	.id_table = zl38_spi_ids,
63052e8a94bSSven Van Asbroeck };
63152e8a94bSSven Van Asbroeck module_spi_driver(zl38060_spi_driver);
63252e8a94bSSven Van Asbroeck 
63352e8a94bSSven Van Asbroeck MODULE_DESCRIPTION("ASoC ZL38060 driver");
63452e8a94bSSven Van Asbroeck MODULE_AUTHOR("Sven Van Asbroeck <TheSven73@gmail.com>");
63552e8a94bSSven Van Asbroeck MODULE_LICENSE("GPL v2");
636