diff --git a/include/sound/core.h b/include/sound/core.h index 4104a9d1001f..dd604cc6e2d0 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -133,6 +133,9 @@ struct snd_card { struct device card_dev; /* cardX object for sysfs */ const struct attribute_group *dev_groups[4]; /* assigned sysfs attr */ bool registered; /* card_dev is registered? */ + int offline; /* if this sound card is offline */ + unsigned long offline_change; + wait_queue_head_t offline_poll_wait; #ifdef CONFIG_PM unsigned int power_state; /* power state */ @@ -251,6 +254,8 @@ int snd_component_add(struct snd_card *card, const char *component); int snd_card_file_add(struct snd_card *card, struct file *file); int snd_card_file_remove(struct snd_card *card, struct file *file); #define snd_card_unref(card) put_device(&(card)->card_dev) +void snd_card_change_online_state(struct snd_card *card, int online); +bool snd_card_is_online_state(struct snd_card *card); #define snd_card_set_dev(card, devptr) ((card)->dev = (devptr)) diff --git a/include/sound/jack.h b/include/sound/jack.h index 1e84bfb553cf..471541b9db39 100644 --- a/include/sound/jack.h +++ b/include/sound/jack.h @@ -58,6 +58,9 @@ enum snd_jack_types { SND_JACK_VIDEOOUT = 0x0010, SND_JACK_AVOUT = SND_JACK_LINEOUT | SND_JACK_VIDEOOUT, SND_JACK_LINEIN = 0x0020, + SND_JACK_OC_HPHL = 0x0040, + SND_JACK_OC_HPHR = 0x0080, + SND_JACK_UNSUPPORTED = 0x0100, /* Kept separate from switches to facilitate implementation */ SND_JACK_BTN_0 = 0x4000, diff --git a/include/sound/soc.h b/include/sound/soc.h index 90d0ff18d470..b8c17cc5d5f1 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -584,12 +584,13 @@ int snd_soc_update_bits_locked(struct snd_soc_codec *codec, int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned int reg, unsigned int mask, unsigned int value); +void snd_soc_card_change_online_state(struct snd_soc_card *soc_card, + int online); #ifdef CONFIG_SND_SOC_AC97_BUS struct snd_ac97 *snd_soc_alloc_ac97_codec(struct snd_soc_codec *codec); struct snd_ac97 *snd_soc_new_ac97_codec(struct snd_soc_codec *codec, unsigned int id, unsigned int id_mask); void snd_soc_free_ac97_codec(struct snd_ac97 *ac97); - int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops); int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops, struct platform_device *pdev); diff --git a/sound/core/init.c b/sound/core/init.c index 32ebe2f6bc59..bb5b76ca3a3e 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -58,6 +58,8 @@ static char *slots[SNDRV_CARDS]; module_param_array(slots, charp, NULL, 0444); MODULE_PARM_DESC(slots, "Module names assigned to the slots."); +#define SND_CARD_STATE_MAX_LEN 16 + /* return non-zero if the given index is reserved for the given * module via slots option */ @@ -107,9 +109,39 @@ static void snd_card_id_read(struct snd_info_entry *entry, snd_iprintf(buffer, "%s\n", entry->card->id); } +static ssize_t snd_card_state_read(struct snd_info_entry *entry, + void *file_private_data, struct file *file, + char __user *buf, size_t count, loff_t pos) +{ + int len; + char buffer[SND_CARD_STATE_MAX_LEN]; + + /* make sure offline is updated prior to wake up */ + rmb(); + len = snprintf(buffer, sizeof(buffer), "%s\n", + entry->card->offline ? "OFFLINE" : "ONLINE"); + return simple_read_from_buffer(buf, count, &pos, buffer, len); +} + +static unsigned int snd_card_state_poll(struct snd_info_entry *entry, + void *private_data, struct file *file, + poll_table *wait) +{ + poll_wait(file, &entry->card->offline_poll_wait, wait); + if (xchg(&entry->card->offline_change, 0)) + return POLLIN | POLLPRI | POLLRDNORM; + else + return 0; +} + +static struct snd_info_entry_ops snd_card_state_proc_ops = { + .read = snd_card_state_read, + .poll = snd_card_state_poll, +}; + static int init_info_for_card(struct snd_card *card) { - struct snd_info_entry *entry; + struct snd_info_entry *entry, *entry_state; entry = snd_info_create_card_entry(card, "id", card->proc_root); if (!entry) { @@ -119,6 +151,17 @@ static int init_info_for_card(struct snd_card *card) entry->c.text.read = snd_card_id_read; card->proc_id = entry; + entry_state = snd_info_create_card_entry(card, "state", + card->proc_root); + if (!entry_state) { + dev_dbg(card->dev, "unable to create card entry state\n"); + card->proc_id = NULL; + return -ENOMEM; + } + entry_state->size = SND_CARD_STATE_MAX_LEN; + entry_state->content = SNDRV_INFO_CONTENT_DATA; + entry_state->c.ops = &snd_card_state_proc_ops; + return snd_info_card_register(card); } #else /* !CONFIG_SND_PROC_FS */ @@ -256,6 +299,7 @@ int snd_card_new(struct device *parent, int idx, const char *xid, init_waitqueue_head(&card->power_sleep); #endif + init_waitqueue_head(&card->offline_poll_wait); device_initialize(&card->card_dev); card->card_dev.parent = parent; card->card_dev.class = sound_class; @@ -968,6 +1012,35 @@ int snd_card_file_remove(struct snd_card *card, struct file *file) } EXPORT_SYMBOL(snd_card_file_remove); +/** + * snd_card_change_online_state - mark card's online/offline state + * @card: Card to mark + * @online: whether online of offline + * + * Mutes the DAI DAC. + */ +void snd_card_change_online_state(struct snd_card *card, int online) +{ + snd_printd("snd card %s state change %d -> %d\n", + card->shortname, !card->offline, online); + card->offline = !online; + /* make sure offline is updated prior to wake up */ + wmb(); + xchg(&card->offline_change, 1); + wake_up_interruptible(&card->offline_poll_wait); +} +EXPORT_SYMBOL(snd_card_change_online_state); + +/** + * snd_card_is_online_state - return true if card is online state + * @card: Card to query + */ +bool snd_card_is_online_state(struct snd_card *card) +{ + return !card->offline; +} +EXPORT_SYMBOL(snd_card_is_online_state); + #ifdef CONFIG_PM /** * snd_power_wait - wait until the power-state is changed. diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 004fcd414821..90b1ba9d7180 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3694,6 +3694,17 @@ static int snd_soc_codec_set_bias_level(struct snd_soc_dapm_context *dapm, return codec->driver->set_bias_level(codec, level); } +/** + * snd_soc_card_change_online_state - Mark if soc card is online/offline + * + * @soc_card : soc_card to mark + */ +void snd_soc_card_change_online_state(struct snd_soc_card *soc_card, int online) +{ + snd_card_change_online_state(soc_card->snd_card, online); +} +EXPORT_SYMBOL(snd_soc_card_change_online_state); + /** * snd_soc_register_codec - Register a codec with the ASoC core * diff --git a/sound/soc/soc-io.c b/sound/soc/soc-io.c index 9b3939049cef..a90a1630ab1c 100644 --- a/sound/soc/soc-io.c +++ b/sound/soc/soc-io.c @@ -195,9 +195,14 @@ EXPORT_SYMBOL_GPL(snd_soc_component_test_bits); unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg) { + struct snd_card *snd_card = codec->component.card->snd_card; unsigned int val; int ret; + if (unlikely(!snd_card_is_online_state(snd_card))) { + dev_err(codec->dev, "read 0x%02x while offline\n", reg); + return -ENODEV; + } ret = snd_soc_component_read(&codec->component, reg, &val); if (ret < 0) return -1; @@ -209,6 +214,12 @@ EXPORT_SYMBOL_GPL(snd_soc_read); int snd_soc_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int val) { + struct snd_card *snd_card = codec->component.card->snd_card; + + if (unlikely(!snd_card_is_online_state(snd_card))) { + dev_err(codec->dev, "write 0x%02x while offline\n", reg); + return -ENODEV; + } return snd_soc_component_write(&codec->component, reg, val); } EXPORT_SYMBOL_GPL(snd_soc_write);