123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483 |
- /* Copyright (c) 2010, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
- #include <linux/module.h>
- #include <linux/delay.h>
- #include <linux/err.h>
- #include <linux/init.h>
- #include <linux/slab.h>
- #include <linux/platform_device.h>
- #include <linux/mfd/marimba.h>
- #include <linux/mfd/timpani-audio.h>
- #include <sound/soc.h>
- #include <sound/initval.h>
- #include <sound/soc-dapm.h>
- /* Debug purpose */
- #include <linux/gpio.h>
- #include <linux/clk.h>
- #include <mach/mpp.h>
- /* End of debug purpose */
- #define ADIE_CODEC_MAX 2
- struct adie_codec_register {
- u8 reg;
- u8 mask;
- u8 val;
- };
- static struct adie_codec_register dmic_on[] = {
- {0x80, 0x05, 0x05},
- {0x80, 0x05, 0x00},
- {0x83, 0x0C, 0x00},
- {0x8A, 0xF0, 0x30},
- {0x86, 0xFF, 0xAC},
- {0x87, 0xFF, 0xAC},
- {0x8A, 0xF0, 0xF0},
- {0x82, 0x1F, 0x1E},
- {0x83, 0x0C, 0x0C},
- {0x92, 0x3F, 0x21},
- {0x94, 0x3F, 0x24},
- {0xA3, 0x39, 0x01},
- {0xA8, 0x0F, 0x00},
- {0xAB, 0x3F, 0x00},
- {0x86, 0xFF, 0x00},
- {0x87, 0xFF, 0x00},
- {0x8A, 0xF0, 0xC0},
- };
- static struct adie_codec_register dmic_off[] = {
- {0x8A, 0xF0, 0xF0},
- {0x83, 0x0C, 0x00},
- {0x92, 0xFF, 0x00},
- {0x94, 0xFF, 0x1B},
- };
- static struct adie_codec_register spk_on[] = {
- {0x80, 0x02, 0x02},
- {0x80, 0x02, 0x00},
- {0x83, 0x03, 0x00},
- {0x8A, 0x0F, 0x03},
- {0xA3, 0x02, 0x02},
- {0x84, 0xFF, 0x00},
- {0x85, 0xFF, 0x00},
- {0x8A, 0x0F, 0x0C},
- {0x81, 0xFF, 0x0E},
- {0x83, 0x03, 0x03},
- {0x24, 0x6F, 0x6C},
- {0xB7, 0x01, 0x01},
- {0x31, 0x01, 0x01},
- {0x32, 0xF8, 0x08},
- {0x32, 0xF8, 0x48},
- {0x32, 0xF8, 0xF8},
- {0xE0, 0xFE, 0xAC},
- {0xE1, 0xFE, 0xAC},
- {0x3A, 0x24, 0x24},
- {0xE0, 0xFE, 0x3C},
- {0xE1, 0xFE, 0x3C},
- {0xE0, 0xFE, 0x1C},
- {0xE1, 0xFE, 0x1C},
- {0xE0, 0xFE, 0x10},
- {0xE1, 0xFE, 0x10},
- };
- static struct adie_codec_register spk_off[] = {
- {0x8A, 0x0F, 0x0F},
- {0xE0, 0xFE, 0x1C},
- {0xE1, 0xFE, 0x1C},
- {0xE0, 0xFE, 0x3C},
- {0xE1, 0xFE, 0x3C},
- {0xE0, 0xFC, 0xAC},
- {0xE1, 0xFC, 0xAC},
- {0x32, 0xF8, 0x00},
- {0x31, 0x05, 0x00},
- {0x3A, 0x24, 0x00},
- };
- static struct adie_codec_register spk_mute[] = {
- {0x84, 0xFF, 0xAC},
- {0x85, 0xFF, 0xAC},
- {0x8A, 0x0F, 0x0C},
- };
- static struct adie_codec_register spk_unmute[] = {
- {0x84, 0xFF, 0x00},
- {0x85, 0xFF, 0x00},
- {0x8A, 0x0F, 0x0C},
- };
- struct adie_codec_path {
- int rate; /* sample rate of path */
- u32 reg_owner;
- };
- struct timpani_drv_data { /* member undecided */
- struct snd_soc_codec codec;
- struct adie_codec_path path[ADIE_CODEC_MAX];
- u32 ref_cnt;
- struct marimba_codec_platform_data *codec_pdata;
- };
- static struct snd_soc_codec *timpani_codec;
- enum /* regaccess blk id */
- {
- RA_BLOCK_RX1 = 0,
- RA_BLOCK_RX2,
- RA_BLOCK_TX1,
- RA_BLOCK_TX2,
- RA_BLOCK_LB,
- RA_BLOCK_SHARED_RX_LB,
- RA_BLOCK_SHARED_TX,
- RA_BLOCK_TXFE1,
- RA_BLOCK_TXFE2,
- RA_BLOCK_PA_COMMON,
- RA_BLOCK_PA_EAR,
- RA_BLOCK_PA_HPH,
- RA_BLOCK_PA_LINE,
- RA_BLOCK_PA_AUX,
- RA_BLOCK_ADC,
- RA_BLOCK_DMIC,
- RA_BLOCK_TX_I2S,
- RA_BLOCK_DRV,
- RA_BLOCK_TEST,
- RA_BLOCK_RESERVED,
- RA_BLOCK_NUM,
- };
- enum /* regaccess onwer ID */
- {
- RA_OWNER_NONE = 0,
- RA_OWNER_PATH_RX1,
- RA_OWNER_PATH_RX2,
- RA_OWNER_PATH_TX1,
- RA_OWNER_PATH_TX2,
- RA_OWNER_PATH_LB,
- RA_OWNER_DRV,
- RA_OWNER_NUM,
- };
- struct reg_acc_blk_cfg {
- u8 valid_owners[RA_OWNER_NUM];
- };
- struct timpani_regaccess {
- u8 reg_addr;
- u8 blk_mask[RA_BLOCK_NUM];
- u8 reg_mask;
- u8 reg_default;
- };
- static unsigned int timpani_codec_read(struct snd_soc_codec *codec,
- unsigned int reg)
- {
- struct marimba *pdrv = codec->control_data;
- int rc;
- u8 val;
- rc = marimba_read(pdrv, reg, &val, 1);
- if (IS_ERR_VALUE(rc)) {
- pr_err("%s: fail to write reg %x\n", __func__, reg);
- return 0;
- }
- return val;
- }
- static int timpani_codec_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
- {
- struct marimba *pdrv = codec->control_data;
- int rc;
- rc = marimba_write_bit_mask(pdrv, reg, (u8 *)&value, 1, 0xFF);
- if (IS_ERR_VALUE(rc)) {
- pr_err("%s: fail to write reg %x\n", __func__, reg);
- return -EIO;
- }
- pr_debug("%s: write reg %x val %x\n", __func__, reg, value);
- return 0;
- }
- static void timpani_codec_bring_up(struct snd_soc_codec *codec)
- {
- struct timpani_drv_data *timpani = snd_soc_codec_get_drvdata(codec);
- int rc;
- if (timpani->codec_pdata &&
- timpani->codec_pdata->marimba_codec_power) {
- if (timpani->ref_cnt)
- return;
- /* Codec power up sequence */
- rc = timpani->codec_pdata->marimba_codec_power(1);
- if (rc)
- pr_err("%s: could not power up timpani "
- "codec\n", __func__);
- else {
- timpani_codec_write(codec, 0xFF, 0x08);
- timpani_codec_write(codec, 0xFF, 0x0A);
- timpani_codec_write(codec, 0xFF, 0x0E);
- timpani_codec_write(codec, 0xFF, 0x07);
- timpani_codec_write(codec, 0xFF, 0x17);
- timpani_codec_write(codec, TIMPANI_A_MREF, 0x22);
- msleep(15);
- timpani->ref_cnt++;
- }
- }
- }
- static void timpani_codec_bring_down(struct snd_soc_codec *codec)
- {
- struct timpani_drv_data *timpani = snd_soc_codec_get_drvdata(codec);
- int rc;
- if (timpani->codec_pdata &&
- timpani->codec_pdata->marimba_codec_power) {
- timpani->ref_cnt--;
- if (timpani->ref_cnt >= 1)
- return;
- timpani_codec_write(codec, TIMPANI_A_MREF, TIMPANI_MREF_POR);
- timpani_codec_write(codec, 0xFF, 0x07);
- timpani_codec_write(codec, 0xFF, 0x06);
- timpani_codec_write(codec, 0xFF, 0x0E);
- timpani_codec_write(codec, 0xFF, 0x08);
- rc = timpani->codec_pdata->marimba_codec_power(0);
- if (rc)
- pr_err("%s: could not power down timpani "
- "codec\n", __func__);
- }
- }
- static void timpani_dmic_config(struct snd_soc_codec *codec, int on)
- {
- struct adie_codec_register *regs;
- int regs_sz, i;
- if (on) {
- regs = dmic_on;
- regs_sz = ARRAY_SIZE(dmic_on);
- } else {
- regs = dmic_off;
- regs_sz = ARRAY_SIZE(dmic_off);
- }
- for (i = 0; i < regs_sz; i++)
- timpani_codec_write(codec, regs[i].reg,
- (regs[i].mask & regs[i].val));
- }
- static void timpani_spk_config(struct snd_soc_codec *codec, int on)
- {
- struct adie_codec_register *regs;
- int regs_sz, i;
- if (on) {
- regs = spk_on;
- regs_sz = ARRAY_SIZE(spk_on);
- } else {
- regs = spk_off;
- regs_sz = ARRAY_SIZE(spk_off);
- }
- for (i = 0; i < regs_sz; i++)
- timpani_codec_write(codec, regs[i].reg,
- (regs[i].mask & regs[i].val));
- }
- static int timpani_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
- {
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->card->codec;
- pr_info("%s()\n", __func__);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- pr_info("%s: playback\n", __func__);
- timpani_codec_bring_up(codec);
- timpani_spk_config(codec, 1);
- } else {
- pr_info("%s: Capture\n", __func__);
- timpani_codec_bring_up(codec);
- timpani_dmic_config(codec, 1);
- }
- return 0;
- }
- static void timpani_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
- {
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->card->codec;
- pr_info("%s()\n", __func__);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- timpani_codec_bring_down(codec);
- timpani_spk_config(codec, 0);
- } else {
- timpani_codec_bring_down(codec);
- timpani_dmic_config(codec, 0);
- }
- return;
- }
- int digital_mute(struct snd_soc_dai *dai, int mute)
- {
- struct snd_soc_codec *codec = dai->codec;
- struct adie_codec_register *regs;
- int regs_sz, i;
- if (mute) {
- regs = spk_mute;
- regs_sz = ARRAY_SIZE(spk_mute);
- } else {
- regs = spk_unmute;
- regs_sz = ARRAY_SIZE(spk_unmute);
- }
- for (i = 0; i < regs_sz; i++) {
- timpani_codec_write(codec, regs[i].reg,
- (regs[i].mask & regs[i].val));
- msleep(10);
- }
- return 0;
- }
- static struct snd_soc_dai_ops timpani_dai_ops = {
- .startup = timpani_startup,
- .shutdown = timpani_shutdown,
- };
- struct snd_soc_dai timpani_codec_dai[] = {
- {
- .name = "TIMPANI Rx",
- .playback = {
- .stream_name = "Handset Playback",
- .rates = SNDRV_PCM_RATE_8000_96000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- .rate_max = 96000,
- .rate_min = 8000,
- .channels_min = 1,
- .channels_max = 2,
- },
- .ops = &timpani_dai_ops,
- },
- {
- .name = "TIMPANI Tx",
- .capture = {
- .stream_name = "Handset Capture",
- .rates = SNDRV_PCM_RATE_8000_96000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- .rate_max = 96000,
- .rate_min = 8000,
- .channels_min = 1,
- .channels_max = 2,
- },
- .ops = &timpani_dai_ops,
- }
- };
- static int timpani_soc_probe(struct platform_device *pdev)
- {
- struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec;
- int ret = 0;
- if (!timpani_codec) {
- dev_err(&pdev->dev, "core driver not yet probed\n");
- return -ENODEV;
- }
- socdev->card->codec = timpani_codec;
- codec = timpani_codec;
- ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
- if (ret < 0)
- dev_err(codec->dev, "failed to create pcms\n");
- return ret;
- }
- /* power down chip */
- static int timpani_soc_remove(struct platform_device *pdev)
- {
- struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- snd_soc_free_pcms(socdev);
- return 0;
- }
- struct snd_soc_codec_device soc_codec_dev_timpani = {
- .probe = timpani_soc_probe,
- .remove = timpani_soc_remove,
- };
- EXPORT_SYMBOL_GPL(soc_codec_dev_timpani);
- static int timpani_codec_probe(struct platform_device *pdev)
- {
- struct snd_soc_codec *codec;
- struct timpani_drv_data *priv;
- pr_info("%s()\n", __func__);
- priv = kzalloc(sizeof(struct timpani_drv_data), GFP_KERNEL);
- if (priv == NULL)
- return -ENOMEM;
- codec = &priv->codec;
- snd_soc_codec_set_drvdata(codec, priv);
- priv->codec_pdata = pdev->dev.platform_data;
- mutex_init(&codec->mutex);
- INIT_LIST_HEAD(&codec->dapm_widgets);
- INIT_LIST_HEAD(&codec->dapm_paths);
- codec->name = "TIMPANI";
- codec->owner = THIS_MODULE;
- codec->read = timpani_codec_read;
- codec->write = timpani_codec_write;
- codec->dai = timpani_codec_dai;
- codec->num_dai = ARRAY_SIZE(timpani_codec_dai);
- codec->control_data = platform_get_drvdata(pdev);
- timpani_codec = codec;
- snd_soc_register_dais(timpani_codec_dai, ARRAY_SIZE(timpani_codec_dai));
- snd_soc_register_codec(codec);
- return 0;
- }
- static struct platform_driver timpani_codec_driver = {
- .probe = timpani_codec_probe,
- .driver = {
- .name = "timpani_codec",
- .owner = THIS_MODULE,
- },
- };
- static int __init timpani_codec_init(void)
- {
- return platform_driver_register(&timpani_codec_driver);
- }
- static void __exit timpani_codec_exit(void)
- {
- platform_driver_unregister(&timpani_codec_driver);
- }
- module_init(timpani_codec_init);
- module_exit(timpani_codec_exit);
- MODULE_DESCRIPTION("Timpani codec driver");
- MODULE_VERSION("1.0");
- MODULE_LICENSE("GPL v2");
|