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 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 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 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 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 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 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 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 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 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 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 .non_legacy_dai_naming = 1, 38952e8a94bSSven Van Asbroeck }; 39052e8a94bSSven Van Asbroeck 39152e8a94bSSven Van Asbroeck static void chip_gpio_set(struct gpio_chip *c, unsigned int offset, int val) 39252e8a94bSSven Van Asbroeck { 39352e8a94bSSven Van Asbroeck struct regmap *regmap = gpiochip_get_data(c); 39452e8a94bSSven Van Asbroeck unsigned int mask = BIT(offset); 39552e8a94bSSven Van Asbroeck 39652e8a94bSSven Van Asbroeck regmap_update_bits(regmap, REG_GPIO_DAT, mask, val ? mask : 0); 39752e8a94bSSven Van Asbroeck } 39852e8a94bSSven Van Asbroeck 39952e8a94bSSven Van Asbroeck static int chip_gpio_get(struct gpio_chip *c, unsigned int offset) 40052e8a94bSSven Van Asbroeck { 40152e8a94bSSven Van Asbroeck struct regmap *regmap = gpiochip_get_data(c); 40252e8a94bSSven Van Asbroeck unsigned int mask = BIT(offset); 40352e8a94bSSven Van Asbroeck unsigned int val; 40452e8a94bSSven Van Asbroeck int err; 40552e8a94bSSven Van Asbroeck 40652e8a94bSSven Van Asbroeck err = regmap_read(regmap, REG_GPIO_DAT, &val); 40752e8a94bSSven Van Asbroeck if (err) 40852e8a94bSSven Van Asbroeck return err; 40952e8a94bSSven Van Asbroeck 41052e8a94bSSven Van Asbroeck return !!(val & mask); 41152e8a94bSSven Van Asbroeck } 41252e8a94bSSven Van Asbroeck 41352e8a94bSSven Van Asbroeck static int chip_direction_input(struct gpio_chip *c, unsigned int offset) 41452e8a94bSSven Van Asbroeck { 41552e8a94bSSven Van Asbroeck struct regmap *regmap = gpiochip_get_data(c); 41652e8a94bSSven Van Asbroeck unsigned int mask = BIT(offset); 41752e8a94bSSven Van Asbroeck 41852e8a94bSSven Van Asbroeck return regmap_update_bits(regmap, REG_GPIO_DIR, mask, 0); 41952e8a94bSSven Van Asbroeck } 42052e8a94bSSven Van Asbroeck 42152e8a94bSSven Van Asbroeck static int 42252e8a94bSSven Van Asbroeck chip_direction_output(struct gpio_chip *c, unsigned int offset, int val) 42352e8a94bSSven Van Asbroeck { 42452e8a94bSSven Van Asbroeck struct regmap *regmap = gpiochip_get_data(c); 42552e8a94bSSven Van Asbroeck unsigned int mask = BIT(offset); 42652e8a94bSSven Van Asbroeck 42752e8a94bSSven Van Asbroeck chip_gpio_set(c, offset, val); 42852e8a94bSSven Van Asbroeck return regmap_update_bits(regmap, REG_GPIO_DIR, mask, mask); 42952e8a94bSSven Van Asbroeck } 43052e8a94bSSven Van Asbroeck 43152e8a94bSSven Van Asbroeck static const struct gpio_chip template_chip = { 43252e8a94bSSven Van Asbroeck .owner = THIS_MODULE, 43352e8a94bSSven Van Asbroeck .label = DRV_NAME, 43452e8a94bSSven Van Asbroeck 43552e8a94bSSven Van Asbroeck .base = -1, 43652e8a94bSSven Van Asbroeck .ngpio = 14, 43752e8a94bSSven Van Asbroeck .direction_input = chip_direction_input, 43852e8a94bSSven Van Asbroeck .direction_output = chip_direction_output, 43952e8a94bSSven Van Asbroeck .get = chip_gpio_get, 44052e8a94bSSven Van Asbroeck .set = chip_gpio_set, 44152e8a94bSSven Van Asbroeck 44252e8a94bSSven Van Asbroeck .can_sleep = true, 44352e8a94bSSven Van Asbroeck }; 44452e8a94bSSven Van Asbroeck 44552e8a94bSSven Van Asbroeck static int zl38_check_revision(struct device *dev, struct regmap *regmap) 44652e8a94bSSven Van Asbroeck { 44752e8a94bSSven Van Asbroeck unsigned int hwrev, fwprod, fwrev; 44852e8a94bSSven Van Asbroeck int fw_major, fw_minor, fw_micro; 44952e8a94bSSven Van Asbroeck int err; 45052e8a94bSSven Van Asbroeck 45152e8a94bSSven Van Asbroeck err = regmap_read(regmap, REG_HW_REV, &hwrev); 45252e8a94bSSven Van Asbroeck if (err) 45352e8a94bSSven Van Asbroeck return err; 45452e8a94bSSven Van Asbroeck err = regmap_read(regmap, REG_FW_PROD, &fwprod); 45552e8a94bSSven Van Asbroeck if (err) 45652e8a94bSSven Van Asbroeck return err; 45752e8a94bSSven Van Asbroeck err = regmap_read(regmap, REG_FW_REV, &fwrev); 45852e8a94bSSven Van Asbroeck if (err) 45952e8a94bSSven Van Asbroeck return err; 46052e8a94bSSven Van Asbroeck 46152e8a94bSSven Van Asbroeck fw_major = (fwrev >> 12) & 0xF; 46252e8a94bSSven Van Asbroeck fw_minor = (fwrev >> 8) & 0xF; 46352e8a94bSSven Van Asbroeck fw_micro = fwrev & 0xFF; 46452e8a94bSSven Van Asbroeck dev_info(dev, "hw rev 0x%x, fw product code %d, firmware rev %d.%d.%d", 46552e8a94bSSven Van Asbroeck hwrev & 0x1F, fwprod, fw_major, fw_minor, fw_micro); 46652e8a94bSSven Van Asbroeck 46752e8a94bSSven Van Asbroeck if (fw_major != FIRMWARE_MAJOR || fw_minor < FIRMWARE_MINOR) { 46852e8a94bSSven Van Asbroeck dev_err(dev, "unsupported firmware. driver supports %d.%d", 46952e8a94bSSven Van Asbroeck FIRMWARE_MAJOR, FIRMWARE_MINOR); 47052e8a94bSSven Van Asbroeck return -EINVAL; 47152e8a94bSSven Van Asbroeck } 47252e8a94bSSven Van Asbroeck 47352e8a94bSSven Van Asbroeck return 0; 47452e8a94bSSven Van Asbroeck } 47552e8a94bSSven Van Asbroeck 47652e8a94bSSven Van Asbroeck static int zl38_bus_read(void *context, 47752e8a94bSSven Van Asbroeck const void *reg_buf, size_t reg_size, 47852e8a94bSSven Van Asbroeck void *val_buf, size_t val_size) 47952e8a94bSSven Van Asbroeck { 48052e8a94bSSven Van Asbroeck struct spi_device *spi = context; 48152e8a94bSSven Van Asbroeck const u8 *reg_buf8 = reg_buf; 48252e8a94bSSven Van Asbroeck size_t len = 0; 48352e8a94bSSven Van Asbroeck u8 offs, page; 48452e8a94bSSven Van Asbroeck u8 txbuf[4]; 48552e8a94bSSven Van Asbroeck 48652e8a94bSSven Van Asbroeck if (reg_size != 2 || val_size > ZL38_MAX_RAW_XFER) 48752e8a94bSSven Van Asbroeck return -EINVAL; 48852e8a94bSSven Van Asbroeck 48952e8a94bSSven Van Asbroeck offs = reg_buf8[1] >> 1; 49052e8a94bSSven Van Asbroeck page = reg_buf8[0]; 49152e8a94bSSven Van Asbroeck 49252e8a94bSSven Van Asbroeck if (page) { 49352e8a94bSSven Van Asbroeck txbuf[len++] = 0xFE; 49452e8a94bSSven Van Asbroeck txbuf[len++] = page == HBI_FIRMWARE_PAGE ? 0xFF : page - 1; 49552e8a94bSSven Van Asbroeck txbuf[len++] = offs; 49652e8a94bSSven Van Asbroeck txbuf[len++] = val_size / 2 - 1; 49752e8a94bSSven Van Asbroeck } else { 49852e8a94bSSven Van Asbroeck txbuf[len++] = offs | 0x80; 49952e8a94bSSven Van Asbroeck txbuf[len++] = val_size / 2 - 1; 50052e8a94bSSven Van Asbroeck } 50152e8a94bSSven Van Asbroeck 50252e8a94bSSven Van Asbroeck return spi_write_then_read(spi, txbuf, len, val_buf, val_size); 50352e8a94bSSven Van Asbroeck } 50452e8a94bSSven Van Asbroeck 50552e8a94bSSven Van Asbroeck static int zl38_bus_write(void *context, const void *data, size_t count) 50652e8a94bSSven Van Asbroeck { 50752e8a94bSSven Van Asbroeck struct spi_device *spi = context; 50852e8a94bSSven Van Asbroeck u8 buf[4 + ZL38_MAX_RAW_XFER]; 50952e8a94bSSven Van Asbroeck size_t val_len, len = 0; 51052e8a94bSSven Van Asbroeck const u8 *data8 = data; 51152e8a94bSSven Van Asbroeck u8 offs, page; 51252e8a94bSSven Van Asbroeck 51352e8a94bSSven Van Asbroeck if (count > (2 + ZL38_MAX_RAW_XFER) || count < 4) 51452e8a94bSSven Van Asbroeck return -EINVAL; 51552e8a94bSSven Van Asbroeck val_len = count - 2; 51652e8a94bSSven Van Asbroeck offs = data8[1] >> 1; 51752e8a94bSSven Van Asbroeck page = data8[0]; 51852e8a94bSSven Van Asbroeck 51952e8a94bSSven Van Asbroeck if (page) { 52052e8a94bSSven Van Asbroeck buf[len++] = 0xFE; 52152e8a94bSSven Van Asbroeck buf[len++] = page == HBI_FIRMWARE_PAGE ? 0xFF : page - 1; 52252e8a94bSSven Van Asbroeck buf[len++] = offs; 52352e8a94bSSven Van Asbroeck buf[len++] = (val_len / 2 - 1) | 0x80; 52452e8a94bSSven Van Asbroeck } else { 52552e8a94bSSven Van Asbroeck buf[len++] = offs | 0x80; 52652e8a94bSSven Van Asbroeck buf[len++] = (val_len / 2 - 1) | 0x80; 52752e8a94bSSven Van Asbroeck } 52852e8a94bSSven Van Asbroeck memcpy(buf + len, data8 + 2, val_len); 52952e8a94bSSven Van Asbroeck len += val_len; 53052e8a94bSSven Van Asbroeck 53152e8a94bSSven Van Asbroeck return spi_write(spi, buf, len); 53252e8a94bSSven Van Asbroeck } 53352e8a94bSSven Van Asbroeck 53452e8a94bSSven Van Asbroeck static const struct regmap_bus zl38_regmap_bus = { 53552e8a94bSSven Van Asbroeck .read = zl38_bus_read, 53652e8a94bSSven Van Asbroeck .write = zl38_bus_write, 53752e8a94bSSven Van Asbroeck .max_raw_write = ZL38_MAX_RAW_XFER, 53852e8a94bSSven Van Asbroeck .max_raw_read = ZL38_MAX_RAW_XFER, 53952e8a94bSSven Van Asbroeck }; 54052e8a94bSSven Van Asbroeck 54152e8a94bSSven Van Asbroeck static const struct regmap_config zl38_regmap_conf = { 54252e8a94bSSven Van Asbroeck .reg_bits = 16, 54352e8a94bSSven Van Asbroeck .val_bits = 16, 54452e8a94bSSven Van Asbroeck .reg_stride = 2, 54552e8a94bSSven Van Asbroeck .use_single_read = true, 54652e8a94bSSven Van Asbroeck .use_single_write = true, 54752e8a94bSSven Van Asbroeck }; 54852e8a94bSSven Van Asbroeck 54952e8a94bSSven Van Asbroeck static int zl38_spi_probe(struct spi_device *spi) 55052e8a94bSSven Van Asbroeck { 55152e8a94bSSven Van Asbroeck struct device *dev = &spi->dev; 55252e8a94bSSven Van Asbroeck struct zl38_codec_priv *priv; 55352e8a94bSSven Van Asbroeck struct gpio_desc *reset_gpio; 55452e8a94bSSven Van Asbroeck int err; 55552e8a94bSSven Van Asbroeck 55652e8a94bSSven Van Asbroeck /* get the chip to a known state by putting it in reset */ 55752e8a94bSSven Van Asbroeck reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); 55852e8a94bSSven Van Asbroeck if (IS_ERR(reset_gpio)) 55952e8a94bSSven Van Asbroeck return PTR_ERR(reset_gpio); 56052e8a94bSSven Van Asbroeck if (reset_gpio) { 56152e8a94bSSven Van Asbroeck /* datasheet: need > 10us for a digital + analog reset */ 56252e8a94bSSven Van Asbroeck usleep_range(15, 50); 56352e8a94bSSven Van Asbroeck /* take the chip out of reset */ 56452e8a94bSSven Van Asbroeck gpiod_set_value_cansleep(reset_gpio, 0); 56552e8a94bSSven Van Asbroeck /* datasheet: need > 3ms for digital section to become stable */ 56652e8a94bSSven Van Asbroeck usleep_range(3000, 10000); 56752e8a94bSSven Van Asbroeck } 56852e8a94bSSven Van Asbroeck 56952e8a94bSSven Van Asbroeck priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 57052e8a94bSSven Van Asbroeck if (!priv) 57152e8a94bSSven Van Asbroeck return -ENOMEM; 57252e8a94bSSven Van Asbroeck 57352e8a94bSSven Van Asbroeck priv->dev = dev; 57452e8a94bSSven Van Asbroeck dev_set_drvdata(dev, priv); 57552e8a94bSSven Van Asbroeck priv->regmap = devm_regmap_init(dev, &zl38_regmap_bus, spi, 57652e8a94bSSven Van Asbroeck &zl38_regmap_conf); 57752e8a94bSSven Van Asbroeck if (IS_ERR(priv->regmap)) 57852e8a94bSSven Van Asbroeck return PTR_ERR(priv->regmap); 57952e8a94bSSven Van Asbroeck 58052e8a94bSSven Van Asbroeck err = zl38_load_firmware(dev, priv->regmap); 58152e8a94bSSven Van Asbroeck if (err) 58252e8a94bSSven Van Asbroeck return err; 58352e8a94bSSven Van Asbroeck 58452e8a94bSSven Van Asbroeck err = zl38_check_revision(dev, priv->regmap); 58552e8a94bSSven Van Asbroeck if (err) 58652e8a94bSSven Van Asbroeck return err; 58752e8a94bSSven Van Asbroeck 58852e8a94bSSven Van Asbroeck priv->gpio_chip = devm_kmemdup(dev, &template_chip, 58952e8a94bSSven Van Asbroeck sizeof(template_chip), GFP_KERNEL); 59052e8a94bSSven Van Asbroeck if (!priv->gpio_chip) 59152e8a94bSSven Van Asbroeck return -ENOMEM; 592*766cc7f1SAndy Shevchenko priv->gpio_chip->parent = dev; 59352e8a94bSSven Van Asbroeck err = devm_gpiochip_add_data(dev, priv->gpio_chip, priv->regmap); 59452e8a94bSSven Van Asbroeck if (err) 59552e8a94bSSven Van Asbroeck return err; 59652e8a94bSSven Van Asbroeck 59752e8a94bSSven Van Asbroeck /* setup the cross-point switch for stereo bypass */ 59852e8a94bSSven Van Asbroeck err = regmap_multi_reg_write(priv->regmap, cp_config_stereo_bypass, 59952e8a94bSSven Van Asbroeck ARRAY_SIZE(cp_config_stereo_bypass)); 60052e8a94bSSven Van Asbroeck if (err) 60152e8a94bSSven Van Asbroeck return err; 60252e8a94bSSven Van Asbroeck /* setup for 12MHz crystal connected to the chip */ 60352e8a94bSSven Van Asbroeck err = regmap_update_bits(priv->regmap, REG_CLK_CFG, CLK_CFG_SOURCE_XTAL, 60452e8a94bSSven Van Asbroeck CLK_CFG_SOURCE_XTAL); 60552e8a94bSSven Van Asbroeck if (err) 60652e8a94bSSven Van Asbroeck return err; 60752e8a94bSSven Van Asbroeck 60852e8a94bSSven Van Asbroeck return devm_snd_soc_register_component(dev, &zl38_component_dev, 60952e8a94bSSven Van Asbroeck &zl38_dai, 1); 61052e8a94bSSven Van Asbroeck } 61152e8a94bSSven Van Asbroeck 61252e8a94bSSven Van Asbroeck static const struct of_device_id zl38_dt_ids[] = { 61352e8a94bSSven Van Asbroeck { .compatible = "mscc,zl38060", }, 61452e8a94bSSven Van Asbroeck { /* sentinel */ } 61552e8a94bSSven Van Asbroeck }; 61652e8a94bSSven Van Asbroeck MODULE_DEVICE_TABLE(of, zl38_dt_ids); 61752e8a94bSSven Van Asbroeck 61852e8a94bSSven Van Asbroeck static const struct spi_device_id zl38_spi_ids[] = { 61952e8a94bSSven Van Asbroeck { "zl38060", 0 }, 62052e8a94bSSven Van Asbroeck { /* sentinel */ } 62152e8a94bSSven Van Asbroeck }; 62252e8a94bSSven Van Asbroeck MODULE_DEVICE_TABLE(spi, zl38_spi_ids); 62352e8a94bSSven Van Asbroeck 62452e8a94bSSven Van Asbroeck static struct spi_driver zl38060_spi_driver = { 62552e8a94bSSven Van Asbroeck .driver = { 62652e8a94bSSven Van Asbroeck .name = DRV_NAME, 62752e8a94bSSven Van Asbroeck .of_match_table = of_match_ptr(zl38_dt_ids), 62852e8a94bSSven Van Asbroeck }, 62952e8a94bSSven Van Asbroeck .probe = zl38_spi_probe, 63052e8a94bSSven Van Asbroeck .id_table = zl38_spi_ids, 63152e8a94bSSven Van Asbroeck }; 63252e8a94bSSven Van Asbroeck module_spi_driver(zl38060_spi_driver); 63352e8a94bSSven Van Asbroeck 63452e8a94bSSven Van Asbroeck MODULE_DESCRIPTION("ASoC ZL38060 driver"); 63552e8a94bSSven Van Asbroeck MODULE_AUTHOR("Sven Van Asbroeck <TheSven73@gmail.com>"); 63652e8a94bSSven Van Asbroeck MODULE_LICENSE("GPL v2"); 637