Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 //
0003 // Codec driver for Microsemi ZL38060 Connected Home Audio Processor.
0004 //
0005 // Copyright(c) 2020 Sven Van Asbroeck
0006 
0007 // The ZL38060 is very flexible and configurable. This driver implements only a
0008 // tiny subset of the chip's possible configurations:
0009 //
0010 // - DSP block bypassed: DAI        routed straight to DACs
0011 //                       microphone routed straight to DAI
0012 // - chip's internal clock is driven by a 12 MHz external crystal
0013 // - chip's DAI connected to CPU is I2S, and bit + frame clock master
0014 // - chip must be strapped for "host boot": in this mode, firmware will be
0015 //   provided by this driver.
0016 
0017 #include <linux/gpio/consumer.h>
0018 #include <linux/gpio/driver.h>
0019 #include <linux/property.h>
0020 #include <linux/spi/spi.h>
0021 #include <linux/regmap.h>
0022 #include <linux/module.h>
0023 #include <linux/ihex.h>
0024 
0025 #include <sound/pcm_params.h>
0026 #include <sound/core.h>
0027 #include <sound/pcm.h>
0028 #include <sound/soc.h>
0029 
0030 #define DRV_NAME        "zl38060"
0031 
0032 #define ZL38_RATES      (SNDRV_PCM_RATE_8000  |\
0033                 SNDRV_PCM_RATE_16000 |\
0034                 SNDRV_PCM_RATE_48000)
0035 #define ZL38_FORMATS        SNDRV_PCM_FMTBIT_S16_LE
0036 
0037 #define HBI_FIRMWARE_PAGE   0xFF
0038 #define ZL38_MAX_RAW_XFER   0x100
0039 
0040 #define REG_TDMA_CFG_CLK    0x0262
0041 #define CFG_CLK_PCLK_SHIFT  4
0042 #define CFG_CLK_PCLK_MASK   (0x7ff << CFG_CLK_PCLK_SHIFT)
0043 #define CFG_CLK_PCLK(bits)  ((bits - 1) << CFG_CLK_PCLK_SHIFT)
0044 #define CFG_CLK_MASTER      BIT(15)
0045 #define CFG_CLK_FSRATE_MASK 0x7
0046 #define CFG_CLK_FSRATE_8KHZ 0x1
0047 #define CFG_CLK_FSRATE_16KHZ    0x2
0048 #define CFG_CLK_FSRATE_48KHZ    0x6
0049 
0050 #define REG_CLK_CFG     0x0016
0051 #define CLK_CFG_SOURCE_XTAL BIT(15)
0052 
0053 #define REG_CLK_STATUS      0x0014
0054 #define CLK_STATUS_HWRST    BIT(0)
0055 
0056 #define REG_PARAM_RESULT    0x0034
0057 #define PARAM_RESULT_READY  0xD3D3
0058 
0059 #define REG_PG255_BASE_HI   0x000C
0060 #define REG_PG255_OFFS(addr)    ((HBI_FIRMWARE_PAGE << 8) | (addr & 0xFF))
0061 #define REG_FWR_EXEC        0x012C
0062 
0063 #define REG_CMD         0x0032
0064 #define REG_HW_REV      0x0020
0065 #define REG_FW_PROD     0x0022
0066 #define REG_FW_REV      0x0024
0067 
0068 #define REG_SEMA_FLAGS      0x0006
0069 #define SEMA_FLAGS_BOOT_CMD BIT(0)
0070 #define SEMA_FLAGS_APP_REBOOT   BIT(1)
0071 
0072 #define REG_HW_REV      0x0020
0073 #define REG_FW_PROD     0x0022
0074 #define REG_FW_REV      0x0024
0075 #define REG_GPIO_DIR        0x02DC
0076 #define REG_GPIO_DAT        0x02DA
0077 
0078 #define BOOTCMD_LOAD_COMPLETE   0x000D
0079 #define BOOTCMD_FW_GO       0x0008
0080 
0081 #define FIRMWARE_MAJOR      2
0082 #define FIRMWARE_MINOR      2
0083 
0084 struct zl38_codec_priv {
0085     struct device *dev;
0086     struct regmap *regmap;
0087     bool is_stream_in_use[2];
0088     struct gpio_chip *gpio_chip;
0089 };
0090 
0091 static int zl38_fw_issue_command(struct regmap *regmap, u16 cmd)
0092 {
0093     unsigned int val;
0094     int err;
0095 
0096     err = regmap_read_poll_timeout(regmap, REG_SEMA_FLAGS, val,
0097                        !(val & SEMA_FLAGS_BOOT_CMD), 10000,
0098                        10000 * 100);
0099     if (err)
0100         return err;
0101     err = regmap_write(regmap, REG_CMD, cmd);
0102     if (err)
0103         return err;
0104     err = regmap_update_bits(regmap, REG_SEMA_FLAGS, SEMA_FLAGS_BOOT_CMD,
0105                  SEMA_FLAGS_BOOT_CMD);
0106     if (err)
0107         return err;
0108 
0109     return regmap_read_poll_timeout(regmap, REG_CMD, val, !val, 10000,
0110                     10000 * 100);
0111 }
0112 
0113 static int zl38_fw_go(struct regmap *regmap)
0114 {
0115     int err;
0116 
0117     err = zl38_fw_issue_command(regmap, BOOTCMD_LOAD_COMPLETE);
0118     if (err)
0119         return err;
0120 
0121     return zl38_fw_issue_command(regmap, BOOTCMD_FW_GO);
0122 }
0123 
0124 static int zl38_fw_enter_boot_mode(struct regmap *regmap)
0125 {
0126     unsigned int val;
0127     int err;
0128 
0129     err = regmap_update_bits(regmap, REG_CLK_STATUS, CLK_STATUS_HWRST,
0130                  CLK_STATUS_HWRST);
0131     if (err)
0132         return err;
0133 
0134     return regmap_read_poll_timeout(regmap, REG_PARAM_RESULT, val,
0135                     val == PARAM_RESULT_READY, 1000, 50000);
0136 }
0137 
0138 static int
0139 zl38_fw_send_data(struct regmap *regmap, u32 addr, const void *data, u16 len)
0140 {
0141     __be32 addr_base = cpu_to_be32(addr & ~0xFF);
0142     int err;
0143 
0144     err = regmap_raw_write(regmap, REG_PG255_BASE_HI, &addr_base,
0145                    sizeof(addr_base));
0146     if (err)
0147         return err;
0148     return regmap_raw_write(regmap, REG_PG255_OFFS(addr), data, len);
0149 }
0150 
0151 static int zl38_fw_send_xaddr(struct regmap *regmap, const void *data)
0152 {
0153     /* execution address from ihex: 32-bit little endian.
0154      * device register expects 32-bit big endian.
0155      */
0156     u32 addr = le32_to_cpup(data);
0157     __be32 baddr = cpu_to_be32(addr);
0158 
0159     return regmap_raw_write(regmap, REG_FWR_EXEC, &baddr, sizeof(baddr));
0160 }
0161 
0162 static int zl38_load_firmware(struct device *dev, struct regmap *regmap)
0163 {
0164     const struct ihex_binrec *rec;
0165     const struct firmware *fw;
0166     u32 addr;
0167     u16 len;
0168     int err;
0169 
0170     /* how to get this firmware:
0171      * 1. request and download chip firmware from Microsemi
0172      *    (provided by Microsemi in srec format)
0173      * 2. convert downloaded firmware from srec to ihex. Simple tool:
0174      *    https://gitlab.com/TheSven73/s3-to-irec
0175      * 3. convert ihex to binary (.fw) using ihex2fw tool which is included
0176      *    with the Linux kernel sources
0177      */
0178     err = request_ihex_firmware(&fw, "zl38060.fw", dev);
0179     if (err)
0180         return err;
0181     err = zl38_fw_enter_boot_mode(regmap);
0182     if (err)
0183         goto out;
0184     rec = (const struct ihex_binrec *)fw->data;
0185     while (rec) {
0186         addr = be32_to_cpu(rec->addr);
0187         len = be16_to_cpu(rec->len);
0188         if (addr) {
0189             /* regular data ihex record */
0190             err = zl38_fw_send_data(regmap, addr, rec->data, len);
0191         } else if (len == 4) {
0192             /* execution address ihex record */
0193             err = zl38_fw_send_xaddr(regmap, rec->data);
0194         } else {
0195             err = -EINVAL;
0196         }
0197         if (err)
0198             goto out;
0199         /* next ! */
0200         rec = ihex_next_binrec(rec);
0201     }
0202     err = zl38_fw_go(regmap);
0203 
0204 out:
0205     release_firmware(fw);
0206     return err;
0207 }
0208 
0209 
0210 static int zl38_software_reset(struct regmap *regmap)
0211 {
0212     unsigned int val;
0213     int err;
0214 
0215     err = regmap_update_bits(regmap, REG_SEMA_FLAGS, SEMA_FLAGS_APP_REBOOT,
0216                  SEMA_FLAGS_APP_REBOOT);
0217     if (err)
0218         return err;
0219 
0220     /* wait for host bus interface to settle.
0221      * Not sure if this is required: Microsemi's vendor driver does this,
0222      * but the firmware manual does not mention it. Leave it in, there's
0223      * little downside, apart from a slower reset.
0224      */
0225     msleep(50);
0226 
0227     return regmap_read_poll_timeout(regmap, REG_SEMA_FLAGS, val,
0228                     !(val & SEMA_FLAGS_APP_REBOOT), 10000,
0229                     10000 * 100);
0230 }
0231 
0232 static int zl38_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
0233 {
0234     struct zl38_codec_priv *priv = snd_soc_dai_get_drvdata(dai);
0235     int err;
0236 
0237     switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
0238     case SND_SOC_DAIFMT_I2S:
0239         /* firmware default is normal i2s */
0240         break;
0241     default:
0242         return -EINVAL;
0243     }
0244 
0245     switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
0246     case SND_SOC_DAIFMT_NB_NF:
0247         /* firmware default is normal bitclock and frame */
0248         break;
0249     default:
0250         return -EINVAL;
0251     }
0252 
0253     switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
0254     case SND_SOC_DAIFMT_CBP_CFP:
0255         /* always 32 bits per frame (= 16 bits/channel, 2 channels) */
0256         err = regmap_update_bits(priv->regmap, REG_TDMA_CFG_CLK,
0257                      CFG_CLK_MASTER | CFG_CLK_PCLK_MASK,
0258                      CFG_CLK_MASTER | CFG_CLK_PCLK(32));
0259         if (err)
0260             return err;
0261         break;
0262     default:
0263         return -EINVAL;
0264     }
0265 
0266     return 0;
0267 }
0268 
0269 static int zl38_hw_params(struct snd_pcm_substream *substream,
0270               struct snd_pcm_hw_params *params,
0271               struct snd_soc_dai *dai)
0272 {
0273     struct zl38_codec_priv *priv = snd_soc_dai_get_drvdata(dai);
0274     bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
0275     unsigned int fsrate;
0276     int err;
0277 
0278     /* We cannot change hw_params while the dai is already in use - the
0279      * software reset will corrupt the audio. However, this is not required,
0280      * as the chip's TDM buses are fully symmetric, which mandates identical
0281      * rates, channels, and samplebits for record and playback.
0282      */
0283     if (priv->is_stream_in_use[!tx])
0284         goto skip_setup;
0285 
0286     switch (params_rate(params)) {
0287     case 8000:
0288         fsrate = CFG_CLK_FSRATE_8KHZ;
0289         break;
0290     case 16000:
0291         fsrate = CFG_CLK_FSRATE_16KHZ;
0292         break;
0293     case 48000:
0294         fsrate = CFG_CLK_FSRATE_48KHZ;
0295         break;
0296     default:
0297         return -EINVAL;
0298     }
0299 
0300     err = regmap_update_bits(priv->regmap, REG_TDMA_CFG_CLK,
0301                  CFG_CLK_FSRATE_MASK, fsrate);
0302     if (err)
0303         return err;
0304 
0305     /* chip requires a software reset to apply audio register changes */
0306     err = zl38_software_reset(priv->regmap);
0307     if (err)
0308         return err;
0309 
0310 skip_setup:
0311     priv->is_stream_in_use[tx] = true;
0312 
0313     return 0;
0314 }
0315 
0316 static int zl38_hw_free(struct snd_pcm_substream *substream,
0317             struct snd_soc_dai *dai)
0318 {
0319     struct zl38_codec_priv *priv = snd_soc_dai_get_drvdata(dai);
0320     bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
0321 
0322     priv->is_stream_in_use[tx] = false;
0323 
0324     return 0;
0325 }
0326 
0327 /* stereo bypass with no AEC */
0328 static const struct reg_sequence cp_config_stereo_bypass[] = {
0329     /* interconnects must be programmed first */
0330     { 0x0210, 0x0005 }, /* DAC1   in <= I2S1-L */
0331     { 0x0212, 0x0006 }, /* DAC2   in <= I2S1-R */
0332     { 0x0214, 0x0001 }, /* I2S1-L in <= MIC1   */
0333     { 0x0216, 0x0001 }, /* I2S1-R in <= MIC1   */
0334     { 0x0224, 0x0000 }, /* AEC-S  in <= n/a    */
0335     { 0x0226, 0x0000 }, /* AEC-R  in <= n/a    */
0336     /* output enables must be programmed next */
0337     { 0x0202, 0x000F }, /* enable I2S1 + DAC   */
0338 };
0339 
0340 static const struct snd_soc_dai_ops zl38_dai_ops = {
0341     .set_fmt = zl38_set_fmt,
0342     .hw_params = zl38_hw_params,
0343     .hw_free = zl38_hw_free,
0344 };
0345 
0346 static struct snd_soc_dai_driver zl38_dai = {
0347     .name = "zl38060-tdma",
0348     .playback = {
0349         .stream_name = "Playback",
0350         .channels_min = 2,
0351         .channels_max = 2,
0352         .rates = ZL38_RATES,
0353         .formats = ZL38_FORMATS,
0354     },
0355     .capture = {
0356         .stream_name = "Capture",
0357         .channels_min = 2,
0358         .channels_max = 2,
0359         .rates = ZL38_RATES,
0360         .formats = ZL38_FORMATS,
0361     },
0362     .ops = &zl38_dai_ops,
0363     .symmetric_rate = 1,
0364     .symmetric_sample_bits = 1,
0365     .symmetric_channels = 1,
0366 };
0367 
0368 static const struct snd_soc_dapm_widget zl38_dapm_widgets[] = {
0369     SND_SOC_DAPM_OUTPUT("DAC1"),
0370     SND_SOC_DAPM_OUTPUT("DAC2"),
0371 
0372     SND_SOC_DAPM_INPUT("DMICL"),
0373 };
0374 
0375 static const struct snd_soc_dapm_route zl38_dapm_routes[] = {
0376     { "DAC1",  NULL, "Playback" },
0377     { "DAC2",  NULL, "Playback" },
0378 
0379     { "Capture",  NULL, "DMICL" },
0380 };
0381 
0382 static const struct snd_soc_component_driver zl38_component_dev = {
0383     .dapm_widgets       = zl38_dapm_widgets,
0384     .num_dapm_widgets   = ARRAY_SIZE(zl38_dapm_widgets),
0385     .dapm_routes        = zl38_dapm_routes,
0386     .num_dapm_routes    = ARRAY_SIZE(zl38_dapm_routes),
0387     .endianness     = 1,
0388 };
0389 
0390 static void chip_gpio_set(struct gpio_chip *c, unsigned int offset, int val)
0391 {
0392     struct regmap *regmap = gpiochip_get_data(c);
0393     unsigned int mask = BIT(offset);
0394 
0395     regmap_update_bits(regmap, REG_GPIO_DAT, mask, val ? mask : 0);
0396 }
0397 
0398 static int chip_gpio_get(struct gpio_chip *c, unsigned int offset)
0399 {
0400     struct regmap *regmap = gpiochip_get_data(c);
0401     unsigned int mask = BIT(offset);
0402     unsigned int val;
0403     int err;
0404 
0405     err = regmap_read(regmap, REG_GPIO_DAT, &val);
0406     if (err)
0407         return err;
0408 
0409     return !!(val & mask);
0410 }
0411 
0412 static int chip_direction_input(struct gpio_chip *c, unsigned int offset)
0413 {
0414     struct regmap *regmap = gpiochip_get_data(c);
0415     unsigned int mask = BIT(offset);
0416 
0417     return regmap_update_bits(regmap, REG_GPIO_DIR, mask, 0);
0418 }
0419 
0420 static int
0421 chip_direction_output(struct gpio_chip *c, unsigned int offset, int val)
0422 {
0423     struct regmap *regmap = gpiochip_get_data(c);
0424     unsigned int mask = BIT(offset);
0425 
0426     chip_gpio_set(c, offset, val);
0427     return regmap_update_bits(regmap, REG_GPIO_DIR, mask, mask);
0428 }
0429 
0430 static const struct gpio_chip template_chip = {
0431     .owner = THIS_MODULE,
0432     .label = DRV_NAME,
0433 
0434     .base = -1,
0435     .ngpio = 14,
0436     .direction_input = chip_direction_input,
0437     .direction_output = chip_direction_output,
0438     .get = chip_gpio_get,
0439     .set = chip_gpio_set,
0440 
0441     .can_sleep = true,
0442 };
0443 
0444 static int zl38_check_revision(struct device *dev, struct regmap *regmap)
0445 {
0446     unsigned int hwrev, fwprod, fwrev;
0447     int fw_major, fw_minor, fw_micro;
0448     int err;
0449 
0450     err = regmap_read(regmap, REG_HW_REV, &hwrev);
0451     if (err)
0452         return err;
0453     err = regmap_read(regmap, REG_FW_PROD, &fwprod);
0454     if (err)
0455         return err;
0456     err = regmap_read(regmap, REG_FW_REV, &fwrev);
0457     if (err)
0458         return err;
0459 
0460     fw_major = (fwrev >> 12) & 0xF;
0461     fw_minor = (fwrev >>  8) & 0xF;
0462     fw_micro = fwrev & 0xFF;
0463     dev_info(dev, "hw rev 0x%x, fw product code %d, firmware rev %d.%d.%d",
0464          hwrev & 0x1F, fwprod, fw_major, fw_minor, fw_micro);
0465 
0466     if (fw_major != FIRMWARE_MAJOR || fw_minor < FIRMWARE_MINOR) {
0467         dev_err(dev, "unsupported firmware. driver supports %d.%d",
0468             FIRMWARE_MAJOR, FIRMWARE_MINOR);
0469         return -EINVAL;
0470     }
0471 
0472     return 0;
0473 }
0474 
0475 static int zl38_bus_read(void *context,
0476              const void *reg_buf, size_t reg_size,
0477              void *val_buf, size_t val_size)
0478 {
0479     struct spi_device *spi = context;
0480     const u8 *reg_buf8 = reg_buf;
0481     size_t len = 0;
0482     u8 offs, page;
0483     u8 txbuf[4];
0484 
0485     if (reg_size != 2 || val_size > ZL38_MAX_RAW_XFER)
0486         return -EINVAL;
0487 
0488     offs = reg_buf8[1] >> 1;
0489     page = reg_buf8[0];
0490 
0491     if (page) {
0492         txbuf[len++] = 0xFE;
0493         txbuf[len++] = page == HBI_FIRMWARE_PAGE ? 0xFF : page - 1;
0494         txbuf[len++] = offs;
0495         txbuf[len++] = val_size / 2 - 1;
0496     } else {
0497         txbuf[len++] = offs | 0x80;
0498         txbuf[len++] = val_size / 2 - 1;
0499     }
0500 
0501     return spi_write_then_read(spi, txbuf, len, val_buf, val_size);
0502 }
0503 
0504 static int zl38_bus_write(void *context, const void *data, size_t count)
0505 {
0506     struct spi_device *spi = context;
0507     u8 buf[4 + ZL38_MAX_RAW_XFER];
0508     size_t val_len, len = 0;
0509     const u8 *data8 = data;
0510     u8 offs, page;
0511 
0512     if (count > (2 + ZL38_MAX_RAW_XFER) || count < 4)
0513         return -EINVAL;
0514     val_len = count - 2;
0515     offs = data8[1] >> 1;
0516     page = data8[0];
0517 
0518     if (page) {
0519         buf[len++] = 0xFE;
0520         buf[len++] = page == HBI_FIRMWARE_PAGE ? 0xFF : page - 1;
0521         buf[len++] = offs;
0522         buf[len++] = (val_len / 2 - 1) | 0x80;
0523     } else {
0524         buf[len++] = offs | 0x80;
0525         buf[len++] = (val_len / 2 - 1) | 0x80;
0526     }
0527     memcpy(buf + len, data8 + 2, val_len);
0528     len += val_len;
0529 
0530     return spi_write(spi, buf, len);
0531 }
0532 
0533 static const struct regmap_bus zl38_regmap_bus = {
0534     .read = zl38_bus_read,
0535     .write = zl38_bus_write,
0536     .max_raw_write = ZL38_MAX_RAW_XFER,
0537     .max_raw_read = ZL38_MAX_RAW_XFER,
0538 };
0539 
0540 static const struct regmap_config zl38_regmap_conf = {
0541     .reg_bits = 16,
0542     .val_bits = 16,
0543     .reg_stride = 2,
0544     .use_single_read = true,
0545     .use_single_write = true,
0546 };
0547 
0548 static int zl38_spi_probe(struct spi_device *spi)
0549 {
0550     struct device *dev = &spi->dev;
0551     struct zl38_codec_priv *priv;
0552     struct gpio_desc *reset_gpio;
0553     int err;
0554 
0555     /* get the chip to a known state by putting it in reset */
0556     reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
0557     if (IS_ERR(reset_gpio))
0558         return PTR_ERR(reset_gpio);
0559     if (reset_gpio) {
0560         /* datasheet: need > 10us for a digital + analog reset */
0561         usleep_range(15, 50);
0562         /* take the chip out of reset */
0563         gpiod_set_value_cansleep(reset_gpio, 0);
0564         /* datasheet: need > 3ms for digital section to become stable */
0565         usleep_range(3000, 10000);
0566     }
0567 
0568     priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
0569     if (!priv)
0570         return -ENOMEM;
0571 
0572     priv->dev = dev;
0573     dev_set_drvdata(dev, priv);
0574     priv->regmap = devm_regmap_init(dev, &zl38_regmap_bus, spi,
0575                     &zl38_regmap_conf);
0576     if (IS_ERR(priv->regmap))
0577         return PTR_ERR(priv->regmap);
0578 
0579     err = zl38_load_firmware(dev, priv->regmap);
0580     if (err)
0581         return err;
0582 
0583     err = zl38_check_revision(dev, priv->regmap);
0584     if (err)
0585         return err;
0586 
0587     priv->gpio_chip = devm_kmemdup(dev, &template_chip,
0588                        sizeof(template_chip), GFP_KERNEL);
0589     if (!priv->gpio_chip)
0590         return -ENOMEM;
0591     priv->gpio_chip->parent = dev;
0592     err = devm_gpiochip_add_data(dev, priv->gpio_chip, priv->regmap);
0593     if (err)
0594         return err;
0595 
0596     /* setup the cross-point switch for stereo bypass */
0597     err = regmap_multi_reg_write(priv->regmap, cp_config_stereo_bypass,
0598                      ARRAY_SIZE(cp_config_stereo_bypass));
0599     if (err)
0600         return err;
0601     /* setup for 12MHz crystal connected to the chip */
0602     err = regmap_update_bits(priv->regmap, REG_CLK_CFG, CLK_CFG_SOURCE_XTAL,
0603                  CLK_CFG_SOURCE_XTAL);
0604     if (err)
0605         return err;
0606 
0607     return devm_snd_soc_register_component(dev, &zl38_component_dev,
0608                            &zl38_dai, 1);
0609 }
0610 
0611 static const struct of_device_id zl38_dt_ids[] = {
0612     { .compatible = "mscc,zl38060", },
0613     { /* sentinel */ }
0614 };
0615 MODULE_DEVICE_TABLE(of, zl38_dt_ids);
0616 
0617 static const struct spi_device_id zl38_spi_ids[] = {
0618     { "zl38060", 0 },
0619     { /* sentinel */ }
0620 };
0621 MODULE_DEVICE_TABLE(spi, zl38_spi_ids);
0622 
0623 static struct spi_driver zl38060_spi_driver = {
0624     .driver = {
0625         .name = DRV_NAME,
0626         .of_match_table = of_match_ptr(zl38_dt_ids),
0627     },
0628     .probe = zl38_spi_probe,
0629     .id_table = zl38_spi_ids,
0630 };
0631 module_spi_driver(zl38060_spi_driver);
0632 
0633 MODULE_DESCRIPTION("ASoC ZL38060 driver");
0634 MODULE_AUTHOR("Sven Van Asbroeck <TheSven73@gmail.com>");
0635 MODULE_LICENSE("GPL v2");