diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2006-07-10 07:44:39 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-07-10 16:24:19 -0400 |
commit | 6a4f57874538fc05b99bd3bf7106f3df9b23a4ab (patch) | |
tree | 9731131b78823ba2f28a57c6d4f860e8c42e237f | |
parent | 14b42963f64b98ab61fa9723c03d71aa5ef4f862 (diff) |
[PATCH] aoa: tas: fix initialisation/reset
This patch fixes the initialisation and reset of the tas codec. The tas will
often reset if the i2s clocks go away so it needs to be completely
re-initialised when clocks come back.
Also, this patch adds some code for DRC that will be exploited later to add a
DRC control again, fixing a regression over snd-powermac.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r-- | sound/aoa/codecs/snd-aoa-codec-tas.c | 126 | ||||
-rw-r--r-- | sound/aoa/codecs/snd-aoa-codec-tas.h | 8 |
2 files changed, 110 insertions, 24 deletions
diff --git a/sound/aoa/codecs/snd-aoa-codec-tas.c b/sound/aoa/codecs/snd-aoa-codec-tas.c index 27c1e2eea22b..23643b342cb0 100644 --- a/sound/aoa/codecs/snd-aoa-codec-tas.c +++ b/sound/aoa/codecs/snd-aoa-codec-tas.c | |||
@@ -75,19 +75,24 @@ MODULE_DESCRIPTION("tas codec driver for snd-aoa"); | |||
75 | #include "../aoa.h" | 75 | #include "../aoa.h" |
76 | #include "../soundbus/soundbus.h" | 76 | #include "../soundbus/soundbus.h" |
77 | 77 | ||
78 | |||
79 | #define PFX "snd-aoa-codec-tas: " | 78 | #define PFX "snd-aoa-codec-tas: " |
80 | 79 | ||
80 | |||
81 | struct tas { | 81 | struct tas { |
82 | struct aoa_codec codec; | 82 | struct aoa_codec codec; |
83 | struct i2c_client i2c; | 83 | struct i2c_client i2c; |
84 | u32 muted_l:1, muted_r:1, | 84 | u32 mute_l:1, mute_r:1 , |
85 | controls_created:1; | 85 | controls_created:1 , |
86 | drc_enabled:1, | ||
87 | hw_enabled:1; | ||
86 | u8 cached_volume_l, cached_volume_r; | 88 | u8 cached_volume_l, cached_volume_r; |
87 | u8 mixer_l[3], mixer_r[3]; | 89 | u8 mixer_l[3], mixer_r[3]; |
88 | u8 acr; | 90 | u8 acr; |
91 | int drc_range; | ||
89 | }; | 92 | }; |
90 | 93 | ||
94 | static int tas_reset_init(struct tas *tas); | ||
95 | |||
91 | static struct tas *codec_to_tas(struct aoa_codec *codec) | 96 | static struct tas *codec_to_tas(struct aoa_codec *codec) |
92 | { | 97 | { |
93 | return container_of(codec, struct tas, codec); | 98 | return container_of(codec, struct tas, codec); |
@@ -101,6 +106,28 @@ static inline int tas_write_reg(struct tas *tas, u8 reg, u8 len, u8 *data) | |||
101 | return i2c_smbus_write_i2c_block_data(&tas->i2c, reg, len, data); | 106 | return i2c_smbus_write_i2c_block_data(&tas->i2c, reg, len, data); |
102 | } | 107 | } |
103 | 108 | ||
109 | static void tas3004_set_drc(struct tas *tas) | ||
110 | { | ||
111 | unsigned char val[6]; | ||
112 | |||
113 | if (tas->drc_enabled) | ||
114 | val[0] = 0x50; /* 3:1 above threshold */ | ||
115 | else | ||
116 | val[0] = 0x51; /* disabled */ | ||
117 | val[1] = 0x02; /* 1:1 below threshold */ | ||
118 | if (tas->drc_range > 0xef) | ||
119 | val[2] = 0xef; | ||
120 | else if (tas->drc_range < 0) | ||
121 | val[2] = 0x00; | ||
122 | else | ||
123 | val[2] = tas->drc_range; | ||
124 | val[3] = 0xb0; | ||
125 | val[4] = 0x60; | ||
126 | val[5] = 0xa0; | ||
127 | |||
128 | tas_write_reg(tas, TAS_REG_DRC, 6, val); | ||
129 | } | ||
130 | |||
104 | static void tas_set_volume(struct tas *tas) | 131 | static void tas_set_volume(struct tas *tas) |
105 | { | 132 | { |
106 | u8 block[6]; | 133 | u8 block[6]; |
@@ -113,8 +140,8 @@ static void tas_set_volume(struct tas *tas) | |||
113 | if (left > 177) left = 177; | 140 | if (left > 177) left = 177; |
114 | if (right > 177) right = 177; | 141 | if (right > 177) right = 177; |
115 | 142 | ||
116 | if (tas->muted_l) left = 0; | 143 | if (tas->mute_l) left = 0; |
117 | if (tas->muted_r) right = 0; | 144 | if (tas->mute_r) right = 0; |
118 | 145 | ||
119 | /* analysing the volume and mixer tables shows | 146 | /* analysing the volume and mixer tables shows |
120 | * that they are similar enough when we shift | 147 | * that they are similar enough when we shift |
@@ -202,7 +229,8 @@ static int tas_snd_vol_put(struct snd_kcontrol *kcontrol, | |||
202 | 229 | ||
203 | tas->cached_volume_l = ucontrol->value.integer.value[0]; | 230 | tas->cached_volume_l = ucontrol->value.integer.value[0]; |
204 | tas->cached_volume_r = ucontrol->value.integer.value[1]; | 231 | tas->cached_volume_r = ucontrol->value.integer.value[1]; |
205 | tas_set_volume(tas); | 232 | if (tas->hw_enabled) |
233 | tas_set_volume(tas); | ||
206 | return 1; | 234 | return 1; |
207 | } | 235 | } |
208 | 236 | ||
@@ -230,8 +258,8 @@ static int tas_snd_mute_get(struct snd_kcontrol *kcontrol, | |||
230 | { | 258 | { |
231 | struct tas *tas = snd_kcontrol_chip(kcontrol); | 259 | struct tas *tas = snd_kcontrol_chip(kcontrol); |
232 | 260 | ||
233 | ucontrol->value.integer.value[0] = !tas->muted_l; | 261 | ucontrol->value.integer.value[0] = !tas->mute_l; |
234 | ucontrol->value.integer.value[1] = !tas->muted_r; | 262 | ucontrol->value.integer.value[1] = !tas->mute_r; |
235 | return 0; | 263 | return 0; |
236 | } | 264 | } |
237 | 265 | ||
@@ -240,13 +268,14 @@ static int tas_snd_mute_put(struct snd_kcontrol *kcontrol, | |||
240 | { | 268 | { |
241 | struct tas *tas = snd_kcontrol_chip(kcontrol); | 269 | struct tas *tas = snd_kcontrol_chip(kcontrol); |
242 | 270 | ||
243 | if (tas->muted_l == !ucontrol->value.integer.value[0] | 271 | if (tas->mute_l == !ucontrol->value.integer.value[0] |
244 | && tas->muted_r == !ucontrol->value.integer.value[1]) | 272 | && tas->mute_r == !ucontrol->value.integer.value[1]) |
245 | return 0; | 273 | return 0; |
246 | 274 | ||
247 | tas->muted_l = !ucontrol->value.integer.value[0]; | 275 | tas->mute_l = !ucontrol->value.integer.value[0]; |
248 | tas->muted_r = !ucontrol->value.integer.value[1]; | 276 | tas->mute_r = !ucontrol->value.integer.value[1]; |
249 | tas_set_volume(tas); | 277 | if (tas->hw_enabled) |
278 | tas_set_volume(tas); | ||
250 | return 1; | 279 | return 1; |
251 | } | 280 | } |
252 | 281 | ||
@@ -294,7 +323,8 @@ static int tas_snd_mixer_put(struct snd_kcontrol *kcontrol, | |||
294 | tas->mixer_l[idx] = ucontrol->value.integer.value[0]; | 323 | tas->mixer_l[idx] = ucontrol->value.integer.value[0]; |
295 | tas->mixer_r[idx] = ucontrol->value.integer.value[1]; | 324 | tas->mixer_r[idx] = ucontrol->value.integer.value[1]; |
296 | 325 | ||
297 | tas_set_mixer(tas); | 326 | if (tas->hw_enabled) |
327 | tas_set_mixer(tas); | ||
298 | return 1; | 328 | return 1; |
299 | } | 329 | } |
300 | 330 | ||
@@ -346,7 +376,8 @@ static int tas_snd_capture_source_put(struct snd_kcontrol *kcontrol, | |||
346 | tas->acr |= TAS_ACR_INPUT_B; | 376 | tas->acr |= TAS_ACR_INPUT_B; |
347 | if (oldacr == tas->acr) | 377 | if (oldacr == tas->acr) |
348 | return 0; | 378 | return 0; |
349 | tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr); | 379 | if (tas->hw_enabled) |
380 | tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr); | ||
350 | return 1; | 381 | return 1; |
351 | } | 382 | } |
352 | 383 | ||
@@ -399,26 +430,66 @@ static int tas_usable(struct codec_info_item *cii, | |||
399 | static int tas_reset_init(struct tas *tas) | 430 | static int tas_reset_init(struct tas *tas) |
400 | { | 431 | { |
401 | u8 tmp; | 432 | u8 tmp; |
433 | |||
434 | tas->codec.gpio->methods->all_amps_off(tas->codec.gpio); | ||
435 | msleep(5); | ||
402 | tas->codec.gpio->methods->set_hw_reset(tas->codec.gpio, 0); | 436 | tas->codec.gpio->methods->set_hw_reset(tas->codec.gpio, 0); |
403 | msleep(1); | 437 | msleep(5); |
404 | tas->codec.gpio->methods->set_hw_reset(tas->codec.gpio, 1); | 438 | tas->codec.gpio->methods->set_hw_reset(tas->codec.gpio, 1); |
405 | msleep(1); | 439 | msleep(20); |
406 | tas->codec.gpio->methods->set_hw_reset(tas->codec.gpio, 0); | 440 | tas->codec.gpio->methods->set_hw_reset(tas->codec.gpio, 0); |
407 | msleep(1); | 441 | msleep(10); |
408 | 442 | tas->codec.gpio->methods->all_amps_restore(tas->codec.gpio); | |
409 | tas->acr &= ~TAS_ACR_ANALOG_PDOWN; | ||
410 | tas->acr |= TAS_ACR_B_MONAUREAL | TAS_ACR_B_MON_SEL_RIGHT; | ||
411 | if (tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr)) | ||
412 | return -ENODEV; | ||
413 | 443 | ||
414 | tmp = TAS_MCS_SCLK64 | TAS_MCS_SPORT_MODE_I2S | TAS_MCS_SPORT_WL_24BIT; | 444 | tmp = TAS_MCS_SCLK64 | TAS_MCS_SPORT_MODE_I2S | TAS_MCS_SPORT_WL_24BIT; |
415 | if (tas_write_reg(tas, TAS_REG_MCS, 1, &tmp)) | 445 | if (tas_write_reg(tas, TAS_REG_MCS, 1, &tmp)) |
416 | return -ENODEV; | 446 | return -ENODEV; |
417 | 447 | ||
448 | tas->acr |= TAS_ACR_ANALOG_PDOWN | TAS_ACR_B_MONAUREAL | | ||
449 | TAS_ACR_B_MON_SEL_RIGHT; | ||
450 | if (tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr)) | ||
451 | return -ENODEV; | ||
452 | |||
418 | tmp = 0; | 453 | tmp = 0; |
419 | if (tas_write_reg(tas, TAS_REG_MCS2, 1, &tmp)) | 454 | if (tas_write_reg(tas, TAS_REG_MCS2, 1, &tmp)) |
420 | return -ENODEV; | 455 | return -ENODEV; |
421 | 456 | ||
457 | tas3004_set_drc(tas); | ||
458 | |||
459 | /* Set treble & bass to 0dB */ | ||
460 | tmp = 114; | ||
461 | tas_write_reg(tas, TAS_REG_TREBLE, 1, &tmp); | ||
462 | tas_write_reg(tas, TAS_REG_BASS, 1, &tmp); | ||
463 | |||
464 | tas->acr &= ~TAS_ACR_ANALOG_PDOWN; | ||
465 | if (tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr)) | ||
466 | return -ENODEV; | ||
467 | |||
468 | return 0; | ||
469 | } | ||
470 | |||
471 | static int tas_switch_clock(struct codec_info_item *cii, enum clock_switch clock) | ||
472 | { | ||
473 | struct tas *tas = cii->codec_data; | ||
474 | |||
475 | switch(clock) { | ||
476 | case CLOCK_SWITCH_PREPARE_SLAVE: | ||
477 | /* Clocks are going away, mute mute mute */ | ||
478 | tas->codec.gpio->methods->all_amps_off(tas->codec.gpio); | ||
479 | tas->hw_enabled = 0; | ||
480 | break; | ||
481 | case CLOCK_SWITCH_SLAVE: | ||
482 | /* Clocks are back, re-init the codec */ | ||
483 | tas_reset_init(tas); | ||
484 | tas_set_volume(tas); | ||
485 | tas_set_mixer(tas); | ||
486 | tas->hw_enabled = 1; | ||
487 | tas->codec.gpio->methods->all_amps_restore(tas->codec.gpio); | ||
488 | break; | ||
489 | default: | ||
490 | /* doesn't happen as of now */ | ||
491 | return -EINVAL; | ||
492 | } | ||
422 | return 0; | 493 | return 0; |
423 | } | 494 | } |
424 | 495 | ||
@@ -427,6 +498,7 @@ static int tas_reset_init(struct tas *tas) | |||
427 | * our i2c device is suspended, and then take note of that! */ | 498 | * our i2c device is suspended, and then take note of that! */ |
428 | static int tas_suspend(struct tas *tas) | 499 | static int tas_suspend(struct tas *tas) |
429 | { | 500 | { |
501 | tas->hw_enabled = 0; | ||
430 | tas->acr |= TAS_ACR_ANALOG_PDOWN; | 502 | tas->acr |= TAS_ACR_ANALOG_PDOWN; |
431 | tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr); | 503 | tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr); |
432 | return 0; | 504 | return 0; |
@@ -438,6 +510,7 @@ static int tas_resume(struct tas *tas) | |||
438 | tas_reset_init(tas); | 510 | tas_reset_init(tas); |
439 | tas_set_volume(tas); | 511 | tas_set_volume(tas); |
440 | tas_set_mixer(tas); | 512 | tas_set_mixer(tas); |
513 | tas->hw_enabled = 1; | ||
441 | return 0; | 514 | return 0; |
442 | } | 515 | } |
443 | 516 | ||
@@ -463,6 +536,7 @@ static struct codec_info tas_codec_info = { | |||
463 | .bus_factor = 64, | 536 | .bus_factor = 64, |
464 | .owner = THIS_MODULE, | 537 | .owner = THIS_MODULE, |
465 | .usable = tas_usable, | 538 | .usable = tas_usable, |
539 | .switch_clock = tas_switch_clock, | ||
466 | #ifdef CONFIG_PM | 540 | #ifdef CONFIG_PM |
467 | .suspend = _tas_suspend, | 541 | .suspend = _tas_suspend, |
468 | .resume = _tas_resume, | 542 | .resume = _tas_resume, |
@@ -483,6 +557,7 @@ static int tas_init_codec(struct aoa_codec *codec) | |||
483 | printk(KERN_ERR PFX "tas failed to initialise\n"); | 557 | printk(KERN_ERR PFX "tas failed to initialise\n"); |
484 | return -ENXIO; | 558 | return -ENXIO; |
485 | } | 559 | } |
560 | tas->hw_enabled = 1; | ||
486 | 561 | ||
487 | if (tas->codec.soundbus_dev->attach_codec(tas->codec.soundbus_dev, | 562 | if (tas->codec.soundbus_dev->attach_codec(tas->codec.soundbus_dev, |
488 | aoa_get_card(), | 563 | aoa_get_card(), |
@@ -548,6 +623,7 @@ static int tas_create(struct i2c_adapter *adapter, | |||
548 | tas->i2c.driver = &tas_driver; | 623 | tas->i2c.driver = &tas_driver; |
549 | tas->i2c.adapter = adapter; | 624 | tas->i2c.adapter = adapter; |
550 | tas->i2c.addr = addr; | 625 | tas->i2c.addr = addr; |
626 | tas->drc_range = TAS3004_DRC_MAX; | ||
551 | strlcpy(tas->i2c.name, "tas audio codec", I2C_NAME_SIZE-1); | 627 | strlcpy(tas->i2c.name, "tas audio codec", I2C_NAME_SIZE-1); |
552 | 628 | ||
553 | if (i2c_attach_client(&tas->i2c)) { | 629 | if (i2c_attach_client(&tas->i2c)) { |
@@ -564,7 +640,9 @@ static int tas_create(struct i2c_adapter *adapter, | |||
564 | if (aoa_codec_register(&tas->codec)) { | 640 | if (aoa_codec_register(&tas->codec)) { |
565 | goto detach; | 641 | goto detach; |
566 | } | 642 | } |
567 | printk(KERN_DEBUG "snd-aoa-codec-tas: created and attached tas instance\n"); | 643 | printk(KERN_DEBUG |
644 | "snd-aoa-codec-tas: tas found, addr 0x%02x on %s\n", | ||
645 | addr, node->full_name); | ||
568 | return 0; | 646 | return 0; |
569 | detach: | 647 | detach: |
570 | i2c_detach_client(&tas->i2c); | 648 | i2c_detach_client(&tas->i2c); |
diff --git a/sound/aoa/codecs/snd-aoa-codec-tas.h b/sound/aoa/codecs/snd-aoa-codec-tas.h index daf81f45d83a..ae177e3466e6 100644 --- a/sound/aoa/codecs/snd-aoa-codec-tas.h +++ b/sound/aoa/codecs/snd-aoa-codec-tas.h | |||
@@ -44,4 +44,12 @@ | |||
44 | #define TAS_REG_LEFT_BIQUAD6 0x10 | 44 | #define TAS_REG_LEFT_BIQUAD6 0x10 |
45 | #define TAS_REG_RIGHT_BIQUAD6 0x19 | 45 | #define TAS_REG_RIGHT_BIQUAD6 0x19 |
46 | 46 | ||
47 | #define TAS_REG_LEFT_LOUDNESS 0x21 | ||
48 | #define TAS_REG_RIGHT_LOUDNESS 0x22 | ||
49 | #define TAS_REG_LEFT_LOUDNESS_GAIN 0x23 | ||
50 | #define TAS_REG_RIGHT_LOUDNESS_GAIN 0x24 | ||
51 | |||
52 | #define TAS3001_DRC_MAX 0x5f | ||
53 | #define TAS3004_DRC_MAX 0xef | ||
54 | |||
47 | #endif /* __SND_AOA_CODECTASH */ | 55 | #endif /* __SND_AOA_CODECTASH */ |