From 731ca45a7610ad4db35eb4f91f5483686d55c5c7 Mon Sep 17 00:00:00 2001 From: =?utf8?q?=E6=9E=97=E8=BE=89=E8=BE=89?= Date: Mon, 7 Jun 2010 13:06:14 +0000 Subject: [PATCH] add rk1000 control and rk1000 codec --- drivers/staging/Kconfig | 1 + drivers/staging/Makefile | 1 + drivers/staging/rk2818/rk1000_control/Kconfig | 7 + .../staging/rk2818/rk1000_control/Makefile | 4 + .../rk2818/rk1000_control/rk1000_control.c | 93 ++ sound/soc/codecs/Kconfig | 4 +- sound/soc/codecs/Makefile | 4 +- sound/soc/codecs/rk1000_codec.c | 1012 +++++++++++++++++ sound/soc/codecs/rk1000_codec.h | 265 +++++ sound/soc/rk2818/Kconfig | 2 +- sound/soc/rk2818/rk2818_rk1000codec.c | 183 +++ 11 files changed, 1572 insertions(+), 4 deletions(-) create mode 100755 drivers/staging/rk2818/rk1000_control/Kconfig create mode 100755 drivers/staging/rk2818/rk1000_control/Makefile create mode 100755 drivers/staging/rk2818/rk1000_control/rk1000_control.c create mode 100644 sound/soc/codecs/rk1000_codec.c create mode 100644 sound/soc/codecs/rk1000_codec.h create mode 100644 sound/soc/rk2818/rk2818_rk1000codec.c diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index ee4ea388c651..88f862c9bb54 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -127,5 +127,6 @@ source "drivers/staging/iio/Kconfig" source "drivers/staging/rk2818/rk2818_dsp/Kconfig" +source "drivers/staging/rk2818/rk1000_control/Kconfig" endif # !STAGING_EXCLUDE_BUILD endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index f671915a19ce..76107aa2c112 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -45,3 +45,4 @@ obj-$(CONFIG_RAR_REGISTER) += rar/ obj-$(CONFIG_DX_SEP) += sep/ obj-$(CONFIG_IIO) += iio/ obj-$(CONFIG_RK2818_DSP) += rk2818/rk2818_dsp/ +obj-$(CONFIG_RK1000_CONTROL) += rk2818/rk1000_control/ diff --git a/drivers/staging/rk2818/rk1000_control/Kconfig b/drivers/staging/rk2818/rk1000_control/Kconfig new file mode 100755 index 000000000000..033c8044f858 --- /dev/null +++ b/drivers/staging/rk2818/rk1000_control/Kconfig @@ -0,0 +1,7 @@ +menu "RK1000 control" +config RK1000_CONTROL + tristate "ROCKCHIP RK1000 CONTROL" + default n + help + rk1000 control module. +endmenu diff --git a/drivers/staging/rk2818/rk1000_control/Makefile b/drivers/staging/rk2818/rk1000_control/Makefile new file mode 100755 index 000000000000..3c56204d5848 --- /dev/null +++ b/drivers/staging/rk2818/rk1000_control/Makefile @@ -0,0 +1,4 @@ +# +# Makefile for the rk1000 control. +# +obj-$(CONFIG_RK1000_CONTROL) += rk1000_control.o \ No newline at end of file diff --git a/drivers/staging/rk2818/rk1000_control/rk1000_control.c b/drivers/staging/rk2818/rk1000_control/rk1000_control.c new file mode 100755 index 000000000000..b6b4cf96832c --- /dev/null +++ b/drivers/staging/rk2818/rk1000_control/rk1000_control.c @@ -0,0 +1,93 @@ +#include +#include +#include +#include +#include + +int reg_send_data(struct i2c_client *client, const char start_reg, + const char *buf, int count, unsigned int scl_rate) +{ + int ret; + struct i2c_adapter *adap = client->adapter; + struct i2c_msg msg; + char tx_buf[count + 1]; + + tx_buf[0] = start_reg; + memcpy(tx_buf+1, buf, count); + + msg.addr = client->addr; + msg.buf = tx_buf; + msg.len = count +1; + msg.flags = client->flags; + msg.scl_rate = scl_rate; + + ret = i2c_transfer(adap, &msg, 1); + + return ret; +} +static int rk1000_control_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + /* reg[0x00] = 0x88, --> ADC_CON + reg[0x01] = 0x0d, --> CODEC_CON + reg[0x02] = 0x22, --> I2C_CON + reg[0x03] = 0x00, --> TVE_CON + */ + char data[4] = {0x88, 0x0d, 0x22, 0x00}; + char start_reg = 0x00; + unsigned int scl_rate = 100 * 10000; /* 100kHz */ + + #if (CONFIG_SND_SOC_RK1000) + data[1] = 0x00; + #endif + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + { + dev_err(&client->dev, "i2c bus does not support the rk1000_control\n"); + return -EIO; + } + mdelay(10); + ret = reg_send_data(client, start_reg, data, 4, scl_rate); + if (ret > 0) + ret = 0; + return ret; +} + +static int rk1000_control_remove(struct i2c_client *client) +{ + return 0; +} + +static const struct i2c_device_id rk1000_control_id[] = { + { "rk1000_control", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rk1000_control_id); + +static struct i2c_driver rk1000_control_driver = { + .driver = { + .name = "rk1000_control", + }, + .probe = rk1000_control_probe, + .remove = rk1000_control_remove, + .id_table = rk1000_control_id, +}; + +static int __init rk1000_control_init(void) +{ + return i2c_add_driver(&rk1000_control_driver); +} + +static void __exit rk1000_control_exit(void) +{ + i2c_del_driver(&rk1000_control_driver); +} + +module_init(rk1000_control_init); +module_exit(rk1000_control_exit); + + +MODULE_DESCRIPTION("RK1000 control driver"); +MODULE_AUTHOR("Rock-chips, "); +MODULE_LICENSE("GPL"); + diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 0edca93af3b0..0d64c692012d 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -216,7 +216,9 @@ config SND_SOC_WM9712 config SND_SOC_WM9713 tristate - +config SND_SOC_RK1000 + tristate + depends on RK1000_CONTROL # Amp config SND_SOC_MAX9877 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index fb4af28486ba..61b5ae365a36 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -44,7 +44,7 @@ snd-soc-wm9705-objs := wm9705.o snd-soc-wm9712-objs := wm9712.o snd-soc-wm9713-objs := wm9713.o snd-soc-wm-hubs-objs := wm_hubs.o - +snd-soc-rk1000-objs := rk1000_codec.o # Amp snd-soc-max9877-objs := max9877.o @@ -94,6 +94,6 @@ obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o - +obj-$(CONFIG_SND_SOC_RK1000) += snd-soc-rk1000.o # Amp obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o diff --git a/sound/soc/codecs/rk1000_codec.c b/sound/soc/codecs/rk1000_codec.c new file mode 100644 index 000000000000..dac31cadcd1a --- /dev/null +++ b/sound/soc/codecs/rk1000_codec.c @@ -0,0 +1,1012 @@ +/* + * rk1000.c -- RK1000 ALSA SoC audio driver + * + * Copyright (C) 2009 rockchip lhh + * + * + * Based on RK1000.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rk1000_codec.h" + +/* + * Debug + */ +#if 0 +#define DBG(x...) printk(KERN_INFO x) +#else +#define DBG(x...) +#endif + +#define err(format, arg...) \ + printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg) +#define info(format, arg...) \ + printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg) + +#define OUT_CAPLESS (0) //ÊÇ·ñΪÎÞµçÈÝÊä³ö£¬1:ÎÞµçÈÝÊä³ö£¬0:ÓеçÈÝÊä³ö + +///static u32 gVolReg = 0x0f; ///0x0f; //ÓÃÓڼǼÒôÁ¿¼Ä´æÆ÷ +//static u32 gCodecVol = 0x0f; +static u8 gR0AReg = 0; //ÓÃÓڼǼR0A¼Ä´æÆ÷µÄÖµ£¬ÓÃÓڸıä²ÉÑùÂÊǰͨ¹ýR0AÍ£Ö¹clk +static u8 gR0BReg = 0; //ÓÃÓڼǼR0B¼Ä´æÆ÷µÄÖµ£¬ÓÃÓڸıä²ÉÑùÂÊǰͨ¹ýR0BÍ£Ö¹interplateºÍdecimation +static u8 gR1314Reg = 0; //ÓÃÓڼǼR13,R14¼Ä´æÆ÷µÄÖµ£¬ÓÃÓÚFMÒôÁ¿Îª0ʱ + +/* + * rk1000 register cache + * We can't read the RK1000 register space when we + * are using 2 wire for device control, so we cache them instead. + */ +static const u16 rk1000_codec_reg[] = { + 0x0005, 0x0004, 0x00fd, 0x00f3, /* 0 */ + 0x0003, 0x0000, 0x0000, 0x0000, /* 4 */ + 0x0000, 0x0005, 0x0000, 0x0000, /* 8 */ + 0x0097, 0x0097, 0x0097, 0x0097, /* 12 */ + 0x0097, 0x0097, 0x00cc, 0x0000, /* 16 */ + 0x0000, 0x00f1, 0x0090, 0x00ff, /* 20 */ + 0x00ff, 0x00ff, 0x009c, 0x0000, /* 24 */ + 0x0000, 0x00ff, 0x00ff, 0x00ff, /* 28 */ +}; + + +/* codec private data */ +struct rk1000_codec_priv { + unsigned int sysclk; + struct snd_soc_codec codec; + struct snd_pcm_hw_constraint_list *sysclk_constraints; + u16 reg_cache[RK1000_CODEC_NUM_REG]; +}; + +/* + * read rk1000 register cache + */ +static inline unsigned int rk1000_codec_read_reg_cache(struct snd_soc_codec *codec, + unsigned int reg) +{ + u16 *cache = codec->reg_cache; + if (reg > RK1000_CACHE_REGNUM) + return -1; + return cache[reg]; +} + +static unsigned int rk1000_codec_read(struct snd_soc_codec *codec, unsigned int r) +{ + struct i2c_msg xfer[1]; + u8 reg = r; + int ret; + struct i2c_client *client = codec->control_data; + + /* Read register */ + xfer[0].addr = (client->addr& 0x60)|(reg+1); + xfer[0].flags = I2C_M_RD; + xfer[0].len = 1; + xfer[0].buf = ® + + ret = i2c_transfer(client->adapter, xfer, 1); + if (ret != 1) { + dev_err(&client->dev, "i2c_transfer() returned %d\n", ret); + return 0; + } + + return reg; +} +/* + * write rk1000 register cache + */ +static inline void rk1000_codec_write_reg_cache(struct snd_soc_codec *codec, + unsigned int reg, unsigned int value) +{ + u16 *cache = codec->reg_cache; + if (reg > RK1000_CACHE_REGNUM) + return; + cache[reg] = value; +} + +static int rk1000_codec_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + u8 data[2]; + struct i2c_client *i2c; + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + data[0] = value & 0x00ff; + rk1000_codec_write_reg_cache (codec, reg, value); + i2c = (struct i2c_client *)codec->control_data; + i2c->addr = (i2c->addr & 0x60)|reg; + if (codec->hw_write(codec->control_data, data, 1) == 2) + return 0; + else + return -EIO; +} + +static const struct snd_kcontrol_new rk1000_codec_snd_controls[] = { + +SOC_DOUBLE_R("Capture Volume", ACCELCODEC_R0C, ACCELCODEC_R0D, 0, 15, 0), +SOC_DOUBLE_R("Capture Switch", ACCELCODEC_R0C, ACCELCODEC_R0D, 7, 1, 1), + +SOC_DOUBLE_R("PCM Volume", ACCELCODEC_R0D, ACCELCODEC_R0E, 0, 7, 0), + +//SOC_SINGLE("Left ADC Capture Volume", ACCELCODEC_R17, 0, 63, 0), +//SOC_SINGLE("Right ADC Capture Volume", ACCELCODEC_R18, 0, 63, 0), + + +}; + + +/* Left Mixer */ +static const struct snd_kcontrol_new rk1000_codec_left_mixer_controls[] = { +SOC_DAPM_SINGLE("Playback Switch", ACCELCODEC_R15, 6, 1, 0), +SOC_DAPM_SINGLE("Left Bypass Switch", ACCELCODEC_R15, 2, 1, 0), + +}; + +/* Right Mixer */ +static const struct snd_kcontrol_new rk1000_codec_right_mixer_controls[] = { +SOC_DAPM_SINGLE("Playback Switch", ACCELCODEC_R15, 7, 1, 0), +SOC_DAPM_SINGLE("Left Bypass Switch", ACCELCODEC_R15, 3, 1, 0), + +}; + + +static const struct snd_soc_dapm_widget rk1000_codec_dapm_widgets[] = { + SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0, + &rk1000_codec_left_mixer_controls[0], + ARRAY_SIZE(rk1000_codec_left_mixer_controls)), + SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0, + &rk1000_codec_right_mixer_controls[0], + ARRAY_SIZE(rk1000_codec_right_mixer_controls)), + + //SND_SOC_DAPM_PGA("Right Out 1", ACCELCODEC_R1E, 0, 0, NULL, 0), + //SND_SOC_DAPM_PGA("Left Out 1", ACCELCODEC_R1E, 1, 0, NULL, 0), + //SND_SOC_DAPM_DAC("Right DAC", "Right Playback", ACCELCODEC_R1F, 1, 0), + //SND_SOC_DAPM_DAC("Left DAC", "Left Playback", ACCELCODEC_R1F, 2, 0), + + SND_SOC_DAPM_ADC("ADC", "Capture", ACCELCODEC_R1D, 6, 1), + SND_SOC_DAPM_ADC("ADC BUFF", "Capture BUFF", ACCELCODEC_R1D, 2, 0), + + + SND_SOC_DAPM_OUTPUT("LOUT1"), + SND_SOC_DAPM_OUTPUT("ROUT1"), + + SND_SOC_DAPM_INPUT("LINPUT1"), + SND_SOC_DAPM_INPUT("RINPUT1"), +}; + +static const struct snd_soc_dapm_route audio_map[] = { + /* left mixer */ + {"Left Mixer", "Playback Switch", "Left DAC"}, + {"Left Mixer", "Left Bypass Switch", "Left Line Mux"}, + {"Right Mixer", "Playback Switch", "Right DAC"}, + {"Right Mixer", "Right Bypass Switch", "Right Line Mux"}, + + /* left out 1 */ + {"Left Out 1", NULL, "Left Mixer"}, + {"LOUT1", NULL, "Left Out 1"}, + + + /* right out 1 */ + {"Right Out 1", NULL, "Right Mixer"}, + {"ROUT1", NULL, "Right Out 1"}, + + /* Left Line Mux */ + {"Left Line Mux", "Line 1", "LINPUT1"}, + {"Left Line Mux", "PGA", "Left PGA Mux"}, + {"Left Line Mux", "Differential", "Differential Mux"}, + + /* Right Line Mux */ + {"Right Line Mux", "Line 1", "RINPUT1"}, + {"Right Line Mux", "PGA", "Right PGA Mux"}, + {"Right Line Mux", "Differential", "Differential Mux"}, + + /* Left PGA Mux */ + {"Left PGA Mux", "Line 1", "LINPUT1"}, + {"Left PGA Mux", "Line 2", "LINPUT2"}, + {"Left PGA Mux", "Line 3", "LINPUT3"}, + {"Left PGA Mux", "Differential", "Differential Mux"}, + + /* Right PGA Mux */ + {"Right PGA Mux", "Line 1", "RINPUT1"}, + {"Right PGA Mux", "Differential", "Differential Mux"}, + + /* Differential Mux */ + {"Differential Mux", "Line 1", "LINPUT1"}, + {"Differential Mux", "Line 1", "RINPUT1"}, + + /* Left ADC Mux */ + {"Left ADC Mux", "Stereo", "Left PGA Mux"}, + {"Left ADC Mux", "Mono (Left)", "Left PGA Mux"}, + {"Left ADC Mux", "Digital Mono", "Left PGA Mux"}, + + /* Right ADC Mux */ + {"Right ADC Mux", "Stereo", "Right PGA Mux"}, + {"Right ADC Mux", "Mono (Right)", "Right PGA Mux"}, + {"Right ADC Mux", "Digital Mono", "Right PGA Mux"}, + + /* ADC */ + {"Left ADC", NULL, "Left ADC Mux"}, + {"Right ADC", NULL, "Right ADC Mux"}, + + /* terminator */ + {NULL, NULL, NULL}, +}; + +struct _coeff_div { + u32 mclk; + u32 rate; + u16 fs; + u8 sr:5; + u8 usb:1; + u8 bclk; +}; + +/* codec hifi mclk clock divider coefficients */ +static const struct _coeff_div coeff_div[] = { + /* 8k */ + {12288000, 8000, 1536, 0x6, 0x0,ASC_BCLKDIV_16}, + {11289600, 8000, 1408, 0x16, 0x0,ASC_BCLKDIV_16}, + {18432000, 8000, 2304, 0x7, 0x0,ASC_BCLKDIV_16}, + {16934400, 8000, 2112, 0x17, 0x0,ASC_BCLKDIV_16}, + {8192000, 8000, 1024, 0x0, 0x0,ASC_BCLKDIV_16}, + {12000000, 8000, 1500, 0x6, 0x1,ASC_BCLKDIV_16}, + + /* 11.025k */ + {11289600, 11025, 1024, 0x18, 0x0,ASC_BCLKDIV_16}, + {16934400, 11025, 1536, 0x19, 0x0,ASC_BCLKDIV_16}, + {12000000, 11025, 1088, 0x19, 0x1,ASC_BCLKDIV_16}, + + /* 12k */ + {12288000, 12000, 1024, 0x8, 0x0,ASC_BCLKDIV_16}, + {18432000, 12000, 1536, 0x9, 0x0,ASC_BCLKDIV_16}, + {12000000, 12000, 1000, 0x8, 0x1,ASC_BCLKDIV_16}, + + /* 16k */ + {12288000, 16000, 768, 0xa, 0x0,ASC_BCLKDIV_8}, + {18432000, 16000, 1152, 0xb, 0x0,ASC_BCLKDIV_8}, + {12000000, 16000, 750, 0xa, 0x1,ASC_BCLKDIV_8}, + + /* 22.05k */ + {11289600, 22050, 512, 0x1a, 0x0,ASC_BCLKDIV_8}, + {16934400, 22050, 768, 0x1b, 0x0,ASC_BCLKDIV_8}, + {12000000, 22050, 544, 0x1b, 0x1,ASC_BCLKDIV_8}, + + /* 24k */ + {12288000, 24000, 512, 0x1c, 0x0,ASC_BCLKDIV_8}, + {18432000, 24000, 768, 0x1d, 0x0,ASC_BCLKDIV_8}, + {12000000, 24000, 500, 0x1c, 0x1,ASC_BCLKDIV_8}, + + /* 32k */ + {12288000, 32000, 384, 0xc, 0x0,ASC_BCLKDIV_8}, + {18432000, 32000, 576, 0xd, 0x0,ASC_BCLKDIV_8}, + {12000000, 32000, 375, 0xa, 0x1,ASC_BCLKDIV_8}, + + /* 44.1k */ + {11289600, 44100, 256, 0x10, 0x0,ASC_BCLKDIV_8}, + {16934400, 44100, 384, 0x11, 0x0,ASC_BCLKDIV_8}, + {12000000, 44100, 272, 0x11, 0x1,ASC_BCLKDIV_8}, + + /* 48k */ + {12288000, 48000, 256, 0x0, 0x0,ASC_BCLKDIV_4}, + {18432000, 48000, 384, 0x1, 0x0,ASC_BCLKDIV_4}, + {12000000, 48000, 250, 0x0, 0x1,ASC_BCLKDIV_4}, + + /* 88.2k */ + {11289600, 88200, 128, 0x1e, 0x0,ASC_BCLKDIV_4}, + {16934400, 88200, 192, 0x1f, 0x0,ASC_BCLKDIV_4}, + {12000000, 88200, 136, 0x1f, 0x1,ASC_BCLKDIV_4}, + + /* 96k */ + {12288000, 96000, 128, 0xe, 0x0,ASC_BCLKDIV_4}, + {18432000, 96000, 192, 0xf, 0x0,ASC_BCLKDIV_4}, + {12000000, 96000, 125, 0xe, 0x1,ASC_BCLKDIV_4}, +}; + +static inline int get_coeff(int mclk, int rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { + if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) + return i; + } + + return -EINVAL; +} + +/* The set of rates we can generate from the above for each SYSCLK */ + +static unsigned int rates_12288[] = { + 8000, 12000, 16000, 24000, 24000, 32000, 48000, 96000, +}; + +static struct snd_pcm_hw_constraint_list constraints_12288 = { + .count = ARRAY_SIZE(rates_12288), + .list = rates_12288, +}; + +static unsigned int rates_112896[] = { + 8000, 11025, 22050, 44100, +}; + +static struct snd_pcm_hw_constraint_list constraints_112896 = { + .count = ARRAY_SIZE(rates_112896), + .list = rates_112896, +}; + +static unsigned int rates_12[] = { + 8000, 11025, 12000, 16000, 22050, 2400, 32000, 41100, 48000, + 48000, 88235, 96000, +}; + +static struct snd_pcm_hw_constraint_list constraints_12 = { + .count = ARRAY_SIZE(rates_12), + .list = rates_12, +}; + +/* + * Note that this should be called from init rather than from hw_params. + */ +static int rk1000_codec_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct rk1000_codec_priv *rk1000_codec = codec->private_data; + + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + + switch (freq) { + case 11289600: + case 18432000: + case 22579200: + case 36864000: + rk1000_codec->sysclk_constraints = &constraints_112896; + rk1000_codec->sysclk = freq; + return 0; + + case 12288000: + case 16934400: + case 24576000: + case 33868800: + rk1000_codec->sysclk_constraints = &constraints_12288; + rk1000_codec->sysclk = freq; + return 0; + + case 12000000: + case 24000000: + rk1000_codec->sysclk_constraints = &constraints_12; + rk1000_codec->sysclk = freq; + return 0; + } + return -EINVAL; +} + +static int rk1000_codec_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u16 iface = 0; + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + iface = 0x0040; + break; + case SND_SOC_DAIFMT_CBS_CFS: + iface = 0x0020; + break; + default: + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface |= 0x0002; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + iface |= 0x0001; + break; + case SND_SOC_DAIFMT_DSP_A: + iface |= 0x0003; + break; + case SND_SOC_DAIFMT_DSP_B: + iface |= 0x0013; + break; + default: + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + iface |= 0x0090; + break; + case SND_SOC_DAIFMT_IB_NF: + iface |= 0x0080; + break; + case SND_SOC_DAIFMT_NB_IF: + iface |= 0x0010; + break; + default: + return -EINVAL; + } + + DBG("Enter::%s----%d iface=%x\n",__FUNCTION__,__LINE__,iface); + rk1000_codec_write(codec, ACCELCODEC_R09, iface); + return 0; +} + +static int rk1000_codec_pcm_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct rk1000_codec_priv *rk1000_codec = codec->private_data; + + /* The set of sample rates that can be supported depends on the + * MCLK supplied to the CODEC - enforce this. + */ + DBG("Enter::%s----%d rk1000_codec->sysclk=%d\n",__FUNCTION__,__LINE__,rk1000_codec->sysclk); + if (!rk1000_codec->sysclk) { + dev_err(codec->dev, + "No MCLK configured, call set_sysclk() on init\n"); + return -EINVAL; + } + + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + rk1000_codec->sysclk_constraints); + + return 0; +} + +static int rk1000_codec_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + 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; + struct rk1000_codec_priv *rk1000_codec = codec->private_data; + u16 iface = rk1000_codec_read_reg_cache(codec, ACCELCODEC_R09) & 0x1f3; + u16 srate = rk1000_codec_read_reg_cache(codec, ACCELCODEC_R00) & 0x180; + int coeff; + + /*by Vincent Hsiung for EQ Vol Change*/ + #define HW_PARAMS_FLAG_EQVOL_ON 0x21 + #define HW_PARAMS_FLAG_EQVOL_OFF 0x22 + if (params->flags == HW_PARAMS_FLAG_EQVOL_ON) + { + u16 r17 = rk1000_codec_read_reg_cache(codec, ACCELCODEC_R17); + u16 r18 = rk1000_codec_read_reg_cache(codec, ACCELCODEC_R18); + + r17 &= (~0x3f); //6db + r18 &= (~0x3f); //6db + + rk1000_codec_write(codec, ACCELCODEC_R17, r17); + rk1000_codec_write(codec, ACCELCODEC_R18, r18); + + return 0; + } + else if (params->flags == HW_PARAMS_FLAG_EQVOL_OFF) + { + u16 r17 = rk1000_codec_read_reg_cache(codec, ACCELCODEC_R17); + u16 r18 = rk1000_codec_read_reg_cache(codec, ACCELCODEC_R18); + + r17 &= (~0x3f); + r17 |= 0x0f; //0db + + r18 &= (~0x3f); + r18 |= 0x0f; //0db + + rk1000_codec_write(codec, ACCELCODEC_R17, r17); + rk1000_codec_write(codec, ACCELCODEC_R18, r18); + return 0; + } + + coeff = get_coeff(rk1000_codec->sysclk, params_rate(params)); + + /* bit size */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + break; + case SNDRV_PCM_FORMAT_S20_3LE: + iface |= 0x0004; + break; + case SNDRV_PCM_FORMAT_S24_LE: + iface |= 0x0008; + break; + case SNDRV_PCM_FORMAT_S32_LE: + iface |= 0x000c; + break; + } + DBG("Enter::%s----%d iface=%x srate =%x rate=%d\n",__FUNCTION__,__LINE__,iface,srate,params_rate(params)); + + rk1000_codec_write(codec,ACCELCODEC_R0C, 0x17); + rk1000_codec_write(codec,ACCELCODEC_R04, ASC_INT_MUTE_L|ASC_INT_MUTE_R|ASC_SIDETONE_L_OFF|ASC_SIDETONE_R_OFF); //soft mute + //±ØÐëÏȽ«clkºÍEN_INT¶¼disableµô£¬·ñÔòÇл»bclk·ÖƵֵ¿ÉÄܵ¼ÖÂcodecÄÚ²¿Ê±Ðò»ìÂÒµô£¬ + //±íÏÖ³öÀ´µÄÏÖÏóÊÇ£¬ÒÔºóµÄÒôÀÖ¶¼±ä³ÉÁËÔëÒô£¬¶øÇÒ¾ÍËã°ÑÊäÈëcodecµÄI2S_DATAOUT¶Ï¿ªÒ²Ò»Ñù³öÔëÒô + rk1000_codec_write(codec,ACCELCODEC_R0B, ASC_DEC_DISABLE|ASC_INT_DISABLE); //0x00 + + /* set iface & srate */ + rk1000_codec_write(codec, ACCELCODEC_R09, iface); + if (coeff >= 0){ + rk1000_codec_write(codec, ACCELCODEC_R0A, (coeff_div[coeff].sr << 1) | coeff_div[coeff].usb|ASC_CLKNODIV|ASC_CLK_ENABLE); + rk1000_codec_write(codec, ACCELCODEC_R00, srate|coeff_div[coeff].bclk); + } + rk1000_codec_write(codec,ACCELCODEC_R0B, gR0BReg); + return 0; +} + +void PhaseOut(struct snd_soc_codec *codec,u32 nStep, u32 us) +{ + DBG("%s[%d]\n",__FUNCTION__,__LINE__); + rk1000_codec_write(codec,ACCELCODEC_R17, 0x0F|ASC_OUTPUT_ACTIVE|ASC_CROSSZERO_EN); //AOL + rk1000_codec_write(codec,ACCELCODEC_R18, 0x0F|ASC_OUTPUT_ACTIVE|ASC_CROSSZERO_EN); //AOR + udelay(us); +} + +void PhaseIn(struct snd_soc_codec *codec,u32 nStep, u32 us) +{ + DBG("%s[%d]\n",__FUNCTION__,__LINE__); + rk1000_codec_write(codec,ACCELCODEC_R17, 0x0f|ASC_OUTPUT_ACTIVE|ASC_CROSSZERO_EN); //AOL gVolReg|ASC_OUTPUT_ACTIVE|ASC_CROSSZERO_EN); //AOL + rk1000_codec_write(codec,ACCELCODEC_R18, 0x0f|ASC_OUTPUT_ACTIVE|ASC_CROSSZERO_EN); //gVolReg|ASC_OUTPUT_ACTIVE|ASC_CROSSZERO_EN); //AOR + udelay(us); +} + +static int rk1000_codec_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + + DBG("Enter::%s----%d--mute=%d\n",__FUNCTION__,__LINE__,mute); + if (mute){ + PhaseOut(codec,1, 5000); + rk1000_codec_write(codec,ACCELCODEC_R19, 0xFF); //AOM + rk1000_codec_write(codec,ACCELCODEC_R04, ASC_INT_MUTE_L|ASC_INT_MUTE_R|ASC_SIDETONE_L_OFF|ASC_SIDETONE_R_OFF); //soft mute + }else{ + rk1000_codec_write(codec,ACCELCODEC_R1D, 0x2a); //setup Vmid and Vref, other module power down + rk1000_codec_write(codec,ACCELCODEC_R1E, 0x40); ///|ASC_PDASDML_ENABLE); + rk1000_codec_write(codec,ACCELCODEC_R1F, 0x09|ASC_PDMIXM_ENABLE|ASC_PDPAM_ENABLE); ///|ASC_PDMICB_ENABLE|ASC_PDMIXM_ENABLE); + PhaseIn(codec,1, 5000); + ///if(gCodecVol != 0){ + rk1000_codec_write(codec,ACCELCODEC_R04, ASC_INT_ACTIVE_L|ASC_INT_ACTIVE_R|ASC_SIDETONE_L_OFF|ASC_SIDETONE_R_OFF); + //} + rk1000_codec_write(codec,ACCELCODEC_R19, 0x7F); //AOM + #if 0 + /*disable speaker */ + rockchip_mux_api_set(SPK_IOMUX_PIN_NAME, SPK_IOMUX_PIN_DIR); + GPIOSetPinDirection(SPK_CTRL_PIN,GPIO_OUT); + GPIOSetPinLevel(SPK_CTRL_PIN,GPIO_HIGH); + #endif + } + return 0; +} + +static int rk1000_codec_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + u16 pwr_reg = rk1000_codec_read_reg_cache(codec, ACCELCODEC_R1D) & ~0x1c1; + DBG("Enter::%s----%d level =%d\n",__FUNCTION__,__LINE__,level); + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + /* VREF, VMID=2x50k, digital enabled */ + rk1000_codec_write(codec, ACCELCODEC_R1D, pwr_reg | 0x0080); + break; + + case SND_SOC_BIAS_STANDBY: + if (codec->bias_level == SND_SOC_BIAS_OFF) { + /* VREF, VMID=2x5k */ + rk1000_codec_write(codec, ACCELCODEC_R1D, pwr_reg | 0x0080); + + /* Charge caps */ + msleep(100); + } + + /* VREF, VMID=2*500k, digital stopped */ + rk1000_codec_write(codec, ACCELCODEC_R1D, pwr_reg | 0x0080); + break; + + case SND_SOC_BIAS_OFF: + rk1000_codec_write(codec, ACCELCODEC_R1D, 0x0000); + break; + } + codec->bias_level = level; + return 0; +} + +#define RK1000_CODEC_RATES SNDRV_PCM_RATE_8000_96000 + +#define RK1000_CODEC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_ops rk1000_codec_ops = { + .startup = rk1000_codec_pcm_startup, + .hw_params = rk1000_codec_pcm_hw_params, + .set_fmt = rk1000_codec_set_dai_fmt, + .set_sysclk = rk1000_codec_set_dai_sysclk, + .digital_mute = rk1000_codec_mute, +}; + +struct snd_soc_dai rk1000_codec_dai = { + .name = "rk1000_codec", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RK1000_CODEC_RATES, + .formats = RK1000_CODEC_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RK1000_CODEC_RATES, + .formats = RK1000_CODEC_FORMATS, + }, + .ops = &rk1000_codec_ops, + .symmetric_rates = 1, +}; +EXPORT_SYMBOL_GPL(rk1000_codec_dai); + +static int rk1000_codec_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->card->codec; + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + rk1000_codec_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static int rk1000_codec_resume(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->card->codec; + int i; + u8 data[2]; + struct i2c_client *i2c; + u16 *cache = codec->reg_cache; + + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + /* Sync reg_cache with the hardware */ + for (i = 0; i < RK1000_CODEC_NUM_REG; i++) { + data[0] = cache[i] & 0x00ff; + i2c = (struct i2c_client *)codec->control_data; + i2c->addr = (i2c->addr & 0x60)|i; + codec->hw_write(codec->control_data, data, 1); + } + + rk1000_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + return 0; +} + +static struct snd_soc_codec *rk1000_codec_codec; + +static int rk1000_codec_probe(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec; + int ret = 0; + + if (rk1000_codec_codec == NULL) { + dev_err(&pdev->dev, "Codec device not registered\n"); + return -ENODEV; + } + + socdev->card->codec = rk1000_codec_codec; + codec = rk1000_codec_codec; + + /* register pcms */ + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); + if (ret < 0) { + dev_err(codec->dev, "failed to create pcms: %d\n", ret); + goto pcm_err; + } + + snd_soc_add_controls(codec, rk1000_codec_snd_controls, + ARRAY_SIZE(rk1000_codec_snd_controls)); + snd_soc_dapm_new_controls(codec, rk1000_codec_dapm_widgets, + ARRAY_SIZE(rk1000_codec_dapm_widgets)); + snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_new_widgets(codec); + + ret = snd_soc_init_card(socdev); + if (ret < 0) { + dev_err(codec->dev, "failed to register card: %d\n", ret); + goto card_err; + } + + return ret; + +card_err: + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); +pcm_err: + return ret; +} + +static int rk1000_codec_remove(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); + return 0; +} + +struct snd_soc_codec_device soc_codec_dev_rk1000_codec = { + .probe = rk1000_codec_probe, + .remove = rk1000_codec_remove, + .suspend = rk1000_codec_suspend, + .resume = rk1000_codec_resume, +}; +EXPORT_SYMBOL_GPL(soc_codec_dev_rk1000_codec); + +static int rk1000_codec_register(struct rk1000_codec_priv *rk1000_codec, + enum snd_soc_control_type control) +{ + struct snd_soc_codec *codec = &rk1000_codec->codec; + int ret; + + if (rk1000_codec_codec) { + dev_err(codec->dev, "Another rk1000 codec is registered\n"); + ret = -EINVAL; + goto err; + } + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + mutex_init(&codec->mutex); + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + + codec->private_data = rk1000_codec; + codec->name = "RK1000_CODEC"; + codec->owner = THIS_MODULE; + codec->dai = &rk1000_codec_dai; + codec->num_dai = 1; + codec->reg_cache_size = ARRAY_SIZE(rk1000_codec->reg_cache); + codec->reg_cache = &rk1000_codec->reg_cache; + codec->bias_level = SND_SOC_BIAS_OFF; + codec->set_bias_level = rk1000_codec_set_bias_level; + + memcpy(codec->reg_cache, rk1000_codec_reg, + sizeof(rk1000_codec_reg)); + + codec->write = rk1000_codec_write; + codec->read = rk1000_codec_read; + codec->hw_write = (hw_write_t)i2c_master_send; + + //ret = snd_soc_codec_set_cache_io(codec, 0, 8, control); + //if (ret < 0) { + //dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); + //goto err; + //} + +#if 1 + gpio_request(RK2818_PIN_PF7, "rk1000_codec"); + rk2818_mux_api_set(GPIOE_SPI1_FLASH_SEL_NAME, IOMUXA_GPIO1_A3B7); + gpio_direction_output(RK2818_PIN_PF7,GPIO_HIGH); + +#endif +#if 1 + rk1000_codec_write(codec,ACCELCODEC_R1D, 0x00); + rk1000_codec_write(codec,ACCELCODEC_R17, 0xFF); //AOL + rk1000_codec_write(codec,ACCELCODEC_R18, 0xFF); //AOR + rk1000_codec_write(codec,ACCELCODEC_R19, 0xFF); //AOM + + rk1000_codec_write(codec,ACCELCODEC_R1F, 0xDF); + mdelay(10); + rk1000_codec_write(codec,ACCELCODEC_R1F, 0x5F); + rk1000_codec_write(codec,ACCELCODEC_R19, 0x7F); //AOM + rk1000_codec_write(codec,ACCELCODEC_R15, 0xC1);//rk1000_codec_write(codec,ACCELCODEC_R15, 0xCD);//by Vincent Hsiung + rk1000_codec_write(codec,ACCELCODEC_R1A, 0x1C); + mdelay(100); + rk1000_codec_write(codec,ACCELCODEC_R1F, 0x09); + rk1000_codec_write(codec,ACCELCODEC_R1E, 0x00); + mdelay(10); + rk1000_codec_write(codec,ACCELCODEC_R1A, 0x14); + rk1000_codec_write(codec,ACCELCODEC_R1D, 0xFE); + rk1000_codec_write(codec,ACCELCODEC_R17, 0xBF); //AOL + rk1000_codec_write(codec,ACCELCODEC_R18, 0xBF); //AOR + rk1000_codec_write(codec,ACCELCODEC_R19, 0x7F); //AOM + rk1000_codec_write(codec,ACCELCODEC_R1F, 0xDF); + + //2soft mute + rk1000_codec_write(codec,ACCELCODEC_R04, ASC_INT_MUTE_L|ASC_INT_MUTE_R|ASC_SIDETONE_L_OFF|ASC_SIDETONE_R_OFF); //soft mute + + //2set default SR and clk + rk1000_codec_write(codec,ACCELCODEC_R0A, ASC_USB_MODE|FREQ48kHz|ASC_CLKNODIV|ASC_CLK_DISABLE); + gR0AReg = ASC_USB_MODE|FREQ48kHz|ASC_CLKNODIV|ASC_CLK_DISABLE; + //2Config audio interface + rk1000_codec_write(codec,ACCELCODEC_R09, ASC_I2S_MODE|ASC_16BIT_MODE|ASC_NORMAL_LRCLK|ASC_LRSWAP_DISABLE|ASC_MASTER_MODE|ASC_NORMAL_BCLK); + rk1000_codec_write(codec,ACCELCODEC_R00, ASC_HPF_ENABLE|ASC_DSM_MODE_DISABLE|ASC_SCRAMBLE_ENABLE|ASC_DITHER_ENABLE|ASC_BCLKDIV_8); //BCLK div 8 + //2volume,input,outpu + rk1000_codec_write(codec,ACCELCODEC_R05, 0x0e); + rk1000_codec_write(codec,ACCELCODEC_R06, 0x42); + rk1000_codec_write(codec,ACCELCODEC_R07, 0x0e); + rk1000_codec_write(codec,ACCELCODEC_R08, 0x42); + + rk1000_codec_write(codec,ACCELCODEC_R0C, 0x10|ASC_INPUT_VOL_0DB|ASC_INPUT_MUTE); //LIL + rk1000_codec_write(codec,ACCELCODEC_R0D, 0x10|ASC_INPUT_VOL_0DB); //LIR + rk1000_codec_write(codec,ACCELCODEC_R0E, 0x10|ASC_INPUT_VOL_0DB); //MIC + rk1000_codec_write(codec,ACCELCODEC_R12, 0x4c|ASC_MIC_INPUT|ASC_MIC_BOOST_20DB); //mic input and boost 20dB + rk1000_codec_write(codec,ACCELCODEC_R13, ASC_LPGAMX_DISABLE|ASC_ALMX_DISABLE|((LINE_2_MIXER_GAIN & 0x7) << 4)|0x0); + rk1000_codec_write(codec,ACCELCODEC_R14, ASC_RPGAMX_DISABLE|ASC_ARMX_DISABLE|((LINE_2_MIXER_GAIN & 0x7) << 4)|0x0); + gR1314Reg = ASC_RPGAMX_DISABLE|ASC_ARMX_DISABLE|((LINE_2_MIXER_GAIN & 0x7) << 4)|0x0; + + //2other + rk1000_codec_write(codec,ACCELCODEC_R0B, ASC_DEC_DISABLE|ASC_INT_DISABLE); //0x00 + gR0BReg = ASC_DEC_DISABLE|ASC_INT_DISABLE; + rk1000_codec_write(codec,ACCELCODEC_R15, \ + 0x01|ASC_RLPFMX_DISABLE|ASC_LLPFMX_DISABLE|ASC_LDAMX_DISABLE|ASC_RDAMX_DISABLE|ASC_LSCF_ACTIVE|ASC_RSCF_ACTIVE); //0x3c + rk1000_codec_write(codec,ACCELCODEC_R1B, 0x32); + rk1000_codec_write(codec,ACCELCODEC_R1C, ASC_DEM_ENABLE); ///0x00); //use default value + + ///dac mode + rk1000_codec_write(codec,ACCELCODEC_R17, 0xBF); //AOL ÒôÁ¿×îµÍ + rk1000_codec_write(codec,ACCELCODEC_R18, 0xBF); //AOR + + //2power down useless module + rk1000_codec_write(codec,ACCELCODEC_R1D, 0x2a|ASC_PDSDL_ENABLE|ASC_PDBSTL_ENABLE|ASC_PDPGAL_ENABLE); //setup Vmid and Vref, other module power down + rk1000_codec_write(codec,ACCELCODEC_R1E, 0x40|ASC_PDASDML_ENABLE); + #if OUT_CAPLESS + rk1000_codec_write(codec,ACCELCODEC_R1F, 0x09|ASC_PDMICB_ENABLE|ASC_PDMIXM_ENABLE); + #else + rk1000_codec_write(codec,ACCELCODEC_R1F, 0x09|ASC_PDMICB_ENABLE|ASC_PDMIXM_ENABLE|ASC_PDPAM_ENABLE); + #endif + + //2other + rk1000_codec_write(codec,ACCELCODEC_R0B, ASC_DEC_DISABLE|ASC_INT_ENABLE); + gR0BReg = ASC_DEC_ENABLE|ASC_INT_ENABLE; //ASC_DEC_DISABLE|ASC_INT_ENABLE; + rk1000_codec_write(codec,ACCELCODEC_R15, 0xC1);//rk1000_codec_write(codec,ACCELCODEC_R15, 0xCD);//by Vincent Hsiung + rk1000_codec_write(codec,ACCELCODEC_R0C, 0x10|ASC_INPUT_VOL_0DB|ASC_INPUT_MUTE); //LIL + rk1000_codec_write(codec,ACCELCODEC_R0D, 0x10|ASC_INPUT_VOL_0DB); //LIR + rk1000_codec_write(codec,ACCELCODEC_R0E, 0x10|ASC_INPUT_VOL_0DB); //MIC + rk1000_codec_write(codec,ACCELCODEC_R12, 0x4c|ASC_MIC_INPUT|ASC_MIC_BOOST_20DB); //mic input and boost 20dB + rk1000_codec_write(codec,ACCELCODEC_R13, 0x00); + rk1000_codec_write(codec,ACCELCODEC_R14, 0x00); + gR1314Reg = 0x00; + rk1000_codec_write(codec,ACCELCODEC_R1C, ASC_DEM_ENABLE); //0x00); //use default value +#endif + + rk1000_codec_set_bias_level(&rk1000_codec->codec, SND_SOC_BIAS_STANDBY); + + rk1000_codec_dai.dev = codec->dev; + + rk1000_codec_codec = codec; + + ret = snd_soc_register_codec(codec); + if (ret != 0) { + dev_err(codec->dev, "Failed to register codec: %d\n", ret); + goto err; + } + + ret = snd_soc_register_dai(&rk1000_codec_dai); + if (ret != 0) { + dev_err(codec->dev, "Failed to register DAI: %d\n", ret); + snd_soc_unregister_codec(codec); + goto err_codec; + } + + return 0; + +err_codec: + snd_soc_unregister_codec(codec); +err: + kfree(rk1000_codec); + return ret; +} + +static void rk1000_codec_unregister(struct rk1000_codec_priv *rk1000_codec) +{ + rk1000_codec_set_bias_level(&rk1000_codec->codec, SND_SOC_BIAS_OFF); + snd_soc_unregister_dai(&rk1000_codec_dai); + snd_soc_unregister_codec(&rk1000_codec->codec); + kfree(rk1000_codec); + rk1000_codec_codec = NULL; +} + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +static int rk1000_codec_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct rk1000_codec_priv *rk1000_codec; + struct snd_soc_codec *codec; + + rk1000_codec = kzalloc(sizeof(struct rk1000_codec_priv), GFP_KERNEL); + if (rk1000_codec == NULL) + return -ENOMEM; + + codec = &rk1000_codec->codec; + + i2c_set_clientdata(i2c, rk1000_codec); + codec->control_data = i2c; + + codec->dev = &i2c->dev; + + return rk1000_codec_register(rk1000_codec, SND_SOC_I2C); +} + +static int rk1000_codec_i2c_remove(struct i2c_client *client) +{ + struct rk1000_codec_priv *rk1000_codec = i2c_get_clientdata(client); + rk1000_codec_unregister(rk1000_codec); + return 0; +} + +#ifdef CONFIG_PM +static int rk1000_codec_i2c_suspend(struct i2c_client *client, pm_message_t msg) +{ + return snd_soc_suspend_device(&client->dev); +} + +static int rk1000_codec_i2c_resume(struct i2c_client *client) +{ + return snd_soc_resume_device(&client->dev); +} +#else +#define rk1000_codec_i2c_suspend NULL +#define rk1000_codec_i2c_resume NULL +#endif + +static const struct i2c_device_id rk1000_codec_i2c_id[] = { + { "rk1000_i2c_codec", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rk1000_codec_i2c_id); + +/* corgi i2c codec control layer */ +static struct i2c_driver rk1000_codec_i2c_driver = { + .driver = { + .name = "RK1000_CODEC", + .owner = THIS_MODULE, + }, + .probe = rk1000_codec_i2c_probe, + .remove = rk1000_codec_i2c_remove, + .suspend = rk1000_codec_i2c_suspend, + .resume = rk1000_codec_i2c_resume, + .id_table = rk1000_codec_i2c_id, +}; +#endif + +static int __init rk1000_codec_modinit(void) +{ + int ret; + + ret = i2c_add_driver(&rk1000_codec_i2c_driver); + if (ret != 0) + pr_err("rk1000 codec: Unable to register I2C driver: %d\n", ret); + return ret; +} +module_init(rk1000_codec_modinit); + +static void __exit rk1000_codec_exit(void) +{ + i2c_del_driver(&rk1000_codec_i2c_driver); +} +module_exit(rk1000_codec_exit); + +MODULE_DESCRIPTION("ASoC RK1000 CODEC driver"); +MODULE_AUTHOR("lhh lhh@rock-chips.com"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rk1000_codec.h b/sound/soc/codecs/rk1000_codec.h new file mode 100644 index 000000000000..96e564f8bde3 --- /dev/null +++ b/sound/soc/codecs/rk1000_codec.h @@ -0,0 +1,265 @@ +/* + * + * Copyright (C) 2009 rockchip lhh + * + * Based on WM8750.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef _RK1000_CODEC_H +#define _RK1000_CODEC_H + +/* RK1000 register space */ + +#define ACCELCODEC_R00 0x00 //ADC High Pass Filter / DSM +#define ACCELCODEC_R01 0x01 //DITHER power +#define ACCELCODEC_R02 0x02 //DITHER power +#define ACCELCODEC_R03 0x03 //DITHER power +#define ACCELCODEC_R04 0x04 //Soft mute / sidetone gain control +#define ACCELCODEC_R05 0x05 //Right interpolate filter volume control (MSB) +#define ACCELCODEC_R06 0x06 //Right interpolate filter volume control (LSB) +#define ACCELCODEC_R07 0x07 //Left interpolate filter volume control (MSB) +#define ACCELCODEC_R08 0x08 //Left interpolate filter volume control (LSB) +#define ACCELCODEC_R09 0x09 //Audio interface control +#define ACCELCODEC_R0A 0x0A //Sample Rate / CLK control +#define ACCELCODEC_R0B 0x0B //Decimation filter / Interpolate filter enable +#define ACCELCODEC_R0C 0x0C //LIN volume +#define ACCELCODEC_R0D 0x0D //LIP volume +#define ACCELCODEC_R0E 0x0E //AL volume +//#define ACCELCODEC_R0F 0x0F //RIN volume +//#define ACCELCODEC_R10 0x10 //RIP volume +//#define ACCELCODEC_R11 0x11 //AR volume +#define ACCELCODEC_R12 0x12 //Input volume +#define ACCELCODEC_R13 0x13 //Left out mix +#define ACCELCODEC_R14 0x14 //Right out mix +#define ACCELCODEC_R15 0x15 //LPF out mix / SCF +#define ACCELCODEC_R16 0x16 //SCF control +#define ACCELCODEC_R17 0x17 //LOUT (AOL) volume +#define ACCELCODEC_R18 0x18 //ROUT (AOR) volume +#define ACCELCODEC_R19 0x19 //MONOOUT (AOM) volume +#define ACCELCODEC_R1A 0x1A //MONOOUT / Reference control +#define ACCELCODEC_R1B 0x1B //Bias Current control +#define ACCELCODEC_R1C 0x1C //ADC control +#define ACCELCODEC_R1D 0x1D //Power Mrg 1 +#define ACCELCODEC_R1E 0x1E //Power Mrg 2 +#define ACCELCODEC_R1F 0x1F //Power Mrg 3 + +#define RK1000_CACHE_REGNUM 0x1F + +//ACCELCODEC_R00 +#define ASC_HPF_ENABLE (0x1) //high_pass filter +#define ASC_HPF_DISABLE (0x0) + +#define ASC_DSM_MODE_ENABLE (0x1 << 1) +#define ASC_DSM_MODE_DISABLE (0x0 << 1) + +#define ASC_SCRAMBLE_ENABLE (0x1 << 2) +#define ASC_SCRAMBLE_DISABLE (0x0 << 2) + +#define ASC_DITHER_ENABLE (0x1 << 3) +#define ASC_DITHER_DISABLE (0x0 << 3) + +#define ASC_BCLKDIV_4 (0x1 << 4) +#define ASC_BCLKDIV_8 (0x2 << 4) +#define ASC_BCLKDIV_16 (0x3 << 4) + +//ACCECODEC_R04 +#define ASC_INT_MUTE_L (0x1) +#define ASC_INT_ACTIVE_L (0x0) +#define ASC_INT_MUTE_R (0x1 << 1) +#define ASC_INT_ACTIVE_R (0x0 << 1) + +#define ASC_SIDETONE_L_OFF (0x0 << 2) +#define ASC_SIDETONE_L_GAIN_MAX (0x1 << 2) +#define ASC_SIDETONE_R_OFF (0x0 << 5) +#define ASC_SIDETONE_R_GAIN_MAX (0x1 << 5) + + +//ACCELCODEC_R05 +#define ASC_INT_VOL_0DB (0x0) + + +//ACCELCODEC_R09 +#define ASC_DSP_MODE (0x3) +#define ASC_I2S_MODE (0x2) +#define ASC_LEFT_MODE (0x1) +//#define ASC_RIGHT_MODE (0x0) + +#define ASC_32BIT_MODE (0x3 << 2) +#define ASC_24BIT_MODE (0x2 << 2) +#define ASC_20BIT_MODE (0x1 << 2) +#define ASC_16BIT_MODE (0x0 << 2) + +#define ASC_INVERT_LRCLK (0x1 << 4) +#define ASC_NORMAL_LRCLK (0x0 << 4) + +#define ASC_LRSWAP_ENABLE (0x1 << 5) +#define ASC_LRSWAP_DISABLE (0x0 << 5) + +#define ASC_MASTER_MODE (0x1 << 6) +#define ASC_SLAVE_MODE (0x0 << 6) + +#define ASC_INVERT_BCLK (0x1 << 7) +#define ASC_NORMAL_BCLK (0x0 << 7) + +//ACCELCODEC_R0A +#define ASC_USB_MODE (0x1) +#define ASC_NORMAL_MODE (0x0) + +#define FREQ96kHz (0x0e << 1) +#define FREQ48kHz (0x00 << 1) +#define FREQ441kHz (0x11 << 1) +#define FREQ32kHz (0x0c << 1) +#define FREQ24kHz (0x1c << 1) +#define FREQ2205kHz (0x1B << 1) +#define FREQ16kHz (0x0a << 1) +#define FREQ12kHz (0x08 << 1) +#define FREQ11025kHz (0x19 << 1) +//#define FREQ9k6Hz 0x09 +#define FREQ8kHz (0x06<<1) + +#define ASC_CLKDIV2 (0x1 << 6) +#define ASC_CLKNODIV (0x0 << 6) + +#define ASC_CLK_ENABLE (0x1 << 7) +#define ASC_CLK_DISABLE (0x0 << 7) + +//ACCELCODEC_R0B +#define ASC_DEC_ENABLE (0x1) //decimation filter enable +#define ASC_DEC_DISABLE (0x0) +#define ASC_INT_ENABLE (0x1 << 1) //interpolate filter enable +#define ASC_INT_DISABLE (0x0 << 1) + +//Input +#define ASC_INPUT_MUTE (0x1 << 7) +#define ASC_INPUT_ACTIVE (0x0 << 7) +#define ASC_INPUT_VOL_0DB (0x0) + +//ACCELCODEC_R12 +#define ASC_LINE_INPUT (0) +#define ASC_MIC_INPUT (1 << 7) + +#define ASC_MIC_BOOST_0DB (0) +#define ASC_MIC_BOOST_20DB (1 << 5) + +//ACCELCODEC_R13 +#define ASC_LPGAMXVOL_0DB (0x5) +#define ASC_LPGAMX_ENABLE (0x1 << 3) //the left channel PGA output is directly fed into the left mixer +#define ASC_LPGAMX_DISABLE (0x0 << 3) +#define ASC_ALMXVOL_0DB (0x5 << 4) +#define ASC_ALMX_ENABLE (0x1 << 7) //the left second line input is directly fed into the left mixer +#define ASC_ALMX_DISABLE (0x0 << 7) + +//ACCELCODEC_R14 +#define ASC_RPGAMXVOL_0DB (0x5) +#define ASC_RPGAMX_ENABLE (0x1 << 3) //the right channel PGA output is directly fed into the right mixer +#define ASC_RPGAMX_DISABLE (0x0 << 3) +#define ASC_ARMXVOL_0DB (0x5 << 4) +#define ASC_ARMX_ENABLE (0x1 << 7) //)the right second line input is directly fed into the right mixer +#define ASC_ARMX_DISABLE (0x0 << 7) + +//ACCELCODEC_R15 +#define ASC_LDAMX_ENABLE (0x1 << 2) //the left differential signal from DAC is directly fed into the left mixer +#define ASC_LDAMX_DISABLE (0x0 << 2) +#define ASC_RDAMX_ENABLE (0x1 << 3) //the right differential signal from DAC is directly fed into the right mixer +#define ASC_RDAMX_DISABLE (0x0 << 3) +#define ASC_LSCF_MUTE (0x1 << 4) //the left channel LPF is mute +#define ASC_LSCF_ACTIVE (0x0 << 4) +#define ASC_RSCF_MUTE (0x1 << 5) //the right channel LPF is mute +#define ASC_RSCF_ACTIVE (0x0 << 5) +#define ASC_LLPFMX_ENABLE (0x1 << 6) //the left channel LPF output is fed into the left into the mixer +#define ASC_LLPFMX_DISABLE (0x0 << 6) +#define ASC_RLPFMX_ENABLE (0x1 << 7) //the right channel LPF output is fed into the right into the mixer. +#define ASC_RLPFMX_DISABLE (0x0 << 7) + +//ACCELCODEC_R17/R18 +#define ASC_OUTPUT_MUTE (0x1 << 6) +#define ASC_OUTPUT_ACTIVE (0x0 << 6) +#define ASC_CROSSZERO_EN (0x1 << 7) +#define ASC_OUTPUT_VOL_0DB (0x0F) +//ACCELCODEC_R19 +#define ASC_MONO_OUTPUT_MUTE (0x1 << 7) +#define ASC_MONO_OUTPUT_ACTIVE (0x0 << 7) +#define ASC_MONO_CROSSZERO_EN (0x1 << 6) + +//ACCELCODEC_R1A +#define ASC_VMDSCL_SLOWEST (0x0 << 2) +#define ASC_VMDSCL_SLOW (0x1 << 2) +#define ASC_VMDSCL_FAST (0x2 << 2) +#define ASC_VMDSCL_FASTEST (0x3 << 2) + +#define ASC_MICBIAS_09 (0x1 << 4) +#define ASC_MICBIAS_06 (0x0 << 4) + +#define ASC_L2M_ENABLE (0x1 << 5) //the right channel LPF output is fed to mono PA +#define ASC_L2M_DISABLE (0x0 << 5) +#define ASC_R2M_ENABLE (0x1 << 6) //the left channel LPF output is fed to mono PA +#define ASC_R2M_DISABLE (0x0 << 6) +#define ASC_CAPLESS_ENABLE (0x1 << 7) //the capless connection is enable +#define ASC_CAPLESS_DISABLE (0x0 << 7) + +//ACCELCODEC_R1C +#define ASC_DITH_0_DIV (0x0 << 3) //the amplitude setting of the ASDM dither(div=vdd/48) +#define ASC_DITH_2_DIV (0x1 << 3) +#define ASC_DITH_4_DIV (0x2 << 3) +#define ASC_DITH_8_DIV (0x3 << 3) + +#define ASC_DITH_ENABLE (0x1 << 5) //the ASDM dither is enabled +#define ASC_DITH_DISABLE (0x0 << 5) + +#define ASC_DEM_ENABLE (0x1 << 7) //the ASDM DEM is enabled +#define ASC_DEM_DISABLE (0x0 << 7) + +//ACCELCODEC_R1D +#define ASC_PDVMID_ENABLE (0x1) //the VMID reference is powered down. VMID is connected to GND +#define ASC_PDVMID_DISABLE (0x0) +#define ASC_PDSDL_ENABLE (0x1 << 2) //the PGA S2D buffer is power down +#define ASC_PDSDL_DISABLE (0x0 << 2) +#define ASC_PDBSTL_ENABLE (0x1 << 4) //the micphone input Op-Amp is power down +#define ASC_PDBSTL_DISABLE (0x0 << 4) +#define ASC_PDPGAL_ENABLE (0x1 << 6) //the PGA is power down +#define ASC_PDPGAL_DISABLE (0x0 << 6) +#define ASC_PDREF_ENABLE (0x1 << 7) //reference generator is power down +#define ASC_PDREF_DISABLE (0x0 << 7) + +//ACCELCODEC_R1E +#define ASC_PDPAR_ENABLE (0x1) //the right channel PA is power down +#define ASC_PDPAR_DISABLE (0x0) +#define ASC_PDPAL_ENABLE (0x1 << 1) //the left channel power amplifier is power down +#define ASC_PDPAL_DISABLE (0x0 << 1) +#define ASC_PDMIXR_ENABLE (0x1 << 2) //the right mixer is power down +#define ASC_PDMIXR_DISABLE (0x0 << 2) +#define ASC_PDMIXL_ENABLE (0x1 << 3) //the left mixer is power down +#define ASC_PDMIXL_DISABLE (0x0 << 3) +#define ASC_PDLPFR_ENABLE (0x1 << 4) //the right RC LPF is power down +#define ASC_PDLPFR_DISABLE (0x0 << 4) +#define ASC_PDLPFL_ENABLE (0x1 << 5) //the left channel RC LPF is power down +#define ASC_PDLPFL_DISABLE (0x0 << 5) +#define ASC_PDASDML_ENABLE (0x1 << 7) //the ASDM is power down +#define ASC_PDASDML_DISABLE (0x0 << 7) + +//ACCELCODEC_R1F +#define ASC_PDSCFR_ENABLE (0x1 << 1) //the right channel DAC is power down +#define ASC_PDSCFR_DISABLE (0x0 << 1) +#define ASC_PDSCFL_ENABLE (0x1 << 2) //the left channel DAC is power down +#define ASC_PDSCFL_DISABLE (0x0 << 2) +#define ASC_PDMICB_ENABLE (0x1 << 4) //the micbias is power down +#define ASC_PDMICB_DISABLE (0x0 << 4) +#define ASC_PDIB_ENABLE (0x1 << 5) //the left channel LPF is power down +#define ASC_PDIB_DISABLE (0x0 << 5) +#define ASC_PDMIXM_ENABLE (0x1 << 6) //the mon mixer is power down +#define ASC_PDMIXM_DISABLE (0x0 << 6) +#define ASC_PDPAM_ENABLE (0x1 << 7) //the mono PA is power down. +#define ASC_PDPAM_DISABLE (0x0 << 7) + +#define LINE_2_MIXER_GAIN (0x5) //left and right PA gain +#define RK1000_CODEC_NUM_REG 0x20 + +extern struct snd_soc_dai rk1000_codec_dai; +extern struct snd_soc_codec_device soc_codec_dev_rk1000_codec; + +#endif diff --git a/sound/soc/rk2818/Kconfig b/sound/soc/rk2818/Kconfig index 5e5c5da1b684..ae359b45d92f 100755 --- a/sound/soc/rk2818/Kconfig +++ b/sound/soc/rk2818/Kconfig @@ -29,7 +29,7 @@ config SND_ROCKCHIP_SOC_WM8994 config SND_ROCKCHIP_SOC_RK1000 tristate "SoC I2S Audio support for rockchip - RK1000" - depends on SND_ROCKCHIP_SOC + depends on SND_ROCKCHIP_SOC && RK1000_CONTROL select SND_ROCKCHIP_SOC_I2S select SND_SOC_RK1000 help diff --git a/sound/soc/rk2818/rk2818_rk1000codec.c b/sound/soc/rk2818/rk2818_rk1000codec.c new file mode 100644 index 000000000000..4474e88554f8 --- /dev/null +++ b/sound/soc/rk2818/rk2818_rk1000codec.c @@ -0,0 +1,183 @@ +/* + * rk2818_wm8988.c -- SoC audio for rockchip + * + * Driver for rockchip wm8988 audio + * Copyright (C) 2009 lhh + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../codecs/rk1000_codec.h" +#include "rk2818_pcm.h" +#include "rk2818_i2s.h" + +#if 0 +#define DBG(x...) printk(KERN_INFO x) +#else +#define DBG(x...) +#endif + +static int rk2818_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + int ret; + + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + /*by Vincent Hsiung for EQ Vol Change*/ + #define HW_PARAMS_FLAG_EQVOL_ON 0x21 + #define HW_PARAMS_FLAG_EQVOL_OFF 0x22 + if ((params->flags == HW_PARAMS_FLAG_EQVOL_ON)||(params->flags == HW_PARAMS_FLAG_EQVOL_OFF)) + { + ret = codec_dai->ops->hw_params(substream, params, codec_dai); //by Vincent + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + } + else + { + /* set codec DAI configuration */ + #if defined (CONFIG_SND_ROCKCHIP_SOC_MASTER) + ret = codec_dai->ops->set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); + #endif + #if defined (CONFIG_SND_ROCKCHIP_SOC_SLAVE) + ret = codec_dai->ops->set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM ); + #endif + if (ret < 0) + return ret; + /* set cpu DAI configuration */ + #if defined (CONFIG_SND_ROCKCHIP_SOC_MASTER) + ret = cpu_dai->ops->set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); + #endif + #if defined (CONFIG_SND_ROCKCHIP_SOC_SLAVE) + ret = cpu_dai->ops->set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); + #endif + if (ret < 0) + return ret; + } + + return 0; +} + +static const struct snd_soc_dapm_widget rk2818_dapm_widgets[] = { + SND_SOC_DAPM_LINE("Audio Out", NULL), + SND_SOC_DAPM_LINE("Line in", NULL), + SND_SOC_DAPM_MIC("Micn", NULL), + SND_SOC_DAPM_MIC("Micp", NULL), +}; + +static const struct snd_soc_dapm_route audio_map[]= { + + {"Audio Out", NULL, "LOUT1"}, + {"Audio Out", NULL, "ROUT1"}, + {"Line in", NULL, "RINPUT1"}, + {"Line in", NULL, "LINPUT1"}, + {"Micn", NULL, "RINPUT2"}, + {"Micp", NULL, "LINPUT2"}, +}; + +/* + * Logic for a rk1000 codec as connected on a rockchip board. + */ +static int rk2818_rk1000_codec_init(struct snd_soc_codec *codec) +{ + struct snd_soc_dai *codec_dai = &codec->dai[0]; + int ret; + + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + + ret = snd_soc_dai_set_sysclk(codec_dai, 0, + 12000000, SND_SOC_CLOCK_IN); + if (ret < 0) { + printk(KERN_ERR "Failed to set WM8988 SYSCLK: %d\n", ret); + return ret; + } + + /* Add specific widgets */ + snd_soc_dapm_new_controls(codec, rk2818_dapm_widgets, + ARRAY_SIZE(rk2818_dapm_widgets)); + + /* Set up specific audio path audio_mapnects */ + snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + + snd_soc_dapm_sync(codec); + + return 0; +} + +static struct snd_soc_ops rk2818_ops = { + .hw_params = rk2818_hw_params, +}; + +static struct snd_soc_dai_link rk2818_dai = { + .name = "RK1000_CODEC", + .stream_name = "RK1000 CODEC PCM", + .cpu_dai = &rk2818_i2s_dai, + .codec_dai = &rk1000_codec_dai, + .init = rk2818_rk1000_codec_init, + .ops = &rk2818_ops, +}; + +static struct snd_soc_card snd_soc_card_rk2818 = { + .name = "RK1000_CODEC", + .platform = &rk2818_soc_platform, + .dai_link = &rk2818_dai, + .num_links = 1, +}; + + +static struct snd_soc_device rk2818_snd_devdata = { + .card = &snd_soc_card_rk2818, + .codec_dev = &soc_codec_dev_rk1000_codec, +}; + +static struct platform_device *rk2818_snd_device; + +static int __init audio_card_init(void) +{ + int ret =0; + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + rk2818_snd_device = platform_device_alloc("soc-audio", -1); + if (!rk2818_snd_device) { + DBG("platform device allocation failed\n"); + ret = -ENOMEM; + return ret; + } + platform_set_drvdata(rk2818_snd_device, &rk2818_snd_devdata); + rk2818_snd_devdata.dev = &rk2818_snd_device->dev; + ret = platform_device_add(rk2818_snd_device); + if (ret) { + DBG("platform device add failed\n"); + platform_device_put(rk2818_snd_device); + } + return ret; +} +static void __exit audio_card_exit(void) +{ + platform_device_unregister(rk2818_snd_device); +} + +module_init(audio_card_init); +module_exit(audio_card_exit); +/* Module information */ +MODULE_AUTHOR("lhh lhh@rock-chips.com"); +MODULE_DESCRIPTION("ROCKCHIP i2s ASoC Interface"); +MODULE_LICENSE("GPL"); -- 2.34.1