1*1a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * ALSA driver for ICEnsemble ICE1712 (Envy24)
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * AK4524 / AK4528 / AK4529 / AK4355 / AK4381 interface
61da177e4SLinus Torvalds *
7c1017a4cSJaroslav Kysela * Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
81da177e4SLinus Torvalds */
91da177e4SLinus Torvalds
106cbbfe1cSTakashi Iwai #include <linux/io.h>
111da177e4SLinus Torvalds #include <linux/delay.h>
121da177e4SLinus Torvalds #include <linux/interrupt.h>
135a0e3ad6STejun Heo #include <linux/slab.h>
141da177e4SLinus Torvalds #include <linux/init.h>
15da155d5bSPaul Gortmaker #include <linux/module.h>
161da177e4SLinus Torvalds #include <sound/core.h>
171da177e4SLinus Torvalds #include <sound/initval.h>
181da177e4SLinus Torvalds #include "ice1712.h"
191da177e4SLinus Torvalds
20c1017a4cSJaroslav Kysela MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
211da177e4SLinus Torvalds MODULE_DESCRIPTION("ICEnsemble ICE17xx <-> AK4xxx AD/DA chip interface");
221da177e4SLinus Torvalds MODULE_LICENSE("GPL");
231da177e4SLinus Torvalds
snd_ice1712_akm4xxx_lock(struct snd_akm4xxx * ak,int chip)246ca308d4STakashi Iwai static void snd_ice1712_akm4xxx_lock(struct snd_akm4xxx *ak, int chip)
251da177e4SLinus Torvalds {
266ca308d4STakashi Iwai struct snd_ice1712 *ice = ak->private_data[0];
271da177e4SLinus Torvalds
281da177e4SLinus Torvalds snd_ice1712_save_gpio_status(ice);
291da177e4SLinus Torvalds }
301da177e4SLinus Torvalds
snd_ice1712_akm4xxx_unlock(struct snd_akm4xxx * ak,int chip)316ca308d4STakashi Iwai static void snd_ice1712_akm4xxx_unlock(struct snd_akm4xxx *ak, int chip)
321da177e4SLinus Torvalds {
336ca308d4STakashi Iwai struct snd_ice1712 *ice = ak->private_data[0];
341da177e4SLinus Torvalds
351da177e4SLinus Torvalds snd_ice1712_restore_gpio_status(ice);
361da177e4SLinus Torvalds }
371da177e4SLinus Torvalds
381da177e4SLinus Torvalds /*
391da177e4SLinus Torvalds * write AK4xxx register
401da177e4SLinus Torvalds */
snd_ice1712_akm4xxx_write(struct snd_akm4xxx * ak,int chip,unsigned char addr,unsigned char data)416ca308d4STakashi Iwai static void snd_ice1712_akm4xxx_write(struct snd_akm4xxx *ak, int chip,
421da177e4SLinus Torvalds unsigned char addr, unsigned char data)
431da177e4SLinus Torvalds {
441da177e4SLinus Torvalds unsigned int tmp;
451da177e4SLinus Torvalds int idx;
461da177e4SLinus Torvalds unsigned int addrdata;
471da177e4SLinus Torvalds struct snd_ak4xxx_private *priv = (void *)ak->private_value[0];
486ca308d4STakashi Iwai struct snd_ice1712 *ice = ak->private_data[0];
491da177e4SLinus Torvalds
50da3cec35STakashi Iwai if (snd_BUG_ON(chip < 0 || chip >= 4))
51da3cec35STakashi Iwai return;
521da177e4SLinus Torvalds
531da177e4SLinus Torvalds tmp = snd_ice1712_gpio_read(ice);
541da177e4SLinus Torvalds tmp |= priv->add_flags;
551da177e4SLinus Torvalds tmp &= ~priv->mask_flags;
561da177e4SLinus Torvalds if (priv->cs_mask == priv->cs_addr) {
571da177e4SLinus Torvalds if (priv->cif) {
581da177e4SLinus Torvalds tmp |= priv->cs_mask; /* start without chip select */
591da177e4SLinus Torvalds } else {
601da177e4SLinus Torvalds tmp &= ~priv->cs_mask; /* chip select low */
611da177e4SLinus Torvalds snd_ice1712_gpio_write(ice, tmp);
621da177e4SLinus Torvalds udelay(1);
631da177e4SLinus Torvalds }
641da177e4SLinus Torvalds } else {
651da177e4SLinus Torvalds /* doesn't handle cf=1 yet */
661da177e4SLinus Torvalds tmp &= ~priv->cs_mask;
671da177e4SLinus Torvalds tmp |= priv->cs_addr;
681da177e4SLinus Torvalds snd_ice1712_gpio_write(ice, tmp);
691da177e4SLinus Torvalds udelay(1);
701da177e4SLinus Torvalds }
711da177e4SLinus Torvalds
721da177e4SLinus Torvalds /* build I2C address + data byte */
731da177e4SLinus Torvalds addrdata = (priv->caddr << 6) | 0x20 | (addr & 0x1f);
741da177e4SLinus Torvalds addrdata = (addrdata << 8) | data;
751da177e4SLinus Torvalds for (idx = 15; idx >= 0; idx--) {
761da177e4SLinus Torvalds /* drop clock */
771da177e4SLinus Torvalds tmp &= ~priv->clk_mask;
781da177e4SLinus Torvalds snd_ice1712_gpio_write(ice, tmp);
791da177e4SLinus Torvalds udelay(1);
801da177e4SLinus Torvalds /* set data */
811da177e4SLinus Torvalds if (addrdata & (1 << idx))
821da177e4SLinus Torvalds tmp |= priv->data_mask;
831da177e4SLinus Torvalds else
841da177e4SLinus Torvalds tmp &= ~priv->data_mask;
851da177e4SLinus Torvalds snd_ice1712_gpio_write(ice, tmp);
861da177e4SLinus Torvalds udelay(1);
871da177e4SLinus Torvalds /* raise clock */
881da177e4SLinus Torvalds tmp |= priv->clk_mask;
891da177e4SLinus Torvalds snd_ice1712_gpio_write(ice, tmp);
901da177e4SLinus Torvalds udelay(1);
911da177e4SLinus Torvalds }
921da177e4SLinus Torvalds
931da177e4SLinus Torvalds if (priv->cs_mask == priv->cs_addr) {
941da177e4SLinus Torvalds if (priv->cif) {
951da177e4SLinus Torvalds /* assert a cs pulse to trigger */
961da177e4SLinus Torvalds tmp &= ~priv->cs_mask;
971da177e4SLinus Torvalds snd_ice1712_gpio_write(ice, tmp);
981da177e4SLinus Torvalds udelay(1);
991da177e4SLinus Torvalds }
1001da177e4SLinus Torvalds tmp |= priv->cs_mask; /* chip select high to trigger */
1011da177e4SLinus Torvalds } else {
1021da177e4SLinus Torvalds tmp &= ~priv->cs_mask;
1031da177e4SLinus Torvalds tmp |= priv->cs_none; /* deselect address */
1041da177e4SLinus Torvalds }
1051da177e4SLinus Torvalds snd_ice1712_gpio_write(ice, tmp);
1061da177e4SLinus Torvalds udelay(1);
1071da177e4SLinus Torvalds }
1081da177e4SLinus Torvalds
1091da177e4SLinus Torvalds /*
1106ca308d4STakashi Iwai * initialize the struct snd_akm4xxx record with the template
1111da177e4SLinus Torvalds */
snd_ice1712_akm4xxx_init(struct snd_akm4xxx * ak,const struct snd_akm4xxx * temp,const struct snd_ak4xxx_private * _priv,struct snd_ice1712 * ice)1126ca308d4STakashi Iwai int snd_ice1712_akm4xxx_init(struct snd_akm4xxx *ak, const struct snd_akm4xxx *temp,
1136ca308d4STakashi Iwai const struct snd_ak4xxx_private *_priv, struct snd_ice1712 *ice)
1141da177e4SLinus Torvalds {
1151da177e4SLinus Torvalds struct snd_ak4xxx_private *priv;
1161da177e4SLinus Torvalds
1171da177e4SLinus Torvalds if (_priv != NULL) {
1181da177e4SLinus Torvalds priv = kmalloc(sizeof(*priv), GFP_KERNEL);
1191da177e4SLinus Torvalds if (priv == NULL)
1201da177e4SLinus Torvalds return -ENOMEM;
1211da177e4SLinus Torvalds *priv = *_priv;
1221da177e4SLinus Torvalds } else {
1231da177e4SLinus Torvalds priv = NULL;
1241da177e4SLinus Torvalds }
1251da177e4SLinus Torvalds *ak = *temp;
1261da177e4SLinus Torvalds ak->card = ice->card;
1271da177e4SLinus Torvalds ak->private_value[0] = (unsigned long)priv;
1281da177e4SLinus Torvalds ak->private_data[0] = ice;
1291da177e4SLinus Torvalds if (ak->ops.lock == NULL)
1301da177e4SLinus Torvalds ak->ops.lock = snd_ice1712_akm4xxx_lock;
1311da177e4SLinus Torvalds if (ak->ops.unlock == NULL)
1321da177e4SLinus Torvalds ak->ops.unlock = snd_ice1712_akm4xxx_unlock;
1331da177e4SLinus Torvalds if (ak->ops.write == NULL)
1341da177e4SLinus Torvalds ak->ops.write = snd_ice1712_akm4xxx_write;
1351da177e4SLinus Torvalds snd_akm4xxx_init(ak);
1361da177e4SLinus Torvalds return 0;
1371da177e4SLinus Torvalds }
1381da177e4SLinus Torvalds
snd_ice1712_akm4xxx_free(struct snd_ice1712 * ice)1396ca308d4STakashi Iwai void snd_ice1712_akm4xxx_free(struct snd_ice1712 *ice)
1401da177e4SLinus Torvalds {
1411da177e4SLinus Torvalds unsigned int akidx;
1421da177e4SLinus Torvalds if (ice->akm == NULL)
1431da177e4SLinus Torvalds return;
1441da177e4SLinus Torvalds for (akidx = 0; akidx < ice->akm_codecs; akidx++) {
1456ca308d4STakashi Iwai struct snd_akm4xxx *ak = &ice->akm[akidx];
1461da177e4SLinus Torvalds kfree((void*)ak->private_value[0]);
1471da177e4SLinus Torvalds }
1481da177e4SLinus Torvalds kfree(ice->akm);
1491da177e4SLinus Torvalds }
1501da177e4SLinus Torvalds
1511da177e4SLinus Torvalds /*
1521da177e4SLinus Torvalds * build AK4xxx controls
1531da177e4SLinus Torvalds */
snd_ice1712_akm4xxx_build_controls(struct snd_ice1712 * ice)1546ca308d4STakashi Iwai int snd_ice1712_akm4xxx_build_controls(struct snd_ice1712 *ice)
1551da177e4SLinus Torvalds {
1561da177e4SLinus Torvalds unsigned int akidx;
1571da177e4SLinus Torvalds int err;
1581da177e4SLinus Torvalds
1591da177e4SLinus Torvalds for (akidx = 0; akidx < ice->akm_codecs; akidx++) {
1606ca308d4STakashi Iwai struct snd_akm4xxx *ak = &ice->akm[akidx];
1611da177e4SLinus Torvalds err = snd_akm4xxx_build_controls(ak);
1621da177e4SLinus Torvalds if (err < 0)
1631da177e4SLinus Torvalds return err;
1641da177e4SLinus Torvalds }
1651da177e4SLinus Torvalds return 0;
1661da177e4SLinus Torvalds }
1671da177e4SLinus Torvalds
1681da177e4SLinus Torvalds EXPORT_SYMBOL(snd_ice1712_akm4xxx_init);
1691da177e4SLinus Torvalds EXPORT_SYMBOL(snd_ice1712_akm4xxx_free);
1701da177e4SLinus Torvalds EXPORT_SYMBOL(snd_ice1712_akm4xxx_build_controls);
171