171 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * ALSA driver for ICEnsemble ICE1712 (Envy24)4 *5 * AK4524 / AK4528 / AK4529 / AK4355 / AK4381 interface6 *7 * Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>8 */ 9 10#include <linux/io.h>11#include <linux/delay.h>12#include <linux/interrupt.h>13#include <linux/slab.h>14#include <linux/init.h>15#include <linux/module.h>16#include <sound/core.h>17#include <sound/initval.h>18#include "ice1712.h"19 20MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");21MODULE_DESCRIPTION("ICEnsemble ICE17xx <-> AK4xxx AD/DA chip interface");22MODULE_LICENSE("GPL");23 24static void snd_ice1712_akm4xxx_lock(struct snd_akm4xxx *ak, int chip)25{26 struct snd_ice1712 *ice = ak->private_data[0];27 28 snd_ice1712_save_gpio_status(ice);29}30 31static void snd_ice1712_akm4xxx_unlock(struct snd_akm4xxx *ak, int chip)32{33 struct snd_ice1712 *ice = ak->private_data[0];34 35 snd_ice1712_restore_gpio_status(ice);36}37 38/*39 * write AK4xxx register40 */41static void snd_ice1712_akm4xxx_write(struct snd_akm4xxx *ak, int chip,42 unsigned char addr, unsigned char data)43{44 unsigned int tmp;45 int idx;46 unsigned int addrdata;47 struct snd_ak4xxx_private *priv = (void *)ak->private_value[0];48 struct snd_ice1712 *ice = ak->private_data[0];49 50 if (snd_BUG_ON(chip < 0 || chip >= 4))51 return;52 53 tmp = snd_ice1712_gpio_read(ice);54 tmp |= priv->add_flags;55 tmp &= ~priv->mask_flags;56 if (priv->cs_mask == priv->cs_addr) {57 if (priv->cif) {58 tmp |= priv->cs_mask; /* start without chip select */59 } else {60 tmp &= ~priv->cs_mask; /* chip select low */61 snd_ice1712_gpio_write(ice, tmp);62 udelay(1);63 }64 } else {65 /* doesn't handle cf=1 yet */66 tmp &= ~priv->cs_mask;67 tmp |= priv->cs_addr;68 snd_ice1712_gpio_write(ice, tmp);69 udelay(1);70 }71 72 /* build I2C address + data byte */73 addrdata = (priv->caddr << 6) | 0x20 | (addr & 0x1f);74 addrdata = (addrdata << 8) | data;75 for (idx = 15; idx >= 0; idx--) {76 /* drop clock */77 tmp &= ~priv->clk_mask;78 snd_ice1712_gpio_write(ice, tmp);79 udelay(1);80 /* set data */81 if (addrdata & (1 << idx))82 tmp |= priv->data_mask;83 else84 tmp &= ~priv->data_mask;85 snd_ice1712_gpio_write(ice, tmp);86 udelay(1);87 /* raise clock */88 tmp |= priv->clk_mask;89 snd_ice1712_gpio_write(ice, tmp);90 udelay(1);91 }92 93 if (priv->cs_mask == priv->cs_addr) {94 if (priv->cif) {95 /* assert a cs pulse to trigger */96 tmp &= ~priv->cs_mask;97 snd_ice1712_gpio_write(ice, tmp);98 udelay(1);99 }100 tmp |= priv->cs_mask; /* chip select high to trigger */101 } else {102 tmp &= ~priv->cs_mask;103 tmp |= priv->cs_none; /* deselect address */104 }105 snd_ice1712_gpio_write(ice, tmp);106 udelay(1);107}108 109/*110 * initialize the struct snd_akm4xxx record with the template111 */112int snd_ice1712_akm4xxx_init(struct snd_akm4xxx *ak, const struct snd_akm4xxx *temp,113 const struct snd_ak4xxx_private *_priv, struct snd_ice1712 *ice)114{115 struct snd_ak4xxx_private *priv;116 117 if (_priv != NULL) {118 priv = kmalloc(sizeof(*priv), GFP_KERNEL);119 if (priv == NULL)120 return -ENOMEM;121 *priv = *_priv;122 } else {123 priv = NULL;124 }125 *ak = *temp;126 ak->card = ice->card;127 ak->private_value[0] = (unsigned long)priv;128 ak->private_data[0] = ice;129 if (ak->ops.lock == NULL)130 ak->ops.lock = snd_ice1712_akm4xxx_lock;131 if (ak->ops.unlock == NULL)132 ak->ops.unlock = snd_ice1712_akm4xxx_unlock;133 if (ak->ops.write == NULL)134 ak->ops.write = snd_ice1712_akm4xxx_write;135 snd_akm4xxx_init(ak);136 return 0;137}138 139void snd_ice1712_akm4xxx_free(struct snd_ice1712 *ice)140{141 unsigned int akidx;142 if (ice->akm == NULL)143 return;144 for (akidx = 0; akidx < ice->akm_codecs; akidx++) {145 struct snd_akm4xxx *ak = &ice->akm[akidx];146 kfree((void*)ak->private_value[0]);147 }148 kfree(ice->akm);149}150 151/*152 * build AK4xxx controls153 */154int snd_ice1712_akm4xxx_build_controls(struct snd_ice1712 *ice)155{156 unsigned int akidx;157 int err;158 159 for (akidx = 0; akidx < ice->akm_codecs; akidx++) {160 struct snd_akm4xxx *ak = &ice->akm[akidx];161 err = snd_akm4xxx_build_controls(ak);162 if (err < 0)163 return err;164 }165 return 0;166}167 168EXPORT_SYMBOL(snd_ice1712_akm4xxx_init);169EXPORT_SYMBOL(snd_ice1712_akm4xxx_free);170EXPORT_SYMBOL(snd_ice1712_akm4xxx_build_controls);171