aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorRandolph Chung <rchung42@gmail.com>2010-08-20 00:47:53 -0400
committerMark Brown <broonie@opensource.wolfsonmicro.com>2010-08-20 07:14:46 -0400
commit6184f105aa75009e6d380b59316305079a44a6ee (patch)
tree77086d538a4938dd96cf49b0c27ebfaf4bbef0d9 /sound
parentc04019d450a885a095a2ca38fcd5db8d57cd2718 (diff)
ASoC: Add support for tlv320aic3007 to tlv320aic3x codec.
This patch adds support for the tlv320aic3007 codec to the tlv320aic3x driver. The tlv320aic3007 is similar to the aic31, but has an additional class-D speaker amp. The speaker amp control register overlaps with the mono output register of other codecs in this family, so we add logic to identify the actual codec being registered to set things up accordingly. Signed-off-by: Randolph Chung <tausq@parisc-linux.org> Acked-by: Liam Girdwood <lrg@slimlogic.co.uk> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/codecs/tlv320aic3x.c79
-rw-r--r--sound/soc/codecs/tlv320aic3x.h2
2 files changed, 71 insertions, 10 deletions
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index 867bf1fb1825..c07465720cdb 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -12,11 +12,11 @@
12 * 12 *
13 * Notes: 13 * Notes:
14 * The AIC3X is a driver for a low power stereo audio 14 * The AIC3X is a driver for a low power stereo audio
15 * codecs aic31, aic32, aic33. 15 * codecs aic31, aic32, aic33, aic3007.
16 * 16 *
17 * It supports full aic33 codec functionality. 17 * It supports full aic33 codec functionality.
18 * The compatibility with aic32, aic31 is as follows: 18 * The compatibility with aic32, aic31 and aic3007 is as follows:
19 * aic32 | aic31 19 * aic32/aic3007 | aic31
20 * --------------------------------------- 20 * ---------------------------------------
21 * MONO_LOUT -> N/A | MONO_LOUT -> N/A 21 * MONO_LOUT -> N/A | MONO_LOUT -> N/A
22 * | IN1L -> LINE1L 22 * | IN1L -> LINE1L
@@ -70,6 +70,10 @@ struct aic3x_priv {
70 unsigned int sysclk; 70 unsigned int sysclk;
71 int master; 71 int master;
72 int gpio_reset; 72 int gpio_reset;
73#define AIC3X_MODEL_3X 0
74#define AIC3X_MODEL_33 1
75#define AIC3X_MODEL_3007 2
76 u16 model;
73}; 77};
74 78
75/* 79/*
@@ -361,6 +365,14 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = {
361 SOC_ENUM("ADC HPF Cut-off", aic3x_enum[ADC_HPF_ENUM]), 365 SOC_ENUM("ADC HPF Cut-off", aic3x_enum[ADC_HPF_ENUM]),
362}; 366};
363 367
368/*
369 * Class-D amplifier gain. From 0 to 18 dB in 6 dB steps
370 */
371static DECLARE_TLV_DB_SCALE(classd_amp_tlv, 0, 600, 0);
372
373static const struct snd_kcontrol_new aic3x_classd_amp_gain_ctrl =
374 SOC_DOUBLE_TLV("Class-D Amplifier Gain", CLASSD_CTRL, 6, 4, 3, 0, classd_amp_tlv);
375
364/* Left DAC Mux */ 376/* Left DAC Mux */
365static const struct snd_kcontrol_new aic3x_left_dac_mux_controls = 377static const struct snd_kcontrol_new aic3x_left_dac_mux_controls =
366SOC_DAPM_ENUM("Route", aic3x_enum[LDAC_ENUM]); 378SOC_DAPM_ENUM("Route", aic3x_enum[LDAC_ENUM]);
@@ -589,6 +601,15 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
589 SND_SOC_DAPM_INPUT("LINE2R"), 601 SND_SOC_DAPM_INPUT("LINE2R"),
590}; 602};
591 603
604static const struct snd_soc_dapm_widget aic3007_dapm_widgets[] = {
605 /* Class-D outputs */
606 SND_SOC_DAPM_PGA("Left Class-D Out", CLASSD_CTRL, 3, 0, NULL, 0),
607 SND_SOC_DAPM_PGA("Right Class-D Out", CLASSD_CTRL, 2, 0, NULL, 0),
608
609 SND_SOC_DAPM_OUTPUT("SPOP"),
610 SND_SOC_DAPM_OUTPUT("SPOM"),
611};
612
592static const struct snd_soc_dapm_route intercon[] = { 613static const struct snd_soc_dapm_route intercon[] = {
593 /* Left Output */ 614 /* Left Output */
594 {"Left DAC Mux", "DAC_L1", "Left DAC"}, 615 {"Left DAC Mux", "DAC_L1", "Left DAC"},
@@ -759,14 +780,30 @@ static const struct snd_soc_dapm_route intercon[] = {
759 {"GPIO1 dmic modclk", NULL, "DMic Rate 32"}, 780 {"GPIO1 dmic modclk", NULL, "DMic Rate 32"},
760}; 781};
761 782
783static const struct snd_soc_dapm_route intercon_3007[] = {
784 /* Class-D outputs */
785 {"Left Class-D Out", NULL, "Left Line Out"},
786 {"Right Class-D Out", NULL, "Left Line Out"},
787 {"SPOP", NULL, "Left Class-D Out"},
788 {"SPOM", NULL, "Right Class-D Out"},
789};
790
762static int aic3x_add_widgets(struct snd_soc_codec *codec) 791static int aic3x_add_widgets(struct snd_soc_codec *codec)
763{ 792{
793 struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
794
764 snd_soc_dapm_new_controls(codec, aic3x_dapm_widgets, 795 snd_soc_dapm_new_controls(codec, aic3x_dapm_widgets,
765 ARRAY_SIZE(aic3x_dapm_widgets)); 796 ARRAY_SIZE(aic3x_dapm_widgets));
766 797
767 /* set up audio path interconnects */ 798 /* set up audio path interconnects */
768 snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); 799 snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
769 800
801 if (aic3x->model == AIC3X_MODEL_3007) {
802 snd_soc_dapm_new_controls(codec, aic3007_dapm_widgets,
803 ARRAY_SIZE(aic3007_dapm_widgets));
804 snd_soc_dapm_add_routes(codec, intercon_3007, ARRAY_SIZE(intercon_3007));
805 }
806
770 return 0; 807 return 0;
771} 808}
772 809
@@ -1151,6 +1188,7 @@ static int aic3x_resume(struct snd_soc_codec *codec)
1151 */ 1188 */
1152static int aic3x_init(struct snd_soc_codec *codec) 1189static int aic3x_init(struct snd_soc_codec *codec)
1153{ 1190{
1191 struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
1154 int reg; 1192 int reg;
1155 1193
1156 aic3x_write(codec, AIC3X_PAGE_SELECT, PAGE0_SELECT); 1194 aic3x_write(codec, AIC3X_PAGE_SELECT, PAGE0_SELECT);
@@ -1219,6 +1257,17 @@ static int aic3x_init(struct snd_soc_codec *codec)
1219 aic3x_write(codec, LINE2L_2_MONOLOPM_VOL, DEFAULT_VOL); 1257 aic3x_write(codec, LINE2L_2_MONOLOPM_VOL, DEFAULT_VOL);
1220 aic3x_write(codec, LINE2R_2_MONOLOPM_VOL, DEFAULT_VOL); 1258 aic3x_write(codec, LINE2R_2_MONOLOPM_VOL, DEFAULT_VOL);
1221 1259
1260 if (aic3x->model == AIC3X_MODEL_3007) {
1261 /* Class-D speaker driver init; datasheet p. 46 */
1262 aic3x_write(codec, AIC3X_PAGE_SELECT, 0x0D);
1263 aic3x_write(codec, 0xD, 0x0D);
1264 aic3x_write(codec, 0x8, 0x5C);
1265 aic3x_write(codec, 0x8, 0x5D);
1266 aic3x_write(codec, 0x8, 0x5C);
1267 aic3x_write(codec, AIC3X_PAGE_SELECT, 0x00);
1268 aic3x_write(codec, CLASSD_CTRL, 0);
1269 }
1270
1222 /* off, with power on */ 1271 /* off, with power on */
1223 aic3x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); 1272 aic3x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
1224 1273
@@ -1244,6 +1293,8 @@ static int aic3x_probe(struct snd_soc_codec *codec)
1244 1293
1245 snd_soc_add_controls(codec, aic3x_snd_controls, 1294 snd_soc_add_controls(codec, aic3x_snd_controls,
1246 ARRAY_SIZE(aic3x_snd_controls)); 1295 ARRAY_SIZE(aic3x_snd_controls));
1296 if (aic3x->model == AIC3X_MODEL_3007)
1297 snd_soc_add_controls(codec, &aic3x_classd_amp_gain_ctrl, 1);
1247 1298
1248 aic3x_add_widgets(codec); 1299 aic3x_add_widgets(codec);
1249 1300
@@ -1275,6 +1326,14 @@ static struct snd_soc_codec_driver soc_codec_dev_aic3x = {
1275 * 0x18, 0x19, 0x1A, 0x1B 1326 * 0x18, 0x19, 0x1A, 0x1B
1276 */ 1327 */
1277 1328
1329static const struct i2c_device_id aic3x_i2c_id[] = {
1330 [AIC3X_MODEL_3X] = { "tlv320aic3x", 0 },
1331 [AIC3X_MODEL_33] = { "tlv320aic33", 0 },
1332 [AIC3X_MODEL_3007] = { "tlv320aic3007", 0 },
1333 { }
1334};
1335MODULE_DEVICE_TABLE(i2c, aic3x_i2c_id);
1336
1278/* 1337/*
1279 * If the i2c layer weren't so broken, we could pass this kind of data 1338 * If the i2c layer weren't so broken, we could pass this kind of data
1280 * around 1339 * around
@@ -1286,6 +1345,7 @@ static int aic3x_i2c_probe(struct i2c_client *i2c,
1286 struct aic3x_setup_data *setup = pdata->setup; 1345 struct aic3x_setup_data *setup = pdata->setup;
1287 struct aic3x_priv *aic3x; 1346 struct aic3x_priv *aic3x;
1288 int ret, i; 1347 int ret, i;
1348 const struct i2c_device_id *tbl;
1289 1349
1290 aic3x = kzalloc(sizeof(struct aic3x_priv), GFP_KERNEL); 1350 aic3x = kzalloc(sizeof(struct aic3x_priv), GFP_KERNEL);
1291 if (aic3x == NULL) { 1351 if (aic3x == NULL) {
@@ -1306,6 +1366,12 @@ static int aic3x_i2c_probe(struct i2c_client *i2c,
1306 gpio_direction_output(aic3x->gpio_reset, 0); 1366 gpio_direction_output(aic3x->gpio_reset, 0);
1307 } 1367 }
1308 1368
1369 for (tbl = aic3x_i2c_id; tbl->name[0]; tbl++) {
1370 if (!strcmp(tbl->name, id->name))
1371 break;
1372 }
1373 aic3x->model = tbl - aic3x_i2c_id;
1374
1309 for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++) 1375 for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++)
1310 aic3x->supplies[i].supply = aic3x_supply_names[i]; 1376 aic3x->supplies[i].supply = aic3x_supply_names[i];
1311 1377
@@ -1360,13 +1426,6 @@ static int aic3x_i2c_remove(struct i2c_client *client)
1360 return 0; 1426 return 0;
1361} 1427}
1362 1428
1363static const struct i2c_device_id aic3x_i2c_id[] = {
1364 { "tlv320aic3x", 0 },
1365 { "tlv320aic33", 0 },
1366 { }
1367};
1368MODULE_DEVICE_TABLE(i2c, aic3x_i2c_id);
1369
1370/* machine i2c codec control layer */ 1429/* machine i2c codec control layer */
1371static struct i2c_driver aic3x_i2c_driver = { 1430static struct i2c_driver aic3x_i2c_driver = {
1372 .driver = { 1431 .driver = {
diff --git a/sound/soc/codecs/tlv320aic3x.h b/sound/soc/codecs/tlv320aic3x.h
index f6e3d9b42daf..98e44395b662 100644
--- a/sound/soc/codecs/tlv320aic3x.h
+++ b/sound/soc/codecs/tlv320aic3x.h
@@ -111,6 +111,8 @@
111#define DACL1_2_MONOLOPM_VOL 75 111#define DACL1_2_MONOLOPM_VOL 75
112#define DACR1_2_MONOLOPM_VOL 78 112#define DACR1_2_MONOLOPM_VOL 78
113#define MONOLOPM_CTRL 79 113#define MONOLOPM_CTRL 79
114/* Class-D speaker driver on tlv320aic3007 */
115#define CLASSD_CTRL 73
114/* Line Output Plus/Minus control registers */ 116/* Line Output Plus/Minus control registers */
115#define LINE2L_2_LLOPM_VOL 80 117#define LINE2L_2_LLOPM_VOL 80
116#define LINE2L_2_RLOPM_VOL 87 118#define LINE2L_2_RLOPM_VOL 87