diff options
Diffstat (limited to 'sound/soc/codecs/wm8350.c')
-rw-r--r-- | sound/soc/codecs/wm8350.c | 166 |
1 files changed, 135 insertions, 31 deletions
diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c index 35d99750c383..3b1d0993bed9 100644 --- a/sound/soc/codecs/wm8350.c +++ b/sound/soc/codecs/wm8350.c | |||
@@ -51,10 +51,17 @@ struct wm8350_output { | |||
51 | u16 mute; | 51 | u16 mute; |
52 | }; | 52 | }; |
53 | 53 | ||
54 | struct wm8350_jack_data { | ||
55 | struct snd_soc_jack *jack; | ||
56 | int report; | ||
57 | }; | ||
58 | |||
54 | struct wm8350_data { | 59 | struct wm8350_data { |
55 | struct snd_soc_codec codec; | 60 | struct snd_soc_codec codec; |
56 | struct wm8350_output out1; | 61 | struct wm8350_output out1; |
57 | struct wm8350_output out2; | 62 | struct wm8350_output out2; |
63 | struct wm8350_jack_data hpl; | ||
64 | struct wm8350_jack_data hpr; | ||
58 | struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)]; | 65 | struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)]; |
59 | }; | 66 | }; |
60 | 67 | ||
@@ -775,21 +782,6 @@ static const struct snd_soc_dapm_route audio_map[] = { | |||
775 | {"Beep", NULL, "IN3R PGA"}, | 782 | {"Beep", NULL, "IN3R PGA"}, |
776 | }; | 783 | }; |
777 | 784 | ||
778 | static int wm8350_add_controls(struct snd_soc_codec *codec) | ||
779 | { | ||
780 | int err, i; | ||
781 | |||
782 | for (i = 0; i < ARRAY_SIZE(wm8350_snd_controls); i++) { | ||
783 | err = snd_ctl_add(codec->card, | ||
784 | snd_soc_cnew(&wm8350_snd_controls[i], | ||
785 | codec, NULL)); | ||
786 | if (err < 0) | ||
787 | return err; | ||
788 | } | ||
789 | |||
790 | return 0; | ||
791 | } | ||
792 | |||
793 | static int wm8350_add_widgets(struct snd_soc_codec *codec) | 785 | static int wm8350_add_widgets(struct snd_soc_codec *codec) |
794 | { | 786 | { |
795 | int ret; | 787 | int ret; |
@@ -1309,7 +1301,7 @@ static int wm8350_set_bias_level(struct snd_soc_codec *codec, | |||
1309 | static int wm8350_suspend(struct platform_device *pdev, pm_message_t state) | 1301 | static int wm8350_suspend(struct platform_device *pdev, pm_message_t state) |
1310 | { | 1302 | { |
1311 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | 1303 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); |
1312 | struct snd_soc_codec *codec = socdev->codec; | 1304 | struct snd_soc_codec *codec = socdev->card->codec; |
1313 | 1305 | ||
1314 | wm8350_set_bias_level(codec, SND_SOC_BIAS_OFF); | 1306 | wm8350_set_bias_level(codec, SND_SOC_BIAS_OFF); |
1315 | return 0; | 1307 | return 0; |
@@ -1318,7 +1310,7 @@ static int wm8350_suspend(struct platform_device *pdev, pm_message_t state) | |||
1318 | static int wm8350_resume(struct platform_device *pdev) | 1310 | static int wm8350_resume(struct platform_device *pdev) |
1319 | { | 1311 | { |
1320 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | 1312 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); |
1321 | struct snd_soc_codec *codec = socdev->codec; | 1313 | struct snd_soc_codec *codec = socdev->card->codec; |
1322 | 1314 | ||
1323 | wm8350_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | 1315 | wm8350_set_bias_level(codec, SND_SOC_BIAS_STANDBY); |
1324 | 1316 | ||
@@ -1328,6 +1320,95 @@ static int wm8350_resume(struct platform_device *pdev) | |||
1328 | return 0; | 1320 | return 0; |
1329 | } | 1321 | } |
1330 | 1322 | ||
1323 | static void wm8350_hp_jack_handler(struct wm8350 *wm8350, int irq, void *data) | ||
1324 | { | ||
1325 | struct wm8350_data *priv = data; | ||
1326 | u16 reg; | ||
1327 | int report; | ||
1328 | int mask; | ||
1329 | struct wm8350_jack_data *jack = NULL; | ||
1330 | |||
1331 | switch (irq) { | ||
1332 | case WM8350_IRQ_CODEC_JCK_DET_L: | ||
1333 | jack = &priv->hpl; | ||
1334 | mask = WM8350_JACK_L_LVL; | ||
1335 | break; | ||
1336 | |||
1337 | case WM8350_IRQ_CODEC_JCK_DET_R: | ||
1338 | jack = &priv->hpr; | ||
1339 | mask = WM8350_JACK_R_LVL; | ||
1340 | break; | ||
1341 | |||
1342 | default: | ||
1343 | BUG(); | ||
1344 | } | ||
1345 | |||
1346 | if (!jack->jack) { | ||
1347 | dev_warn(wm8350->dev, "Jack interrupt called with no jack\n"); | ||
1348 | return; | ||
1349 | } | ||
1350 | |||
1351 | /* Debounce */ | ||
1352 | msleep(200); | ||
1353 | |||
1354 | reg = wm8350_reg_read(wm8350, WM8350_JACK_PIN_STATUS); | ||
1355 | if (reg & mask) | ||
1356 | report = jack->report; | ||
1357 | else | ||
1358 | report = 0; | ||
1359 | |||
1360 | snd_soc_jack_report(jack->jack, report, jack->report); | ||
1361 | } | ||
1362 | |||
1363 | /** | ||
1364 | * wm8350_hp_jack_detect - Enable headphone jack detection. | ||
1365 | * | ||
1366 | * @codec: WM8350 codec | ||
1367 | * @which: left or right jack detect signal | ||
1368 | * @jack: jack to report detection events on | ||
1369 | * @report: value to report | ||
1370 | * | ||
1371 | * Enables the headphone jack detection of the WM8350. | ||
1372 | */ | ||
1373 | int wm8350_hp_jack_detect(struct snd_soc_codec *codec, enum wm8350_jack which, | ||
1374 | struct snd_soc_jack *jack, int report) | ||
1375 | { | ||
1376 | struct wm8350_data *priv = codec->private_data; | ||
1377 | struct wm8350 *wm8350 = codec->control_data; | ||
1378 | int irq; | ||
1379 | int ena; | ||
1380 | |||
1381 | switch (which) { | ||
1382 | case WM8350_JDL: | ||
1383 | priv->hpl.jack = jack; | ||
1384 | priv->hpl.report = report; | ||
1385 | irq = WM8350_IRQ_CODEC_JCK_DET_L; | ||
1386 | ena = WM8350_JDL_ENA; | ||
1387 | break; | ||
1388 | |||
1389 | case WM8350_JDR: | ||
1390 | priv->hpr.jack = jack; | ||
1391 | priv->hpr.report = report; | ||
1392 | irq = WM8350_IRQ_CODEC_JCK_DET_R; | ||
1393 | ena = WM8350_JDR_ENA; | ||
1394 | break; | ||
1395 | |||
1396 | default: | ||
1397 | return -EINVAL; | ||
1398 | } | ||
1399 | |||
1400 | wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_TOCLK_ENA); | ||
1401 | wm8350_set_bits(wm8350, WM8350_JACK_DETECT, ena); | ||
1402 | |||
1403 | /* Sync status */ | ||
1404 | wm8350_hp_jack_handler(wm8350, irq, priv); | ||
1405 | |||
1406 | wm8350_unmask_irq(wm8350, irq); | ||
1407 | |||
1408 | return 0; | ||
1409 | } | ||
1410 | EXPORT_SYMBOL_GPL(wm8350_hp_jack_detect); | ||
1411 | |||
1331 | static struct snd_soc_codec *wm8350_codec; | 1412 | static struct snd_soc_codec *wm8350_codec; |
1332 | 1413 | ||
1333 | static int wm8350_probe(struct platform_device *pdev) | 1414 | static int wm8350_probe(struct platform_device *pdev) |
@@ -1342,8 +1423,8 @@ static int wm8350_probe(struct platform_device *pdev) | |||
1342 | 1423 | ||
1343 | BUG_ON(!wm8350_codec); | 1424 | BUG_ON(!wm8350_codec); |
1344 | 1425 | ||
1345 | socdev->codec = wm8350_codec; | 1426 | socdev->card->codec = wm8350_codec; |
1346 | codec = socdev->codec; | 1427 | codec = socdev->card->codec; |
1347 | wm8350 = codec->control_data; | 1428 | wm8350 = codec->control_data; |
1348 | priv = codec->private_data; | 1429 | priv = codec->private_data; |
1349 | 1430 | ||
@@ -1381,13 +1462,21 @@ static int wm8350_probe(struct platform_device *pdev) | |||
1381 | wm8350_set_bits(wm8350, WM8350_ROUT2_VOLUME, | 1462 | wm8350_set_bits(wm8350, WM8350_ROUT2_VOLUME, |
1382 | WM8350_OUT2_VU | WM8350_OUT2R_MUTE); | 1463 | WM8350_OUT2_VU | WM8350_OUT2R_MUTE); |
1383 | 1464 | ||
1465 | wm8350_mask_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L); | ||
1466 | wm8350_mask_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R); | ||
1467 | wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L, | ||
1468 | wm8350_hp_jack_handler, priv); | ||
1469 | wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R, | ||
1470 | wm8350_hp_jack_handler, priv); | ||
1471 | |||
1384 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); | 1472 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); |
1385 | if (ret < 0) { | 1473 | if (ret < 0) { |
1386 | dev_err(&pdev->dev, "failed to create pcms\n"); | 1474 | dev_err(&pdev->dev, "failed to create pcms\n"); |
1387 | return ret; | 1475 | return ret; |
1388 | } | 1476 | } |
1389 | 1477 | ||
1390 | wm8350_add_controls(codec); | 1478 | snd_soc_add_controls(codec, wm8350_snd_controls, |
1479 | ARRAY_SIZE(wm8350_snd_controls)); | ||
1391 | wm8350_add_widgets(codec); | 1480 | wm8350_add_widgets(codec); |
1392 | 1481 | ||
1393 | wm8350_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | 1482 | wm8350_set_bias_level(codec, SND_SOC_BIAS_STANDBY); |
@@ -1409,10 +1498,23 @@ card_err: | |||
1409 | static int wm8350_remove(struct platform_device *pdev) | 1498 | static int wm8350_remove(struct platform_device *pdev) |
1410 | { | 1499 | { |
1411 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | 1500 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); |
1412 | struct snd_soc_codec *codec = socdev->codec; | 1501 | struct snd_soc_codec *codec = socdev->card->codec; |
1413 | struct wm8350 *wm8350 = codec->control_data; | 1502 | struct wm8350 *wm8350 = codec->control_data; |
1503 | struct wm8350_data *priv = codec->private_data; | ||
1414 | int ret; | 1504 | int ret; |
1415 | 1505 | ||
1506 | wm8350_clear_bits(wm8350, WM8350_JACK_DETECT, | ||
1507 | WM8350_JDL_ENA | WM8350_JDR_ENA); | ||
1508 | wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_TOCLK_ENA); | ||
1509 | |||
1510 | wm8350_mask_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L); | ||
1511 | wm8350_mask_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R); | ||
1512 | wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L); | ||
1513 | wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R); | ||
1514 | |||
1515 | priv->hpl.jack = NULL; | ||
1516 | priv->hpr.jack = NULL; | ||
1517 | |||
1416 | /* cancel any work waiting to be queued. */ | 1518 | /* cancel any work waiting to be queued. */ |
1417 | ret = cancel_delayed_work(&codec->delayed_work); | 1519 | ret = cancel_delayed_work(&codec->delayed_work); |
1418 | 1520 | ||
@@ -1436,6 +1538,16 @@ static int wm8350_remove(struct platform_device *pdev) | |||
1436 | SNDRV_PCM_FMTBIT_S20_3LE |\ | 1538 | SNDRV_PCM_FMTBIT_S20_3LE |\ |
1437 | SNDRV_PCM_FMTBIT_S24_LE) | 1539 | SNDRV_PCM_FMTBIT_S24_LE) |
1438 | 1540 | ||
1541 | static struct snd_soc_dai_ops wm8350_dai_ops = { | ||
1542 | .hw_params = wm8350_pcm_hw_params, | ||
1543 | .digital_mute = wm8350_mute, | ||
1544 | .trigger = wm8350_pcm_trigger, | ||
1545 | .set_fmt = wm8350_set_dai_fmt, | ||
1546 | .set_sysclk = wm8350_set_dai_sysclk, | ||
1547 | .set_pll = wm8350_set_fll, | ||
1548 | .set_clkdiv = wm8350_set_clkdiv, | ||
1549 | }; | ||
1550 | |||
1439 | struct snd_soc_dai wm8350_dai = { | 1551 | struct snd_soc_dai wm8350_dai = { |
1440 | .name = "WM8350", | 1552 | .name = "WM8350", |
1441 | .playback = { | 1553 | .playback = { |
@@ -1452,15 +1564,7 @@ struct snd_soc_dai wm8350_dai = { | |||
1452 | .rates = WM8350_RATES, | 1564 | .rates = WM8350_RATES, |
1453 | .formats = WM8350_FORMATS, | 1565 | .formats = WM8350_FORMATS, |
1454 | }, | 1566 | }, |
1455 | .ops = { | 1567 | .ops = &wm8350_dai_ops, |
1456 | .hw_params = wm8350_pcm_hw_params, | ||
1457 | .digital_mute = wm8350_mute, | ||
1458 | .trigger = wm8350_pcm_trigger, | ||
1459 | .set_fmt = wm8350_set_dai_fmt, | ||
1460 | .set_sysclk = wm8350_set_dai_sysclk, | ||
1461 | .set_pll = wm8350_set_fll, | ||
1462 | .set_clkdiv = wm8350_set_clkdiv, | ||
1463 | }, | ||
1464 | }; | 1568 | }; |
1465 | EXPORT_SYMBOL_GPL(wm8350_dai); | 1569 | EXPORT_SYMBOL_GPL(wm8350_dai); |
1466 | 1570 | ||
@@ -1472,7 +1576,7 @@ struct snd_soc_codec_device soc_codec_dev_wm8350 = { | |||
1472 | }; | 1576 | }; |
1473 | EXPORT_SYMBOL_GPL(soc_codec_dev_wm8350); | 1577 | EXPORT_SYMBOL_GPL(soc_codec_dev_wm8350); |
1474 | 1578 | ||
1475 | static int wm8350_codec_probe(struct platform_device *pdev) | 1579 | static __devinit int wm8350_codec_probe(struct platform_device *pdev) |
1476 | { | 1580 | { |
1477 | struct wm8350 *wm8350 = platform_get_drvdata(pdev); | 1581 | struct wm8350 *wm8350 = platform_get_drvdata(pdev); |
1478 | struct wm8350_data *priv; | 1582 | struct wm8350_data *priv; |