From 8ad9f9efcc7656cafb56bbbcd545f817a742bf32 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 16 Jun 2014 16:33:46 +0200 Subject: ASoC: Drop const from struct snd_soc_dai_link *of_node members MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dropping the const qualifiers prevents "passing argument 1 of ‘of_node_put’ discards ‘const’ qualifier from pointer target type" type warnings when compiling the code dropping reference to cpu_of_node, codec_of_node or platform_of_node with with an of_node_put() function call. This lets us to avoid casting to struct device_node * or caching variables internally in drivers just to be able to properly drop a reference to the OF node on clean up paths. Signed-off-by: Sylwester Nawrocki Signed-off-by: Mark Brown --- include/sound/soc.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index be6ecae247b0..fd58371c63ff 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -897,7 +897,7 @@ struct snd_soc_dai_link { * only for codec to codec links, or systems using device tree. */ const char *cpu_name; - const struct device_node *cpu_of_node; + struct device_node *cpu_of_node; /* * You MAY specify the DAI name of the CPU DAI. If this information is * omitted, the CPU-side DAI is matched using .cpu_name/.cpu_of_node @@ -909,7 +909,7 @@ struct snd_soc_dai_link { * DT/OF node, but not both. */ const char *codec_name; - const struct device_node *codec_of_node; + struct device_node *codec_of_node; /* You MUST specify the DAI name within the codec */ const char *codec_dai_name; @@ -922,7 +922,7 @@ struct snd_soc_dai_link { * do not need a platform. */ const char *platform_name; - const struct device_node *platform_of_node; + struct device_node *platform_of_node; int be_id; /* optional ID for machine driver BE identification */ const struct snd_soc_pcm_stream *params; -- cgit v1.3-14-g43fede From eef5bb2445ca49911c93c08ed0fb2ea7363ea945 Mon Sep 17 00:00:00 2001 From: Brian Austin Date: Mon, 4 Aug 2014 15:11:16 -0500 Subject: ASoC: cs35l32: Add support for CS35L32 Boosted Amplifier This patch adds support for the Cirrus Logic CS35L32 Boosted Amplifier I2S output provides monitor data to the SOC/CODEC/DSP for speaker protection/enhancement algorithms Signed-off-by: Brian Austin Signed-off-by: Mark Brown --- include/dt-bindings/sound/cs35l32.h | 26 ++ sound/soc/codecs/Kconfig | 5 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/cs35l32.c | 647 ++++++++++++++++++++++++++++++++++++ sound/soc/codecs/cs35l32.h | 93 ++++++ 5 files changed, 773 insertions(+) create mode 100644 include/dt-bindings/sound/cs35l32.h create mode 100644 sound/soc/codecs/cs35l32.c create mode 100644 sound/soc/codecs/cs35l32.h (limited to 'include') diff --git a/include/dt-bindings/sound/cs35l32.h b/include/dt-bindings/sound/cs35l32.h new file mode 100644 index 000000000000..0c6d6a3c15a2 --- /dev/null +++ b/include/dt-bindings/sound/cs35l32.h @@ -0,0 +1,26 @@ +#ifndef __DT_CS35L32_H +#define __DT_CS35L32_H + +#define CS35L32_BOOST_MGR_AUTO 0 +#define CS35L32_BOOST_MGR_AUTO_AUDIO 1 +#define CS35L32_BOOST_MGR_BYPASS 2 +#define CS35L32_BOOST_MGR_FIXED 3 + +#define CS35L32_DATA_CFG_LR_VP 0 +#define CS35L32_DATA_CFG_LR_STAT 1 +#define CS35L32_DATA_CFG_LR 2 +#define CS35L32_DATA_CFG_LR_VPSTAT 3 + +#define CS35L32_BATT_THRESH_3_1V 0 +#define CS35L32_BATT_THRESH_3_2V 1 +#define CS35L32_BATT_THRESH_3_3V 2 +#define CS35L32_BATT_THRESH_3_4V 3 + +#define CS35L32_BATT_RECOV_3_1V 0 +#define CS35L32_BATT_RECOV_3_2V 1 +#define CS35L32_BATT_RECOV_3_3V 2 +#define CS35L32_BATT_RECOV_3_4V 3 +#define CS35L32_BATT_RECOV_3_5V 4 +#define CS35L32_BATT_RECOV_3_6V 5 + +#endif /* __DT_CS35L32_H */ diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 8838838e25ed..77e5383b4361 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -43,6 +43,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_ALC5623 if I2C select SND_SOC_ALC5632 if I2C select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC + select SND_SOC_CS35L32 if I2C select SND_SOC_CS42L51_I2C if I2C select SND_SOC_CS42L52 if I2C && INPUT select SND_SOC_CS42L56 if I2C && INPUT @@ -323,6 +324,10 @@ config SND_SOC_ALC5632 config SND_SOC_CQ0093VC tristate +config SND_SOC_CS35L32 + tristate "Cirrus Logic CS35L32 CODEC" + depends on I2C + config SND_SOC_CS42L51 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 20afe0f0c5be..1dacefbdac3c 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -32,6 +32,7 @@ snd-soc-ak4671-objs := ak4671.o snd-soc-ak5386-objs := ak5386.o snd-soc-arizona-objs := arizona.o snd-soc-cq93vc-objs := cq93vc.o +snd-soc-cs35l32-objs := cs35l32.o snd-soc-cs42l51-objs := cs42l51.o snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o snd-soc-cs42l52-objs := cs42l52.o @@ -203,6 +204,7 @@ obj-$(CONFIG_SND_SOC_ALC5623) += snd-soc-alc5623.o obj-$(CONFIG_SND_SOC_ALC5632) += snd-soc-alc5632.o obj-$(CONFIG_SND_SOC_ARIZONA) += snd-soc-arizona.o obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o +obj-$(CONFIG_SND_SOC_CS35L32) += snd-soc-cs35l32.o obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o obj-$(CONFIG_SND_SOC_CS42L51_I2C) += snd-soc-cs42l51-i2c.o obj-$(CONFIG_SND_SOC_CS42L52) += snd-soc-cs42l52.o diff --git a/sound/soc/codecs/cs35l32.c b/sound/soc/codecs/cs35l32.c new file mode 100644 index 000000000000..90565d59def7 --- /dev/null +++ b/sound/soc/codecs/cs35l32.c @@ -0,0 +1,647 @@ +/* + * cs35l32.c -- CS35L32 ALSA SoC audio driver + * + * Copyright 2014 CirrusLogic, Inc. + * + * Author: Brian Austin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cs35l32.h" + +#define CS35L32_NUM_SUPPLIES 2 +static const char *const cs35l32_supply_names[CS35L32_NUM_SUPPLIES] = { + "VA", + "VP", +}; + +struct cs35l32_private { + struct regmap *regmap; + struct snd_soc_codec *codec; + struct regulator_bulk_data supplies[CS35L32_NUM_SUPPLIES]; + struct cs35l32_platform_data pdata; + struct gpio_desc *reset_gpio; +}; + +static const struct reg_default cs35l32_reg_defaults[] = { + + { 0x06, 0x04 }, /* Power Ctl 1 */ + { 0x07, 0xE8 }, /* Power Ctl 2 */ + { 0x08, 0x40 }, /* Clock Ctl */ + { 0x09, 0x20 }, /* Low Battery Threshold */ + { 0x0A, 0x00 }, /* Voltage Monitor [RO] */ + { 0x0B, 0x40 }, /* Conv Peak Curr Protection CTL */ + { 0x0C, 0x07 }, /* IMON Scaling */ + { 0x0D, 0x03 }, /* Audio/LED Pwr Manager */ + { 0x0F, 0x20 }, /* Serial Port Control */ + { 0x10, 0x14 }, /* Class D Amp CTL */ + { 0x11, 0x00 }, /* Protection Release CTL */ + { 0x12, 0xFF }, /* Interrupt Mask 1 */ + { 0x13, 0xFF }, /* Interrupt Mask 2 */ + { 0x14, 0xFF }, /* Interrupt Mask 3 */ + { 0x19, 0x00 }, /* LED Flash Mode Current */ + { 0x1A, 0x00 }, /* LED Movie Mode Current */ + { 0x1B, 0x20 }, /* LED Flash Timer */ + { 0x1C, 0x00 }, /* LED Flash Inhibit Current */ +}; + +static bool cs35l32_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L32_DEVID_AB: + case CS35L32_DEVID_CD: + case CS35L32_DEVID_E: + case CS35L32_FAB_ID: + case CS35L32_REV_ID: + case CS35L32_PWRCTL1: + case CS35L32_PWRCTL2: + case CS35L32_CLK_CTL: + case CS35L32_BATT_THRESHOLD: + case CS35L32_VMON: + case CS35L32_BST_CPCP_CTL: + case CS35L32_IMON_SCALING: + case CS35L32_AUDIO_LED_MNGR: + case CS35L32_ADSP_CTL: + case CS35L32_CLASSD_CTL: + case CS35L32_PROTECT_CTL: + case CS35L32_INT_MASK_1: + case CS35L32_INT_MASK_2: + case CS35L32_INT_MASK_3: + case CS35L32_INT_STATUS_1: + case CS35L32_INT_STATUS_2: + case CS35L32_INT_STATUS_3: + case CS35L32_LED_STATUS: + case CS35L32_FLASH_MODE: + case CS35L32_MOVIE_MODE: + case CS35L32_FLASH_TIMER: + case CS35L32_FLASH_INHIBIT: + return true; + default: + return false; + } +} + +static bool cs35l32_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L32_DEVID_AB: + case CS35L32_DEVID_CD: + case CS35L32_DEVID_E: + case CS35L32_FAB_ID: + case CS35L32_REV_ID: + case CS35L32_INT_STATUS_1: + case CS35L32_INT_STATUS_2: + case CS35L32_INT_STATUS_3: + case CS35L32_LED_STATUS: + return 1; + default: + return 0; + } +} + +static bool cs35l32_precious_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L32_INT_STATUS_1: + case CS35L32_INT_STATUS_2: + case CS35L32_INT_STATUS_3: + case CS35L32_LED_STATUS: + return 1; + default: + return 0; + } +} + +static DECLARE_TLV_DB_SCALE(classd_ctl_tlv, 900, 300, 0); + +static const struct snd_kcontrol_new imon_ctl = + SOC_DAPM_SINGLE("Switch", CS35L32_PWRCTL2, 6, 1, 1); + +static const struct snd_kcontrol_new vmon_ctl = + SOC_DAPM_SINGLE("Switch", CS35L32_PWRCTL2, 7, 1, 1); + +static const struct snd_kcontrol_new vpmon_ctl = + SOC_DAPM_SINGLE("Switch", CS35L32_PWRCTL2, 5, 1, 1); + +static const struct snd_kcontrol_new cs35l32_snd_controls[] = { + SOC_SINGLE_TLV("Speaker Volume", CS35L32_CLASSD_CTL, + 3, 0x04, 1, classd_ctl_tlv), + SOC_SINGLE("Zero Cross Switch", CS35L32_CLASSD_CTL, 2, 1, 0), + SOC_SINGLE("Gain Manager Switch", CS35L32_AUDIO_LED_MNGR, 3, 1, 0), +}; + +static const struct snd_soc_dapm_widget cs35l32_dapm_widgets[] = { + + SND_SOC_DAPM_SUPPLY("BOOST", CS35L32_PWRCTL1, 2, 1, NULL, 0), + SND_SOC_DAPM_OUT_DRV("Speaker", CS35L32_PWRCTL1, 7, 1, NULL, 0), + + SND_SOC_DAPM_AIF_OUT("SDOUT", NULL, 0, CS35L32_PWRCTL2, 3, 1), + + SND_SOC_DAPM_INPUT("VP"), + SND_SOC_DAPM_INPUT("ISENSE"), + SND_SOC_DAPM_INPUT("VSENSE"), + + SND_SOC_DAPM_SWITCH("VMON ADC", CS35L32_PWRCTL2, 7, 1, &vmon_ctl), + SND_SOC_DAPM_SWITCH("IMON ADC", CS35L32_PWRCTL2, 6, 1, &imon_ctl), + SND_SOC_DAPM_SWITCH("VPMON ADC", CS35L32_PWRCTL2, 5, 1, &vpmon_ctl), +}; + +static const struct snd_soc_dapm_route cs35l32_audio_map[] = { + + {"Speaker", NULL, "BOOST"}, + + {"VMON ADC", NULL, "VSENSE"}, + {"IMON ADC", NULL, "ISENSE"}, + {"VPMON ADC", NULL, "VP"}, + + {"SDOUT", "Switch", "VMON ADC"}, + {"SDOUT", "Switch", "IMON ADC"}, + {"SDOUT", "Switch", "VPMON ADC"}, + + {"Capture", NULL, "SDOUT"}, +}; + +static int cs35l32_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + snd_soc_update_bits(codec, CS35L32_ADSP_CTL, + CS35L32_ADSP_MASTER_MASK, + CS35L32_ADSP_MASTER_MASK); + break; + case SND_SOC_DAIFMT_CBS_CFS: + snd_soc_update_bits(codec, CS35L32_ADSP_CTL, + CS35L32_ADSP_MASTER_MASK, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int cs35l32_set_tristate(struct snd_soc_dai *dai, int tristate) +{ + struct snd_soc_codec *codec = dai->codec; + + return snd_soc_update_bits(codec, CS35L32_PWRCTL2, + CS35L32_SDOUT_3ST, tristate << 3); +} + +static const struct snd_soc_dai_ops cs35l32_ops = { + .set_fmt = cs35l32_set_dai_fmt, + .set_tristate = cs35l32_set_tristate, +}; + +static struct snd_soc_dai_driver cs35l32_dai[] = { + { + .name = "cs35l32-monitor", + .id = 0, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = CS35L32_RATES, + .formats = CS35L32_FORMATS, + }, + .ops = &cs35l32_ops, + .symmetric_rates = 1, + } +}; + +static int cs35l32_codec_set_sysclk(struct snd_soc_codec *codec, + int clk_id, int source, unsigned int freq, int dir) +{ + + switch (freq) { + case 6000000: + snd_soc_update_bits(codec, CS35L32_CLK_CTL, + CS35L32_MCLK_DIV2_MASK, 0); + snd_soc_update_bits(codec, CS35L32_CLK_CTL, + CS35L32_MCLK_RATIO_MASK, + CS35L32_MCLK_RATIO); + break; + case 12000000: + snd_soc_update_bits(codec, CS35L32_CLK_CTL, + CS35L32_MCLK_DIV2_MASK, + CS35L32_MCLK_DIV2_MASK); + snd_soc_update_bits(codec, CS35L32_CLK_CTL, + CS35L32_MCLK_RATIO_MASK, + CS35L32_MCLK_RATIO); + break; + case 6144000: + snd_soc_update_bits(codec, CS35L32_CLK_CTL, + CS35L32_MCLK_DIV2_MASK, 0); + snd_soc_update_bits(codec, CS35L32_CLK_CTL, + CS35L32_MCLK_RATIO_MASK, 0); + break; + case 12288000: + snd_soc_update_bits(codec, CS35L32_CLK_CTL, + CS35L32_MCLK_DIV2_MASK, + CS35L32_MCLK_DIV2_MASK); + snd_soc_update_bits(codec, CS35L32_CLK_CTL, + CS35L32_MCLK_RATIO_MASK, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static struct snd_soc_codec_driver soc_codec_dev_cs35l32 = { + .set_sysclk = cs35l32_codec_set_sysclk, + + .dapm_widgets = cs35l32_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs35l32_dapm_widgets), + .dapm_routes = cs35l32_audio_map, + .num_dapm_routes = ARRAY_SIZE(cs35l32_audio_map), + + .controls = cs35l32_snd_controls, + .num_controls = ARRAY_SIZE(cs35l32_snd_controls), +}; + +/* Current and threshold powerup sequence Pg37 in datasheet */ +static const struct reg_default cs35l32_monitor_patch[] = { + + { 0x00, 0x99 }, + { 0x48, 0x17 }, + { 0x49, 0x56 }, + { 0x43, 0x01 }, + { 0x3B, 0x62 }, + { 0x3C, 0x80 }, + { 0x00, 0x00 }, +}; + +static struct regmap_config cs35l32_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = CS35L32_MAX_REGISTER, + .reg_defaults = cs35l32_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(cs35l32_reg_defaults), + .volatile_reg = cs35l32_volatile_register, + .readable_reg = cs35l32_readable_register, + .precious_reg = cs35l32_precious_register, + .cache_type = REGCACHE_RBTREE, +}; + +static int cs35l32_handle_of_data(struct i2c_client *i2c_client, + struct cs35l32_platform_data *pdata) +{ + struct device_node *np = i2c_client->dev.of_node; + unsigned int val; + + if (of_property_read_u32(np, "cirrus,sdout-share", &val) >= 0) + pdata->sdout_share = val; + + of_property_read_u32(np, "cirrus,boost-manager", &val); + switch (val) { + case CS35L32_BOOST_MGR_AUTO: + case CS35L32_BOOST_MGR_AUTO_AUDIO: + case CS35L32_BOOST_MGR_BYPASS: + case CS35L32_BOOST_MGR_FIXED: + pdata->boost_mng = val; + break; + default: + dev_err(&i2c_client->dev, + "Wrong cirrus,boost-manager DT value %d\n", val); + pdata->boost_mng = CS35L32_BOOST_MGR_BYPASS; + } + + of_property_read_u32(np, "cirrus,sdout-datacfg", &val); + switch (val) { + case CS35L32_DATA_CFG_LR_VP: + case CS35L32_DATA_CFG_LR_STAT: + case CS35L32_DATA_CFG_LR: + case CS35L32_DATA_CFG_LR_VPSTAT: + pdata->sdout_datacfg = val; + break; + default: + dev_err(&i2c_client->dev, + "Wrong cirrus,sdout-datacfg DT value %d\n", val); + pdata->sdout_datacfg = CS35L32_DATA_CFG_LR; + } + + of_property_read_u32(np, "cirrus,battery-threshold", &val); + switch (val) { + case CS35L32_BATT_THRESH_3_1V: + case CS35L32_BATT_THRESH_3_2V: + case CS35L32_BATT_THRESH_3_3V: + case CS35L32_BATT_THRESH_3_4V: + pdata->batt_thresh = val; + break; + default: + dev_err(&i2c_client->dev, + "Wrong cirrus,battery-threshold DT value %d\n", val); + pdata->batt_thresh = CS35L32_BATT_THRESH_3_3V; + } + + of_property_read_u32(np, "cirrus,battery-recovery", &val); + switch (val) { + case CS35L32_BATT_RECOV_3_1V: + case CS35L32_BATT_RECOV_3_2V: + case CS35L32_BATT_RECOV_3_3V: + case CS35L32_BATT_RECOV_3_4V: + case CS35L32_BATT_RECOV_3_5V: + case CS35L32_BATT_RECOV_3_6V: + pdata->batt_recov = val; + break; + default: + dev_err(&i2c_client->dev, + "Wrong cirrus,battery-recovery DT value %d\n", val); + pdata->batt_recov = CS35L32_BATT_RECOV_3_4V; + } + + return 0; +} + +static int cs35l32_i2c_probe(struct i2c_client *i2c_client, + const struct i2c_device_id *id) +{ + struct cs35l32_private *cs35l32; + struct cs35l32_platform_data *pdata = + dev_get_platdata(&i2c_client->dev); + int ret, i; + unsigned int devid = 0; + unsigned int reg; + + + cs35l32 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs35l32_private), + GFP_KERNEL); + if (!cs35l32) { + dev_err(&i2c_client->dev, "could not allocate codec\n"); + return -ENOMEM; + } + + i2c_set_clientdata(i2c_client, cs35l32); + + cs35l32->regmap = devm_regmap_init_i2c(i2c_client, &cs35l32_regmap); + if (IS_ERR(cs35l32->regmap)) { + ret = PTR_ERR(cs35l32->regmap); + dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret); + return ret; + } + + if (pdata) { + cs35l32->pdata = *pdata; + } else { + pdata = devm_kzalloc(&i2c_client->dev, + sizeof(struct cs35l32_platform_data), + GFP_KERNEL); + if (!pdata) { + dev_err(&i2c_client->dev, "could not allocate pdata\n"); + return -ENOMEM; + } + if (i2c_client->dev.of_node) { + ret = cs35l32_handle_of_data(i2c_client, + &cs35l32->pdata); + if (ret != 0) + return ret; + } + } + + for (i = 0; i < ARRAY_SIZE(cs35l32->supplies); i++) + cs35l32->supplies[i].supply = cs35l32_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c_client->dev, + ARRAY_SIZE(cs35l32->supplies), + cs35l32->supplies); + if (ret != 0) { + dev_err(&i2c_client->dev, + "Failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(cs35l32->supplies), + cs35l32->supplies); + if (ret != 0) { + dev_err(&i2c_client->dev, + "Failed to enable supplies: %d\n", ret); + return ret; + } + + /* Reset the Device */ + cs35l32->reset_gpio = devm_gpiod_get(&i2c_client->dev, + "reset-gpios"); + if (IS_ERR(cs35l32->reset_gpio)) { + ret = PTR_ERR(cs35l32->reset_gpio); + if (ret != -ENOENT && ret != -ENOSYS) + return ret; + + cs35l32->reset_gpio = NULL; + } else { + ret = gpiod_direction_output(cs35l32->reset_gpio, 0); + if (ret) + return ret; + gpiod_set_value_cansleep(cs35l32->reset_gpio, 1); + } + + /* initialize codec */ + ret = regmap_read(cs35l32->regmap, CS35L32_DEVID_AB, ®); + devid = (reg & 0xFF) << 12; + + ret = regmap_read(cs35l32->regmap, CS35L32_DEVID_CD, ®); + devid |= (reg & 0xFF) << 4; + + ret = regmap_read(cs35l32->regmap, CS35L32_DEVID_E, ®); + devid |= (reg & 0xF0) >> 4; + + if (devid != CS35L32_CHIP_ID) { + ret = -ENODEV; + dev_err(&i2c_client->dev, + "CS35L32 Device ID (%X). Expected %X\n", + devid, CS35L32_CHIP_ID); + return ret; + } + + ret = regmap_read(cs35l32->regmap, CS35L32_REV_ID, ®); + if (ret < 0) { + dev_err(&i2c_client->dev, "Get Revision ID failed\n"); + return ret; + } + + ret = regmap_register_patch(cs35l32->regmap, cs35l32_monitor_patch, + ARRAY_SIZE(cs35l32_monitor_patch)); + if (ret < 0) { + dev_err(&i2c_client->dev, "Failed to apply errata patch\n"); + return ret; + } + + dev_info(&i2c_client->dev, + "Cirrus Logic CS35L32, Revision: %02X\n", reg & 0xFF); + + /* Setup VBOOST Management */ + if (cs35l32->pdata.boost_mng) + regmap_update_bits(cs35l32->regmap, CS35L32_AUDIO_LED_MNGR, + CS35L32_BOOST_MASK, + cs35l32->pdata.boost_mng); + + /* Setup ADSP Format Config */ + if (cs35l32->pdata.sdout_share) + regmap_update_bits(cs35l32->regmap, CS35L32_ADSP_CTL, + CS35L32_ADSP_SHARE_MASK, + cs35l32->pdata.sdout_share << 3); + + /* Setup ADSP Data Configuration */ + if (cs35l32->pdata.sdout_datacfg) + regmap_update_bits(cs35l32->regmap, CS35L32_ADSP_CTL, + CS35L32_ADSP_DATACFG_MASK, + cs35l32->pdata.sdout_datacfg << 4); + + /* Setup Low Battery Recovery */ + if (cs35l32->pdata.batt_recov) + regmap_update_bits(cs35l32->regmap, CS35L32_BATT_THRESHOLD, + CS35L32_BATT_REC_MASK, + cs35l32->pdata.batt_recov << 1); + + /* Setup Low Battery Threshold */ + if (cs35l32->pdata.batt_thresh) + regmap_update_bits(cs35l32->regmap, CS35L32_BATT_THRESHOLD, + CS35L32_BATT_THRESH_MASK, + cs35l32->pdata.batt_thresh << 4); + + /* Power down the AMP */ + regmap_update_bits(cs35l32->regmap, CS35L32_PWRCTL1, CS35L32_PDN_AMP, + CS35L32_PDN_AMP); + + /* Clear MCLK Error Bit since we don't have the clock yet */ + ret = regmap_read(cs35l32->regmap, CS35L32_INT_STATUS_1, ®); + + ret = snd_soc_register_codec(&i2c_client->dev, + &soc_codec_dev_cs35l32, cs35l32_dai, + ARRAY_SIZE(cs35l32_dai)); + if (ret < 0) + goto err_disable; + + return 0; + +err_disable: + regulator_bulk_disable(ARRAY_SIZE(cs35l32->supplies), + cs35l32->supplies); +} + +static int cs35l32_i2c_remove(struct i2c_client *i2c_client) +{ + struct cs35l32_private *cs35l32 = i2c_get_clientdata(i2c_client); + + snd_soc_unregister_codec(&i2c_client->dev); + + /* Hold down reset */ + if (cs35l32->reset_gpio) + gpiod_set_value_cansleep(cs35l32->reset_gpio, 0); + + regulator_bulk_free(ARRAY_SIZE(cs35l32->supplies), cs35l32->supplies); + + return 0; +} + +#ifdef CONFIG_PM_RUNTIME +static int cs35l32_runtime_suspend(struct device *dev) +{ + struct cs35l32_private *cs35l32 = dev_get_drvdata(dev); + + regcache_cache_only(cs35l32->regmap, true); + regcache_mark_dirty(cs35l32->regmap); + + /* Hold down reset */ + if (cs35l32->reset_gpio) + gpiod_set_value_cansleep(cs35l32->reset_gpio, 0); + + /* remove power */ + regulator_bulk_disable(ARRAY_SIZE(cs35l32->supplies), + cs35l32->supplies); + + return 0; +} + +static int cs35l32_runtime_resume(struct device *dev) +{ + struct cs35l32_private *cs35l32 = dev_get_drvdata(dev); + int ret; + + /* Enable power */ + ret = regulator_bulk_enable(ARRAY_SIZE(cs35l32->supplies), + cs35l32->supplies); + if (ret != 0) { + dev_err(dev, "Failed to enable supplies: %d\n", + ret); + return ret; + } + + if (cs35l32->reset_gpio) + gpiod_set_value_cansleep(cs35l32->reset_gpio, 1); + + regcache_cache_only(cs35l32->regmap, false); + regcache_sync(cs35l32->regmap); + + return 0; +} +#endif + +static const struct dev_pm_ops cs35l32_runtime_pm = { + SET_RUNTIME_PM_OPS(cs35l32_runtime_suspend, cs35l32_runtime_resume, + NULL) +}; + +static const struct of_device_id cs35l32_of_match[] = { + { .compatible = "cirrus,cs35l32", }, + {}, +}; +MODULE_DEVICE_TABLE(of, cs35l32_of_match); + + +static const struct i2c_device_id cs35l32_id[] = { + {"cs35l32", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, cs35l32_id); + +static struct i2c_driver cs35l32_i2c_driver = { + .driver = { + .name = "cs35l32", + .owner = THIS_MODULE, + .pm = &cs35l32_runtime_pm, + .of_match_table = cs35l32_of_match, + }, + .id_table = cs35l32_id, + .probe = cs35l32_i2c_probe, + .remove = cs35l32_i2c_remove, +}; + +module_i2c_driver(cs35l32_i2c_driver); + +MODULE_DESCRIPTION("ASoC CS35L32 driver"); +MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs35l32.h b/sound/soc/codecs/cs35l32.h new file mode 100644 index 000000000000..31ab804a22bc --- /dev/null +++ b/sound/soc/codecs/cs35l32.h @@ -0,0 +1,93 @@ +/* + * cs35l32.h -- CS35L32 ALSA SoC audio driver + * + * Copyright 2014 CirrusLogic, Inc. + * + * Author: Brian Austin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef __CS35L32_H__ +#define __CS35L32_H__ + +struct cs35l32_platform_data { + /* Low Battery Threshold */ + unsigned int batt_thresh; + /* Low Battery Recovery */ + unsigned int batt_recov; + /* LED Current Management*/ + unsigned int led_mng; + /* Audio Gain w/ LED */ + unsigned int audiogain_mng; + /* Boost Management */ + unsigned int boost_mng; + /* Data CFG for DUAL device */ + unsigned int sdout_datacfg; + /* SDOUT Sharing */ + unsigned int sdout_share; +}; + +#define CS35L32_CHIP_ID 0x00035A32 +#define CS35L32_DEVID_AB 0x01 /* Device ID A & B [RO] */ +#define CS35L32_DEVID_CD 0x02 /* Device ID C & D [RO] */ +#define CS35L32_DEVID_E 0x03 /* Device ID E [RO] */ +#define CS35L32_FAB_ID 0x04 /* Fab ID [RO] */ +#define CS35L32_REV_ID 0x05 /* Revision ID [RO] */ +#define CS35L32_PWRCTL1 0x06 /* Power Ctl 1 */ +#define CS35L32_PWRCTL2 0x07 /* Power Ctl 2 */ +#define CS35L32_CLK_CTL 0x08 /* Clock Ctl */ +#define CS35L32_BATT_THRESHOLD 0x09 /* Low Battery Threshold */ +#define CS35L32_VMON 0x0A /* Voltage Monitor [RO] */ +#define CS35L32_BST_CPCP_CTL 0x0B /* Conv Peak Curr Protection CTL */ +#define CS35L32_IMON_SCALING 0x0C /* IMON Scaling */ +#define CS35L32_AUDIO_LED_MNGR 0x0D /* Audio/LED Pwr Manager */ +#define CS35L32_ADSP_CTL 0x0F /* Serial Port Control */ +#define CS35L32_CLASSD_CTL 0x10 /* Class D Amp CTL */ +#define CS35L32_PROTECT_CTL 0x11 /* Protection Release CTL */ +#define CS35L32_INT_MASK_1 0x12 /* Interrupt Mask 1 */ +#define CS35L32_INT_MASK_2 0x13 /* Interrupt Mask 2 */ +#define CS35L32_INT_MASK_3 0x14 /* Interrupt Mask 3 */ +#define CS35L32_INT_STATUS_1 0x15 /* Interrupt Status 1 [RO] */ +#define CS35L32_INT_STATUS_2 0x16 /* Interrupt Status 2 [RO] */ +#define CS35L32_INT_STATUS_3 0x17 /* Interrupt Status 3 [RO] */ +#define CS35L32_LED_STATUS 0x18 /* LED Lighting Status [RO] */ +#define CS35L32_FLASH_MODE 0x19 /* LED Flash Mode Current */ +#define CS35L32_MOVIE_MODE 0x1A /* LED Movie Mode Current */ +#define CS35L32_FLASH_TIMER 0x1B /* LED Flash Timer */ +#define CS35L32_FLASH_INHIBIT 0x1C /* LED Flash Inhibit Current */ +#define CS35L32_MAX_REGISTER 0x1C + +#define CS35L32_MCLK_DIV2 0x01 +#define CS35L32_MCLK_RATIO 0x01 +#define CS35L32_MCLKDIS 0x80 +#define CS35L32_PDN_ALL 0x01 +#define CS35L32_PDN_AMP 0x80 +#define CS35L32_PDN_BOOST 0x04 +#define CS35L32_PDN_IMON 0x40 +#define CS35L32_PDN_VMON 0x80 +#define CS35L32_PDN_VPMON 0x20 +#define CS35L32_PDN_ADSP 0x08 + +#define CS35L32_MCLK_DIV2_MASK 0x40 +#define CS35L32_MCLK_RATIO_MASK 0x01 +#define CS35L32_MCLK_MASK 0x41 +#define CS35L32_ADSP_MASTER_MASK 0x40 +#define CS35L32_BOOST_MASK 0x03 +#define CS35L32_GAIN_MGR_MASK 0x08 +#define CS35L32_ADSP_SHARE_MASK 0x08 +#define CS35L32_ADSP_DATACFG_MASK 0x30 +#define CS35L32_SDOUT_3ST 0x80 +#define CS35L32_BATT_REC_MASK 0x0E +#define CS35L32_BATT_THRESH_MASK 0x30 + +#define CS35L32_RATES (SNDRV_PCM_RATE_48000) +#define CS35L32_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + + +#endif -- cgit v1.3-14-g43fede From 81c7cfd1b22a0ee5e40efef72ec2cd17dbf12e6d Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 19 Aug 2014 15:51:18 +0200 Subject: ASoC: Move debugfs registration to the component level The debugfs registration is mostly identical between platforms and CODECs. This patches consolidates the two implementations at the component level. Unfortunately there are still a couple of CODEC specific debugfs files that are related to legacy ASoC IO that need to be registered. For this a new callback is added to the component struct that will be initialized when a CODEC is registered and will be used to register the CODEC specific files. Once there are no drivers left using legacy IO this can be removed again. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 20 ++++++--- sound/soc/soc-core.c | 122 ++++++++++++++++++++++----------------------------- 2 files changed, 67 insertions(+), 75 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index be6ecae247b0..0ab8b1e4a5d2 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -728,9 +728,24 @@ struct snd_soc_component { struct mutex io_mutex; +#ifdef CONFIG_DEBUG_FS + struct dentry *debugfs_root; +#endif + + /* + * DO NOT use any of the fields below in drivers, they are temporary and + * are going to be removed again soon. If you use them in driver code the + * driver will be marked as BROKEN when these fields are removed. + */ + /* Don't use these, use snd_soc_component_get_dapm() */ struct snd_soc_dapm_context dapm; struct snd_soc_dapm_context *dapm_ptr; + +#ifdef CONFIG_DEBUG_FS + void (*init_debugfs)(struct snd_soc_component *component); + const char *debugfs_prefix; +#endif }; /* SoC Audio Codec device */ @@ -766,7 +781,6 @@ struct snd_soc_codec { struct snd_soc_dapm_context dapm; #ifdef CONFIG_DEBUG_FS - struct dentry *debugfs_codec_root; struct dentry *debugfs_reg; #endif }; @@ -879,10 +893,6 @@ struct snd_soc_platform { struct list_head list; struct snd_soc_component component; - -#ifdef CONFIG_DEBUG_FS - struct dentry *debugfs_platform_root; -#endif }; struct snd_soc_dai_link { diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index d4bfd4a9076f..79371a77f324 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -270,79 +270,56 @@ static const struct file_operations codec_reg_fops = { .llseek = default_llseek, }; -static struct dentry *soc_debugfs_create_dir(struct dentry *parent, - const char *fmt, ...) +static void soc_init_component_debugfs(struct snd_soc_component *component) { - struct dentry *de; - va_list ap; - char *s; + if (component->debugfs_prefix) { + char *name; - va_start(ap, fmt); - s = kvasprintf(GFP_KERNEL, fmt, ap); - va_end(ap); + name = kasprintf(GFP_KERNEL, "%s:%s", + component->debugfs_prefix, component->name); + if (name) { + component->debugfs_root = debugfs_create_dir(name, + component->card->debugfs_card_root); + kfree(name); + } + } else { + component->debugfs_root = debugfs_create_dir(component->name, + component->card->debugfs_card_root); + } - if (!s) - return NULL; + if (!component->debugfs_root) { + dev_warn(component->dev, + "ASoC: Failed to create component debugfs directory\n"); + return; + } - de = debugfs_create_dir(s, parent); - kfree(s); + snd_soc_dapm_debugfs_init(snd_soc_component_get_dapm(component), + component->debugfs_root); - return de; + if (component->init_debugfs) + component->init_debugfs(component); } -static void soc_init_codec_debugfs(struct snd_soc_codec *codec) +static void soc_cleanup_component_debugfs(struct snd_soc_component *component) { - struct dentry *debugfs_card_root = codec->component.card->debugfs_card_root; + debugfs_remove_recursive(component->debugfs_root); +} - codec->debugfs_codec_root = soc_debugfs_create_dir(debugfs_card_root, - "codec:%s", - codec->component.name); - if (!codec->debugfs_codec_root) { - dev_warn(codec->dev, - "ASoC: Failed to create codec debugfs directory\n"); - return; - } +static void soc_init_codec_debugfs(struct snd_soc_component *component) +{ + struct snd_soc_codec *codec = snd_soc_component_to_codec(component); - debugfs_create_bool("cache_sync", 0444, codec->debugfs_codec_root, + debugfs_create_bool("cache_sync", 0444, codec->component.debugfs_root, &codec->cache_sync); - debugfs_create_bool("cache_only", 0444, codec->debugfs_codec_root, + debugfs_create_bool("cache_only", 0444, codec->component.debugfs_root, &codec->cache_only); codec->debugfs_reg = debugfs_create_file("codec_reg", 0644, - codec->debugfs_codec_root, + codec->component.debugfs_root, codec, &codec_reg_fops); if (!codec->debugfs_reg) dev_warn(codec->dev, "ASoC: Failed to create codec register debugfs file\n"); - - snd_soc_dapm_debugfs_init(&codec->dapm, codec->debugfs_codec_root); -} - -static void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec) -{ - debugfs_remove_recursive(codec->debugfs_codec_root); -} - -static void soc_init_platform_debugfs(struct snd_soc_platform *platform) -{ - struct dentry *debugfs_card_root = platform->component.card->debugfs_card_root; - - platform->debugfs_platform_root = soc_debugfs_create_dir(debugfs_card_root, - "platform:%s", - platform->component.name); - if (!platform->debugfs_platform_root) { - dev_warn(platform->dev, - "ASoC: Failed to create platform debugfs directory\n"); - return; - } - - snd_soc_dapm_debugfs_init(&platform->component.dapm, - platform->debugfs_platform_root); -} - -static void soc_cleanup_platform_debugfs(struct snd_soc_platform *platform) -{ - debugfs_remove_recursive(platform->debugfs_platform_root); } static ssize_t codec_list_read_file(struct file *file, char __user *user_buf, @@ -474,19 +451,15 @@ static void soc_cleanup_card_debugfs(struct snd_soc_card *card) #else -static inline void soc_init_codec_debugfs(struct snd_soc_codec *codec) -{ -} +#define soc_init_codec_debugfs NULL -static inline void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec) +static inline void soc_init_component_debugfs( + struct snd_soc_component *component) { } -static inline void soc_init_platform_debugfs(struct snd_soc_platform *platform) -{ -} - -static inline void soc_cleanup_platform_debugfs(struct snd_soc_platform *platform) +static inline void soc_cleanup_component_debugfs( + struct snd_soc_component *component) { } @@ -1026,7 +999,7 @@ static int soc_remove_platform(struct snd_soc_platform *platform) /* Make sure all DAPM widgets are freed */ snd_soc_dapm_free(&platform->component.dapm); - soc_cleanup_platform_debugfs(platform); + soc_cleanup_component_debugfs(&platform->component); platform->probed = 0; module_put(platform->dev->driver->owner); @@ -1046,7 +1019,7 @@ static void soc_remove_codec(struct snd_soc_codec *codec) /* Make sure all DAPM widgets are freed */ snd_soc_dapm_free(&codec->dapm); - soc_cleanup_codec_debugfs(codec); + soc_cleanup_component_debugfs(&codec->component); codec->probed = 0; list_del(&codec->card_list); module_put(codec->dev->driver->owner); @@ -1187,7 +1160,7 @@ static int soc_probe_codec(struct snd_soc_card *card, if (!try_module_get(codec->dev->driver->owner)) return -ENODEV; - soc_init_codec_debugfs(codec); + soc_init_component_debugfs(&codec->component); if (driver->dapm_widgets) { ret = snd_soc_dapm_new_controls(&codec->dapm, @@ -1242,7 +1215,7 @@ static int soc_probe_codec(struct snd_soc_card *card, return 0; err_probe: - soc_cleanup_codec_debugfs(codec); + soc_cleanup_component_debugfs(&codec->component); module_put(codec->dev->driver->owner); return ret; @@ -1262,7 +1235,7 @@ static int soc_probe_platform(struct snd_soc_card *card, if (!try_module_get(platform->dev->driver->owner)) return -ENODEV; - soc_init_platform_debugfs(platform); + soc_init_component_debugfs(&platform->component); if (driver->dapm_widgets) snd_soc_dapm_new_controls(&platform->component.dapm, @@ -1302,7 +1275,7 @@ static int soc_probe_platform(struct snd_soc_card *card, return 0; err_probe: - soc_cleanup_platform_debugfs(platform); + soc_cleanup_component_debugfs(&platform->component); module_put(platform->dev->driver->owner); return ret; @@ -4266,6 +4239,10 @@ int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform, if (platform_drv->read) platform->component.read = snd_soc_platform_drv_read; +#ifdef CONFIG_DEBUG_FS + platform->component.debugfs_prefix = "platform"; +#endif + mutex_lock(&client_mutex); snd_soc_component_add_unlocked(&platform->component); list_add(&platform->list, &platform_list); @@ -4455,6 +4432,11 @@ int snd_soc_register_codec(struct device *dev, codec->component.val_bytes = codec_drv->reg_word_size; mutex_init(&codec->mutex); +#ifdef CONFIG_DEBUG_FS + codec->component.init_debugfs = soc_init_codec_debugfs; + codec->component.debugfs_prefix = "codec"; +#endif + if (!codec->component.write) { if (codec_drv->get_regmap) regmap = codec_drv->get_regmap(dev); -- cgit v1.3-14-g43fede From f1d45cc3ae96a6173129b2c164c216272faa5fc0 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 19 Aug 2014 15:51:19 +0200 Subject: ASoC: Consolidate platform and CODEC probe/remove The platform and CODEC probe and remove code is now largely identical. This patch consolidates it at the component level. The resulting code is slightly larger due to all the boiler plate code setting up the indirection for the table based control and DAPM registration. Once all drivers have been update to no longer use the snd_soc_codec_driver and snd_soc_platform_driver specific fields for this the indirection can be removed again. This patch contains two noteworthy hacks that are only meant to be temporary to be able to update drivers and the core in separate incremental patches. The first hack is related to that some DPCM platforms expect that the DAPM widgets for the DAIs of a snd_soc_component are created in the DAPM context of the snd_soc_platform that has the same parent device. For handling this the steal_sibling_dai_widgets attribute is introduced. It gets set for snd_soc_platforms that register DAPM elements. When creating the DAI widgets for a component this flag is checked and if it is found on one of the siblings the component will not create any DAI widgets in its own DAPM context. If the attribute is set on a platform it will look for siblings components and create DAI widgets for them in its own context. The fix for this will be to update the offending drivers to only register a single component rather than two. The second hack deals with the fact that the ASoC card suspend and resume code still needs a list of CODECs that have been registered for the card. To handle this the generic probe and remove path have a check to see if the component is CODEC and if yes add/remove it to the card's CODEC list. While it is possible to clean up the suspend/resume code to not need the CODEC list anymore this is a bit of a chicken and egg problem since it will become easier to clean up the suspend/resume code once there is a unified component layer. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 27 ++- sound/soc/soc-core.c | 335 ++++++++++++++++++---------------- sound/soc/soc-generic-dmaengine-pcm.c | 4 +- 3 files changed, 194 insertions(+), 172 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 0ab8b1e4a5d2..22543acfae4b 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -697,6 +697,10 @@ struct snd_soc_component_driver { void (*seq_notifier)(struct snd_soc_component *, enum snd_soc_dapm_type, int subseq); int (*stream_event)(struct snd_soc_component *, int event); + + /* probe ordering - for components with runtime dependencies */ + int probe_order; + int remove_order; }; struct snd_soc_component { @@ -710,6 +714,7 @@ struct snd_soc_component { unsigned int ignore_pmdown_time:1; /* pmdown_time is ignored at stop */ unsigned int registered_as_component:1; + unsigned int probed:1; struct list_head list; @@ -742,6 +747,18 @@ struct snd_soc_component { struct snd_soc_dapm_context dapm; struct snd_soc_dapm_context *dapm_ptr; + const struct snd_kcontrol_new *controls; + unsigned int num_controls; + const struct snd_soc_dapm_widget *dapm_widgets; + unsigned int num_dapm_widgets; + const struct snd_soc_dapm_route *dapm_routes; + unsigned int num_dapm_routes; + bool steal_sibling_dai_widgets; + struct snd_soc_codec *codec; + + int (*probe)(struct snd_soc_component *); + void (*remove)(struct snd_soc_component *); + #ifdef CONFIG_DEBUG_FS void (*init_debugfs)(struct snd_soc_component *component); const char *debugfs_prefix; @@ -761,7 +778,6 @@ struct snd_soc_codec { struct snd_ac97 *ac97; /* for ad-hoc ac97 devices */ unsigned int cache_bypass:1; /* Suppress access to the cache */ unsigned int suspended:1; /* Codec is in suspend PM state */ - unsigned int probed:1; /* Codec has been probed */ unsigned int ac97_registered:1; /* Codec has been AC97 registered */ unsigned int ac97_created:1; /* Codec has been created by SoC */ unsigned int cache_init:1; /* codec cache has been initialized */ @@ -827,10 +843,6 @@ struct snd_soc_codec_driver { enum snd_soc_dapm_type, int); bool ignore_pmdown_time; /* Doesn't benefit from pmdown delay */ - - /* probe ordering - for components with runtime dependencies */ - int probe_order; - int remove_order; }; /* SoC platform interface */ @@ -867,10 +879,6 @@ struct snd_soc_platform_driver { /* platform stream compress ops */ const struct snd_compr_ops *compr_ops; - /* probe ordering - for components with runtime dependencies */ - int probe_order; - int remove_order; - /* platform IO - used for platform DAPM */ unsigned int (*read)(struct snd_soc_platform *, unsigned int); int (*write)(struct snd_soc_platform *, unsigned int, unsigned int); @@ -888,7 +896,6 @@ struct snd_soc_platform { const struct snd_soc_platform_driver *driver; unsigned int suspended:1; /* platform is suspended */ - unsigned int probed:1; struct list_head list; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 79371a77f324..b833cc6fd86d 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -985,44 +985,20 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num) return 0; } -static int soc_remove_platform(struct snd_soc_platform *platform) +static void soc_remove_component(struct snd_soc_component *component) { - int ret; - - if (platform->driver->remove) { - ret = platform->driver->remove(platform); - if (ret < 0) - dev_err(platform->dev, "ASoC: failed to remove %d\n", - ret); - } - - /* Make sure all DAPM widgets are freed */ - snd_soc_dapm_free(&platform->component.dapm); - - soc_cleanup_component_debugfs(&platform->component); - platform->probed = 0; - module_put(platform->dev->driver->owner); - - return 0; -} - -static void soc_remove_codec(struct snd_soc_codec *codec) -{ - int err; + /* This is a HACK and will be removed soon */ + if (component->codec) + list_del(&component->codec->card_list); - if (codec->driver->remove) { - err = codec->driver->remove(codec); - if (err < 0) - dev_err(codec->dev, "ASoC: failed to remove %d\n", err); - } + if (component->remove) + component->remove(component); - /* Make sure all DAPM widgets are freed */ - snd_soc_dapm_free(&codec->dapm); + snd_soc_dapm_free(snd_soc_component_get_dapm(component)); - soc_cleanup_component_debugfs(&codec->component); - codec->probed = 0; - list_del(&codec->card_list); - module_put(codec->dev->driver->owner); + soc_cleanup_component_debugfs(component); + component->probed = 0; + module_put(component->dev->driver->owner); } static void soc_remove_codec_dai(struct snd_soc_dai *codec_dai, int order) @@ -1086,25 +1062,24 @@ static void soc_remove_link_components(struct snd_soc_card *card, int num, int i; /* remove the platform */ - if (platform && platform->probed && - platform->driver->remove_order == order) { - soc_remove_platform(platform); - } + if (platform && platform->component.probed && + platform->component.driver->remove_order == order) + soc_remove_component(&platform->component); /* remove the CODEC-side CODEC */ for (i = 0; i < rtd->num_codecs; i++) { codec = rtd->codec_dais[i]->codec; - if (codec && codec->probed && - codec->driver->remove_order == order) - soc_remove_codec(codec); + if (codec && codec->component.probed && + codec->component.driver->remove_order == order) + soc_remove_component(&codec->component); } /* remove any CPU-side CODEC */ if (cpu_dai) { codec = cpu_dai->codec; - if (codec && codec->probed && - codec->driver->remove_order == order) - soc_remove_codec(codec); + if (codec && codec->component.probed && + codec->component.driver->remove_order == order) + soc_remove_component(&codec->component); } } @@ -1146,137 +1121,108 @@ static void soc_set_name_prefix(struct snd_soc_card *card, } } -static int soc_probe_codec(struct snd_soc_card *card, - struct snd_soc_codec *codec) +static int soc_probe_component(struct snd_soc_card *card, + struct snd_soc_component *component) { - int ret = 0; - const struct snd_soc_codec_driver *driver = codec->driver; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct snd_soc_component *dai_component, *component2; struct snd_soc_dai *dai; + int ret; - codec->component.card = card; - codec->dapm.card = card; - soc_set_name_prefix(card, &codec->component); + component->card = card; + dapm->card = card; + soc_set_name_prefix(card, component); - if (!try_module_get(codec->dev->driver->owner)) + if (!try_module_get(component->dev->driver->owner)) return -ENODEV; - soc_init_component_debugfs(&codec->component); + soc_init_component_debugfs(component); - if (driver->dapm_widgets) { - ret = snd_soc_dapm_new_controls(&codec->dapm, - driver->dapm_widgets, - driver->num_dapm_widgets); + if (component->dapm_widgets) { + ret = snd_soc_dapm_new_controls(dapm, component->dapm_widgets, + component->num_dapm_widgets); if (ret != 0) { - dev_err(codec->dev, + dev_err(component->dev, "Failed to create new controls %d\n", ret); goto err_probe; } } - /* Create DAPM widgets for each DAI stream */ - list_for_each_entry(dai, &codec->component.dai_list, list) { - ret = snd_soc_dapm_new_dai_widgets(&codec->dapm, dai); + /* + * This is rather ugly, but certain platforms expect that the DAPM + * widgets for the DAIs for components with the same parent device are + * created in the platforms DAPM context. Until that is fixed we need to + * keep this. + */ + if (component->steal_sibling_dai_widgets) { + dai_component = NULL; + list_for_each_entry(component2, &component_list, list) { + if (component == component2) + continue; - if (ret != 0) { - dev_err(codec->dev, - "Failed to create DAI widgets %d\n", ret); - goto err_probe; + if (component2->dev == component->dev && + !list_empty(&component2->dai_list)) { + dai_component = component2; + break; + } } - } - - codec->dapm.idle_bias_off = driver->idle_bias_off; - - if (driver->probe) { - ret = driver->probe(codec); - if (ret < 0) { - dev_err(codec->dev, - "ASoC: failed to probe CODEC %d\n", ret); - goto err_probe; + } else { + dai_component = component; + list_for_each_entry(component2, &component_list, list) { + if (component2->dev == component->dev && + component2->steal_sibling_dai_widgets) { + dai_component = NULL; + break; + } } - WARN(codec->dapm.idle_bias_off && - codec->dapm.bias_level != SND_SOC_BIAS_OFF, - "codec %s can not start from non-off bias with idle_bias_off==1\n", - codec->component.name); } - if (driver->controls) - snd_soc_add_codec_controls(codec, driver->controls, - driver->num_controls); - if (driver->dapm_routes) - snd_soc_dapm_add_routes(&codec->dapm, driver->dapm_routes, - driver->num_dapm_routes); - - /* mark codec as probed and add to card codec list */ - codec->probed = 1; - list_add(&codec->card_list, &card->codec_dev_list); - list_add(&codec->dapm.list, &card->dapm_list); - - return 0; - -err_probe: - soc_cleanup_component_debugfs(&codec->component); - module_put(codec->dev->driver->owner); - - return ret; -} - -static int soc_probe_platform(struct snd_soc_card *card, - struct snd_soc_platform *platform) -{ - int ret = 0; - const struct snd_soc_platform_driver *driver = platform->driver; - struct snd_soc_component *component; - struct snd_soc_dai *dai; - - platform->component.card = card; - platform->component.dapm.card = card; - - if (!try_module_get(platform->dev->driver->owner)) - return -ENODEV; - - soc_init_component_debugfs(&platform->component); - - if (driver->dapm_widgets) - snd_soc_dapm_new_controls(&platform->component.dapm, - driver->dapm_widgets, driver->num_dapm_widgets); - - /* Create DAPM widgets for each DAI stream */ - list_for_each_entry(component, &component_list, list) { - if (component->dev != platform->dev) - continue; - list_for_each_entry(dai, &component->dai_list, list) - snd_soc_dapm_new_dai_widgets(&platform->component.dapm, - dai); + if (dai_component) { + list_for_each_entry(dai, &dai_component->dai_list, list) { + snd_soc_dapm_new_dai_widgets(dapm, dai); + if (ret != 0) { + dev_err(component->dev, + "Failed to create DAI widgets %d\n", + ret); + goto err_probe; + } + } } - platform->component.dapm.idle_bias_off = 1; - - if (driver->probe) { - ret = driver->probe(platform); + if (component->probe) { + ret = component->probe(component); if (ret < 0) { - dev_err(platform->dev, - "ASoC: failed to probe platform %d\n", ret); + dev_err(component->dev, + "ASoC: failed to probe component %d\n", ret); goto err_probe; } + + WARN(dapm->idle_bias_off && + dapm->bias_level != SND_SOC_BIAS_OFF, + "codec %s can not start from non-off bias with idle_bias_off==1\n", + component->name); } - if (driver->controls) - snd_soc_add_platform_controls(platform, driver->controls, - driver->num_controls); - if (driver->dapm_routes) - snd_soc_dapm_add_routes(&platform->component.dapm, - driver->dapm_routes, driver->num_dapm_routes); + if (component->controls) + snd_soc_add_component_controls(component, component->controls, + component->num_controls); + if (component->dapm_routes) + snd_soc_dapm_add_routes(dapm, component->dapm_routes, + component->num_dapm_routes); - /* mark platform as probed and add to card platform list */ - platform->probed = 1; - list_add(&platform->component.dapm.list, &card->dapm_list); + component->probed = 1; + list_add(&dapm->list, &card->dapm_list); + + /* This is a HACK and will be removed soon */ + if (component->codec) + list_add(&component->codec->card_list, &card->codec_dev_list); return 0; err_probe: - soc_cleanup_component_debugfs(&platform->component); - module_put(platform->dev->driver->owner); + soc_cleanup_component_debugfs(component); + module_put(component->dev->driver->owner); return ret; } @@ -1334,33 +1280,36 @@ static int soc_probe_link_components(struct snd_soc_card *card, int num, int order) { struct snd_soc_pcm_runtime *rtd = &card->rtd[num]; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; int i, ret; /* probe the CPU-side component, if it is a CODEC */ - if (cpu_dai->codec && - !cpu_dai->codec->probed && - cpu_dai->codec->driver->probe_order == order) { - ret = soc_probe_codec(card, cpu_dai->codec); - if (ret < 0) - return ret; + if (rtd->cpu_dai->codec) { + component = &rtd->cpu_dai->codec->component; + if (!component->probed && + component->driver->probe_order == order) { + ret = soc_probe_component(card, component); + if (ret < 0) + return ret; + } } /* probe the CODEC-side components */ for (i = 0; i < rtd->num_codecs; i++) { - if (!rtd->codec_dais[i]->codec->probed && - rtd->codec_dais[i]->codec->driver->probe_order == order) { - ret = soc_probe_codec(card, rtd->codec_dais[i]->codec); + component = &rtd->codec_dais[i]->codec->component; + if (!component->probed && + component->driver->probe_order == order) { + ret = soc_probe_component(card, component); if (ret < 0) return ret; } } /* probe the platform */ - if (!platform->probed && - platform->driver->probe_order == order) { - ret = soc_probe_platform(card, platform); + if (!platform->component.probed && + platform->component.driver->probe_order == order) { + ret = soc_probe_component(card, &platform->component); if (ret < 0) return ret; } @@ -1647,12 +1596,12 @@ static int soc_probe_aux_dev(struct snd_soc_card *card, int num) struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num]; int ret; - if (rtd->codec->probed) { + if (rtd->codec->component.probed) { dev_err(rtd->codec->dev, "ASoC: codec already probed\n"); return -EBUSY; } - ret = soc_probe_codec(card, rtd->codec); + ret = soc_probe_component(card, &rtd->codec->component); if (ret < 0) return ret; @@ -1681,8 +1630,8 @@ static void soc_remove_aux_dev(struct snd_soc_card *card, int num) rtd->dev_registered = 0; } - if (codec && codec->probed) - soc_remove_codec(codec); + if (codec && codec->component.probed) + soc_remove_component(&codec->component); } static int snd_soc_init_codec_cache(struct snd_soc_codec *codec) @@ -4198,6 +4147,20 @@ found: } EXPORT_SYMBOL_GPL(snd_soc_unregister_component); +static int snd_soc_platform_drv_probe(struct snd_soc_component *component) +{ + struct snd_soc_platform *platform = snd_soc_component_to_platform(component); + + return platform->driver->probe(platform); +} + +static void snd_soc_platform_drv_remove(struct snd_soc_component *component) +{ + struct snd_soc_platform *platform = snd_soc_component_to_platform(component); + + platform->driver->remove(platform); +} + static int snd_soc_platform_drv_write(struct snd_soc_component *component, unsigned int reg, unsigned int val) { @@ -4234,6 +4197,24 @@ int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform, platform->dev = dev; platform->driver = platform_drv; + if (platform_drv->controls) { + platform->component.controls = platform_drv->controls; + platform->component.num_controls = platform_drv->num_controls; + } + if (platform_drv->dapm_widgets) { + platform->component.dapm_widgets = platform_drv->dapm_widgets; + platform->component.num_dapm_widgets = platform_drv->num_dapm_widgets; + platform->component.steal_sibling_dai_widgets = true; + } + if (platform_drv->dapm_routes) { + platform->component.dapm_routes = platform_drv->dapm_routes; + platform->component.num_dapm_routes = platform_drv->num_dapm_routes; + } + + if (platform_drv->probe) + platform->component.probe = snd_soc_platform_drv_probe; + if (platform_drv->remove) + platform->component.remove = snd_soc_platform_drv_remove; if (platform_drv->write) platform->component.write = snd_soc_platform_drv_write; if (platform_drv->read) @@ -4363,6 +4344,20 @@ static void fixup_codec_formats(struct snd_soc_pcm_stream *stream) stream->formats |= codec_format_map[i]; } +static int snd_soc_codec_drv_probe(struct snd_soc_component *component) +{ + struct snd_soc_codec *codec = snd_soc_component_to_codec(component); + + return codec->driver->probe(codec); +} + +static void snd_soc_codec_drv_remove(struct snd_soc_component *component) +{ + struct snd_soc_codec *codec = snd_soc_component_to_codec(component); + + codec->driver->remove(codec); +} + static int snd_soc_codec_drv_write(struct snd_soc_component *component, unsigned int reg, unsigned int val) { @@ -4411,12 +4406,30 @@ int snd_soc_register_codec(struct device *dev, return -ENOMEM; codec->component.dapm_ptr = &codec->dapm; + codec->component.codec = codec; ret = snd_soc_component_initialize(&codec->component, &codec_drv->component_driver, dev); if (ret) goto err_free; + if (codec_drv->controls) { + codec->component.controls = codec_drv->controls; + codec->component.num_controls = codec_drv->num_controls; + } + if (codec_drv->dapm_widgets) { + codec->component.dapm_widgets = codec_drv->dapm_widgets; + codec->component.num_dapm_widgets = codec_drv->num_dapm_widgets; + } + if (codec_drv->dapm_routes) { + codec->component.dapm_routes = codec_drv->dapm_routes; + codec->component.num_dapm_routes = codec_drv->num_dapm_routes; + } + + if (codec_drv->probe) + codec->component.probe = snd_soc_codec_drv_probe; + if (codec_drv->remove) + codec->component.remove = snd_soc_codec_drv_remove; if (codec_drv->write) codec->component.write = snd_soc_codec_drv_write; if (codec_drv->read) diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index 6307f85e871b..b329b84bc5af 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -336,10 +336,12 @@ static const struct snd_pcm_ops dmaengine_pcm_ops = { }; static const struct snd_soc_platform_driver dmaengine_pcm_platform = { + .component_driver = { + .probe_order = SND_SOC_COMP_ORDER_LATE, + }, .ops = &dmaengine_pcm_ops, .pcm_new = dmaengine_pcm_new, .pcm_free = dmaengine_pcm_free, - .probe_order = SND_SOC_COMP_ORDER_LATE, }; static const char * const dmaengine_pcm_dma_channel_names[] = { -- cgit v1.3-14-g43fede From 61aca5646b736a794d40de29a197144db3f0c5ba Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 19 Aug 2014 15:51:21 +0200 Subject: ASoC: Add component level probe/remove support Now that we have a unified probe and remove path make sure to call them for all components. soc_{probe,remove}_component are responsible for setting up the DAPM context for the component, initialize the component prefix, manage the debugfs entries as well as do the registration of table based controls and DAPM elements. They also call the component drivers probe and remove callbacks. This patch makes these things available for generic snd_soc_component drivers rather than only having them for snd_soc_codec and snd_soc_platform drivers. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 11 +++++++++++ sound/soc/soc-core.c | 42 ++++++++++++++++++++++++------------------ 2 files changed, 35 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 22543acfae4b..4a223a895f00 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -690,6 +690,17 @@ struct snd_soc_compr_ops { struct snd_soc_component_driver { const char *name; + /* Default control and setup, added after probe() is run */ + const struct snd_kcontrol_new *controls; + unsigned int num_controls; + const struct snd_soc_dapm_widget *dapm_widgets; + unsigned int num_dapm_widgets; + const struct snd_soc_dapm_route *dapm_routes; + unsigned int num_dapm_routes; + + int (*probe)(struct snd_soc_component *); + void (*remove)(struct snd_soc_component *); + /* DT */ int (*of_xlate_dai_name)(struct snd_soc_component *component, struct of_phandle_args *args, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 1c705c28389c..08fd85e8c751 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1058,7 +1058,7 @@ static void soc_remove_link_components(struct snd_soc_card *card, int num, struct snd_soc_pcm_runtime *rtd = &card->rtd[num]; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_platform *platform = rtd->platform; - struct snd_soc_codec *codec; + struct snd_soc_component *component; int i; /* remove the platform */ @@ -1068,18 +1068,17 @@ static void soc_remove_link_components(struct snd_soc_card *card, int num, /* remove the CODEC-side CODEC */ for (i = 0; i < rtd->num_codecs; i++) { - codec = rtd->codec_dais[i]->codec; - if (codec && codec->component.probed && - codec->component.driver->remove_order == order) - soc_remove_component(&codec->component); + component = rtd->codec_dais[i]->component; + if (component->probed && + component->driver->remove_order == order) + soc_remove_component(component); } /* remove any CPU-side CODEC */ if (cpu_dai) { - codec = cpu_dai->codec; - if (codec && codec->component.probed && - codec->component.driver->remove_order == order) - soc_remove_component(&codec->component); + if (cpu_dai->component->probed && + cpu_dai->component->driver->remove_order == order) + soc_remove_component(cpu_dai->component); } } @@ -1289,19 +1288,17 @@ static int soc_probe_link_components(struct snd_soc_card *card, int num, int i, ret; /* probe the CPU-side component, if it is a CODEC */ - if (rtd->cpu_dai->codec) { - component = &rtd->cpu_dai->codec->component; - if (!component->probed && - component->driver->probe_order == order) { - ret = soc_probe_component(card, component); - if (ret < 0) - return ret; - } + component = rtd->cpu_dai->component; + if (!component->probed && + component->driver->probe_order == order) { + ret = soc_probe_component(card, component); + if (ret < 0) + return ret; } /* probe the CODEC-side components */ for (i = 0; i < rtd->num_codecs; i++) { - component = &rtd->codec_dais[i]->codec->component; + component = rtd->codec_dais[i]->component; if (!component->probed && component->driver->probe_order == order) { ret = soc_probe_component(card, component); @@ -4042,6 +4039,8 @@ static int snd_soc_component_initialize(struct snd_soc_component *component, component->dev = dev; component->driver = driver; + component->probe = component->driver->probe; + component->remove = component->driver->remove; if (!component->dapm_ptr) component->dapm_ptr = &component->dapm; @@ -4055,6 +4054,13 @@ static int snd_soc_component_initialize(struct snd_soc_component *component, if (driver->stream_event) dapm->stream_event = snd_soc_component_stream_event; + component->controls = driver->controls; + component->num_controls = driver->num_controls; + component->dapm_widgets = driver->dapm_widgets; + component->num_dapm_widgets = driver->num_dapm_widgets; + component->dapm_routes = driver->dapm_routes; + component->num_dapm_routes = driver->num_dapm_routes; + INIT_LIST_HEAD(&component->dai_list); mutex_init(&component->io_mutex); -- cgit v1.3-14-g43fede From 65d9361f0cb50a20641802ee3075145d72e4409c Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 19 Aug 2014 15:51:22 +0200 Subject: ASoC: Move AUX dev support to the component level This patch makes it possible to register arbitrary components as a AUX dev for a card. This was previously only possible for CODEC components. With componentization having made it possible for components to have DAPM contexts and controls there is no reason why AUX devs should be artificially limited to snd_soc_codec devices. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 1 + sound/soc/soc-core.c | 48 ++++++++++++++++++++++++++++++++++++------------ 2 files changed, 37 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 4a223a895f00..fbc2ad840244 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1140,6 +1140,7 @@ struct snd_soc_pcm_runtime { struct snd_soc_platform *platform; struct snd_soc_dai *codec_dai; struct snd_soc_dai *cpu_dai; + struct snd_soc_component *component; /* Only valid for AUX dev rtds */ struct snd_soc_dai **codec_dais; unsigned int num_codecs; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 08fd85e8c751..08c04f4c7e62 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -860,6 +860,23 @@ EXPORT_SYMBOL_GPL(snd_soc_resume); static const struct snd_soc_dai_ops null_dai_ops = { }; +static struct snd_soc_component *soc_find_component( + const struct device_node *of_node, const char *name) +{ + struct snd_soc_component *component; + + list_for_each_entry(component, &component_list, list) { + if (of_node) { + if (component->dev->of_node == of_node) + return component; + } else if (strcmp(component->name, name) == 0) { + return component; + } + } + + return NULL; +} + static struct snd_soc_codec *soc_find_codec( const struct device_node *codec_of_node, const char *codec_name) @@ -1577,17 +1594,24 @@ static int soc_bind_aux_dev(struct snd_soc_card *card, int num) { struct snd_soc_pcm_runtime *rtd = &card->rtd_aux[num]; struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num]; - const char *codecname = aux_dev->codec_name; + const char *name = aux_dev->codec_name; - rtd->codec = soc_find_codec(aux_dev->codec_of_node, codecname); - if (!rtd->codec) { + rtd->component = soc_find_component(aux_dev->codec_of_node, name); + if (!rtd->component) { if (aux_dev->codec_of_node) - codecname = of_node_full_name(aux_dev->codec_of_node); + name = of_node_full_name(aux_dev->codec_of_node); - dev_err(card->dev, "ASoC: %s not registered\n", codecname); + dev_err(card->dev, "ASoC: %s not registered\n", name); return -EPROBE_DEFER; } + /* + * Some places still reference rtd->codec, so we have to keep that + * initialized if the component is a CODEC. Once all those references + * have been removed, this code can be removed as well. + */ + rtd->codec = rtd->component->codec; + return 0; } @@ -1597,18 +1621,18 @@ static int soc_probe_aux_dev(struct snd_soc_card *card, int num) struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num]; int ret; - if (rtd->codec->component.probed) { - dev_err(rtd->codec->dev, "ASoC: codec already probed\n"); + if (rtd->component->probed) { + dev_err(rtd->dev, "ASoC: codec already probed\n"); return -EBUSY; } - ret = soc_probe_component(card, &rtd->codec->component); + ret = soc_probe_component(card, rtd->component); if (ret < 0) return ret; /* do machine specific initialization */ if (aux_dev->init) { - ret = aux_dev->init(&rtd->codec->dapm); + ret = aux_dev->init(snd_soc_component_get_dapm(rtd->component)); if (ret < 0) { dev_err(card->dev, "ASoC: failed to init %s: %d\n", aux_dev->name, ret); @@ -1622,7 +1646,7 @@ static int soc_probe_aux_dev(struct snd_soc_card *card, int num) static void soc_remove_aux_dev(struct snd_soc_card *card, int num) { struct snd_soc_pcm_runtime *rtd = &card->rtd_aux[num]; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_component *component = rtd->component; /* unregister the rtd device */ if (rtd->dev_registered) { @@ -1631,8 +1655,8 @@ static void soc_remove_aux_dev(struct snd_soc_card *card, int num) rtd->dev_registered = 0; } - if (codec && codec->component.probed) - soc_remove_component(&codec->component); + if (component && component->probed) + soc_remove_component(component); } static int snd_soc_init_codec_cache(struct snd_soc_codec *codec) -- cgit v1.3-14-g43fede From 57bf772687700e206c760ba2e4097f78bde97887 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 19 Aug 2014 15:51:23 +0200 Subject: ASoC: Pass component instead of DAPM context to AUX dev init callback Given that the component is the containing structure it makes more sense to pass the component rather than the DAPM context to the AUX dev init callback. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 2 +- sound/soc/samsung/speyside.c | 6 ++++-- sound/soc/soc-core.c | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index fbc2ad840244..3a0031e1f9b4 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1022,7 +1022,7 @@ struct snd_soc_aux_dev { const struct device_node *codec_of_node; /* codec/machine specific init - e.g. add machine controls */ - int (*init)(struct snd_soc_dapm_context *dapm); + int (*init)(struct snd_soc_component *component); }; /* SoC card */ diff --git a/sound/soc/samsung/speyside.c b/sound/soc/samsung/speyside.c index 9902efcb8ea1..a05482651aae 100644 --- a/sound/soc/samsung/speyside.c +++ b/sound/soc/samsung/speyside.c @@ -228,10 +228,12 @@ static struct snd_soc_dai_link speyside_dai[] = { }, }; -static int speyside_wm9081_init(struct snd_soc_dapm_context *dapm) +static int speyside_wm9081_init(struct snd_soc_component *component) { + struct snd_soc_codec *codec = snd_soc_component_to_codec(component); + /* At any time the WM9081 is active it will have this clock */ - return snd_soc_codec_set_sysclk(dapm->codec, WM9081_SYSCLK_MCLK, 0, + return snd_soc_codec_set_sysclk(codec, WM9081_SYSCLK_MCLK, 0, MCLK_AUDIO_RATE, 0); } diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 08c04f4c7e62..4393bc33d3af 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1632,7 +1632,7 @@ static int soc_probe_aux_dev(struct snd_soc_card *card, int num) /* do machine specific initialization */ if (aux_dev->init) { - ret = aux_dev->init(snd_soc_component_get_dapm(rtd->component)); + ret = aux_dev->init(rtd->component); if (ret < 0) { dev_err(card->dev, "ASoC: failed to init %s: %d\n", aux_dev->name, ret); -- cgit v1.3-14-g43fede From 886f5692253de1a9509f5cb708432b2157afb57c Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 19 Aug 2014 15:51:28 +0200 Subject: ASoC: Automatically initialize regmap for all components So far regmap is only automatically initialized for CODECs. Now that we have the infrastructure in place to let components have DAPM widgets and controls that want to use the generic regmap based IO also make sure to automatically initialize regmap for all components. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 3 --- sound/soc/soc-core.c | 35 +++++++++++++++++------------------ sound/soc/soc-io.c | 28 ---------------------------- 3 files changed, 17 insertions(+), 49 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 3a0031e1f9b4..8ebee30311e3 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1289,9 +1289,6 @@ void snd_soc_component_async_complete(struct snd_soc_component *component); int snd_soc_component_test_bits(struct snd_soc_component *component, unsigned int reg, unsigned int mask, unsigned int value); -int snd_soc_component_init_io(struct snd_soc_component *component, - struct regmap *regmap); - /* device driver data */ static inline void snd_soc_card_set_drvdata(struct snd_soc_card *card, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 140f43f91635..96f286643ca1 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -4032,8 +4032,23 @@ static int snd_soc_component_initialize(struct snd_soc_component *component, return 0; } +static void snd_soc_component_init_regmap(struct snd_soc_component *component) +{ + if (!component->regmap) + component->regmap = dev_get_regmap(component->dev, NULL); + if (component->regmap) { + int val_bytes = regmap_get_val_bytes(component->regmap); + /* Errors are legitimate for non-integer byte multiples */ + if (val_bytes > 0) + component->val_bytes = val_bytes; + } +} + static void snd_soc_component_add_unlocked(struct snd_soc_component *component) { + if (!component->write && !component->read) + snd_soc_component_init_regmap(component); + list_add(&component->list, &component_list); } @@ -4371,7 +4386,6 @@ int snd_soc_register_codec(struct device *dev, { struct snd_soc_codec *codec; struct snd_soc_dai *dai; - struct regmap *regmap; int ret, i; dev_dbg(dev, "codec register %s\n", dev_name(dev)); @@ -4425,23 +4439,8 @@ int snd_soc_register_codec(struct device *dev, codec->component.debugfs_prefix = "codec"; #endif - if (!codec->component.write) { - if (codec_drv->get_regmap) - regmap = codec_drv->get_regmap(dev); - else - regmap = dev_get_regmap(dev, NULL); - - if (regmap) { - ret = snd_soc_component_init_io(&codec->component, - regmap); - if (ret) { - dev_err(codec->dev, - "Failed to set cache I/O:%d\n", - ret); - goto err_cleanup; - } - } - } + if (codec_drv->get_regmap) + codec->component.regmap = codec_drv->get_regmap(dev); for (i = 0; i < num_dai; i++) { fixup_codec_formats(&dai_drv[i].playback); diff --git a/sound/soc/soc-io.c b/sound/soc/soc-io.c index 7767fbd73eb7..9b3939049cef 100644 --- a/sound/soc/soc-io.c +++ b/sound/soc/soc-io.c @@ -271,31 +271,3 @@ int snd_soc_platform_write(struct snd_soc_platform *platform, return snd_soc_component_write(&platform->component, reg, val); } EXPORT_SYMBOL_GPL(snd_soc_platform_write); - -/** - * snd_soc_component_init_io() - Initialize regmap IO - * - * @component: component to initialize - * @regmap: regmap instance to use for IO operations - * - * Return: 0 on success, a negative error code otherwise - */ -int snd_soc_component_init_io(struct snd_soc_component *component, - struct regmap *regmap) -{ - int ret; - - if (!regmap) - return -EINVAL; - - ret = regmap_get_val_bytes(regmap); - /* Errors are legitimate for non-integer byte - * multiples */ - if (ret > 0) - component->val_bytes = ret; - - component->regmap = regmap; - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_component_init_io); -- cgit v1.3-14-g43fede From 75af7c081982d76cef0daf26e96b5d1e8cb9d631 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 19 Aug 2014 15:51:29 +0200 Subject: ASoC: Remove support for legacy snd_soc_platform IO There were never any actual users of this in upstream and by we have with regmap a replacement in place, which should be used by new drivers. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 3 --- sound/soc/soc-core.c | 22 ---------------------- 2 files changed, 25 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 8ebee30311e3..edbb0d72ab38 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -890,9 +890,6 @@ struct snd_soc_platform_driver { /* platform stream compress ops */ const struct snd_compr_ops *compr_ops; - /* platform IO - used for platform DAPM */ - unsigned int (*read)(struct snd_soc_platform *, unsigned int); - int (*write)(struct snd_soc_platform *, unsigned int, unsigned int); int (*bespoke_trigger)(struct snd_pcm_substream *, int); }; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 96f286643ca1..2d7a9ecbb0e3 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -4151,24 +4151,6 @@ static void snd_soc_platform_drv_remove(struct snd_soc_component *component) platform->driver->remove(platform); } -static int snd_soc_platform_drv_write(struct snd_soc_component *component, - unsigned int reg, unsigned int val) -{ - struct snd_soc_platform *platform = snd_soc_component_to_platform(component); - - return platform->driver->write(platform, reg, val); -} - -static int snd_soc_platform_drv_read(struct snd_soc_component *component, - unsigned int reg, unsigned int *val) -{ - struct snd_soc_platform *platform = snd_soc_component_to_platform(component); - - *val = platform->driver->read(platform, reg); - - return 0; -} - /** * snd_soc_add_platform - Add a platform to the ASoC core * @dev: The parent device for the platform @@ -4205,10 +4187,6 @@ int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform, platform->component.probe = snd_soc_platform_drv_probe; if (platform_drv->remove) platform->component.remove = snd_soc_platform_drv_remove; - if (platform_drv->write) - platform->component.write = snd_soc_platform_drv_write; - if (platform_drv->read) - platform->component.read = snd_soc_platform_drv_read; #ifdef CONFIG_DEBUG_FS platform->component.debugfs_prefix = "platform"; -- cgit v1.3-14-g43fede From 2d15d974618db4ed3adafe9b9fe092db0f5076a0 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 27 Aug 2014 19:50:34 +0800 Subject: ASoC: rt5677: Add DMIC2 clock selection There are two pins can be used for rt5677's DMIC2 clock. This patch add the select options for it. Signed-off-by: Bard Liao Signed-off-by: Mark Brown --- include/sound/rt5677.h | 8 +++++++ sound/soc/codecs/rt5677.c | 57 ++++++++++++++++++++++++++++++++++++++++------- sound/soc/codecs/rt5677.h | 10 +++++++++ 3 files changed, 67 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/sound/rt5677.h b/include/sound/rt5677.h index 3da14313bcfc..a676717f74f4 100644 --- a/include/sound/rt5677.h +++ b/include/sound/rt5677.h @@ -12,10 +12,18 @@ #ifndef __LINUX_SND_RT5677_H #define __LINUX_SND_RT5677_H +enum rt5677_dmic2_clk { + RT5677_DMIC_CLK1 = 0, + RT5677_DMIC_CLK2 = 1, +}; + + struct rt5677_platform_data { /* IN1 IN2 can optionally be differential */ bool in1_diff; bool in2_diff; + /* DMIC2 clock source selection */ + enum rt5677_dmic2_clk dmic2_clk_pin; }; #endif diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index 67f14556462f..f0b751bf1d6c 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -1700,14 +1700,19 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = { SND_SOC_DAPM_INPUT("Haptic Generator"), - SND_SOC_DAPM_PGA("DMIC1", RT5677_DMIC_CTRL1, RT5677_DMIC_1_EN_SFT, 0, - NULL, 0), - SND_SOC_DAPM_PGA("DMIC2", RT5677_DMIC_CTRL1, RT5677_DMIC_2_EN_SFT, 0, - NULL, 0), - SND_SOC_DAPM_PGA("DMIC3", RT5677_DMIC_CTRL1, RT5677_DMIC_3_EN_SFT, 0, - NULL, 0), - SND_SOC_DAPM_PGA("DMIC4", RT5677_DMIC_CTRL2, RT5677_DMIC_4_EN_SFT, 0, - NULL, 0), + SND_SOC_DAPM_PGA("DMIC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DMIC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DMIC3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DMIC4", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("DMIC1 power", RT5677_DMIC_CTRL1, + RT5677_DMIC_1_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DMIC2 power", RT5677_DMIC_CTRL1, + RT5677_DMIC_2_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DMIC3 power", RT5677_DMIC_CTRL1, + RT5677_DMIC_3_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DMIC4 power", RT5677_DMIC_CTRL2, + RT5677_DMIC_4_EN_SFT, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0, set_dmic_clk, SND_SOC_DAPM_PRE_PMU), @@ -2130,6 +2135,13 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = { { "DMIC L4", NULL, "DMIC CLK" }, { "DMIC R4", NULL, "DMIC CLK" }, + { "DMIC L1", NULL, "DMIC1 power" }, + { "DMIC R1", NULL, "DMIC1 power" }, + { "DMIC L3", NULL, "DMIC3 power" }, + { "DMIC R3", NULL, "DMIC3 power" }, + { "DMIC L4", NULL, "DMIC4 power" }, + { "DMIC R4", NULL, "DMIC4 power" }, + { "BST1", NULL, "IN1P" }, { "BST1", NULL, "IN1N" }, { "BST2", NULL, "IN2P" }, @@ -2793,6 +2805,16 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = { { "PDM2R", NULL, "PDM2 R Mux" }, }; +static const struct snd_soc_dapm_route rt5677_dmic2_clk_1[] = { + { "DMIC L2", NULL, "DMIC1 power" }, + { "DMIC R2", NULL, "DMIC1 power" }, +}; + +static const struct snd_soc_dapm_route rt5677_dmic2_clk_2[] = { + { "DMIC L2", NULL, "DMIC2 power" }, + { "DMIC R2", NULL, "DMIC2 power" }, +}; + static int rt5677_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { @@ -3144,6 +3166,16 @@ static int rt5677_probe(struct snd_soc_codec *codec) rt5677->codec = codec; + if (rt5677->pdata.dmic2_clk_pin == RT5677_DMIC_CLK2) { + snd_soc_dapm_add_routes(&codec->dapm, + rt5677_dmic2_clk_2, + ARRAY_SIZE(rt5677_dmic2_clk_2)); + } else { /*use dmic1 clock by default*/ + snd_soc_dapm_add_routes(&codec->dapm, + rt5677_dmic2_clk_1, + ARRAY_SIZE(rt5677_dmic2_clk_1)); + } + rt5677_set_bias_level(codec, SND_SOC_BIAS_OFF); regmap_write(rt5677->regmap, RT5677_DIG_MISC, 0x0020); @@ -3381,6 +3413,15 @@ static int rt5677_i2c_probe(struct i2c_client *i2c, regmap_update_bits(rt5677->regmap, RT5677_IN1, RT5677_IN_DF2, RT5677_IN_DF2); + if (rt5677->pdata.dmic2_clk_pin == RT5677_DMIC_CLK2) { + regmap_update_bits(rt5677->regmap, RT5677_GEN_CTRL2, + RT5677_GPIO5_FUNC_MASK, + RT5677_GPIO5_FUNC_DMIC); + regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL2, + RT5677_GPIO5_DIR_MASK, + RT5677_GPIO5_DIR_OUT); + } + return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5677, rt5677_dai, ARRAY_SIZE(rt5677_dai)); } diff --git a/sound/soc/codecs/rt5677.h b/sound/soc/codecs/rt5677.h index 863393e62096..8791ab9637f3 100644 --- a/sound/soc/codecs/rt5677.h +++ b/sound/soc/codecs/rt5677.h @@ -1363,6 +1363,11 @@ #define RT5677_SEL_SRC_IB01 (0x1 << 0) #define RT5677_SEL_SRC_IB01_SFT 0 +/* GPIO Control 2 (0xc1) */ +#define RT5677_GPIO5_DIR_MASK (0x1 << 14) +#define RT5677_GPIO5_DIR_IN (0x0 << 14) +#define RT5677_GPIO5_DIR_OUT (0x1 << 14) + /* Virtual DSP Mixer Control (0xf7 0xf8 0xf9) */ #define RT5677_DSP_IB_01_H (0x1 << 15) #define RT5677_DSP_IB_01_H_SFT 15 @@ -1393,6 +1398,11 @@ #define RT5677_DSP_IB_9_L (0x1 << 1) #define RT5677_DSP_IB_9_L_SFT 1 +/* General Control2 (0xfc)*/ +#define RT5677_GPIO5_FUNC_MASK (0x1 << 9) +#define RT5677_GPIO5_FUNC_GPIO (0x0 << 9) +#define RT5677_GPIO5_FUNC_DMIC (0x1 << 9) + /* System Clock Source */ enum { RT5677_SCLK_S_MCLK, -- cgit v1.3-14-g43fede From b792346fa8660a22a06f118cebe47709f507914f Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Thu, 28 Aug 2014 14:07:11 +0300 Subject: ASoC: Remove unused cache_only from struct snd_soc_codec There are no real users for cache_only in "struct snd_soc_codec" so remove it and needless debugfs node. Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown --- include/sound/soc.h | 1 - sound/soc/soc-core.c | 2 -- 2 files changed, 3 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index edbb0d72ab38..ce09302bfd6d 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -792,7 +792,6 @@ struct snd_soc_codec { unsigned int ac97_registered:1; /* Codec has been AC97 registered */ unsigned int ac97_created:1; /* Codec has been created by SoC */ unsigned int cache_init:1; /* codec cache has been initialized */ - u32 cache_only; /* Suppress writes to hardware */ u32 cache_sync; /* Cache needs to be synced to hardware */ /* codec IO */ diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 419682693886..1b422c5c36c8 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -311,8 +311,6 @@ static void soc_init_codec_debugfs(struct snd_soc_component *component) debugfs_create_bool("cache_sync", 0444, codec->component.debugfs_root, &codec->cache_sync); - debugfs_create_bool("cache_only", 0444, codec->component.debugfs_root, - &codec->cache_only); codec->debugfs_reg = debugfs_create_file("codec_reg", 0644, codec->component.debugfs_root, -- cgit v1.3-14-g43fede From 257f8cce5d40b811d229ed71602882baa0012808 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 29 Aug 2014 15:32:29 +0200 Subject: ALSA: pcm: Allow nonatomic trigger operations Currently, many PCM operations are performed in a critical section protected by spinlock, typically the trigger and pointer callbacks are assumed to be atomic. This is basically because some trigger action (e.g. PCM stop after drain or xrun) is done in the interrupt handler. If a driver runs in a threaded irq, however, this doesn't have to be atomic. And many devices want to handle trigger in a non-atomic context due to lengthy communications. This patch tries all PCM calls operational in non-atomic context. What it does is very simple: replaces the substream spinlock with the corresponding substream mutex when pcm->nonatomic flag is set. The driver that wants to use the non-atomic PCM ops just needs to set the flag and keep the rest as is. (Of course, it must not handle any PCM ops in irq context.) Note that the code doesn't check whether it's atomic-safe or not, but trust in 100% that the driver sets pcm->nonatomic correctly. One possible problem is the case where linked PCM substreams have inconsistent nonatomic states. For avoiding this, snd_pcm_link() returns an error if one tries to link an inconsistent PCM substream. Signed-off-by: Takashi Iwai --- include/sound/pcm.h | 58 +++++++++++++++++++++++++++++-------- sound/core/pcm.c | 1 + sound/core/pcm_native.c | 76 ++++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 116 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 6f3e10ca0e32..bc79962f4aa6 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -365,6 +365,7 @@ struct snd_pcm_runtime { struct snd_pcm_group { /* keep linked substreams */ spinlock_t lock; + struct mutex mutex; struct list_head substreams; int count; }; @@ -460,6 +461,7 @@ struct snd_pcm { void (*private_free) (struct snd_pcm *pcm); struct device *dev; /* actual hw device this belongs to */ bool internal; /* pcm is for internal use only */ + bool nonatomic; /* whole PCM operations are in non-atomic context */ #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) struct snd_pcm_oss oss; #endif @@ -493,6 +495,7 @@ int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree); */ extern rwlock_t snd_pcm_link_rwlock; +extern struct rw_semaphore snd_pcm_link_rwsem; int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info); int snd_pcm_info_user(struct snd_pcm_substream *substream, @@ -539,38 +542,69 @@ static inline int snd_pcm_stream_linked(struct snd_pcm_substream *substream) static inline void snd_pcm_stream_lock(struct snd_pcm_substream *substream) { - read_lock(&snd_pcm_link_rwlock); - spin_lock(&substream->self_group.lock); + if (substream->pcm->nonatomic) { + down_read(&snd_pcm_link_rwsem); + mutex_lock(&substream->self_group.mutex); + } else { + read_lock(&snd_pcm_link_rwlock); + spin_lock(&substream->self_group.lock); + } } static inline void snd_pcm_stream_unlock(struct snd_pcm_substream *substream) { - spin_unlock(&substream->self_group.lock); - read_unlock(&snd_pcm_link_rwlock); + if (substream->pcm->nonatomic) { + mutex_unlock(&substream->self_group.mutex); + up_read(&snd_pcm_link_rwsem); + } else { + spin_unlock(&substream->self_group.lock); + read_unlock(&snd_pcm_link_rwlock); + } } static inline void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream) { - read_lock_irq(&snd_pcm_link_rwlock); - spin_lock(&substream->self_group.lock); + if (substream->pcm->nonatomic) { + down_read(&snd_pcm_link_rwsem); + mutex_lock(&substream->self_group.mutex); + } else { + read_lock_irq(&snd_pcm_link_rwlock); + spin_lock(&substream->self_group.lock); + } } static inline void snd_pcm_stream_unlock_irq(struct snd_pcm_substream *substream) { - spin_unlock(&substream->self_group.lock); - read_unlock_irq(&snd_pcm_link_rwlock); + if (substream->pcm->nonatomic) { + mutex_unlock(&substream->self_group.mutex); + up_read(&snd_pcm_link_rwsem); + } else { + spin_unlock(&substream->self_group.lock); + read_unlock_irq(&snd_pcm_link_rwlock); + } } #define snd_pcm_stream_lock_irqsave(substream, flags) \ do { \ - read_lock_irqsave(&snd_pcm_link_rwlock, (flags)); \ - spin_lock(&substream->self_group.lock); \ + if ((substream)->pcm->nonatomic) { \ + (flags) = 0; /* XXX for avoid warning */ \ + down_read(&snd_pcm_link_rwsem); \ + mutex_lock(&(substream)->self_group.mutex); \ + } else { \ + read_lock_irqsave(&snd_pcm_link_rwlock, (flags)); \ + spin_lock(&(substream)->self_group.lock); \ + } \ } while (0) #define snd_pcm_stream_unlock_irqrestore(substream, flags) \ do { \ - spin_unlock(&substream->self_group.lock); \ - read_unlock_irqrestore(&snd_pcm_link_rwlock, (flags)); \ + if ((substream)->pcm->nonatomic) { \ + mutex_unlock(&(substream)->self_group.mutex); \ + up_read(&snd_pcm_link_rwsem); \ + } else { \ + spin_unlock(&(substream)->self_group.lock); \ + read_unlock_irqrestore(&snd_pcm_link_rwlock, (flags)); \ + } \ } while (0) #define snd_pcm_group_for_each_entry(s, substream) \ diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 43932e8dce66..afccdc553ef9 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -698,6 +698,7 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count) } substream->group = &substream->self_group; spin_lock_init(&substream->self_group.lock); + mutex_init(&substream->self_group.mutex); INIT_LIST_HEAD(&substream->self_group.substreams); list_add_tail(&substream->link_list, &substream->self_group.substreams); atomic_set(&substream->mmap_count, 0); diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 8cd2f930ad0b..16d9b7e15f8b 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -77,7 +77,8 @@ static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream); DEFINE_RWLOCK(snd_pcm_link_rwlock); EXPORT_SYMBOL(snd_pcm_link_rwlock); -static DECLARE_RWSEM(snd_pcm_link_rwsem); +DECLARE_RWSEM(snd_pcm_link_rwsem); +EXPORT_SYMBOL(snd_pcm_link_rwsem); static inline mm_segment_t snd_enter_user(void) { @@ -727,9 +728,14 @@ static int snd_pcm_action_group(struct action_ops *ops, int res = 0; snd_pcm_group_for_each_entry(s, substream) { - if (do_lock && s != substream) - spin_lock_nested(&s->self_group.lock, - SINGLE_DEPTH_NESTING); + if (do_lock && s != substream) { + if (s->pcm->nonatomic) + mutex_lock_nested(&s->self_group.mutex, + SINGLE_DEPTH_NESTING); + else + spin_lock_nested(&s->self_group.lock, + SINGLE_DEPTH_NESTING); + } res = ops->pre_action(s, state); if (res < 0) goto _unlock; @@ -755,8 +761,12 @@ static int snd_pcm_action_group(struct action_ops *ops, if (do_lock) { /* unlock streams */ snd_pcm_group_for_each_entry(s1, substream) { - if (s1 != substream) - spin_unlock(&s1->self_group.lock); + if (s1 != substream) { + if (s->pcm->nonatomic) + mutex_unlock(&s1->self_group.mutex); + else + spin_unlock(&s1->self_group.lock); + } if (s1 == s) /* end */ break; } @@ -784,6 +794,27 @@ static int snd_pcm_action_single(struct action_ops *ops, return res; } +/* call in mutex-protected context */ +static int snd_pcm_action_mutex(struct action_ops *ops, + struct snd_pcm_substream *substream, + int state) +{ + int res; + + if (snd_pcm_stream_linked(substream)) { + if (!mutex_trylock(&substream->group->mutex)) { + mutex_unlock(&substream->self_group.mutex); + mutex_lock(&substream->group->mutex); + mutex_lock(&substream->self_group.mutex); + } + res = snd_pcm_action_group(ops, substream, state, 1); + mutex_unlock(&substream->group->mutex); + } else { + res = snd_pcm_action_single(ops, substream, state); + } + return res; +} + /* * Note: call with stream lock */ @@ -793,6 +824,9 @@ static int snd_pcm_action(struct action_ops *ops, { int res; + if (substream->pcm->nonatomic) + return snd_pcm_action_mutex(ops, substream, state); + if (snd_pcm_stream_linked(substream)) { if (!spin_trylock(&substream->group->lock)) { spin_unlock(&substream->self_group.lock); @@ -807,6 +841,29 @@ static int snd_pcm_action(struct action_ops *ops, return res; } +static int snd_pcm_action_lock_mutex(struct action_ops *ops, + struct snd_pcm_substream *substream, + int state) +{ + int res; + + down_read(&snd_pcm_link_rwsem); + if (snd_pcm_stream_linked(substream)) { + mutex_lock(&substream->group->mutex); + mutex_lock_nested(&substream->self_group.mutex, + SINGLE_DEPTH_NESTING); + res = snd_pcm_action_group(ops, substream, state, 1); + mutex_unlock(&substream->self_group.mutex); + mutex_unlock(&substream->group->mutex); + } else { + mutex_lock(&substream->self_group.mutex); + res = snd_pcm_action_single(ops, substream, state); + mutex_unlock(&substream->self_group.mutex); + } + up_read(&snd_pcm_link_rwsem); + return res; +} + /* * Note: don't use any locks before */ @@ -816,6 +873,9 @@ static int snd_pcm_action_lock_irq(struct action_ops *ops, { int res; + if (substream->pcm->nonatomic) + return snd_pcm_action_lock_mutex(ops, substream, state); + read_lock_irq(&snd_pcm_link_rwlock); if (snd_pcm_stream_linked(substream)) { spin_lock(&substream->group->lock); @@ -1634,7 +1694,8 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) down_write(&snd_pcm_link_rwsem); write_lock_irq(&snd_pcm_link_rwlock); if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN || - substream->runtime->status->state != substream1->runtime->status->state) { + substream->runtime->status->state != substream1->runtime->status->state || + substream->pcm->nonatomic != substream1->pcm->nonatomic) { res = -EBADFD; goto _end; } @@ -1646,6 +1707,7 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) substream->group = group; group = NULL; spin_lock_init(&substream->group->lock); + mutex_init(&substream->group->mutex); INIT_LIST_HEAD(&substream->group->substreams); list_add_tail(&substream->link_list, &substream->group->substreams); substream->group->count = 1; -- cgit v1.3-14-g43fede From 7af142f752116e86adbe2073f2922d8265a77709 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 1 Sep 2014 11:19:37 +0200 Subject: ALSA: pcm: Uninline snd_pcm_stream_lock() and _unlock() The previous commit for the non-atomic PCM ops added more codes to snd_pcm_stream_lock() and its variants. Since they are inlined functions, it resulted in a significant code size bloat. For reducing the size bloat, this patch changes the inline functions to the normal function calls. The export of rwlock and rwsem are removed as well, since they are referred only in pcm_native.c now. Signed-off-by: Takashi Iwai --- include/sound/pcm.h | 81 ++++++++----------------------------------------- sound/core/pcm_native.c | 64 +++++++++++++++++++++++++++++++++++--- 2 files changed, 72 insertions(+), 73 deletions(-) (limited to 'include') diff --git a/include/sound/pcm.h b/include/sound/pcm.h index bc79962f4aa6..67e0bdb9f0fa 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -494,9 +494,6 @@ int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree); * Native I/O */ -extern rwlock_t snd_pcm_link_rwlock; -extern struct rw_semaphore snd_pcm_link_rwsem; - int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info); int snd_pcm_info_user(struct snd_pcm_substream *substream, struct snd_pcm_info __user *info); @@ -540,72 +537,18 @@ static inline int snd_pcm_stream_linked(struct snd_pcm_substream *substream) return substream->group != &substream->self_group; } -static inline void snd_pcm_stream_lock(struct snd_pcm_substream *substream) -{ - if (substream->pcm->nonatomic) { - down_read(&snd_pcm_link_rwsem); - mutex_lock(&substream->self_group.mutex); - } else { - read_lock(&snd_pcm_link_rwlock); - spin_lock(&substream->self_group.lock); - } -} - -static inline void snd_pcm_stream_unlock(struct snd_pcm_substream *substream) -{ - if (substream->pcm->nonatomic) { - mutex_unlock(&substream->self_group.mutex); - up_read(&snd_pcm_link_rwsem); - } else { - spin_unlock(&substream->self_group.lock); - read_unlock(&snd_pcm_link_rwlock); - } -} - -static inline void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream) -{ - if (substream->pcm->nonatomic) { - down_read(&snd_pcm_link_rwsem); - mutex_lock(&substream->self_group.mutex); - } else { - read_lock_irq(&snd_pcm_link_rwlock); - spin_lock(&substream->self_group.lock); - } -} - -static inline void snd_pcm_stream_unlock_irq(struct snd_pcm_substream *substream) -{ - if (substream->pcm->nonatomic) { - mutex_unlock(&substream->self_group.mutex); - up_read(&snd_pcm_link_rwsem); - } else { - spin_unlock(&substream->self_group.lock); - read_unlock_irq(&snd_pcm_link_rwlock); - } -} - -#define snd_pcm_stream_lock_irqsave(substream, flags) \ -do { \ - if ((substream)->pcm->nonatomic) { \ - (flags) = 0; /* XXX for avoid warning */ \ - down_read(&snd_pcm_link_rwsem); \ - mutex_lock(&(substream)->self_group.mutex); \ - } else { \ - read_lock_irqsave(&snd_pcm_link_rwlock, (flags)); \ - spin_lock(&(substream)->self_group.lock); \ - } \ -} while (0) - -#define snd_pcm_stream_unlock_irqrestore(substream, flags) \ -do { \ - if ((substream)->pcm->nonatomic) { \ - mutex_unlock(&(substream)->self_group.mutex); \ - up_read(&snd_pcm_link_rwsem); \ - } else { \ - spin_unlock(&(substream)->self_group.lock); \ - read_unlock_irqrestore(&snd_pcm_link_rwlock, (flags)); \ - } \ -} while (0) +void snd_pcm_stream_lock(struct snd_pcm_substream *substream); +void snd_pcm_stream_unlock(struct snd_pcm_substream *substream); +void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream); +void snd_pcm_stream_unlock_irq(struct snd_pcm_substream *substream); +unsigned long _snd_pcm_stream_lock_irqsave(struct snd_pcm_substream *substream); +#define snd_pcm_stream_lock_irqsave(substream, flags) \ + do { \ + typecheck(unsigned long, flags); \ + flags = _snd_pcm_stream_lock_irqsave(substream); \ + } while (0) +void snd_pcm_stream_unlock_irqrestore(struct snd_pcm_substream *substream, + unsigned long flags); #define snd_pcm_group_for_each_entry(s, substream) \ list_for_each_entry(s, &substream->group->substreams, link_list) diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 16d9b7e15f8b..85fe1a216225 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -74,11 +74,67 @@ static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream); * */ -DEFINE_RWLOCK(snd_pcm_link_rwlock); -EXPORT_SYMBOL(snd_pcm_link_rwlock); +static DEFINE_RWLOCK(snd_pcm_link_rwlock); +static DECLARE_RWSEM(snd_pcm_link_rwsem); -DECLARE_RWSEM(snd_pcm_link_rwsem); -EXPORT_SYMBOL(snd_pcm_link_rwsem); +void snd_pcm_stream_lock(struct snd_pcm_substream *substream) +{ + if (substream->pcm->nonatomic) { + down_read(&snd_pcm_link_rwsem); + mutex_lock(&substream->self_group.mutex); + } else { + read_lock(&snd_pcm_link_rwlock); + spin_lock(&substream->self_group.lock); + } +} +EXPORT_SYMBOL_GPL(snd_pcm_stream_lock); + +void snd_pcm_stream_unlock(struct snd_pcm_substream *substream) +{ + if (substream->pcm->nonatomic) { + mutex_unlock(&substream->self_group.mutex); + up_read(&snd_pcm_link_rwsem); + } else { + spin_unlock(&substream->self_group.lock); + read_unlock(&snd_pcm_link_rwlock); + } +} +EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock); + +void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream) +{ + if (!substream->pcm->nonatomic) + local_irq_disable(); + snd_pcm_stream_lock(substream); +} +EXPORT_SYMBOL_GPL(snd_pcm_stream_lock_irq); + +void snd_pcm_stream_unlock_irq(struct snd_pcm_substream *substream) +{ + snd_pcm_stream_unlock(substream); + if (!substream->pcm->nonatomic) + local_irq_enable(); +} +EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock_irq); + +unsigned long _snd_pcm_stream_lock_irqsave(struct snd_pcm_substream *substream) +{ + unsigned long flags = 0; + if (!substream->pcm->nonatomic) + local_irq_save(flags); + snd_pcm_stream_lock(substream); + return flags; +} +EXPORT_SYMBOL_GPL(_snd_pcm_stream_lock_irqsave); + +void snd_pcm_stream_unlock_irqrestore(struct snd_pcm_substream *substream, + unsigned long flags) +{ + snd_pcm_stream_unlock(substream); + if (!substream->pcm->nonatomic) + local_irq_restore(flags); +} +EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock_irqrestore); static inline mm_segment_t snd_enter_user(void) { -- cgit v1.3-14-g43fede From 86dbf2ac6fcb2d2932d4610f2dfe0954aa0633f7 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 4 Sep 2014 19:44:06 +0200 Subject: ASoC: Add support for automatically going to BIAS_OFF on suspend There is a substantial amount of drivers that in go to SND_SOC_BIAS_OFF on suspend and go back to SND_SOC_BIAS_SUSPEND on resume (Often this is even the only thing done in the suspend and resume handlers). This patch introduces a new suspend_bias_off flag, which when set by a driver will let the ASoC core automatically put the device's DAPM context at the SND_SOC_BIAS_OFF level during suspend. Once the device is resumed the DAPM context will go back to SND_SOC_BIAS_STANDBY (if the context is idle, otherwise to SND_SOC_BIAS_ON). This will allow us to remove a fair bit of duplicated code from the drivers. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 3 ++- include/sound/soc.h | 1 + sound/soc/soc-core.c | 1 + sound/soc/soc-dapm.c | 20 ++++++++++++++++++-- 4 files changed, 22 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index aac04ff84eea..f955d65c5656 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -587,7 +587,8 @@ struct snd_soc_dapm_context { enum snd_soc_bias_level suspend_bias_level; struct delayed_work delayed_work; unsigned int idle_bias_off:1; /* Use BIAS_OFF instead of STANDBY */ - + /* Go to BIAS_OFF in suspend if the DAPM context is idle */ + unsigned int suspend_bias_off:1; void (*seq_notifier)(struct snd_soc_dapm_context *, enum snd_soc_dapm_type, int); diff --git a/include/sound/soc.h b/include/sound/soc.h index ce09302bfd6d..ac99fc083eec 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -848,6 +848,7 @@ struct snd_soc_codec_driver { int (*set_bias_level)(struct snd_soc_codec *, enum snd_soc_bias_level level); bool idle_bias_off; + bool suspend_bias_off; void (*seq_notifier)(struct snd_soc_dapm_context *, enum snd_soc_dapm_type, int); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 068785fa1a06..2bdf9a4ac2b4 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -4402,6 +4402,7 @@ int snd_soc_register_codec(struct device *dev, codec->component.ignore_pmdown_time = codec_drv->ignore_pmdown_time; codec->dapm.codec = codec; codec->dapm.idle_bias_off = codec_drv->idle_bias_off; + codec->dapm.suspend_bias_off = codec_drv->suspend_bias_off; if (codec_drv->seq_notifier) codec->dapm.seq_notifier = codec_drv->seq_notifier; if (codec_drv->set_bias_level) diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 8348352dc2c6..a2025a6b6a29 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1683,6 +1683,22 @@ static void dapm_power_one_widget(struct snd_soc_dapm_widget *w, } } +static bool dapm_idle_bias_off(struct snd_soc_dapm_context *dapm) +{ + if (dapm->idle_bias_off) + return true; + + switch (snd_power_get_state(dapm->card->snd_card)) { + case SNDRV_CTL_POWER_D3hot: + case SNDRV_CTL_POWER_D3cold: + return dapm->suspend_bias_off; + default: + break; + } + + return false; +} + /* * Scan each dapm widget for complete audio path. * A complete path is a route that has valid endpoints i.e.:- @@ -1706,7 +1722,7 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event) trace_snd_soc_dapm_start(card); list_for_each_entry(d, &card->dapm_list, list) { - if (d->idle_bias_off) + if (dapm_idle_bias_off(d)) d->target_bias_level = SND_SOC_BIAS_OFF; else d->target_bias_level = SND_SOC_BIAS_STANDBY; @@ -1772,7 +1788,7 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event) if (d->target_bias_level > bias) bias = d->target_bias_level; list_for_each_entry(d, &card->dapm_list, list) - if (!d->idle_bias_off) + if (!dapm_idle_bias_off(d)) d->target_bias_level = bias; trace_snd_soc_dapm_walk_done(card); -- cgit v1.3-14-g43fede From c815dbb47758bd469927849fdd45fed3ce206e73 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 20 Aug 2014 13:08:46 +0200 Subject: ASoC: Add snd_soc_component_{get,set}_drvdata() Add Add snd_soc_component_{get,set}_drvdata() similar to snd_soc_codec_{get,set}_drvdata() and snd_soc_platform_{get,set}_drvdata(). Also update them to use the new functions internally. Signed-off-by: Lars-Peter Clausen Reviewed-by: Vinod Koul Signed-off-by: Mark Brown --- include/sound/soc.h | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index ce09302bfd6d..f8b23dd7c3a7 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1298,26 +1298,37 @@ static inline void *snd_soc_card_get_drvdata(struct snd_soc_card *card) return card->drvdata; } +static inline void snd_soc_component_set_drvdata(struct snd_soc_component *c, + void *data) +{ + dev_set_drvdata(c->dev, data); +} + +static inline void *snd_soc_component_get_drvdata(struct snd_soc_component *c) +{ + return dev_get_drvdata(c->dev); +} + static inline void snd_soc_codec_set_drvdata(struct snd_soc_codec *codec, void *data) { - dev_set_drvdata(codec->dev, data); + snd_soc_component_set_drvdata(&codec->component, data); } static inline void *snd_soc_codec_get_drvdata(struct snd_soc_codec *codec) { - return dev_get_drvdata(codec->dev); + return snd_soc_component_get_drvdata(&codec->component); } static inline void snd_soc_platform_set_drvdata(struct snd_soc_platform *platform, void *data) { - dev_set_drvdata(platform->dev, data); + snd_soc_component_set_drvdata(&platform->component, data); } static inline void *snd_soc_platform_get_drvdata(struct snd_soc_platform *platform) { - return dev_get_drvdata(platform->dev); + return snd_soc_component_get_drvdata(&platform->component); } static inline void snd_soc_pcm_set_drvdata(struct snd_soc_pcm_runtime *rtd, -- cgit v1.3-14-g43fede From 0634814fe0f29a46c44386a03f259f99c983bf7e Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 20 Aug 2014 13:08:49 +0200 Subject: ASoC: Remove table based DAPM/control setup support from snd_soc_platform_driver There are no users left and new users should rather use the component_driver struct embedded in the snd_soc_platform_driver struct to do this. E.g.: static const struct snd_soc_platform_driver foobar_driver = { .component_driver = { .dapm_widgets = ..., .num_dapm_widgets = ..., ..., }, ... }; instead of static const struct snd_soc_platform_driver foobar_driver = { .dapm_widgets = ..., .num_dapm_widgets = ..., ... }; This also allows us to remove the steal_sibling_dai_widgets hack. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 9 -------- sound/soc/soc-core.c | 58 ++++++---------------------------------------------- 2 files changed, 6 insertions(+), 61 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index f8b23dd7c3a7..cd141a156da2 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -764,7 +764,6 @@ struct snd_soc_component { unsigned int num_dapm_widgets; const struct snd_soc_dapm_route *dapm_routes; unsigned int num_dapm_routes; - bool steal_sibling_dai_widgets; struct snd_soc_codec *codec; int (*probe)(struct snd_soc_component *); @@ -868,14 +867,6 @@ struct snd_soc_platform_driver { int (*pcm_new)(struct snd_soc_pcm_runtime *); void (*pcm_free)(struct snd_pcm *); - /* Default control and setup, added after probe() is run */ - const struct snd_kcontrol_new *controls; - int num_controls; - const struct snd_soc_dapm_widget *dapm_widgets; - int num_dapm_widgets; - const struct snd_soc_dapm_route *dapm_routes; - int num_dapm_routes; - /* * For platform caused delay reporting. * Optional. diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 1b422c5c36c8..052f59c1917f 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1087,7 +1087,6 @@ static int soc_probe_component(struct snd_soc_card *card, struct snd_soc_component *component) { struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); - struct snd_soc_component *dai_component, *component2; struct snd_soc_dai *dai; int ret; @@ -1114,44 +1113,12 @@ static int soc_probe_component(struct snd_soc_card *card, } } - /* - * This is rather ugly, but certain platforms expect that the DAPM - * widgets for the DAIs for components with the same parent device are - * created in the platforms DAPM context. Until that is fixed we need to - * keep this. - */ - if (component->steal_sibling_dai_widgets) { - dai_component = NULL; - list_for_each_entry(component2, &component_list, list) { - if (component == component2) - continue; - - if (component2->dev == component->dev && - !list_empty(&component2->dai_list)) { - dai_component = component2; - break; - } - } - } else { - dai_component = component; - list_for_each_entry(component2, &component_list, list) { - if (component2->dev == component->dev && - component2->steal_sibling_dai_widgets) { - dai_component = NULL; - break; - } - } - } - - if (dai_component) { - list_for_each_entry(dai, &dai_component->dai_list, list) { - snd_soc_dapm_new_dai_widgets(dapm, dai); - if (ret != 0) { - dev_err(component->dev, - "Failed to create DAI widgets %d\n", - ret); - goto err_probe; - } + list_for_each_entry(dai, &component->dai_list, list) { + ret = snd_soc_dapm_new_dai_widgets(dapm, dai); + if (ret != 0) { + dev_err(component->dev, + "Failed to create DAI widgets %d\n", ret); + goto err_probe; } } @@ -4164,19 +4131,6 @@ int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform, platform->dev = dev; platform->driver = platform_drv; - if (platform_drv->controls) { - platform->component.controls = platform_drv->controls; - platform->component.num_controls = platform_drv->num_controls; - } - if (platform_drv->dapm_widgets) { - platform->component.dapm_widgets = platform_drv->dapm_widgets; - platform->component.num_dapm_widgets = platform_drv->num_dapm_widgets; - platform->component.steal_sibling_dai_widgets = true; - } - if (platform_drv->dapm_routes) { - platform->component.dapm_routes = platform_drv->dapm_routes; - platform->component.num_dapm_routes = platform_drv->num_dapm_routes; - } if (platform_drv->probe) platform->component.probe = snd_soc_platform_drv_probe; -- cgit v1.3-14-g43fede From d4288d3fac18bbc31cb6d369679b1fa1d9321ae9 Mon Sep 17 00:00:00 2001 From: Jurgen Kramer Date: Fri, 5 Sep 2014 10:47:56 +0200 Subject: ALSA: pcm: add new DSD sampleformat for native DSD playback on XMOS based devices XMOS based USB DACs with native DSD support expose this feature via a USB alternate setting. The audio format is either 32-bit raw or a 32-bit PCM format. To utilize this feature on linux this patch introduces a new 32-bit DSD sampleformat DSD_U32_LE. A follow up patch will add a quirk for XMOS based devices to utilize the new format. Further patches will add support to alsa-lib. Signed-off-by: Jurgen Kramer Signed-off-by: Takashi Iwai --- include/sound/pcm.h | 1 + include/uapi/sound/asound.h | 3 ++- sound/core/pcm.c | 1 + sound/core/pcm_misc.c | 4 ++++ 4 files changed, 8 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 67e0bdb9f0fa..e862497f7556 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -183,6 +183,7 @@ struct snd_pcm_ops { #define SNDRV_PCM_FMTBIT_G723_40_1B _SNDRV_PCM_FMTBIT(G723_40_1B) #define SNDRV_PCM_FMTBIT_DSD_U8 _SNDRV_PCM_FMTBIT(DSD_U8) #define SNDRV_PCM_FMTBIT_DSD_U16_LE _SNDRV_PCM_FMTBIT(DSD_U16_LE) +#define SNDRV_PCM_FMTBIT_DSD_U32_LE _SNDRV_PCM_FMTBIT(DSD_U32_LE) #ifdef SNDRV_LITTLE_ENDIAN #define SNDRV_PCM_FMTBIT_S16 SNDRV_PCM_FMTBIT_S16_LE diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index 32168f7ffce3..6ee586728df9 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -219,7 +219,8 @@ typedef int __bitwise snd_pcm_format_t; #define SNDRV_PCM_FORMAT_G723_40_1B ((__force snd_pcm_format_t) 47) /* 1 sample in 1 byte */ #define SNDRV_PCM_FORMAT_DSD_U8 ((__force snd_pcm_format_t) 48) /* DSD, 1-byte samples DSD (x8) */ #define SNDRV_PCM_FORMAT_DSD_U16_LE ((__force snd_pcm_format_t) 49) /* DSD, 2-byte samples DSD (x16), little endian */ -#define SNDRV_PCM_FORMAT_LAST SNDRV_PCM_FORMAT_DSD_U16_LE +#define SNDRV_PCM_FORMAT_DSD_U32_LE ((__force snd_pcm_format_t) 50) /* DSD, 4-byte samples DSD (x32), little endian */ +#define SNDRV_PCM_FORMAT_LAST SNDRV_PCM_FORMAT_DSD_U32_LE #ifdef SNDRV_LITTLE_ENDIAN #define SNDRV_PCM_FORMAT_S16 SNDRV_PCM_FORMAT_S16_LE diff --git a/sound/core/pcm.c b/sound/core/pcm.c index afccdc553ef9..42ded997b223 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -215,6 +215,7 @@ static char *snd_pcm_format_names[] = { FORMAT(G723_40_1B), FORMAT(DSD_U8), FORMAT(DSD_U16_LE), + FORMAT(DSD_U32_LE), }; const char *snd_pcm_format_name(snd_pcm_format_t format) diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c index 4560ca0e5651..83f54e1dbac9 100644 --- a/sound/core/pcm_misc.c +++ b/sound/core/pcm_misc.c @@ -148,6 +148,10 @@ static struct pcm_format_data pcm_formats[(INT)SNDRV_PCM_FORMAT_LAST+1] = { .width = 16, .phys = 16, .le = 1, .signd = 0, .silence = {}, }, + [SNDRV_PCM_FORMAT_DSD_U32_LE] = { + .width = 32, .phys = 32, .le = 1, .signd = 0, + .silence = { 0x69, 0x69, 0x69, 0x69 }, + }, /* FIXME: the following three formats are not defined properly yet */ [SNDRV_PCM_FORMAT_MPEG] = { .le = -1, .signd = -1, -- cgit v1.3-14-g43fede From db0a5214b8d6cc7a90ce3336d24a85b90cbb4e67 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 9 Sep 2014 17:17:20 +0200 Subject: ALSA: vx: Use nonatomic PCM ops Rewrite VXpocket and VX222 drivers to use the new PCM nonatomic ops. The former irq tasklet is replaced with a threaded irq handler, and the tasklet for the PCM delayed start is simply merged into the normal PCM trigger, as well as the replacement of spinlock with mutex. Signed-off-by: Takashi Iwai --- include/sound/vx_core.h | 7 ++--- sound/drivers/vx/vx_core.c | 49 +++++++++++++++----------------- sound/drivers/vx/vx_mixer.c | 12 +++----- sound/drivers/vx/vx_pcm.c | 68 +++++++++++++++------------------------------ sound/drivers/vx/vx_uer.c | 23 +++++++-------- sound/pci/vx222/vx222.c | 5 ++-- sound/pcmcia/vx/vxp_ops.c | 10 +++---- sound/pcmcia/vx/vxpocket.c | 13 ++++++--- 8 files changed, 78 insertions(+), 109 deletions(-) (limited to 'include') diff --git a/include/sound/vx_core.h b/include/sound/vx_core.h index f634f8f85db5..cae9c9d4ef22 100644 --- a/include/sound/vx_core.h +++ b/include/sound/vx_core.h @@ -80,8 +80,6 @@ struct vx_pipe { unsigned int references; /* an output pipe may be used for monitoring and/or playback */ struct vx_pipe *monitoring_pipe; /* pointer to the monitoring pipe (capture pipe only)*/ - - struct tasklet_struct start_tq; }; struct vx_core; @@ -165,9 +163,7 @@ struct vx_core { struct snd_vx_hardware *hw; struct snd_vx_ops *ops; - spinlock_t lock; - spinlock_t irq_lock; - struct tasklet_struct tq; + struct mutex lock; unsigned int chip_status; unsigned int pcm_running; @@ -223,6 +219,7 @@ void snd_vx_free_firmware(struct vx_core *chip); * interrupt handler; exported for pcmcia */ irqreturn_t snd_vx_irq_handler(int irq, void *dev); +irqreturn_t snd_vx_threaded_irq_handler(int irq, void *dev); /* * lowlevel functions diff --git a/sound/drivers/vx/vx_core.c b/sound/drivers/vx/vx_core.c index 83596891cde4..e8cc16993903 100644 --- a/sound/drivers/vx/vx_core.c +++ b/sound/drivers/vx/vx_core.c @@ -117,7 +117,7 @@ static int vx_reset_chk(struct vx_core *chip) * * returns 0 if successful, or a negative error code. * the error code can be VX-specific, retrieved via vx_get_error(). - * NB: call with spinlock held! + * NB: call with mutex held! */ static int vx_transfer_end(struct vx_core *chip, int cmd) { @@ -155,7 +155,7 @@ static int vx_transfer_end(struct vx_core *chip, int cmd) * * returns 0 if successful, or a negative error code. * the error code can be VX-specific, retrieved via vx_get_error(). - * NB: call with spinlock held! + * NB: call with mutex held! */ static int vx_read_status(struct vx_core *chip, struct vx_rmh *rmh) { @@ -236,7 +236,7 @@ static int vx_read_status(struct vx_core *chip, struct vx_rmh *rmh) * returns 0 if successful, or a negative error code. * the error code can be VX-specific, retrieved via vx_get_error(). * - * this function doesn't call spinlock at all. + * this function doesn't call mutex lock at all. */ int vx_send_msg_nolock(struct vx_core *chip, struct vx_rmh *rmh) { @@ -337,7 +337,7 @@ int vx_send_msg_nolock(struct vx_core *chip, struct vx_rmh *rmh) /* - * vx_send_msg - send a DSP message with spinlock + * vx_send_msg - send a DSP message with mutex * @rmh: the rmh record to send and receive * * returns 0 if successful, or a negative error code. @@ -345,12 +345,11 @@ int vx_send_msg_nolock(struct vx_core *chip, struct vx_rmh *rmh) */ int vx_send_msg(struct vx_core *chip, struct vx_rmh *rmh) { - unsigned long flags; int err; - spin_lock_irqsave(&chip->lock, flags); + mutex_lock(&chip->lock); err = vx_send_msg_nolock(chip, rmh); - spin_unlock_irqrestore(&chip->lock, flags); + mutex_unlock(&chip->lock); return err; } @@ -362,7 +361,7 @@ int vx_send_msg(struct vx_core *chip, struct vx_rmh *rmh) * returns 0 if successful, or a negative error code. * the error code can be VX-specific, retrieved via vx_get_error(). * - * this function doesn't call spinlock at all. + * this function doesn't call mutex at all. * * unlike RMH, no command is sent to DSP. */ @@ -398,19 +397,18 @@ int vx_send_rih_nolock(struct vx_core *chip, int cmd) /* - * vx_send_rih - send an RIH with spinlock + * vx_send_rih - send an RIH with mutex * @cmd: the command to send * * see vx_send_rih_nolock(). */ int vx_send_rih(struct vx_core *chip, int cmd) { - unsigned long flags; int err; - spin_lock_irqsave(&chip->lock, flags); + mutex_lock(&chip->lock); err = vx_send_rih_nolock(chip, cmd); - spin_unlock_irqrestore(&chip->lock, flags); + mutex_unlock(&chip->lock); return err; } @@ -482,30 +480,30 @@ static int vx_test_irq_src(struct vx_core *chip, unsigned int *ret) int err; vx_init_rmh(&chip->irq_rmh, CMD_TEST_IT); - spin_lock(&chip->lock); + mutex_lock(&chip->lock); err = vx_send_msg_nolock(chip, &chip->irq_rmh); if (err < 0) *ret = 0; else *ret = chip->irq_rmh.Stat[0]; - spin_unlock(&chip->lock); + mutex_unlock(&chip->lock); return err; } /* - * vx_interrupt - soft irq handler + * snd_vx_threaded_irq_handler - threaded irq handler */ -static void vx_interrupt(unsigned long private_data) +irqreturn_t snd_vx_threaded_irq_handler(int irq, void *dev) { - struct vx_core *chip = (struct vx_core *) private_data; + struct vx_core *chip = dev; unsigned int events; if (chip->chip_status & VX_STAT_IS_STALE) - return; + return IRQ_HANDLED; if (vx_test_irq_src(chip, &events) < 0) - return; + return IRQ_HANDLED; #if 0 if (events & 0x000800) @@ -519,7 +517,7 @@ static void vx_interrupt(unsigned long private_data) */ if (events & FATAL_DSP_ERROR) { snd_printk(KERN_ERR "vx_core: fatal DSP error!!\n"); - return; + return IRQ_HANDLED; } /* The start on time code conditions are filled (ie the time code @@ -534,8 +532,9 @@ static void vx_interrupt(unsigned long private_data) /* update the pcm streams */ vx_pcm_update_intr(chip, events); + return IRQ_HANDLED; } - +EXPORT_SYMBOL(snd_vx_threaded_irq_handler); /** * snd_vx_irq_handler - interrupt handler @@ -548,8 +547,8 @@ irqreturn_t snd_vx_irq_handler(int irq, void *dev) (chip->chip_status & VX_STAT_IS_STALE)) return IRQ_NONE; if (! vx_test_and_ack(chip)) - tasklet_schedule(&chip->tq); - return IRQ_HANDLED; + return IRQ_WAKE_THREAD; + return IRQ_NONE; } EXPORT_SYMBOL(snd_vx_irq_handler); @@ -790,13 +789,11 @@ struct vx_core *snd_vx_create(struct snd_card *card, struct snd_vx_hardware *hw, snd_printk(KERN_ERR "vx_core: no memory\n"); return NULL; } - spin_lock_init(&chip->lock); - spin_lock_init(&chip->irq_lock); + mutex_init(&chip->lock); chip->irq = -1; chip->hw = hw; chip->type = hw->type; chip->ops = ops; - tasklet_init(&chip->tq, vx_interrupt, (unsigned long)chip); mutex_init(&chip->mixer_mutex); chip->card = card; diff --git a/sound/drivers/vx/vx_mixer.c b/sound/drivers/vx/vx_mixer.c index c71b8d148d7f..3b6823fc0606 100644 --- a/sound/drivers/vx/vx_mixer.c +++ b/sound/drivers/vx/vx_mixer.c @@ -32,17 +32,15 @@ */ static void vx_write_codec_reg(struct vx_core *chip, int codec, unsigned int data) { - unsigned long flags; - if (snd_BUG_ON(!chip->ops->write_codec)) return; if (chip->chip_status & VX_STAT_IS_STALE) return; - spin_lock_irqsave(&chip->lock, flags); + mutex_lock(&chip->lock); chip->ops->write_codec(chip, codec, data); - spin_unlock_irqrestore(&chip->lock, flags); + mutex_unlock(&chip->lock); } /* @@ -178,14 +176,12 @@ void vx_reset_codec(struct vx_core *chip, int cold_reset) */ static void vx_change_audio_source(struct vx_core *chip, int src) { - unsigned long flags; - if (chip->chip_status & VX_STAT_IS_STALE) return; - spin_lock_irqsave(&chip->lock, flags); + mutex_lock(&chip->lock); chip->ops->change_audio_source(chip, src); - spin_unlock_irqrestore(&chip->lock, flags); + mutex_unlock(&chip->lock); } diff --git a/sound/drivers/vx/vx_pcm.c b/sound/drivers/vx/vx_pcm.c index deed5efff33c..11467272089e 100644 --- a/sound/drivers/vx/vx_pcm.c +++ b/sound/drivers/vx/vx_pcm.c @@ -229,7 +229,7 @@ static int vx_get_pipe_state(struct vx_core *chip, struct vx_pipe *pipe, int *st vx_init_rmh(&rmh, CMD_PIPE_STATE); vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0); - err = vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ + err = vx_send_msg(chip, &rmh); if (! err) *state = (rmh.Stat[0] & (1 << pipe->number)) ? 1 : 0; return err; @@ -280,7 +280,7 @@ static int vx_pipe_can_start(struct vx_core *chip, struct vx_pipe *pipe) vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0); rmh.Cmd[0] |= 1; - err = vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ + err = vx_send_msg(chip, &rmh); if (! err) { if (rmh.Stat[0]) err = 1; @@ -300,7 +300,7 @@ static int vx_conf_pipe(struct vx_core *chip, struct vx_pipe *pipe) if (pipe->is_capture) rmh.Cmd[0] |= COMMAND_RECORD_MASK; rmh.Cmd[1] = 1 << pipe->number; - return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ + return vx_send_msg(chip, &rmh); } /* @@ -311,7 +311,7 @@ static int vx_send_irqa(struct vx_core *chip) struct vx_rmh rmh; vx_init_rmh(&rmh, CMD_SEND_IRQA); - return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ + return vx_send_msg(chip, &rmh); } @@ -389,7 +389,7 @@ static int vx_stop_pipe(struct vx_core *chip, struct vx_pipe *pipe) struct vx_rmh rmh; vx_init_rmh(&rmh, CMD_STOP_PIPE); vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0); - return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ + return vx_send_msg(chip, &rmh); } @@ -477,7 +477,7 @@ static int vx_start_stream(struct vx_core *chip, struct vx_pipe *pipe) vx_init_rmh(&rmh, CMD_START_ONE_STREAM); vx_set_stream_cmd_params(&rmh, pipe->is_capture, pipe->number); vx_set_differed_time(chip, &rmh, pipe); - return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ + return vx_send_msg(chip, &rmh); } @@ -492,7 +492,7 @@ static int vx_stop_stream(struct vx_core *chip, struct vx_pipe *pipe) vx_init_rmh(&rmh, CMD_STOP_STREAM); vx_set_stream_cmd_params(&rmh, pipe->is_capture, pipe->number); - return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ + return vx_send_msg(chip, &rmh); } @@ -520,8 +520,6 @@ static struct snd_pcm_hardware vx_pcm_playback_hw = { }; -static void vx_pcm_delayed_start(unsigned long arg); - /* * vx_pcm_playback_open - open callback for playback */ @@ -553,7 +551,6 @@ static int vx_pcm_playback_open(struct snd_pcm_substream *subs) pipe->references++; pipe->substream = subs; - tasklet_init(&pipe->start_tq, vx_pcm_delayed_start, (unsigned long)subs); chip->playback_pipes[audio] = pipe; runtime->hw = vx_pcm_playback_hw; @@ -646,12 +643,12 @@ static int vx_pcm_playback_transfer_chunk(struct vx_core *chip, /* we don't need irqsave here, because this function * is called from either trigger callback or irq handler */ - spin_lock(&chip->lock); + mutex_lock(&chip->lock); vx_pseudo_dma_write(chip, runtime, pipe, size); err = vx_notify_end_of_buffer(chip, pipe); /* disconnect the host, SIZE_HBUF command always switches to the stream mode */ vx_send_rih_nolock(chip, IRQ_CONNECT_STREAM_NEXT); - spin_unlock(&chip->lock); + mutex_unlock(&chip->lock); return err; } @@ -727,31 +724,6 @@ static void vx_pcm_playback_update(struct vx_core *chip, } } -/* - * start the stream and pipe. - * this function is called from tasklet, which is invoked by the trigger - * START callback. - */ -static void vx_pcm_delayed_start(unsigned long arg) -{ - struct snd_pcm_substream *subs = (struct snd_pcm_substream *)arg; - struct vx_core *chip = subs->pcm->private_data; - struct vx_pipe *pipe = subs->runtime->private_data; - int err; - - /* printk( KERN_DEBUG "DDDD tasklet delayed start jiffies = %ld\n", jiffies);*/ - - if ((err = vx_start_stream(chip, pipe)) < 0) { - snd_printk(KERN_ERR "vx: cannot start stream\n"); - return; - } - if ((err = vx_toggle_pipe(chip, pipe, 1)) < 0) { - snd_printk(KERN_ERR "vx: cannot start pipe\n"); - return; - } - /* printk( KERN_DEBUG "dddd tasklet delayed start jiffies = %ld \n", jiffies);*/ -} - /* * vx_pcm_playback_trigger - trigger callback for playback */ @@ -769,11 +741,17 @@ static int vx_pcm_trigger(struct snd_pcm_substream *subs, int cmd) case SNDRV_PCM_TRIGGER_RESUME: if (! pipe->is_capture) vx_pcm_playback_transfer(chip, subs, pipe, 2); - /* FIXME: - * we trigger the pipe using tasklet, so that the interrupts are - * issued surely after the trigger is completed. - */ - tasklet_schedule(&pipe->start_tq); + err = vx_start_stream(chip, pipe); + if (err < 0) { + pr_debug("vx: cannot start stream\n"); + return err; + } + err = vx_toggle_pipe(chip, pipe, 1); + if (err < 0) { + pr_debug("vx: cannot start pipe\n"); + vx_stop_stream(chip, pipe); + return err; + } chip->pcm_running++; pipe->running = 1; break; @@ -955,7 +933,6 @@ static int vx_pcm_capture_open(struct snd_pcm_substream *subs) if (err < 0) return err; pipe->substream = subs; - tasklet_init(&pipe->start_tq, vx_pcm_delayed_start, (unsigned long)subs); chip->capture_pipes[audio] = pipe; /* check if monitoring is needed */ @@ -1082,7 +1059,7 @@ static void vx_pcm_capture_update(struct vx_core *chip, struct snd_pcm_substream count -= 3; } /* disconnect the host, SIZE_HBUF command always switches to the stream mode */ - vx_send_rih_nolock(chip, IRQ_CONNECT_STREAM_NEXT); + vx_send_rih(chip, IRQ_CONNECT_STREAM_NEXT); /* read the last pending 6 bytes */ count = DMA_READ_ALIGN; while (count > 0) { @@ -1099,7 +1076,7 @@ static void vx_pcm_capture_update(struct vx_core *chip, struct snd_pcm_substream _error: /* disconnect the host, SIZE_HBUF command always switches to the stream mode */ - vx_send_rih_nolock(chip, IRQ_CONNECT_STREAM_NEXT); + vx_send_rih(chip, IRQ_CONNECT_STREAM_NEXT); return; } @@ -1275,6 +1252,7 @@ int snd_vx_pcm_new(struct vx_core *chip) pcm->private_data = chip; pcm->private_free = snd_vx_pcm_free; pcm->info_flags = 0; + pcm->nonatomic = true; strcpy(pcm->name, chip->card->shortname); chip->pcm[i] = pcm; } diff --git a/sound/drivers/vx/vx_uer.c b/sound/drivers/vx/vx_uer.c index b0560fec6bba..ef0b40c0a594 100644 --- a/sound/drivers/vx/vx_uer.c +++ b/sound/drivers/vx/vx_uer.c @@ -60,9 +60,9 @@ static int vx_modify_board_inputs(struct vx_core *chip) */ static int vx_read_one_cbit(struct vx_core *chip, int index) { - unsigned long flags; int val; - spin_lock_irqsave(&chip->lock, flags); + + mutex_lock(&chip->lock); if (chip->type >= VX_TYPE_VXPOCKET) { vx_outb(chip, CSUER, 1); /* read */ vx_outb(chip, RUER, index & XX_UER_CBITS_OFFSET_MASK); @@ -72,7 +72,7 @@ static int vx_read_one_cbit(struct vx_core *chip, int index) vx_outl(chip, RUER, index & XX_UER_CBITS_OFFSET_MASK); val = (vx_inl(chip, RUER) >> 7) & 0x01; } - spin_unlock_irqrestore(&chip->lock, flags); + mutex_unlock(&chip->lock); return val; } @@ -83,9 +83,8 @@ static int vx_read_one_cbit(struct vx_core *chip, int index) */ static void vx_write_one_cbit(struct vx_core *chip, int index, int val) { - unsigned long flags; val = !!val; /* 0 or 1 */ - spin_lock_irqsave(&chip->lock, flags); + mutex_lock(&chip->lock); if (vx_is_pcmcia(chip)) { vx_outb(chip, CSUER, 0); /* write */ vx_outb(chip, RUER, (val << 7) | (index & XX_UER_CBITS_OFFSET_MASK)); @@ -93,7 +92,7 @@ static void vx_write_one_cbit(struct vx_core *chip, int index, int val) vx_outl(chip, CSUER, 0); /* write */ vx_outl(chip, RUER, (val << 7) | (index & XX_UER_CBITS_OFFSET_MASK)); } - spin_unlock_irqrestore(&chip->lock, flags); + mutex_unlock(&chip->lock); } /* @@ -190,14 +189,12 @@ static int vx_calc_clock_from_freq(struct vx_core *chip, int freq) */ static void vx_change_clock_source(struct vx_core *chip, int source) { - unsigned long flags; - /* we mute DAC to prevent clicks */ vx_toggle_dac_mute(chip, 1); - spin_lock_irqsave(&chip->lock, flags); + mutex_lock(&chip->lock); chip->ops->set_clock_source(chip, source); chip->clock_source = source; - spin_unlock_irqrestore(&chip->lock, flags); + mutex_unlock(&chip->lock); /* unmute */ vx_toggle_dac_mute(chip, 0); } @@ -209,11 +206,11 @@ static void vx_change_clock_source(struct vx_core *chip, int source) void vx_set_internal_clock(struct vx_core *chip, unsigned int freq) { int clock; - unsigned long flags; + /* Get real clock value */ clock = vx_calc_clock_from_freq(chip, freq); snd_printdd(KERN_DEBUG "set internal clock to 0x%x from freq %d\n", clock, freq); - spin_lock_irqsave(&chip->lock, flags); + mutex_lock(&chip->lock); if (vx_is_pcmcia(chip)) { vx_outb(chip, HIFREQ, (clock >> 8) & 0x0f); vx_outb(chip, LOFREQ, clock & 0xff); @@ -221,7 +218,7 @@ void vx_set_internal_clock(struct vx_core *chip, unsigned int freq) vx_outl(chip, HIFREQ, (clock >> 8) & 0x0f); vx_outl(chip, LOFREQ, clock & 0xff); } - spin_unlock_irqrestore(&chip->lock, flags); + mutex_unlock(&chip->lock); } diff --git a/sound/pci/vx222/vx222.c b/sound/pci/vx222/vx222.c index 3dc4732142ee..c5a25e39e3a8 100644 --- a/sound/pci/vx222/vx222.c +++ b/sound/pci/vx222/vx222.c @@ -168,8 +168,9 @@ static int snd_vx222_create(struct snd_card *card, struct pci_dev *pci, for (i = 0; i < 2; i++) vx->port[i] = pci_resource_start(pci, i + 1); - if (request_irq(pci->irq, snd_vx_irq_handler, IRQF_SHARED, - KBUILD_MODNAME, chip)) { + if (request_threaded_irq(pci->irq, snd_vx_irq_handler, + snd_vx_threaded_irq_handler, IRQF_SHARED, + KBUILD_MODNAME, chip)) { dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq); snd_vx222_free(chip); return -EBUSY; diff --git a/sound/pcmcia/vx/vxp_ops.c b/sound/pcmcia/vx/vxp_ops.c index fe33e122e372..281972913c32 100644 --- a/sound/pcmcia/vx/vxp_ops.c +++ b/sound/pcmcia/vx/vxp_ops.c @@ -468,12 +468,11 @@ static void vxp_write_codec_reg(struct vx_core *chip, int codec, unsigned int da void vx_set_mic_boost(struct vx_core *chip, int boost) { struct snd_vxpocket *pchip = (struct snd_vxpocket *)chip; - unsigned long flags; if (chip->chip_status & VX_STAT_IS_STALE) return; - spin_lock_irqsave(&chip->lock, flags); + mutex_lock(&chip->lock); if (pchip->regCDSP & P24_CDSP_MICS_SEL_MASK) { if (boost) { /* boost: 38 dB */ @@ -486,7 +485,7 @@ void vx_set_mic_boost(struct vx_core *chip, int boost) } vx_outb(chip, CDSP, pchip->regCDSP); } - spin_unlock_irqrestore(&chip->lock, flags); + mutex_unlock(&chip->lock); } /* @@ -511,17 +510,16 @@ static int vx_compute_mic_level(int level) void vx_set_mic_level(struct vx_core *chip, int level) { struct snd_vxpocket *pchip = (struct snd_vxpocket *)chip; - unsigned long flags; if (chip->chip_status & VX_STAT_IS_STALE) return; - spin_lock_irqsave(&chip->lock, flags); + mutex_lock(&chip->lock); if (pchip->regCDSP & VXP_CDSP_MIC_SEL_MASK) { level = vx_compute_mic_level(level); vx_outb(chip, MICRO, level); } - spin_unlock_irqrestore(&chip->lock, flags); + mutex_unlock(&chip->lock); } diff --git a/sound/pcmcia/vx/vxpocket.c b/sound/pcmcia/vx/vxpocket.c index 786e7e139c9e..92ec11456e3a 100644 --- a/sound/pcmcia/vx/vxpocket.c +++ b/sound/pcmcia/vx/vxpocket.c @@ -62,6 +62,7 @@ static unsigned int card_alloc; */ static void vxpocket_release(struct pcmcia_device *link) { + free_irq(link->irq, link->priv); pcmcia_disable_device(link); } @@ -227,11 +228,13 @@ static int vxpocket_config(struct pcmcia_device *link) ret = pcmcia_request_io(link); if (ret) - goto failed; + goto failed_preirq; - ret = pcmcia_request_irq(link, snd_vx_irq_handler); + ret = request_threaded_irq(link->irq, snd_vx_irq_handler, + snd_vx_threaded_irq_handler, + IRQF_SHARED, link->devname, link->priv); if (ret) - goto failed; + goto failed_preirq; ret = pcmcia_enable_device(link); if (ret) @@ -245,7 +248,9 @@ static int vxpocket_config(struct pcmcia_device *link) return 0; -failed: + failed: + free_irq(link->irq, link->priv); +failed_preirq: pcmcia_disable_device(link); return -ENODEV; } -- cgit v1.3-14-g43fede From e03f73a01f010b29504ceebda3c4fca25468516d Mon Sep 17 00:00:00 2001 From: Anatol Pomozov Date: Wed, 24 Sep 2014 11:17:14 -0700 Subject: ASoC: trace: Remove trailing new-lines in trace messages These new-lines add empty lines to trace output Signed-off-by: Anatol Pomozov Signed-off-by: Mark Brown --- include/trace/events/asoc.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/trace/events/asoc.h b/include/trace/events/asoc.h index 0194a641e4e2..b04ee7e5a466 100644 --- a/include/trace/events/asoc.h +++ b/include/trace/events/asoc.h @@ -175,7 +175,7 @@ TRACE_EVENT(snd_soc_dapm_output_path, __entry->path_sink = (long)path->sink; ), - TP_printk("%c%s -> %s -> %s\n", + TP_printk("%c%s -> %s -> %s", (int) __entry->path_sink && (int) __entry->path_connect ? '*' : ' ', __get_str(wname), __get_str(pname), __get_str(psname)) @@ -204,7 +204,7 @@ TRACE_EVENT(snd_soc_dapm_input_path, __entry->path_source = (long)path->source; ), - TP_printk("%c%s <- %s <- %s\n", + TP_printk("%c%s <- %s <- %s", (int) __entry->path_source && (int) __entry->path_connect ? '*' : ' ', __get_str(wname), __get_str(pname), __get_str(psname)) @@ -226,7 +226,7 @@ TRACE_EVENT(snd_soc_dapm_connected, __entry->stream = stream; ), - TP_printk("%s: found %d paths\n", + TP_printk("%s: found %d paths", __entry->stream ? "capture" : "playback", __entry->paths) ); -- cgit v1.3-14-g43fede From 0bd2ac3dae74ee25c5ea171cb572731c7a89c248 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 29 Sep 2014 11:40:42 +0200 Subject: ASoC: Remove CODEC pointer from snd_soc_dapm_context The only remaining user of the CODEC pointer in the DAPM struct is to initialize the CODEC pointer in the widget struct. The later is scheduled for removal, but has still a few users left. For now use dapm->component->codec to initialize it. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 1 - sound/soc/soc-core.c | 1 - sound/soc/soc-dapm.c | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index aac04ff84eea..d60c61b4b341 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -593,7 +593,6 @@ struct snd_soc_dapm_context { struct device *dev; /* from parent - for debug */ struct snd_soc_component *component; /* parent component */ - struct snd_soc_codec *codec; /* parent codec */ struct snd_soc_card *card; /* parent card */ /* used during DAPM updates */ diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 052f59c1917f..8d45eec141a9 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -4351,7 +4351,6 @@ int snd_soc_register_codec(struct device *dev, if (codec_drv->read) codec->component.read = snd_soc_codec_drv_read; codec->component.ignore_pmdown_time = codec_drv->ignore_pmdown_time; - codec->dapm.codec = codec; codec->dapm.idle_bias_off = codec_drv->idle_bias_off; if (codec_drv->seq_notifier) codec->dapm.seq_notifier = codec_drv->seq_notifier; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 8348352dc2c6..1f1e9657481a 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -3107,7 +3107,7 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, } w->dapm = dapm; - w->codec = dapm->codec; + w->codec = dapm->component->codec; INIT_LIST_HEAD(&w->sources); INIT_LIST_HEAD(&w->sinks); INIT_LIST_HEAD(&w->list); -- cgit v1.3-14-g43fede From 6f67c380056ceaf5844f18d3a5d769d233247849 Mon Sep 17 00:00:00 2001 From: Anatol Pomozov Date: Fri, 26 Sep 2014 09:57:27 -0700 Subject: ASoC: rt5677: Add dts properties for input/output differential configuration Signed-off-by: Anatol Pomozov Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/rt5677.txt | 9 +++++++++ include/sound/rt5677.h | 5 ++++- sound/soc/codecs/rt5677.c | 23 ++++++++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/sound/rt5677.txt b/Documentation/devicetree/bindings/sound/rt5677.txt index bd28df604b7b..0701b834fc73 100644 --- a/Documentation/devicetree/bindings/sound/rt5677.txt +++ b/Documentation/devicetree/bindings/sound/rt5677.txt @@ -19,6 +19,14 @@ Optional properties: - realtek,pow-ldo2-gpio : The GPIO that controls the CODEC's POW_LDO2 pin. +- realtek,in1-differential +- realtek,in2-differential +- realtek,lout1-differential +- realtek,lout2-differential +- realtek,lout3-differential + Boolean. Indicate MIC1/2 input and LOUT1/2/3 outputs are differential, + rather than single-ended. + Pins on the device (for linking into audio routes): * IN1P @@ -47,4 +55,5 @@ rt5677 { realtek,pow-ldo2-gpio = <&gpio TEGRA_GPIO(V, 3) GPIO_ACTIVE_HIGH>; + realtek,in1-differential = "true"; }; diff --git a/include/sound/rt5677.h b/include/sound/rt5677.h index a676717f74f4..082670e3a353 100644 --- a/include/sound/rt5677.h +++ b/include/sound/rt5677.h @@ -19,9 +19,12 @@ enum rt5677_dmic2_clk { struct rt5677_platform_data { - /* IN1 IN2 can optionally be differential */ + /* IN1/IN2/LOUT1/LOUT2/LOUT3 can optionally be differential */ bool in1_diff; bool in2_diff; + bool lout1_diff; + bool lout2_diff; + bool lout3_diff; /* DMIC2 clock source selection */ enum rt5677_dmic2_clk dmic2_clk_pin; }; diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index d2c6abf38ad2..97dff7172fb6 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -3569,6 +3569,17 @@ MODULE_DEVICE_TABLE(i2c, rt5677_i2c_id); static int rt5677_parse_dt(struct rt5677_priv *rt5677, struct device_node *np) { + rt5677->pdata.in1_diff = of_property_read_bool(np, + "realtek,in1-differential"); + rt5677->pdata.in2_diff = of_property_read_bool(np, + "realtek,in2-differential"); + rt5677->pdata.lout1_diff = of_property_read_bool(np, + "realtek,lout1-differential"); + rt5677->pdata.lout2_diff = of_property_read_bool(np, + "realtek,lout2-differential"); + rt5677->pdata.lout3_diff = of_property_read_bool(np, + "realtek,lout3-differential"); + rt5677->pow_ldo2 = of_get_named_gpio(np, "realtek,pow-ldo2-gpio", 0); @@ -3660,6 +3671,18 @@ static int rt5677_i2c_probe(struct i2c_client *i2c, regmap_update_bits(rt5677->regmap, RT5677_IN1, RT5677_IN_DF2, RT5677_IN_DF2); + if (rt5677->pdata.lout1_diff) + regmap_update_bits(rt5677->regmap, RT5677_LOUT1, + RT5677_LOUT1_L_DF, RT5677_LOUT1_L_DF); + + if (rt5677->pdata.lout2_diff) + regmap_update_bits(rt5677->regmap, RT5677_LOUT1, + RT5677_LOUT2_L_DF, RT5677_LOUT2_L_DF); + + if (rt5677->pdata.lout3_diff) + regmap_update_bits(rt5677->regmap, RT5677_LOUT1, + RT5677_LOUT3_L_DF, RT5677_LOUT3_L_DF); + if (rt5677->pdata.dmic2_clk_pin == RT5677_DMIC_CLK2) { regmap_update_bits(rt5677->regmap, RT5677_GEN_CTRL2, RT5677_GPIO5_FUNC_MASK, -- cgit v1.3-14-g43fede From f3fa1bbd836a7d6efb2abd506ed8e24096f39062 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Fri, 19 Sep 2014 19:15:45 +0800 Subject: ASoC: rt5645: Add headset detect function Add headset detect function Signed-off-by: Oder Chiou Signed-off-by: Mark Brown --- include/sound/rt5645.h | 3 ++ sound/soc/codecs/rt5645.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/rt5645.h | 5 +++ 3 files changed, 107 insertions(+) (limited to 'include') diff --git a/include/sound/rt5645.h b/include/sound/rt5645.h index 1de744c242f6..a5352712194b 100644 --- a/include/sound/rt5645.h +++ b/include/sound/rt5645.h @@ -20,6 +20,9 @@ struct rt5645_platform_data { /* 0 = IN2N; 1 = GPIO5; 2 = GPIO11 */ unsigned int dmic2_data_pin; /* 0 = IN2P; 1 = GPIO6; 2 = GPIO10; 3 = GPIO12 */ + + unsigned int hp_det_gpio; + bool gpio_hp_det_active_high; }; #endif diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index a7762d0a623e..3fb83bf09768 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -2103,6 +2104,77 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec, return 0; } +static int rt5645_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *jack) +{ + struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); + int gpio_state, jack_type = 0; + unsigned int val; + + gpio_state = gpio_get_value(rt5645->pdata.hp_det_gpio); + + dev_dbg(codec->dev, "gpio = %d(%d)\n", rt5645->pdata.hp_det_gpio, + gpio_state); + + if ((rt5645->pdata.gpio_hp_det_active_high && gpio_state) || + (!rt5645->pdata.gpio_hp_det_active_high && !gpio_state)) { + snd_soc_dapm_force_enable_pin(&codec->dapm, "micbias1"); + snd_soc_dapm_force_enable_pin(&codec->dapm, "micbias2"); + snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO2"); + snd_soc_dapm_force_enable_pin(&codec->dapm, "Mic Det Power"); + snd_soc_dapm_sync(&codec->dapm); + + snd_soc_write(codec, RT5645_IN1_CTRL1, 0x0006); + snd_soc_write(codec, RT5645_JD_CTRL3, 0x00b0); + + snd_soc_update_bits(codec, RT5645_IN1_CTRL2, + RT5645_CBJ_MN_JD, 0); + snd_soc_update_bits(codec, RT5645_IN1_CTRL2, + RT5645_CBJ_MN_JD, RT5645_CBJ_MN_JD); + + msleep(400); + val = snd_soc_read(codec, RT5645_IN1_CTRL3) & 0x7; + dev_dbg(codec->dev, "val = %d\n", val); + + if (val == 1 || val == 2) + jack_type = SND_JACK_HEADSET; + else + jack_type = SND_JACK_HEADPHONE; + + snd_soc_dapm_disable_pin(&codec->dapm, "micbias1"); + snd_soc_dapm_disable_pin(&codec->dapm, "micbias2"); + snd_soc_dapm_disable_pin(&codec->dapm, "LDO2"); + snd_soc_dapm_disable_pin(&codec->dapm, "Mic Det Power"); + snd_soc_dapm_sync(&codec->dapm); + } + + snd_soc_jack_report(rt5645->jack, jack_type, SND_JACK_HEADSET); + + return 0; +} + +int rt5645_set_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *jack) +{ + struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); + + rt5645->jack = jack; + + rt5645_jack_detect(codec, rt5645->jack); + + return 0; +} +EXPORT_SYMBOL_GPL(rt5645_set_jack_detect); + +static irqreturn_t rt5645_irq(int irq, void *data) +{ + struct rt5645_priv *rt5645 = data; + + rt5645_jack_detect(rt5645->codec, rt5645->jack); + + return IRQ_HANDLED; +} + static int rt5645_probe(struct snd_soc_codec *codec) { struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); @@ -2250,6 +2322,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, if (rt5645 == NULL) return -ENOMEM; + rt5645->i2c = i2c; i2c_set_clientdata(i2c, rt5645); if (pdata) @@ -2345,12 +2418,38 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, } + if (rt5645->i2c->irq) { + ret = request_threaded_irq(rt5645->i2c->irq, NULL, rt5645_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING + | IRQF_ONESHOT, "rt5645", rt5645); + if (ret) + dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); + } + + if (gpio_is_valid(rt5645->pdata.hp_det_gpio)) { + ret = gpio_request(rt5645->pdata.hp_det_gpio, "rt5645"); + if (ret) + dev_err(&i2c->dev, "Fail gpio_request hp_det_gpio\n"); + + ret = gpio_direction_input(rt5645->pdata.hp_det_gpio); + if (ret) + dev_err(&i2c->dev, "Fail gpio_direction hp_det_gpio\n"); + } + return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5645, rt5645_dai, ARRAY_SIZE(rt5645_dai)); } static int rt5645_i2c_remove(struct i2c_client *i2c) { + struct rt5645_priv *rt5645 = i2c_get_clientdata(i2c); + + if (i2c->irq) + free_irq(i2c->irq, rt5645); + + if (gpio_is_valid(rt5645->pdata.hp_det_gpio)) + gpio_free(rt5645->pdata.hp_det_gpio); + snd_soc_unregister_codec(&i2c->dev); return 0; diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h index 355b7e9eefab..50c62c5668ea 100644 --- a/sound/soc/codecs/rt5645.h +++ b/sound/soc/codecs/rt5645.h @@ -2166,6 +2166,8 @@ struct rt5645_priv { struct snd_soc_codec *codec; struct rt5645_platform_data pdata; struct regmap *regmap; + struct i2c_client *i2c; + struct snd_soc_jack *jack; int sysclk; int sysclk_src; @@ -2178,4 +2180,7 @@ struct rt5645_priv { int pll_out; }; +int rt5645_set_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *jack); + #endif /* __RT5645_H__ */ -- cgit v1.3-14-g43fede From 5dc0158a27f65e7efaa6e3cc496d93b4c4c65d19 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Fri, 19 Sep 2014 16:46:05 +0530 Subject: ASoC: Export dapm_kcontrol_get_value The DSP driver needs to know widget control value in its event handler for widgets like mixers. This is required in the subsequent patches Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 1 + sound/soc/soc-dapm.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index aac04ff84eea..6ae0a1952ad7 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -432,6 +432,7 @@ int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm, int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm, const char *pin); void snd_soc_dapm_auto_nc_pins(struct snd_soc_card *card); +unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol); /* Mostly internal - should not normally be used */ void dapm_mark_io_dirty(struct snd_soc_dapm_context *dapm); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 8348352dc2c6..08c79f09034f 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -326,12 +326,13 @@ static struct list_head *dapm_kcontrol_get_path_list( list_for_each_entry(path, dapm_kcontrol_get_path_list(kcontrol), \ list_kcontrol) -static unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol) +unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol) { struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); return data->value; } +EXPORT_SYMBOL_GPL(dapm_kcontrol_get_value); static bool dapm_kcontrol_set_value(const struct snd_kcontrol *kcontrol, unsigned int value) -- cgit v1.3-14-g43fede