| OLD | NEW |
| 1 /* | 1 /* |
| 2 * 88pm860x-codec.c -- 88PM860x ALSA SoC Audio Driver | 2 * 88pm860x-codec.c -- 88PM860x ALSA SoC Audio Driver |
| 3 * | 3 * |
| 4 * Copyright 2010 Marvell International Ltd. | 4 * Copyright 2010 Marvell International Ltd. |
| 5 * Author: Haojian Zhuang <haojian.zhuang@marvell.com> | 5 * Author: Haojian Zhuang <haojian.zhuang@marvell.com> |
| 6 * | 6 * |
| 7 * This program is free software; you can redistribute it and/or modify | 7 * This program is free software; you can redistribute it and/or modify |
| 8 * it under the terms of the GNU General Public License version 2 as | 8 * it under the terms of the GNU General Public License version 2 as |
| 9 * published by the Free Software Foundation. | 9 * published by the Free Software Foundation. |
| 10 */ | 10 */ |
| 11 | 11 |
| 12 #include <linux/kernel.h> | 12 #include <linux/kernel.h> |
| 13 #include <linux/module.h> | 13 #include <linux/module.h> |
| 14 #include <linux/i2c.h> | 14 #include <linux/i2c.h> |
| 15 #include <linux/platform_device.h> | 15 #include <linux/platform_device.h> |
| 16 #include <linux/mfd/88pm860x.h> | 16 #include <linux/mfd/88pm860x.h> |
| 17 #include <linux/slab.h> | 17 #include <linux/slab.h> |
| 18 #include <sound/core.h> | 18 #include <sound/core.h> |
| 19 #include <sound/pcm.h> | 19 #include <sound/pcm.h> |
| 20 #include <sound/pcm_params.h> | 20 #include <sound/pcm_params.h> |
| 21 #include <sound/soc.h> | 21 #include <sound/soc.h> |
| 22 #include <sound/soc-dapm.h> | |
| 23 #include <sound/tlv.h> | 22 #include <sound/tlv.h> |
| 24 #include <sound/initval.h> | 23 #include <sound/initval.h> |
| 25 #include <sound/jack.h> | 24 #include <sound/jack.h> |
| 25 #include <trace/events/asoc.h> |
| 26 | 26 |
| 27 #include "88pm860x-codec.h" | 27 #include "88pm860x-codec.h" |
| 28 | 28 |
| 29 #define MAX_NAME_LEN 20 | 29 #define MAX_NAME_LEN 20 |
| 30 #define REG_CACHE_SIZE 0x40 | 30 #define REG_CACHE_SIZE 0x40 |
| 31 #define REG_CACHE_BASE 0xb0 | 31 #define REG_CACHE_BASE 0xb0 |
| 32 | 32 |
| 33 /* Status Register 1 (0x01) */ | 33 /* Status Register 1 (0x01) */ |
| 34 #define REG_STATUS_1 0x01 | 34 #define REG_STATUS_1 0x01 |
| 35 #define MIC_STATUS (1 << 7) | 35 #define MIC_STATUS (1 << 7) |
| (...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 139 unsigned int pcmclk; | 139 unsigned int pcmclk; |
| 140 unsigned int dir; | 140 unsigned int dir; |
| 141 unsigned int filter; | 141 unsigned int filter; |
| 142 struct snd_soc_codec *codec; | 142 struct snd_soc_codec *codec; |
| 143 struct i2c_client *i2c; | 143 struct i2c_client *i2c; |
| 144 struct pm860x_chip *chip; | 144 struct pm860x_chip *chip; |
| 145 struct pm860x_det det; | 145 struct pm860x_det det; |
| 146 | 146 |
| 147 int irq[4]; | 147 int irq[4]; |
| 148 unsigned char name[4][MAX_NAME_LEN]; | 148 unsigned char name[4][MAX_NAME_LEN]; |
| 149 unsigned char reg_cache[REG_CACHE_SIZE]; | |
| 150 }; | 149 }; |
| 151 | 150 |
| 152 /* -9450dB to 0dB in 150dB steps ( mute instead of -9450dB) */ | 151 /* -9450dB to 0dB in 150dB steps ( mute instead of -9450dB) */ |
| 153 static const DECLARE_TLV_DB_SCALE(dpga_tlv, -9450, 150, 1); | 152 static const DECLARE_TLV_DB_SCALE(dpga_tlv, -9450, 150, 1); |
| 154 | 153 |
| 155 /* -9dB to 0db in 3dB steps */ | 154 /* -9dB to 0db in 3dB steps */ |
| 156 static const DECLARE_TLV_DB_SCALE(adc_tlv, -900, 300, 0); | 155 static const DECLARE_TLV_DB_SCALE(adc_tlv, -900, 300, 0); |
| 157 | 156 |
| 158 /* {-23, -17, -13.5, -11, -9, -6, -3, 0}dB */ | 157 /* {-23, -17, -13.5, -11, -9, -6, -3, 0}dB */ |
| 159 static const unsigned int mic_tlv[] = { | 158 static const unsigned int mic_tlv[] = { |
| (...skipping 1005 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1165 int data; | 1164 int data; |
| 1166 | 1165 |
| 1167 switch (level) { | 1166 switch (level) { |
| 1168 case SND_SOC_BIAS_ON: | 1167 case SND_SOC_BIAS_ON: |
| 1169 break; | 1168 break; |
| 1170 | 1169 |
| 1171 case SND_SOC_BIAS_PREPARE: | 1170 case SND_SOC_BIAS_PREPARE: |
| 1172 break; | 1171 break; |
| 1173 | 1172 |
| 1174 case SND_SOC_BIAS_STANDBY: | 1173 case SND_SOC_BIAS_STANDBY: |
| 1175 » » if (codec->bias_level == SND_SOC_BIAS_OFF) { | 1174 » » if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { |
| 1176 /* Enable Audio PLL & Audio section */ | 1175 /* Enable Audio PLL & Audio section */ |
| 1177 data = AUDIO_PLL | AUDIO_SECTION_RESET | 1176 data = AUDIO_PLL | AUDIO_SECTION_RESET |
| 1178 | AUDIO_SECTION_ON; | 1177 | AUDIO_SECTION_ON; |
| 1179 pm860x_reg_write(codec->control_data, REG_MISC2, data); | 1178 pm860x_reg_write(codec->control_data, REG_MISC2, data); |
| 1180 } | 1179 } |
| 1181 break; | 1180 break; |
| 1182 | 1181 |
| 1183 case SND_SOC_BIAS_OFF: | 1182 case SND_SOC_BIAS_OFF: |
| 1184 data = AUDIO_PLL | AUDIO_SECTION_RESET | AUDIO_SECTION_ON; | 1183 data = AUDIO_PLL | AUDIO_SECTION_RESET | AUDIO_SECTION_ON; |
| 1185 pm860x_set_bits(codec->control_data, REG_MISC2, data, 0); | 1184 pm860x_set_bits(codec->control_data, REG_MISC2, data, 0); |
| 1186 break; | 1185 break; |
| 1187 } | 1186 } |
| 1188 » codec->bias_level = level; | 1187 » codec->dapm.bias_level = level; |
| 1189 return 0; | 1188 return 0; |
| 1190 } | 1189 } |
| 1191 | 1190 |
| 1192 static struct snd_soc_dai_ops pm860x_pcm_dai_ops = { | 1191 static struct snd_soc_dai_ops pm860x_pcm_dai_ops = { |
| 1193 .digital_mute = pm860x_digital_mute, | 1192 .digital_mute = pm860x_digital_mute, |
| 1194 .hw_params = pm860x_pcm_hw_params, | 1193 .hw_params = pm860x_pcm_hw_params, |
| 1195 .set_fmt = pm860x_pcm_set_dai_fmt, | 1194 .set_fmt = pm860x_pcm_set_dai_fmt, |
| 1196 .set_sysclk = pm860x_set_dai_sysclk, | 1195 .set_sysclk = pm860x_set_dai_sysclk, |
| 1197 }; | 1196 }; |
| 1198 | 1197 |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1256 { | 1255 { |
| 1257 struct pm860x_priv *pm860x = data; | 1256 struct pm860x_priv *pm860x = data; |
| 1258 int status, shrt, report = 0, mic_report = 0; | 1257 int status, shrt, report = 0, mic_report = 0; |
| 1259 int mask; | 1258 int mask; |
| 1260 | 1259 |
| 1261 status = pm860x_reg_read(pm860x->i2c, REG_STATUS_1); | 1260 status = pm860x_reg_read(pm860x->i2c, REG_STATUS_1); |
| 1262 shrt = pm860x_reg_read(pm860x->i2c, REG_SHORTS); | 1261 shrt = pm860x_reg_read(pm860x->i2c, REG_SHORTS); |
| 1263 mask = pm860x->det.hs_shrt | pm860x->det.hook_det | pm860x->det.lo_shrt | 1262 mask = pm860x->det.hs_shrt | pm860x->det.hook_det | pm860x->det.lo_shrt |
| 1264 | pm860x->det.hp_det; | 1263 | pm860x->det.hp_det; |
| 1265 | 1264 |
| 1265 #ifndef CONFIG_SND_SOC_88PM860X_MODULE |
| 1266 if (status & (HEADSET_STATUS | MIC_STATUS | SHORT_HS1 | SHORT_HS2 | |
| 1267 SHORT_LO1 | SHORT_LO2)) |
| 1268 trace_snd_soc_jack_irq(dev_name(pm860x->codec->dev)); |
| 1269 #endif |
| 1270 |
| 1266 if ((pm860x->det.hp_det & SND_JACK_HEADPHONE) | 1271 if ((pm860x->det.hp_det & SND_JACK_HEADPHONE) |
| 1267 && (status & HEADSET_STATUS)) | 1272 && (status & HEADSET_STATUS)) |
| 1268 report |= SND_JACK_HEADPHONE; | 1273 report |= SND_JACK_HEADPHONE; |
| 1269 | 1274 |
| 1270 if ((pm860x->det.mic_det & SND_JACK_MICROPHONE) | 1275 if ((pm860x->det.mic_det & SND_JACK_MICROPHONE) |
| 1271 && (status & MIC_STATUS)) | 1276 && (status & MIC_STATUS)) |
| 1272 mic_report |= SND_JACK_MICROPHONE; | 1277 mic_report |= SND_JACK_MICROPHONE; |
| 1273 | 1278 |
| 1274 if (pm860x->det.hs_shrt && (shrt & (SHORT_HS1 | SHORT_HS2))) | 1279 if (pm860x->det.hs_shrt && (shrt & (SHORT_HS1 | SHORT_HS2))) |
| 1275 report |= pm860x->det.hs_shrt; | 1280 report |= pm860x->det.hs_shrt; |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1339 | 1344 |
| 1340 /* sync status */ | 1345 /* sync status */ |
| 1341 pm860x_codec_handler(0, pm860x); | 1346 pm860x_codec_handler(0, pm860x); |
| 1342 return 0; | 1347 return 0; |
| 1343 } | 1348 } |
| 1344 EXPORT_SYMBOL_GPL(pm860x_mic_jack_detect); | 1349 EXPORT_SYMBOL_GPL(pm860x_mic_jack_detect); |
| 1345 | 1350 |
| 1346 static int pm860x_probe(struct snd_soc_codec *codec) | 1351 static int pm860x_probe(struct snd_soc_codec *codec) |
| 1347 { | 1352 { |
| 1348 struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec); | 1353 struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec); |
| 1354 struct snd_soc_dapm_context *dapm = &codec->dapm; |
| 1349 int i, ret; | 1355 int i, ret; |
| 1350 | 1356 |
| 1351 pm860x->codec = codec; | 1357 pm860x->codec = codec; |
| 1352 | 1358 |
| 1353 codec->control_data = pm860x->i2c; | 1359 codec->control_data = pm860x->i2c; |
| 1354 | 1360 |
| 1355 for (i = 0; i < 4; i++) { | 1361 for (i = 0; i < 4; i++) { |
| 1356 ret = request_threaded_irq(pm860x->irq[i], NULL, | 1362 ret = request_threaded_irq(pm860x->irq[i], NULL, |
| 1357 pm860x_codec_handler, IRQF_ONESHOT, | 1363 pm860x_codec_handler, IRQF_ONESHOT, |
| 1358 pm860x->name[i], pm860x); | 1364 pm860x->name[i], pm860x); |
| 1359 if (ret < 0) { | 1365 if (ret < 0) { |
| 1360 dev_err(codec->dev, "Failed to request IRQ!\n"); | 1366 dev_err(codec->dev, "Failed to request IRQ!\n"); |
| 1361 » » » goto out_irq; | 1367 » » » goto out; |
| 1362 } | 1368 } |
| 1363 } | 1369 } |
| 1364 | 1370 |
| 1365 pm860x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | 1371 pm860x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); |
| 1366 | 1372 |
| 1367 ret = pm860x_bulk_read(codec->control_data, REG_CACHE_BASE, | 1373 ret = pm860x_bulk_read(codec->control_data, REG_CACHE_BASE, |
| 1368 REG_CACHE_SIZE, codec->reg_cache); | 1374 REG_CACHE_SIZE, codec->reg_cache); |
| 1369 if (ret < 0) { | 1375 if (ret < 0) { |
| 1370 dev_err(codec->dev, "Failed to fill register cache: %d\n", | 1376 dev_err(codec->dev, "Failed to fill register cache: %d\n", |
| 1371 ret); | 1377 ret); |
| 1372 » » goto out_codec; | 1378 » » goto out; |
| 1373 } | 1379 } |
| 1374 | 1380 |
| 1375 snd_soc_add_controls(codec, pm860x_snd_controls, | 1381 snd_soc_add_controls(codec, pm860x_snd_controls, |
| 1376 ARRAY_SIZE(pm860x_snd_controls)); | 1382 ARRAY_SIZE(pm860x_snd_controls)); |
| 1377 » snd_soc_dapm_new_controls(codec, pm860x_dapm_widgets, | 1383 » snd_soc_dapm_new_controls(dapm, pm860x_dapm_widgets, |
| 1378 ARRAY_SIZE(pm860x_dapm_widgets)); | 1384 ARRAY_SIZE(pm860x_dapm_widgets)); |
| 1379 » snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); | 1385 » snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); |
| 1380 return 0; | 1386 return 0; |
| 1381 | 1387 |
| 1382 out_codec: | 1388 out: |
| 1383 » i = 3; | 1389 » while (--i >= 0) |
| 1384 out_irq: | |
| 1385 » for (; i >= 0; i--) | |
| 1386 free_irq(pm860x->irq[i], pm860x); | 1390 free_irq(pm860x->irq[i], pm860x); |
| 1387 » return -EINVAL; | 1391 » return ret; |
| 1388 } | 1392 } |
| 1389 | 1393 |
| 1390 static int pm860x_remove(struct snd_soc_codec *codec) | 1394 static int pm860x_remove(struct snd_soc_codec *codec) |
| 1391 { | 1395 { |
| 1392 struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec); | 1396 struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec); |
| 1393 int i; | 1397 int i; |
| 1394 | 1398 |
| 1395 for (i = 3; i >= 0; i--) | 1399 for (i = 3; i >= 0; i--) |
| 1396 free_irq(pm860x->irq[i], pm860x); | 1400 free_irq(pm860x->irq[i], pm860x); |
| 1397 pm860x_set_bias_level(codec, SND_SOC_BIAS_OFF); | 1401 pm860x_set_bias_level(codec, SND_SOC_BIAS_OFF); |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1477 { | 1481 { |
| 1478 platform_driver_unregister(&pm860x_codec_driver); | 1482 platform_driver_unregister(&pm860x_codec_driver); |
| 1479 } | 1483 } |
| 1480 module_exit(pm860x_exit); | 1484 module_exit(pm860x_exit); |
| 1481 | 1485 |
| 1482 MODULE_DESCRIPTION("ASoC 88PM860x driver"); | 1486 MODULE_DESCRIPTION("ASoC 88PM860x driver"); |
| 1483 MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>"); | 1487 MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>"); |
| 1484 MODULE_LICENSE("GPL"); | 1488 MODULE_LICENSE("GPL"); |
| 1485 MODULE_ALIAS("platform:88pm860x-codec"); | 1489 MODULE_ALIAS("platform:88pm860x-codec"); |
| 1486 | 1490 |
| OLD | NEW |