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 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 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 */ 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 */ 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 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 */ 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