diff options
Diffstat (limited to 'sound/soc/codecs/wm8753.c')
-rw-r--r-- | sound/soc/codecs/wm8753.c | 107 |
1 files changed, 104 insertions, 3 deletions
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index ffa2ffe5ec1..d4b822d2d47 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c | |||
@@ -48,6 +48,7 @@ | |||
48 | #include <sound/initval.h> | 48 | #include <sound/initval.h> |
49 | #include <sound/tlv.h> | 49 | #include <sound/tlv.h> |
50 | #include <asm/div64.h> | 50 | #include <asm/div64.h> |
51 | #include <sound/jack.h> | ||
51 | 52 | ||
52 | #include "wm8753.h" | 53 | #include "wm8753.h" |
53 | 54 | ||
@@ -94,6 +95,9 @@ struct wm8753_priv { | |||
94 | unsigned int hifi_fmt; | 95 | unsigned int hifi_fmt; |
95 | 96 | ||
96 | int dai_func; | 97 | int dai_func; |
98 | int irq; | ||
99 | struct snd_soc_jack *headset_jack; | ||
100 | unsigned int debounce_time_hp; | ||
97 | }; | 101 | }; |
98 | 102 | ||
99 | #define wm8753_reset(c) snd_soc_write(c, WM8753_RESET, 0) | 103 | #define wm8753_reset(c) snd_soc_write(c, WM8753_RESET, 0) |
@@ -189,8 +193,12 @@ static int wm8753_set_dai(struct snd_kcontrol *kcontrol, | |||
189 | struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); | 193 | struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); |
190 | u16 ioctl; | 194 | u16 ioctl; |
191 | 195 | ||
196 | if (wm8753->dai_func == ucontrol->value.integer.value[0]) | ||
197 | return 0; | ||
198 | |||
192 | if (codec->active) | 199 | if (codec->active) |
193 | return -EBUSY; | 200 | printk(KERN_WARNING |
201 | "Trying to Change the Dai Mode when codec is active\n"); | ||
194 | 202 | ||
195 | ioctl = snd_soc_read(codec, WM8753_IOCTL); | 203 | ioctl = snd_soc_read(codec, WM8753_IOCTL); |
196 | 204 | ||
@@ -904,6 +912,10 @@ static int wm8753_pcm_hw_params(struct snd_pcm_substream *substream, | |||
904 | /* sample rate */ | 912 | /* sample rate */ |
905 | if (params_rate(params) * 384 == wm8753->pcmclk) | 913 | if (params_rate(params) * 384 == wm8753->pcmclk) |
906 | srate |= 0x80; | 914 | srate |= 0x80; |
915 | |||
916 | /* ADC and V-DAC at same sample rate */ | ||
917 | srate |= 1<<8; | ||
918 | |||
907 | snd_soc_write(codec, WM8753_SRATE1, srate); | 919 | snd_soc_write(codec, WM8753_SRATE1, srate); |
908 | 920 | ||
909 | snd_soc_write(codec, WM8753_PCM, voice); | 921 | snd_soc_write(codec, WM8753_PCM, voice); |
@@ -1123,6 +1135,10 @@ static int wm8753_i2s_hw_params(struct snd_pcm_substream *substream, | |||
1123 | printk(KERN_ERR "wm8753 invalid MCLK or rate\n"); | 1135 | printk(KERN_ERR "wm8753 invalid MCLK or rate\n"); |
1124 | return coeff; | 1136 | return coeff; |
1125 | } | 1137 | } |
1138 | |||
1139 | /* ADC and HiFi-DAC at same sample rate */ | ||
1140 | srate &= ~(1<<8); | ||
1141 | |||
1126 | snd_soc_write(codec, WM8753_SRATE1, srate | (coeff_div[coeff].sr << 1) | | 1142 | snd_soc_write(codec, WM8753_SRATE1, srate | (coeff_div[coeff].sr << 1) | |
1127 | coeff_div[coeff].usb); | 1143 | coeff_div[coeff].usb); |
1128 | 1144 | ||
@@ -1390,13 +1406,18 @@ static void wm8753_work(struct work_struct *work) | |||
1390 | 1406 | ||
1391 | static int wm8753_suspend(struct snd_soc_codec *codec, pm_message_t state) | 1407 | static int wm8753_suspend(struct snd_soc_codec *codec, pm_message_t state) |
1392 | { | 1408 | { |
1409 | struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); | ||
1410 | |||
1393 | wm8753_set_bias_level(codec, SND_SOC_BIAS_OFF); | 1411 | wm8753_set_bias_level(codec, SND_SOC_BIAS_OFF); |
1412 | disable_irq(wm8753->irq); | ||
1413 | |||
1394 | return 0; | 1414 | return 0; |
1395 | } | 1415 | } |
1396 | 1416 | ||
1397 | static int wm8753_resume(struct snd_soc_codec *codec) | 1417 | static int wm8753_resume(struct snd_soc_codec *codec) |
1398 | { | 1418 | { |
1399 | u16 *reg_cache = codec->reg_cache; | 1419 | u16 *reg_cache = codec->reg_cache; |
1420 | struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); | ||
1400 | int i; | 1421 | int i; |
1401 | 1422 | ||
1402 | /* Sync reg_cache with the hardware */ | 1423 | /* Sync reg_cache with the hardware */ |
@@ -1413,6 +1434,8 @@ static int wm8753_resume(struct snd_soc_codec *codec) | |||
1413 | 1434 | ||
1414 | wm8753_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | 1435 | wm8753_set_bias_level(codec, SND_SOC_BIAS_STANDBY); |
1415 | 1436 | ||
1437 | enable_irq(wm8753->irq); | ||
1438 | |||
1416 | /* charge wm8753 caps */ | 1439 | /* charge wm8753 caps */ |
1417 | if (codec->dapm.suspend_bias_level == SND_SOC_BIAS_ON) { | 1440 | if (codec->dapm.suspend_bias_level == SND_SOC_BIAS_ON) { |
1418 | wm8753_set_bias_level(codec, SND_SOC_BIAS_PREPARE); | 1441 | wm8753_set_bias_level(codec, SND_SOC_BIAS_PREPARE); |
@@ -1424,6 +1447,70 @@ static int wm8753_resume(struct snd_soc_codec *codec) | |||
1424 | return 0; | 1447 | return 0; |
1425 | } | 1448 | } |
1426 | 1449 | ||
1450 | static irqreturn_t wm8753_jack_handler(int irq, void *data) | ||
1451 | { | ||
1452 | struct snd_soc_codec *codec = data; | ||
1453 | struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); | ||
1454 | unsigned int value; | ||
1455 | |||
1456 | /* GPIO4 interrupt disable */ | ||
1457 | snd_soc_update_bits(codec, WM8753_INTEN, WM8753_GPIO4IEN_MASK, | ||
1458 | WM8753_GPIO4IEN_DIS); | ||
1459 | |||
1460 | /* sleep for debounce time */ | ||
1461 | msleep(wm8753->debounce_time_hp); | ||
1462 | |||
1463 | /* Invert GPIO4 interrupt polarity */ | ||
1464 | value = snd_soc_read(codec, WM8753_INTPOL); | ||
1465 | if (value & WM8753_GPIO4IPOL_LOW) { | ||
1466 | snd_soc_jack_report(wm8753->headset_jack, SND_JACK_HEADPHONE, | ||
1467 | SND_JACK_HEADPHONE); | ||
1468 | /* interupt when high i.e Headphone disconnected */ | ||
1469 | snd_soc_update_bits(codec, WM8753_INTPOL, WM8753_GPIO4IPOL_MASK, | ||
1470 | WM8753_GPIO4IPOL_HIGH); | ||
1471 | } else { | ||
1472 | snd_soc_jack_report(wm8753->headset_jack, 0, | ||
1473 | SND_JACK_HEADPHONE); | ||
1474 | /* interupt when low i.e Headphone connected */ | ||
1475 | snd_soc_update_bits(codec, WM8753_INTPOL, WM8753_GPIO4IPOL_MASK, | ||
1476 | WM8753_GPIO4IPOL_LOW); | ||
1477 | } | ||
1478 | |||
1479 | /* GPIO4 interrupt enable */ | ||
1480 | snd_soc_update_bits(codec, WM8753_INTEN, WM8753_GPIO4IEN_MASK, | ||
1481 | WM8753_GPIO4IEN_EN); | ||
1482 | |||
1483 | return IRQ_HANDLED; | ||
1484 | } | ||
1485 | |||
1486 | int wm8753_headphone_detect(struct snd_soc_codec *codec, | ||
1487 | struct snd_soc_jack *jack, enum snd_jack_types type, | ||
1488 | unsigned int debounce_time_hp) | ||
1489 | { | ||
1490 | struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); | ||
1491 | |||
1492 | wm8753->headset_jack = jack; | ||
1493 | wm8753->debounce_time_hp = debounce_time_hp; | ||
1494 | |||
1495 | if (wm8753->irq && (type & SND_JACK_HEADPHONE)) { | ||
1496 | /* Configure GPIO2 pin to generate the interrupt */ | ||
1497 | snd_soc_update_bits(codec, WM8753_GPIO2, WM8753_GP2M_MASK, | ||
1498 | WM8753_GP2M_INT); | ||
1499 | /* Active low Interrupt */ | ||
1500 | snd_soc_update_bits(codec, WM8753_GPIO1, WM8753_INTCON_MASK, | ||
1501 | WM8753_INTCON_AL); | ||
1502 | /* interupt when low i.e Headphone connected */ | ||
1503 | snd_soc_update_bits(codec, WM8753_INTPOL, WM8753_GPIO4IPOL_MASK, | ||
1504 | WM8753_GPIO4IPOL_LOW); | ||
1505 | /* GPIO4 interrupt enable */ | ||
1506 | snd_soc_update_bits(codec, WM8753_INTEN, WM8753_GPIO4IEN_MASK, | ||
1507 | WM8753_GPIO4IEN_EN); | ||
1508 | } | ||
1509 | |||
1510 | return 0; | ||
1511 | } | ||
1512 | EXPORT_SYMBOL_GPL(wm8753_headphone_detect); | ||
1513 | |||
1427 | static int wm8753_probe(struct snd_soc_codec *codec) | 1514 | static int wm8753_probe(struct snd_soc_codec *codec) |
1428 | { | 1515 | { |
1429 | struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); | 1516 | struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); |
@@ -1443,6 +1530,19 @@ static int wm8753_probe(struct snd_soc_codec *codec) | |||
1443 | return ret; | 1530 | return ret; |
1444 | } | 1531 | } |
1445 | 1532 | ||
1533 | if (wm8753->irq) { | ||
1534 | /* register an audio interrupt */ | ||
1535 | ret = request_threaded_irq(wm8753->irq, NULL, | ||
1536 | wm8753_jack_handler, | ||
1537 | IRQF_TRIGGER_FALLING, | ||
1538 | "wm8753", codec); | ||
1539 | |||
1540 | if (ret) { | ||
1541 | dev_err(codec->dev, "Failed to request IRQ: %d\n", ret); | ||
1542 | return ret; | ||
1543 | } | ||
1544 | } | ||
1545 | |||
1446 | wm8753_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | 1546 | wm8753_set_bias_level(codec, SND_SOC_BIAS_STANDBY); |
1447 | wm8753->dai_func = 0; | 1547 | wm8753->dai_func = 0; |
1448 | 1548 | ||
@@ -1454,8 +1554,8 @@ static int wm8753_probe(struct snd_soc_codec *codec) | |||
1454 | /* set the update bits */ | 1554 | /* set the update bits */ |
1455 | snd_soc_update_bits(codec, WM8753_LDAC, 0x0100, 0x0100); | 1555 | snd_soc_update_bits(codec, WM8753_LDAC, 0x0100, 0x0100); |
1456 | snd_soc_update_bits(codec, WM8753_RDAC, 0x0100, 0x0100); | 1556 | snd_soc_update_bits(codec, WM8753_RDAC, 0x0100, 0x0100); |
1457 | snd_soc_update_bits(codec, WM8753_LDAC, 0x0100, 0x0100); | 1557 | snd_soc_update_bits(codec, WM8753_LADC, 0x0100, 0x0100); |
1458 | snd_soc_update_bits(codec, WM8753_RDAC, 0x0100, 0x0100); | 1558 | snd_soc_update_bits(codec, WM8753_RADC, 0x0100, 0x0100); |
1459 | snd_soc_update_bits(codec, WM8753_LOUT1V, 0x0100, 0x0100); | 1559 | snd_soc_update_bits(codec, WM8753_LOUT1V, 0x0100, 0x0100); |
1460 | snd_soc_update_bits(codec, WM8753_ROUT1V, 0x0100, 0x0100); | 1560 | snd_soc_update_bits(codec, WM8753_ROUT1V, 0x0100, 0x0100); |
1461 | snd_soc_update_bits(codec, WM8753_LOUT2V, 0x0100, 0x0100); | 1561 | snd_soc_update_bits(codec, WM8753_LOUT2V, 0x0100, 0x0100); |
@@ -1540,6 +1640,7 @@ static __devinit int wm8753_i2c_probe(struct i2c_client *i2c, | |||
1540 | 1640 | ||
1541 | i2c_set_clientdata(i2c, wm8753); | 1641 | i2c_set_clientdata(i2c, wm8753); |
1542 | wm8753->control_type = SND_SOC_I2C; | 1642 | wm8753->control_type = SND_SOC_I2C; |
1643 | wm8753->irq = i2c->irq; | ||
1543 | 1644 | ||
1544 | ret = snd_soc_register_codec(&i2c->dev, | 1645 | ret = snd_soc_register_codec(&i2c->dev, |
1545 | &soc_codec_dev_wm8753, wm8753_dai, ARRAY_SIZE(wm8753_dai)); | 1646 | &soc_codec_dev_wm8753, wm8753_dai, ARRAY_SIZE(wm8753_dai)); |