11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * ALSA driver for ICEnsemble ICE1712 (Envy24) 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * AK4524 / AK4528 / AK4529 / AK4355 / AK4381 interface 51da177e4SLinus Torvalds * 6c1017a4cSJaroslav Kysela * Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz> 71da177e4SLinus Torvalds * 81da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify 91da177e4SLinus Torvalds * it under the terms of the GNU General Public License as published by 101da177e4SLinus Torvalds * the Free Software Foundation; either version 2 of the License, or 111da177e4SLinus Torvalds * (at your option) any later version. 121da177e4SLinus Torvalds * 131da177e4SLinus Torvalds * This program is distributed in the hope that it will be useful, 141da177e4SLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of 151da177e4SLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 161da177e4SLinus Torvalds * GNU General Public License for more details. 171da177e4SLinus Torvalds * 181da177e4SLinus Torvalds * You should have received a copy of the GNU General Public License 191da177e4SLinus Torvalds * along with this program; if not, write to the Free Software 201da177e4SLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 211da177e4SLinus Torvalds * 221da177e4SLinus Torvalds */ 231da177e4SLinus Torvalds 24*6cbbfe1cSTakashi Iwai #include <linux/io.h> 251da177e4SLinus Torvalds #include <linux/delay.h> 261da177e4SLinus Torvalds #include <linux/interrupt.h> 275a0e3ad6STejun Heo #include <linux/slab.h> 281da177e4SLinus Torvalds #include <linux/init.h> 29da155d5bSPaul Gortmaker #include <linux/module.h> 301da177e4SLinus Torvalds #include <sound/core.h> 311da177e4SLinus Torvalds #include <sound/initval.h> 321da177e4SLinus Torvalds #include "ice1712.h" 331da177e4SLinus Torvalds 34c1017a4cSJaroslav Kysela MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); 351da177e4SLinus Torvalds MODULE_DESCRIPTION("ICEnsemble ICE17xx <-> AK4xxx AD/DA chip interface"); 361da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 371da177e4SLinus Torvalds 386ca308d4STakashi Iwai static void snd_ice1712_akm4xxx_lock(struct snd_akm4xxx *ak, int chip) 391da177e4SLinus Torvalds { 406ca308d4STakashi Iwai struct snd_ice1712 *ice = ak->private_data[0]; 411da177e4SLinus Torvalds 421da177e4SLinus Torvalds snd_ice1712_save_gpio_status(ice); 431da177e4SLinus Torvalds } 441da177e4SLinus Torvalds 456ca308d4STakashi Iwai static void snd_ice1712_akm4xxx_unlock(struct snd_akm4xxx *ak, int chip) 461da177e4SLinus Torvalds { 476ca308d4STakashi Iwai struct snd_ice1712 *ice = ak->private_data[0]; 481da177e4SLinus Torvalds 491da177e4SLinus Torvalds snd_ice1712_restore_gpio_status(ice); 501da177e4SLinus Torvalds } 511da177e4SLinus Torvalds 521da177e4SLinus Torvalds /* 531da177e4SLinus Torvalds * write AK4xxx register 541da177e4SLinus Torvalds */ 556ca308d4STakashi Iwai static void snd_ice1712_akm4xxx_write(struct snd_akm4xxx *ak, int chip, 561da177e4SLinus Torvalds unsigned char addr, unsigned char data) 571da177e4SLinus Torvalds { 581da177e4SLinus Torvalds unsigned int tmp; 591da177e4SLinus Torvalds int idx; 601da177e4SLinus Torvalds unsigned int addrdata; 611da177e4SLinus Torvalds struct snd_ak4xxx_private *priv = (void *)ak->private_value[0]; 626ca308d4STakashi Iwai struct snd_ice1712 *ice = ak->private_data[0]; 631da177e4SLinus Torvalds 64da3cec35STakashi Iwai if (snd_BUG_ON(chip < 0 || chip >= 4)) 65da3cec35STakashi Iwai return; 661da177e4SLinus Torvalds 671da177e4SLinus Torvalds tmp = snd_ice1712_gpio_read(ice); 681da177e4SLinus Torvalds tmp |= priv->add_flags; 691da177e4SLinus Torvalds tmp &= ~priv->mask_flags; 701da177e4SLinus Torvalds if (priv->cs_mask == priv->cs_addr) { 711da177e4SLinus Torvalds if (priv->cif) { 721da177e4SLinus Torvalds tmp |= priv->cs_mask; /* start without chip select */ 731da177e4SLinus Torvalds } else { 741da177e4SLinus Torvalds tmp &= ~priv->cs_mask; /* chip select low */ 751da177e4SLinus Torvalds snd_ice1712_gpio_write(ice, tmp); 761da177e4SLinus Torvalds udelay(1); 771da177e4SLinus Torvalds } 781da177e4SLinus Torvalds } else { 791da177e4SLinus Torvalds /* doesn't handle cf=1 yet */ 801da177e4SLinus Torvalds tmp &= ~priv->cs_mask; 811da177e4SLinus Torvalds tmp |= priv->cs_addr; 821da177e4SLinus Torvalds snd_ice1712_gpio_write(ice, tmp); 831da177e4SLinus Torvalds udelay(1); 841da177e4SLinus Torvalds } 851da177e4SLinus Torvalds 861da177e4SLinus Torvalds /* build I2C address + data byte */ 871da177e4SLinus Torvalds addrdata = (priv->caddr << 6) | 0x20 | (addr & 0x1f); 881da177e4SLinus Torvalds addrdata = (addrdata << 8) | data; 891da177e4SLinus Torvalds for (idx = 15; idx >= 0; idx--) { 901da177e4SLinus Torvalds /* drop clock */ 911da177e4SLinus Torvalds tmp &= ~priv->clk_mask; 921da177e4SLinus Torvalds snd_ice1712_gpio_write(ice, tmp); 931da177e4SLinus Torvalds udelay(1); 941da177e4SLinus Torvalds /* set data */ 951da177e4SLinus Torvalds if (addrdata & (1 << idx)) 961da177e4SLinus Torvalds tmp |= priv->data_mask; 971da177e4SLinus Torvalds else 981da177e4SLinus Torvalds tmp &= ~priv->data_mask; 991da177e4SLinus Torvalds snd_ice1712_gpio_write(ice, tmp); 1001da177e4SLinus Torvalds udelay(1); 1011da177e4SLinus Torvalds /* raise clock */ 1021da177e4SLinus Torvalds tmp |= priv->clk_mask; 1031da177e4SLinus Torvalds snd_ice1712_gpio_write(ice, tmp); 1041da177e4SLinus Torvalds udelay(1); 1051da177e4SLinus Torvalds } 1061da177e4SLinus Torvalds 1071da177e4SLinus Torvalds if (priv->cs_mask == priv->cs_addr) { 1081da177e4SLinus Torvalds if (priv->cif) { 1091da177e4SLinus Torvalds /* assert a cs pulse to trigger */ 1101da177e4SLinus Torvalds tmp &= ~priv->cs_mask; 1111da177e4SLinus Torvalds snd_ice1712_gpio_write(ice, tmp); 1121da177e4SLinus Torvalds udelay(1); 1131da177e4SLinus Torvalds } 1141da177e4SLinus Torvalds tmp |= priv->cs_mask; /* chip select high to trigger */ 1151da177e4SLinus Torvalds } else { 1161da177e4SLinus Torvalds tmp &= ~priv->cs_mask; 1171da177e4SLinus Torvalds tmp |= priv->cs_none; /* deselect address */ 1181da177e4SLinus Torvalds } 1191da177e4SLinus Torvalds snd_ice1712_gpio_write(ice, tmp); 1201da177e4SLinus Torvalds udelay(1); 1211da177e4SLinus Torvalds } 1221da177e4SLinus Torvalds 1231da177e4SLinus Torvalds /* 1246ca308d4STakashi Iwai * initialize the struct snd_akm4xxx record with the template 1251da177e4SLinus Torvalds */ 1266ca308d4STakashi Iwai int snd_ice1712_akm4xxx_init(struct snd_akm4xxx *ak, const struct snd_akm4xxx *temp, 1276ca308d4STakashi Iwai const struct snd_ak4xxx_private *_priv, struct snd_ice1712 *ice) 1281da177e4SLinus Torvalds { 1291da177e4SLinus Torvalds struct snd_ak4xxx_private *priv; 1301da177e4SLinus Torvalds 1311da177e4SLinus Torvalds if (_priv != NULL) { 1321da177e4SLinus Torvalds priv = kmalloc(sizeof(*priv), GFP_KERNEL); 1331da177e4SLinus Torvalds if (priv == NULL) 1341da177e4SLinus Torvalds return -ENOMEM; 1351da177e4SLinus Torvalds *priv = *_priv; 1361da177e4SLinus Torvalds } else { 1371da177e4SLinus Torvalds priv = NULL; 1381da177e4SLinus Torvalds } 1391da177e4SLinus Torvalds *ak = *temp; 1401da177e4SLinus Torvalds ak->card = ice->card; 1411da177e4SLinus Torvalds ak->private_value[0] = (unsigned long)priv; 1421da177e4SLinus Torvalds ak->private_data[0] = ice; 1431da177e4SLinus Torvalds if (ak->ops.lock == NULL) 1441da177e4SLinus Torvalds ak->ops.lock = snd_ice1712_akm4xxx_lock; 1451da177e4SLinus Torvalds if (ak->ops.unlock == NULL) 1461da177e4SLinus Torvalds ak->ops.unlock = snd_ice1712_akm4xxx_unlock; 1471da177e4SLinus Torvalds if (ak->ops.write == NULL) 1481da177e4SLinus Torvalds ak->ops.write = snd_ice1712_akm4xxx_write; 1491da177e4SLinus Torvalds snd_akm4xxx_init(ak); 1501da177e4SLinus Torvalds return 0; 1511da177e4SLinus Torvalds } 1521da177e4SLinus Torvalds 1536ca308d4STakashi Iwai void snd_ice1712_akm4xxx_free(struct snd_ice1712 *ice) 1541da177e4SLinus Torvalds { 1551da177e4SLinus Torvalds unsigned int akidx; 1561da177e4SLinus Torvalds if (ice->akm == NULL) 1571da177e4SLinus Torvalds return; 1581da177e4SLinus Torvalds for (akidx = 0; akidx < ice->akm_codecs; akidx++) { 1596ca308d4STakashi Iwai struct snd_akm4xxx *ak = &ice->akm[akidx]; 1601da177e4SLinus Torvalds kfree((void*)ak->private_value[0]); 1611da177e4SLinus Torvalds } 1621da177e4SLinus Torvalds kfree(ice->akm); 1631da177e4SLinus Torvalds } 1641da177e4SLinus Torvalds 1651da177e4SLinus Torvalds /* 1661da177e4SLinus Torvalds * build AK4xxx controls 1671da177e4SLinus Torvalds */ 1686ca308d4STakashi Iwai int snd_ice1712_akm4xxx_build_controls(struct snd_ice1712 *ice) 1691da177e4SLinus Torvalds { 1701da177e4SLinus Torvalds unsigned int akidx; 1711da177e4SLinus Torvalds int err; 1721da177e4SLinus Torvalds 1731da177e4SLinus Torvalds for (akidx = 0; akidx < ice->akm_codecs; akidx++) { 1746ca308d4STakashi Iwai struct snd_akm4xxx *ak = &ice->akm[akidx]; 1751da177e4SLinus Torvalds err = snd_akm4xxx_build_controls(ak); 1761da177e4SLinus Torvalds if (err < 0) 1771da177e4SLinus Torvalds return err; 1781da177e4SLinus Torvalds } 1791da177e4SLinus Torvalds return 0; 1801da177e4SLinus Torvalds } 1811da177e4SLinus Torvalds 1821da177e4SLinus Torvalds static int __init alsa_ice1712_akm4xxx_module_init(void) 1831da177e4SLinus Torvalds { 1841da177e4SLinus Torvalds return 0; 1851da177e4SLinus Torvalds } 1861da177e4SLinus Torvalds 1871da177e4SLinus Torvalds static void __exit alsa_ice1712_akm4xxx_module_exit(void) 1881da177e4SLinus Torvalds { 1891da177e4SLinus Torvalds } 1901da177e4SLinus Torvalds 1911da177e4SLinus Torvalds module_init(alsa_ice1712_akm4xxx_module_init) 1921da177e4SLinus Torvalds module_exit(alsa_ice1712_akm4xxx_module_exit) 1931da177e4SLinus Torvalds 1941da177e4SLinus Torvalds EXPORT_SYMBOL(snd_ice1712_akm4xxx_init); 1951da177e4SLinus Torvalds EXPORT_SYMBOL(snd_ice1712_akm4xxx_free); 1961da177e4SLinus Torvalds EXPORT_SYMBOL(snd_ice1712_akm4xxx_build_controls); 197