aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci/hda/hda_codec.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/pci/hda/hda_codec.c')
-rw-r--r--sound/pci/hda/hda_codec.c192
1 files changed, 184 insertions, 8 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 684307372d73..7a8fcc4c15f8 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -19,6 +19,7 @@
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */ 20 */
21 21
22#include <linux/mm.h>
22#include <linux/init.h> 23#include <linux/init.h>
23#include <linux/delay.h> 24#include <linux/delay.h>
24#include <linux/slab.h> 25#include <linux/slab.h>
@@ -2304,7 +2305,7 @@ typedef int (*map_slave_func_t)(void *, struct snd_kcontrol *);
2304 2305
2305/* apply the function to all matching slave ctls in the mixer list */ 2306/* apply the function to all matching slave ctls in the mixer list */
2306static int map_slaves(struct hda_codec *codec, const char * const *slaves, 2307static int map_slaves(struct hda_codec *codec, const char * const *slaves,
2307 map_slave_func_t func, void *data) 2308 const char *suffix, map_slave_func_t func, void *data)
2308{ 2309{
2309 struct hda_nid_item *items; 2310 struct hda_nid_item *items;
2310 const char * const *s; 2311 const char * const *s;
@@ -2317,7 +2318,14 @@ static int map_slaves(struct hda_codec *codec, const char * const *slaves,
2317 sctl->id.iface != SNDRV_CTL_ELEM_IFACE_MIXER) 2318 sctl->id.iface != SNDRV_CTL_ELEM_IFACE_MIXER)
2318 continue; 2319 continue;
2319 for (s = slaves; *s; s++) { 2320 for (s = slaves; *s; s++) {
2320 if (!strcmp(sctl->id.name, *s)) { 2321 char tmpname[sizeof(sctl->id.name)];
2322 const char *name = *s;
2323 if (suffix) {
2324 snprintf(tmpname, sizeof(tmpname), "%s %s",
2325 name, suffix);
2326 name = tmpname;
2327 }
2328 if (!strcmp(sctl->id.name, name)) {
2321 err = func(data, sctl); 2329 err = func(data, sctl);
2322 if (err) 2330 if (err)
2323 return err; 2331 return err;
@@ -2333,12 +2341,65 @@ static int check_slave_present(void *data, struct snd_kcontrol *sctl)
2333 return 1; 2341 return 1;
2334} 2342}
2335 2343
2344/* guess the value corresponding to 0dB */
2345static int get_kctl_0dB_offset(struct snd_kcontrol *kctl)
2346{
2347 int _tlv[4];
2348 const int *tlv = NULL;
2349 int val = -1;
2350
2351 if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
2352 /* FIXME: set_fs() hack for obtaining user-space TLV data */
2353 mm_segment_t fs = get_fs();
2354 set_fs(get_ds());
2355 if (!kctl->tlv.c(kctl, 0, sizeof(_tlv), _tlv))
2356 tlv = _tlv;
2357 set_fs(fs);
2358 } else if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_READ)
2359 tlv = kctl->tlv.p;
2360 if (tlv && tlv[0] == SNDRV_CTL_TLVT_DB_SCALE)
2361 val = -tlv[2] / tlv[3];
2362 return val;
2363}
2364
2365/* call kctl->put with the given value(s) */
2366static int put_kctl_with_value(struct snd_kcontrol *kctl, int val)
2367{
2368 struct snd_ctl_elem_value *ucontrol;
2369 ucontrol = kzalloc(sizeof(*ucontrol), GFP_KERNEL);
2370 if (!ucontrol)
2371 return -ENOMEM;
2372 ucontrol->value.integer.value[0] = val;
2373 ucontrol->value.integer.value[1] = val;
2374 kctl->put(kctl, ucontrol);
2375 kfree(ucontrol);
2376 return 0;
2377}
2378
2379/* initialize the slave volume with 0dB */
2380static int init_slave_0dB(void *data, struct snd_kcontrol *slave)
2381{
2382 int offset = get_kctl_0dB_offset(slave);
2383 if (offset > 0)
2384 put_kctl_with_value(slave, offset);
2385 return 0;
2386}
2387
2388/* unmute the slave */
2389static int init_slave_unmute(void *data, struct snd_kcontrol *slave)
2390{
2391 return put_kctl_with_value(slave, 1);
2392}
2393
2336/** 2394/**
2337 * snd_hda_add_vmaster - create a virtual master control and add slaves 2395 * snd_hda_add_vmaster - create a virtual master control and add slaves
2338 * @codec: HD-audio codec 2396 * @codec: HD-audio codec
2339 * @name: vmaster control name 2397 * @name: vmaster control name
2340 * @tlv: TLV data (optional) 2398 * @tlv: TLV data (optional)
2341 * @slaves: slave control names (optional) 2399 * @slaves: slave control names (optional)
2400 * @suffix: suffix string to each slave name (optional)
2401 * @init_slave_vol: initialize slaves to unmute/0dB
2402 * @ctl_ret: store the vmaster kcontrol in return
2342 * 2403 *
2343 * Create a virtual master control with the given name. The TLV data 2404 * Create a virtual master control with the given name. The TLV data
2344 * must be either NULL or a valid data. 2405 * must be either NULL or a valid data.
@@ -2349,13 +2410,18 @@ static int check_slave_present(void *data, struct snd_kcontrol *sctl)
2349 * 2410 *
2350 * This function returns zero if successful or a negative error code. 2411 * This function returns zero if successful or a negative error code.
2351 */ 2412 */
2352int snd_hda_add_vmaster(struct hda_codec *codec, char *name, 2413int __snd_hda_add_vmaster(struct hda_codec *codec, char *name,
2353 unsigned int *tlv, const char * const *slaves) 2414 unsigned int *tlv, const char * const *slaves,
2415 const char *suffix, bool init_slave_vol,
2416 struct snd_kcontrol **ctl_ret)
2354{ 2417{
2355 struct snd_kcontrol *kctl; 2418 struct snd_kcontrol *kctl;
2356 int err; 2419 int err;
2357 2420
2358 err = map_slaves(codec, slaves, check_slave_present, NULL); 2421 if (ctl_ret)
2422 *ctl_ret = NULL;
2423
2424 err = map_slaves(codec, slaves, suffix, check_slave_present, NULL);
2359 if (err != 1) { 2425 if (err != 1) {
2360 snd_printdd("No slave found for %s\n", name); 2426 snd_printdd("No slave found for %s\n", name);
2361 return 0; 2427 return 0;
@@ -2367,13 +2433,119 @@ int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
2367 if (err < 0) 2433 if (err < 0)
2368 return err; 2434 return err;
2369 2435
2370 err = map_slaves(codec, slaves, (map_slave_func_t)snd_ctl_add_slave, 2436 err = map_slaves(codec, slaves, suffix,
2371 kctl); 2437 (map_slave_func_t)snd_ctl_add_slave, kctl);
2372 if (err < 0) 2438 if (err < 0)
2373 return err; 2439 return err;
2440
2441 /* init with master mute & zero volume */
2442 put_kctl_with_value(kctl, 0);
2443 if (init_slave_vol)
2444 map_slaves(codec, slaves, suffix,
2445 tlv ? init_slave_0dB : init_slave_unmute, kctl);
2446
2447 if (ctl_ret)
2448 *ctl_ret = kctl;
2374 return 0; 2449 return 0;
2375} 2450}
2376EXPORT_SYMBOL_HDA(snd_hda_add_vmaster); 2451EXPORT_SYMBOL_HDA(__snd_hda_add_vmaster);
2452
2453/*
2454 * mute-LED control using vmaster
2455 */
2456static int vmaster_mute_mode_info(struct snd_kcontrol *kcontrol,
2457 struct snd_ctl_elem_info *uinfo)
2458{
2459 static const char * const texts[] = {
2460 "Off", "On", "Follow Master"
2461 };
2462 unsigned int index;
2463
2464 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2465 uinfo->count = 1;
2466 uinfo->value.enumerated.items = 3;
2467 index = uinfo->value.enumerated.item;
2468 if (index >= 3)
2469 index = 2;
2470 strcpy(uinfo->value.enumerated.name, texts[index]);
2471 return 0;
2472}
2473
2474static int vmaster_mute_mode_get(struct snd_kcontrol *kcontrol,
2475 struct snd_ctl_elem_value *ucontrol)
2476{
2477 struct hda_vmaster_mute_hook *hook = snd_kcontrol_chip(kcontrol);
2478 ucontrol->value.enumerated.item[0] = hook->mute_mode;
2479 return 0;
2480}
2481
2482static int vmaster_mute_mode_put(struct snd_kcontrol *kcontrol,
2483 struct snd_ctl_elem_value *ucontrol)
2484{
2485 struct hda_vmaster_mute_hook *hook = snd_kcontrol_chip(kcontrol);
2486 unsigned int old_mode = hook->mute_mode;
2487
2488 hook->mute_mode = ucontrol->value.enumerated.item[0];
2489 if (hook->mute_mode > HDA_VMUTE_FOLLOW_MASTER)
2490 hook->mute_mode = HDA_VMUTE_FOLLOW_MASTER;
2491 if (old_mode == hook->mute_mode)
2492 return 0;
2493 snd_hda_sync_vmaster_hook(hook);
2494 return 1;
2495}
2496
2497static struct snd_kcontrol_new vmaster_mute_mode = {
2498 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2499 .name = "Mute-LED Mode",
2500 .info = vmaster_mute_mode_info,
2501 .get = vmaster_mute_mode_get,
2502 .put = vmaster_mute_mode_put,
2503};
2504
2505/*
2506 * Add a mute-LED hook with the given vmaster switch kctl
2507 * "Mute-LED Mode" control is automatically created and associated with
2508 * the given hook.
2509 */
2510int snd_hda_add_vmaster_hook(struct hda_codec *codec,
2511 struct hda_vmaster_mute_hook *hook,
2512 bool expose_enum_ctl)
2513{
2514 struct snd_kcontrol *kctl;
2515
2516 if (!hook->hook || !hook->sw_kctl)
2517 return 0;
2518 snd_ctl_add_vmaster_hook(hook->sw_kctl, hook->hook, codec);
2519 hook->codec = codec;
2520 hook->mute_mode = HDA_VMUTE_FOLLOW_MASTER;
2521 if (!expose_enum_ctl)
2522 return 0;
2523 kctl = snd_ctl_new1(&vmaster_mute_mode, hook);
2524 if (!kctl)
2525 return -ENOMEM;
2526 return snd_hda_ctl_add(codec, 0, kctl);
2527}
2528EXPORT_SYMBOL_HDA(snd_hda_add_vmaster_hook);
2529
2530/*
2531 * Call the hook with the current value for synchronization
2532 * Should be called in init callback
2533 */
2534void snd_hda_sync_vmaster_hook(struct hda_vmaster_mute_hook *hook)
2535{
2536 if (!hook->hook || !hook->codec)
2537 return;
2538 switch (hook->mute_mode) {
2539 case HDA_VMUTE_FOLLOW_MASTER:
2540 snd_ctl_sync_vmaster_hook(hook->sw_kctl);
2541 break;
2542 default:
2543 hook->hook(hook->codec, hook->mute_mode);
2544 break;
2545 }
2546}
2547EXPORT_SYMBOL_HDA(snd_hda_sync_vmaster_hook);
2548
2377 2549
2378/** 2550/**
2379 * snd_hda_mixer_amp_switch_info - Info callback for a standard AMP mixer switch 2551 * snd_hda_mixer_amp_switch_info - Info callback for a standard AMP mixer switch
@@ -5272,6 +5444,10 @@ int snd_hda_suspend(struct hda_bus *bus)
5272 list_for_each_entry(codec, &bus->codec_list, list) { 5444 list_for_each_entry(codec, &bus->codec_list, list) {
5273 if (hda_codec_is_power_on(codec)) 5445 if (hda_codec_is_power_on(codec))
5274 hda_call_codec_suspend(codec); 5446 hda_call_codec_suspend(codec);
5447 else /* forcibly change the power to D3 even if not used */
5448 hda_set_power_state(codec,
5449 codec->afg ? codec->afg : codec->mfg,
5450 AC_PWRST_D3);
5275 if (codec->patch_ops.post_suspend) 5451 if (codec->patch_ops.post_suspend)
5276 codec->patch_ops.post_suspend(codec); 5452 codec->patch_ops.post_suspend(codec);
5277 } 5453 }