diff options
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/codecs/adau1701.c | 86 | ||||
-rw-r--r-- | sound/soc/codecs/adau1761.c | 22 | ||||
-rw-r--r-- | sound/soc/codecs/adau1781.c | 31 | ||||
-rw-r--r-- | sound/soc/codecs/adau17x1.c | 68 | ||||
-rw-r--r-- | sound/soc/codecs/adau17x1.h | 10 | ||||
-rw-r--r-- | sound/soc/codecs/sigmadsp-i2c.c | 81 | ||||
-rw-r--r-- | sound/soc/codecs/sigmadsp-regmap.c | 46 | ||||
-rw-r--r-- | sound/soc/codecs/sigmadsp.c | 718 | ||||
-rw-r--r-- | sound/soc/codecs/sigmadsp.h | 59 | ||||
-rw-r--r-- | sound/soc/codecs/sirf-audio-codec.c | 6 | ||||
-rw-r--r-- | sound/soc/codecs/sn95031.c | 15 | ||||
-rw-r--r-- | sound/soc/generic/simple-card.c | 3 |
12 files changed, 1000 insertions, 145 deletions
diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c index 370b742117ef..d4e219b6b98f 100644 --- a/sound/soc/codecs/adau1701.c +++ b/sound/soc/codecs/adau1701.c @@ -22,9 +22,14 @@ #include <sound/pcm_params.h> #include <sound/soc.h> +#include <asm/unaligned.h> + #include "sigmadsp.h" #include "adau1701.h" +#define ADAU1701_SAFELOAD_DATA(i) (0x0810 + (i)) +#define ADAU1701_SAFELOAD_ADDR(i) (0x0815 + (i)) + #define ADAU1701_DSPCTRL 0x081c #define ADAU1701_SEROCTL 0x081e #define ADAU1701_SERICTL 0x081f @@ -42,6 +47,7 @@ #define ADAU1701_DSPCTRL_CR (1 << 2) #define ADAU1701_DSPCTRL_DAM (1 << 3) #define ADAU1701_DSPCTRL_ADM (1 << 4) +#define ADAU1701_DSPCTRL_IST (1 << 5) #define ADAU1701_DSPCTRL_SR_48 0x00 #define ADAU1701_DSPCTRL_SR_96 0x01 #define ADAU1701_DSPCTRL_SR_192 0x02 @@ -102,7 +108,10 @@ struct adau1701 { unsigned int pll_clkdiv; unsigned int sysclk; struct regmap *regmap; + struct i2c_client *client; u8 pin_config[12]; + + struct sigmadsp *sigmadsp; }; static const struct snd_kcontrol_new adau1701_controls[] = { @@ -159,6 +168,7 @@ static bool adau1701_volatile_reg(struct device *dev, unsigned int reg) { switch (reg) { case ADAU1701_DACSET: + case ADAU1701_DSPCTRL: return true; default: return false; @@ -238,12 +248,58 @@ static int adau1701_reg_read(void *context, unsigned int reg, return 0; } -static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv) +static int adau1701_safeload(struct sigmadsp *sigmadsp, unsigned int addr, + const uint8_t bytes[], size_t len) +{ + struct i2c_client *client = to_i2c_client(sigmadsp->dev); + struct adau1701 *adau1701 = i2c_get_clientdata(client); + unsigned int val; + unsigned int i; + uint8_t buf[10]; + int ret; + + ret = regmap_read(adau1701->regmap, ADAU1701_DSPCTRL, &val); + if (ret) + return ret; + + if (val & ADAU1701_DSPCTRL_IST) + msleep(50); + + for (i = 0; i < len / 4; i++) { + put_unaligned_le16(ADAU1701_SAFELOAD_DATA(i), buf); + buf[2] = 0x00; + memcpy(buf + 3, bytes + i * 4, 4); + ret = i2c_master_send(client, buf, 7); + if (ret < 0) + return ret; + else if (ret != 7) + return -EIO; + + put_unaligned_le16(ADAU1701_SAFELOAD_ADDR(i), buf); + put_unaligned_le16(addr + i, buf + 2); + ret = i2c_master_send(client, buf, 4); + if (ret < 0) + return ret; + else if (ret != 4) + return -EIO; + } + + return regmap_update_bits(adau1701->regmap, ADAU1701_DSPCTRL, + ADAU1701_DSPCTRL_IST, ADAU1701_DSPCTRL_IST); +} + +static const struct sigmadsp_ops adau1701_sigmadsp_ops = { + .safeload = adau1701_safeload, +}; + +static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv, + unsigned int rate) { struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec); - struct i2c_client *client = to_i2c_client(codec->dev); int ret; + sigmadsp_reset(adau1701->sigmadsp); + if (clkdiv != ADAU1707_CLKDIV_UNSET && gpio_is_valid(adau1701->gpio_pll_mode[0]) && gpio_is_valid(adau1701->gpio_pll_mode[1])) { @@ -284,7 +340,7 @@ static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv) * know the correct PLL setup */ if (clkdiv != ADAU1707_CLKDIV_UNSET) { - ret = process_sigma_firmware(client, ADAU1701_FIRMWARE); + ret = sigmadsp_setup(adau1701->sigmadsp, rate); if (ret) { dev_warn(codec->dev, "Failed to load firmware\n"); return ret; @@ -385,7 +441,7 @@ static int adau1701_hw_params(struct snd_pcm_substream *substream, * firmware upload. */ if (clkdiv != adau1701->pll_clkdiv) { - ret = adau1701_reset(codec, clkdiv); + ret = adau1701_reset(codec, clkdiv, params_rate(params)); if (ret < 0) return ret; } @@ -554,6 +610,14 @@ static int adau1701_set_sysclk(struct snd_soc_codec *codec, int clk_id, return 0; } +static int adau1701_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(dai->codec); + + return sigmadsp_restrict_params(adau1701->sigmadsp, substream); +} + #define ADAU1701_RATES (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | \ SNDRV_PCM_RATE_192000) @@ -564,6 +628,7 @@ static const struct snd_soc_dai_ops adau1701_dai_ops = { .set_fmt = adau1701_set_dai_fmt, .hw_params = adau1701_hw_params, .digital_mute = adau1701_digital_mute, + .startup = adau1701_startup, }; static struct snd_soc_dai_driver adau1701_dai = { @@ -600,6 +665,10 @@ static int adau1701_probe(struct snd_soc_codec *codec) unsigned int val; struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec); + ret = sigmadsp_attach(adau1701->sigmadsp, &codec->component); + if (ret) + return ret; + /* * Let the pll_clkdiv variable default to something that won't happen * at runtime. That way, we can postpone the firmware download from @@ -609,7 +678,7 @@ static int adau1701_probe(struct snd_soc_codec *codec) adau1701->pll_clkdiv = ADAU1707_CLKDIV_UNSET; /* initalize with pre-configured pll mode settings */ - ret = adau1701_reset(codec, adau1701->pll_clkdiv); + ret = adau1701_reset(codec, adau1701->pll_clkdiv, 0); if (ret < 0) return ret; @@ -667,6 +736,7 @@ static int adau1701_i2c_probe(struct i2c_client *client, if (!adau1701) return -ENOMEM; + adau1701->client = client; adau1701->regmap = devm_regmap_init(dev, NULL, client, &adau1701_regmap); if (IS_ERR(adau1701->regmap)) @@ -722,6 +792,12 @@ static int adau1701_i2c_probe(struct i2c_client *client, adau1701->gpio_pll_mode[1] = gpio_pll_mode[1]; i2c_set_clientdata(client, adau1701); + + adau1701->sigmadsp = devm_sigmadsp_init_i2c(client, + &adau1701_sigmadsp_ops, ADAU1701_FIRMWARE); + if (IS_ERR(adau1701->sigmadsp)) + return PTR_ERR(adau1701->sigmadsp); + ret = snd_soc_register_codec(&client->dev, &adau1701_codec_drv, &adau1701_dai, 1); return ret; diff --git a/sound/soc/codecs/adau1761.c b/sound/soc/codecs/adau1761.c index 16093dc89441..a1baeee160f4 100644 --- a/sound/soc/codecs/adau1761.c +++ b/sound/soc/codecs/adau1761.c @@ -703,11 +703,6 @@ static int adau1761_codec_probe(struct snd_soc_codec *codec) ARRAY_SIZE(adau1761_dapm_routes)); if (ret) return ret; - - ret = adau17x1_load_firmware(adau, codec->dev, - ADAU1761_FIRMWARE); - if (ret) - dev_warn(codec->dev, "Failed to firmware\n"); } ret = adau17x1_add_routes(codec); @@ -776,16 +771,20 @@ int adau1761_probe(struct device *dev, struct regmap *regmap, enum adau17x1_type type, void (*switch_mode)(struct device *dev)) { struct snd_soc_dai_driver *dai_drv; + const char *firmware_name; int ret; - ret = adau17x1_probe(dev, regmap, type, switch_mode); - if (ret) - return ret; - - if (type == ADAU1361) + if (type == ADAU1361) { dai_drv = &adau1361_dai_driver; - else + firmware_name = NULL; + } else { dai_drv = &adau1761_dai_driver; + firmware_name = ADAU1761_FIRMWARE; + } + + ret = adau17x1_probe(dev, regmap, type, switch_mode, firmware_name); + if (ret) + return ret; return snd_soc_register_codec(dev, &adau1761_codec_driver, dai_drv, 1); } @@ -799,6 +798,7 @@ const struct regmap_config adau1761_regmap_config = { .num_reg_defaults = ARRAY_SIZE(adau1761_reg_defaults), .readable_reg = adau1761_readable_register, .volatile_reg = adau17x1_volatile_register, + .precious_reg = adau17x1_precious_register, .cache_type = REGCACHE_RBTREE, }; EXPORT_SYMBOL_GPL(adau1761_regmap_config); diff --git a/sound/soc/codecs/adau1781.c b/sound/soc/codecs/adau1781.c index aa6a37cc44b7..35581f43fa6d 100644 --- a/sound/soc/codecs/adau1781.c +++ b/sound/soc/codecs/adau1781.c @@ -385,7 +385,6 @@ static int adau1781_codec_probe(struct snd_soc_codec *codec) { struct adau1781_platform_data *pdata = dev_get_platdata(codec->dev); struct adau *adau = snd_soc_codec_get_drvdata(codec); - const char *firmware; int ret; ret = adau17x1_add_widgets(codec); @@ -422,25 +421,10 @@ static int adau1781_codec_probe(struct snd_soc_codec *codec) return ret; } - switch (adau->type) { - case ADAU1381: - firmware = ADAU1381_FIRMWARE; - break; - case ADAU1781: - firmware = ADAU1781_FIRMWARE; - break; - default: - return -EINVAL; - } - ret = adau17x1_add_routes(codec); if (ret < 0) return ret; - ret = adau17x1_load_firmware(adau, codec->dev, firmware); - if (ret) - dev_warn(codec->dev, "Failed to load firmware\n"); - return 0; } @@ -488,6 +472,7 @@ const struct regmap_config adau1781_regmap_config = { .num_reg_defaults = ARRAY_SIZE(adau1781_reg_defaults), .readable_reg = adau1781_readable_register, .volatile_reg = adau17x1_volatile_register, + .precious_reg = adau17x1_precious_register, .cache_type = REGCACHE_RBTREE, }; EXPORT_SYMBOL_GPL(adau1781_regmap_config); @@ -495,9 +480,21 @@ EXPORT_SYMBOL_GPL(adau1781_regmap_config); int adau1781_probe(struct device *dev, struct regmap *regmap, enum adau17x1_type type, void (*switch_mode)(struct device *dev)) { + const char *firmware_name; int ret; - ret = adau17x1_probe(dev, regmap, type, switch_mode); + switch (type) { + case ADAU1381: + firmware_name = ADAU1381_FIRMWARE; + break; + case ADAU1781: + firmware_name = ADAU1781_FIRMWARE; + break; + default: + return -EINVAL; + } + + ret = adau17x1_probe(dev, regmap, type, switch_mode, firmware_name); if (ret) return ret; diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c index 427ad77bfe56..fa2e690e51c8 100644 --- a/sound/soc/codecs/adau17x1.c +++ b/sound/soc/codecs/adau17x1.c @@ -308,6 +308,7 @@ static int adau17x1_hw_params(struct snd_pcm_substream *substream, struct adau *adau = snd_soc_codec_get_drvdata(codec); unsigned int val, div, dsp_div; unsigned int freq; + int ret; if (adau->clk_src == ADAU17X1_CLK_SRC_PLL) freq = adau->pll_freq; @@ -357,6 +358,12 @@ static int adau17x1_hw_params(struct snd_pcm_substream *substream, regmap_write(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, dsp_div); } + if (adau->sigmadsp) { + ret = adau17x1_setup_firmware(adau, params_rate(params)); + if (ret < 0) + return ret; + } + if (adau->dai_fmt != SND_SOC_DAIFMT_RIGHT_J) return 0; @@ -662,12 +669,24 @@ static int adau17x1_set_dai_tdm_slot(struct snd_soc_dai *dai, return 0; } +static int adau17x1_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct adau *adau = snd_soc_codec_get_drvdata(dai->codec); + + if (adau->sigmadsp) + return sigmadsp_restrict_params(adau->sigmadsp, substream); + + return 0; +} + const struct snd_soc_dai_ops adau17x1_dai_ops = { .hw_params = adau17x1_hw_params, .set_sysclk = adau17x1_set_dai_sysclk, .set_fmt = adau17x1_set_dai_fmt, .set_pll = adau17x1_set_dai_pll, .set_tdm_slot = adau17x1_set_dai_tdm_slot, + .startup = adau17x1_startup, }; EXPORT_SYMBOL_GPL(adau17x1_dai_ops); @@ -688,8 +707,22 @@ int adau17x1_set_micbias_voltage(struct snd_soc_codec *codec, } EXPORT_SYMBOL_GPL(adau17x1_set_micbias_voltage); +bool adau17x1_precious_register(struct device *dev, unsigned int reg) +{ + /* SigmaDSP parameter memory */ + if (reg < 0x400) + return true; + + return false; +} +EXPORT_SYMBOL_GPL(adau17x1_precious_register); + bool adau17x1_readable_register(struct device *dev, unsigned int reg) { + /* SigmaDSP parameter memory */ + if (reg < 0x400) + return true; + switch (reg) { case ADAU17X1_CLOCK_CONTROL: case ADAU17X1_PLL_CONTROL: @@ -746,8 +779,7 @@ bool adau17x1_volatile_register(struct device *dev, unsigned int reg) } EXPORT_SYMBOL_GPL(adau17x1_volatile_register); -int adau17x1_load_firmware(struct adau *adau, struct device *dev, - const char *firmware) +int adau17x1_setup_firmware(struct adau *adau, unsigned int rate) { int ret; int dspsr; @@ -759,7 +791,7 @@ int adau17x1_load_firmware(struct adau *adau, struct device *dev, regmap_write(adau->regmap, ADAU17X1_DSP_ENABLE, 1); regmap_write(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, 0xf); - ret = process_sigma_firmware_regmap(dev, adau->regmap, firmware); + ret = sigmadsp_setup(adau->sigmadsp, rate); if (ret) { regmap_write(adau->regmap, ADAU17X1_DSP_ENABLE, 0); return ret; @@ -768,7 +800,7 @@ int adau17x1_load_firmware(struct adau *adau, struct device *dev, return 0; } -EXPORT_SYMBOL_GPL(adau17x1_load_firmware); +EXPORT_SYMBOL_GPL(adau17x1_setup_firmware); int adau17x1_add_widgets(struct snd_soc_codec *codec) { @@ -788,8 +820,21 @@ int adau17x1_add_widgets(struct snd_soc_codec *codec) ret = snd_soc_dapm_new_controls(&codec->dapm, adau17x1_dsp_dapm_widgets, ARRAY_SIZE(adau17x1_dsp_dapm_widgets)); + if (ret) + return ret; + + if (!adau->sigmadsp) + return 0; + + ret = sigmadsp_attach(adau->sigmadsp, &codec->component); + if (ret) { + dev_err(codec->dev, "Failed to attach firmware: %d\n", + ret); + return ret; + } } - return ret; + + return 0; } EXPORT_SYMBOL_GPL(adau17x1_add_widgets); @@ -830,7 +875,8 @@ int adau17x1_resume(struct snd_soc_codec *codec) EXPORT_SYMBOL_GPL(adau17x1_resume); int adau17x1_probe(struct device *dev, struct regmap *regmap, - enum adau17x1_type type, void (*switch_mode)(struct device *dev)) + enum adau17x1_type type, void (*switch_mode)(struct device *dev), + const char *firmware_name) { struct adau *adau; @@ -847,6 +893,16 @@ int adau17x1_probe(struct device *dev, struct regmap *regmap, dev_set_drvdata(dev, adau); + if (firmware_name) { + adau->sigmadsp = devm_sigmadsp_init_regmap(dev, regmap, NULL, + firmware_name); + if (IS_ERR(adau->sigmadsp)) { + dev_warn(dev, "Could not find firmware file: %ld\n", + PTR_ERR(adau->sigmadsp)); + adau->sigmadsp = NULL; + } + } + if (switch_mode) switch_mode(dev); diff --git a/sound/soc/codecs/adau17x1.h b/sound/soc/codecs/adau17x1.h index e4a557fd7155..e13583e6ff56 100644 --- a/sound/soc/codecs/adau17x1.h +++ b/sound/soc/codecs/adau17x1.h @@ -4,6 +4,8 @@ #include <linux/regmap.h> #include <linux/platform_data/adau17x1.h> +#include "sigmadsp.h" + enum adau17x1_type { ADAU1361, ADAU1761, @@ -42,22 +44,24 @@ struct adau { bool dsp_bypass[2]; struct regmap *regmap; + struct sigmadsp *sigmadsp; }; int adau17x1_add_widgets(struct snd_soc_codec *codec); int adau17x1_add_routes(struct snd_soc_codec *codec); int adau17x1_probe(struct device *dev, struct regmap *regmap, - enum adau17x1_type type, void (*switch_mode)(struct device *dev)); + enum adau17x1_type type, void (*switch_mode)(struct device *dev), + const char *firmware_name); int adau17x1_set_micbias_voltage(struct snd_soc_codec *codec, enum adau17x1_micbias_voltage micbias); bool adau17x1_readable_register(struct device *dev, unsigned int reg); bool adau17x1_volatile_register(struct device *dev, unsigned int reg); +bool adau17x1_precious_register(struct device *dev, unsigned int reg); int adau17x1_resume(struct snd_soc_codec *codec); extern const struct snd_soc_dai_ops adau17x1_dai_ops; -int adau17x1_load_firmware(struct adau *adau, struct device *dev, - const char *firmware); +int adau17x1_setup_firmware(struct adau *adau, unsigned int rate); bool adau17x1_has_dsp(struct adau *adau); #define ADAU17X1_CLOCK_CONTROL 0x4000 diff --git a/sound/soc/codecs/sigmadsp-i2c.c b/sound/soc/codecs/sigmadsp-i2c.c index 246081aae8ca..21ca3a5e9f66 100644 --- a/sound/soc/codecs/sigmadsp-i2c.c +++ b/sound/soc/codecs/sigmadsp-i2c.c @@ -6,29 +6,88 @@ * Licensed under the GPL-2 or later. */ -#include <linux/i2c.h> #include <linux/export.h> +#include <linux/i2c.h> #include <linux/module.h> +#include <linux/slab.h> +#include <asm/unaligned.h> #include "sigmadsp.h" -static int sigma_action_write_i2c(void *control_data, - const struct sigma_action *sa, size_t len) +static int sigmadsp_write_i2c(void *control_data, + unsigned int addr, const uint8_t data[], size_t len) +{ + uint8_t *buf; + int ret; + + buf = kzalloc(2 + len, GFP_KERNEL | GFP_DMA); + if (!buf) + return -ENOMEM; + + put_unaligned_be16(addr, buf); + memcpy(buf + 2, data, len); + + ret = i2c_master_send(control_data, buf, len + 2); + + kfree(buf); + + return ret; +} + +static int sigmadsp_read_i2c(void *control_data, + unsigned int addr, uint8_t data[], size_t len) { - return i2c_master_send(control_data, (const unsigned char *)&sa->addr, - len); + struct i2c_client *client = control_data; + struct i2c_msg msgs[2]; + uint8_t buf[2]; + int ret; + + put_unaligned_be16(addr, buf); + + msgs[0].addr = client->addr; + msgs[0].len = sizeof(buf); + msgs[0].buf = buf; + msgs[0].flags = 0; + + msgs[1].addr = client->addr; + msgs[1].len = len; + msgs[1].buf = data; + msgs[1].flags = I2C_M_RD; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) + return ret; + else if (ret != ARRAY_SIZE(msgs)) + return -EIO; + return 0; } -int process_sigma_firmware(struct i2c_client *client, const char *name) +/** + * devm_sigmadsp_init_i2c() - Initialize SigmaDSP instance + * @client: The parent I2C device + * @ops: The sigmadsp_ops to use for this instance + * @firmware_name: Name of the firmware file to load + * + * Allocates a SigmaDSP instance and loads the specified firmware file. + * + * Returns a pointer to a struct sigmadsp on success, or a PTR_ERR() on error. + */ +struct sigmadsp *devm_sigmadsp_init_i2c(struct i2c_client *client, + const struct sigmadsp_ops *ops, const char *firmware_name) { - struct sigma_firmware ssfw; + struct sigmadsp *sigmadsp; + + sigmadsp = devm_sigmadsp_init(&client->dev, ops, firmware_name); + if (IS_ERR(sigmadsp)) + return sigmadsp; - ssfw.control_data = client; - ssfw.write = sigma_action_write_i2c; + sigmadsp->control_data = client; + sigmadsp->write = sigmadsp_write_i2c; + sigmadsp->read = sigmadsp_read_i2c; - return _process_sigma_firmware(&client->dev, &ssfw, name); + return sigmadsp; } -EXPORT_SYMBOL(process_sigma_firmware); +EXPORT_SYMBOL_GPL(devm_sigmadsp_init_i2c); MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); MODULE_DESCRIPTION("SigmaDSP I2C firmware loader"); diff --git a/sound/soc/codecs/sigmadsp-regmap.c b/sound/soc/codecs/sigmadsp-regmap.c index f78ed8d2cfb2..912861be5b87 100644 --- a/sound/soc/codecs/sigmadsp-regmap.c +++ b/sound/soc/codecs/sigmadsp-regmap.c @@ -12,24 +12,48 @@ #include "sigmadsp.h" -static int sigma_action_write_regmap(void *control_data, - const struct sigma_action *sa, size_t len) +static int sigmadsp_write_regmap(void *control_data, + unsigned int addr, const uint8_t data[], size_t len) { - return regmap_raw_write(control_data, be16_to_cpu(sa->addr), - sa->payload, len - 2); + return regmap_raw_write(control_data, addr, + data, len); } -int process_sigma_firmware_regmap(struct device *dev, struct regmap *regmap, - const char *name) +static int sigmadsp_read_regmap(void *control_data, + unsigned int addr, uint8_t data[], size_t len) { - struct sigma_firmware ssfw; + return regmap_raw_read(control_data, addr, + data, len); +} + +/** + * devm_sigmadsp_init_i2c() - Initialize SigmaDSP instance + * @dev: The parent device + * @regmap: Regmap instance to use + * @ops: The sigmadsp_ops to use for this instance + * @firmware_name: Name of the firmware file to load + * + * Allocates a SigmaDSP instance and loads the specified firmware file. + * + * Returns a pointer to a struct sigmadsp on success, or a PTR_ERR() on error. + */ +struct sigmadsp *devm_sigmadsp_init_regmap(struct device *dev, + struct regmap *regmap, const struct sigmadsp_ops *ops, + const char *firmware_name) +{ + struct sigmadsp *sigmadsp; + + sigmadsp = devm_sigmadsp_init(dev, ops, firmware_name); + if (IS_ERR(sigmadsp)) + return sigmadsp; - ssfw.control_data = regmap; - ssfw.write = sigma_action_write_regmap; + sigmadsp->control_data = regmap; + sigmadsp->write = sigmadsp_write_regmap; + sigmadsp->read = sigmadsp_read_regmap; - return _process_sigma_firmware(dev, &ssfw, name); + return sigmadsp; } -EXPORT_SYMBOL(process_sigma_firmware_regmap); +EXPORT_SYMBOL_GPL(devm_sigmadsp_init_regmap); MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); MODULE_DESCRIPTION("SigmaDSP regmap firmware loader"); diff --git a/sound/soc/codecs/sigmadsp.c b/sound/soc/codecs/sigmadsp.c index 81a38dd9af1f..d53680ac78e4 100644 --- a/sound/soc/codecs/sigmadsp.c +++ b/sound/soc/codecs/sigmadsp.c @@ -1,23 +1,74 @@ /* * Load Analog Devices SigmaStudio firmware files * - * Copyright 2009-2011 Analog Devices Inc. + * Copyright 2009-2014 Analog Devices Inc. * * Licensed under the GPL-2 or later. */ #include <linux/crc32.h> -#include <linux/delay.h> #include <linux/firmware.h> #include <linux/kernel.h> #include <linux/i2c.h> #include <linux/regmap.h> #include <linux/module.h> +#include <linux/slab.h> + +#include <sound/control.h> +#include <sound/soc.h> #include "sigmadsp.h" #define SIGMA_MAGIC "ADISIGM" +#define SIGMA_FW_CHUNK_TYPE_DATA 0 +#define SIGMA_FW_CHUNK_TYPE_CONTROL 1 +#define SIGMA_FW_CHUNK_TYPE_SAMPLERATES 2 + +struct sigmadsp_control { + struct list_head head; + uint32_t samplerates; + unsigned int addr; + unsigned int num_bytes; + const char *name; + struct snd_kcontrol *kcontrol; + bool cached; + uint8_t cache[]; +}; + +struct sigmadsp_data { + struct list_head head; + uint32_t samplerates; + unsigned int addr; + unsigned int length; + uint8_t data[]; +}; + +struct sigma_fw_chunk { + __le32 length; + __le32 tag; + __le32 samplerates; +} __packed; + +struct sigma_fw_chunk_data { + struct sigma_fw_chunk chunk; + __le16 addr; + uint8_t data[]; +} __packed; + +struct sigma_fw_chunk_control { + struct sigma_fw_chunk chunk; + __le16 type; + __le16 addr; + __le16 num_bytes; + const char name[]; +} __packed; + +struct sigma_fw_chunk_samplerate { + struct sigma_fw_chunk chunk; + __le32 samplerates[]; +} __packed; + struct sigma_firmware_header { unsigned char magic[7]; u8 version; @@ -28,12 +79,286 @@ enum { SIGMA_ACTION_WRITEXBYTES = 0, SIGMA_ACTION_WRITESINGLE, SIGMA_ACTION_WRITESAFELOAD, - SIGMA_ACTION_DELAY, - SIGMA_ACTION_PLLWAIT, - SIGMA_ACTION_NOOP, SIGMA_ACTION_END, }; +struct sigma_action { + u8 instr; + u8 len_hi; + __le16 len; + __be16 addr; + unsigned char payload[]; +} __packed; + +static int sigmadsp_write(struct sigmadsp *sigmadsp, unsigned int addr, + const uint8_t data[], size_t len) +{ + return sigmadsp->write(sigmadsp->control_data, addr, data, len); +} + +static int sigmadsp_read(struct sigmadsp *sigmadsp, unsigned int addr, + uint8_t data[], size_t len) +{ + return sigmadsp->read(sigmadsp->control_data, addr, data, len); +} + +static int sigmadsp_ctrl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *info) +{ + struct sigmadsp_control *ctrl = (void *)kcontrol->private_value; + + info->type = SNDRV_CTL_ELEM_TYPE_BYTES; + info->count = ctrl->num_bytes; + + return 0; +} + +static int sigmadsp_ctrl_write(struct sigmadsp *sigmadsp, + struct sigmadsp_control *ctrl, void *data) +{ + /* safeload loads up to 20 bytes in a atomic operation */ + if (ctrl->num_bytes > 4 && ctrl->num_bytes <= 20 && sigmadsp->ops && + sigmadsp->ops->safeload) + return sigmadsp->ops->safeload(sigmadsp, ctrl->addr, data, + ctrl->num_bytes); + else + return sigmadsp_write(sigmadsp, ctrl->addr, data, + ctrl->num_bytes); +} + +static int sigmadsp_ctrl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct sigmadsp_control *ctrl = (void *)kcontrol->private_value; + struct sigmadsp *sigmadsp = snd_kcontrol_chip(kcontrol); + uint8_t *data; + int ret = 0; + + mutex_lock(&sigmadsp->lock); + + data = ucontrol->value.bytes.data; + + if (!(kcontrol->vd[0].access & SNDRV_CTL_ELEM_ACCESS_INACTIVE)) + ret = sigmadsp_ctrl_write(sigmadsp, ctrl, data); + + if (ret == 0) { + memcpy(ctrl->cache, data, ctrl->num_bytes); + ctrl->cached = true; + } + + mutex_unlock(&sigmadsp->lock); + + return ret; +} + +static int sigmadsp_ctrl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct sigmadsp_control *ctrl = (void *)kcontrol->private_value; + struct sigmadsp *sigmadsp = snd_kcontrol_chip(kcontrol); + int ret = 0; + + mutex_lock(&sigmadsp->lock); + + if (!ctrl->cached) { + ret = sigmadsp_read(sigmadsp, ctrl->addr, ctrl->cache, + ctrl->num_bytes); + } + + if (ret == 0) { + ctrl->cached = true; + memcpy(ucontrol->value.bytes.data, ctrl->cache, + ctrl->num_bytes); + } + + mutex_unlock(&sigmadsp->lock); + + return ret; +} + +static void sigmadsp_control_free(struct snd_kcontrol *kcontrol) +{ + struct sigmadsp_control *ctrl = (void *)kcontrol->private_value; + + ctrl->kcontrol = NULL; +} + +static bool sigma_fw_validate_control_name(const char *name, unsigned int len) +{ + unsigned int i; + + for (i = 0; i < len; i++) { + /* Normal ASCII characters are valid */ + if (name[i] < ' ' || name[i] > '~') + return false; + } + + return true; +} + +static int sigma_fw_load_control(struct sigmadsp *sigmadsp, + const struct sigma_fw_chunk *chunk, unsigned int length) +{ + const struct sigma_fw_chunk_control *ctrl_chunk; + struct sigmadsp_control *ctrl; + unsigned int num_bytes; + size_t name_len; + char *name; + int ret; + + if (length <= sizeof(*ctrl_chunk)) + return -EINVAL; + + ctrl_chunk = (const struct sigma_fw_chunk_control *)chunk; + + name_len = length - sizeof(*ctrl_chunk); + if (name_len >= SNDRV_CTL_ELEM_ID_NAME_MAXLEN) + name_len = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - 1; + + /* Make sure there are no non-displayable characaters in the string */ + if (!sigma_fw_validate_control_name(ctrl_chunk->name, name_len)) + return -EINVAL; + + num_bytes = le16_to_cpu(ctrl_chunk->num_bytes); + ctrl = kzalloc(sizeof(*ctrl) + num_bytes, GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + + name = kzalloc(name_len + 1, GFP_KERNEL); + if (!name) { + ret = -ENOMEM; + goto err_free_ctrl; + } + memcpy(name, ctrl_chunk->name, name_len); + name[name_len] = '\0'; + ctrl->name = name; + + ctrl->addr = le16_to_cpu(ctrl_chunk->addr); + ctrl->num_bytes = num_bytes; + ctrl->samplerates = le32_to_cpu(chunk->samplerates); + + list_add_tail(&ctrl->head, &sigmadsp->ctrl_list); + + return 0; + +err_free_ctrl: + kfree(ctrl); + + return ret; +} + +static int sigma_fw_load_data(struct sigmadsp *sigmadsp, + const struct sigma_fw_chunk *chunk, unsigned int length) +{ + const struct sigma_fw_chunk_data *data_chunk; + struct sigmadsp_data *data; + + if (length <= sizeof(*data_chunk)) + return -EINVAL; + + data_chunk = (struct sigma_fw_chunk_data *)chunk; + + length -= sizeof(*data_chunk); + + data = kzalloc(sizeof(*data) + length, GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->addr = le16_to_cpu(data_chunk->addr); + data->length = length; + data->samplerates = le32_to_cpu(chunk->samplerates); + memcpy(data->data, data_chunk->data, length); + list_add_tail(&data->head, &sigmadsp->data_list); + + return 0; +} + +static int sigma_fw_load_samplerates(struct sigmadsp *sigmadsp, + const struct sigma_fw_chunk *chunk, unsigned int length) +{ + const struct sigma_fw_chunk_samplerate *rate_chunk; + unsigned int num_rates; + unsigned int *rates; + unsigned int i; + + rate_chunk = (const struct sigma_fw_chunk_samplerate *)chunk; + + num_rates = (length - sizeof(*rate_chunk)) / sizeof(__le32); + + if (num_rates > 32 || num_rates == 0) + return -EINVAL; + + /* We only allow one samplerates block per file */ + if (sigmadsp->rate_constraints.count) + return -EINVAL; + + rates = kcalloc(num_rates, sizeof(*rates), GFP_KERNEL); + if (!rates) + return -ENOMEM; + + for (i = 0; i < num_rates; i++) + rates[i] = le32_to_cpu(rate_chunk->samplerates[i]); + + sigmadsp->rate_constraints.count = num_rates; + sigmadsp->rate_constraints.list = rates; + + return 0; +} + +static int sigmadsp_fw_load_v2(struct sigmadsp *sigmadsp, + const struct firmware *fw) +{ + struct sigma_fw_chunk *chunk; + unsigned int length, pos; + int ret; + + /* + * Make sure that there is at least one chunk to avoid integer + * underflows later on. Empty firmware is still valid though. + */ + if (fw->size < sizeof(*chunk) + sizeof(struct sigma_firmware_header)) + return 0; + + pos = sizeof(struct sigma_firmware_header); + + while (pos < fw->size - sizeof(*chunk)) { + chunk = (struct sigma_fw_chunk *)(fw->data + pos); + + length = le32_to_cpu(chunk->length); + + if (length > fw->size - pos || length < sizeof(*chunk)) + return -EINVAL; + + switch (le32_to_cpu(chunk->tag)) { + case SIGMA_FW_CHUNK_TYPE_DATA: + ret = sigma_fw_load_data(sigmadsp, chunk, length); + break; + case SIGMA_FW_CHUNK_TYPE_CONTROL: + ret = sigma_fw_load_control(sigmadsp, chunk, length); + break; + case SIGMA_FW_CHUNK_TYPE_SAMPLERATES: + ret = sigma_fw_load_samplerates(sigmadsp, chunk, length); + break; + default: + dev_warn(sigmadsp->dev, "Unknown chunk type: %d\n", + chunk->tag); + ret = 0; + break; + } + + if (ret) + return ret; + + /* + * This can not overflow since if length is larger than the + * maximum firmware size (0x4000000) we'll error out earilier. + */ + pos += ALIGN(length, sizeof(__le32)); + } + + return 0; +} + static inline u32 sigma_action_len(struct sigma_action *sa) { return (sa->len_hi << 16) | le16_to_cpu(sa->len); @@ -62,11 +387,11 @@ static size_t sigma_action_size(struct sigma_action *sa) * Returns a negative error value in case of an error, 0 if processing of * the firmware should be stopped after this action, 1 otherwise. */ -static int -process_sigma_action(struct sigma_firmware *ssfw, struct sigma_action *sa) +static int process_sigma_action(struct sigmadsp *sigmadsp, + struct sigma_action *sa) { size_t len = sigma_action_len(sa); - int ret; + struct sigmadsp_data *data; pr_debug("%s: instr:%i addr:%#x len:%zu\n", __func__, sa->instr, sa->addr, len); @@ -75,13 +400,17 @@ process_sigma_action(struct sigma_firmware *ssfw, struct sigma_action *sa) case SIGMA_ACTION_WRITEXBYTES: case SIGMA_ACTION_WRITESINGLE: case SIGMA_ACTION_WRITESAFELOAD: - ret = ssfw->write(ssfw->control_data, sa, len); - if (ret < 0) + if (len < 3) return -EINVAL; - break; - case SIGMA_ACTION_DELAY: - udelay(len); - len = 0; + + data = kzalloc(sizeof(*data) + len - 2, GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->addr = be16_to_cpu(sa->addr); + data->length = len - 2; + memcpy(data->data, sa->payload, data->length); + list_add_tail(&data->head, &sigmadsp->data_list); break; case SIGMA_ACTION_END: return 0; @@ -92,22 +421,24 @@ process_sigma_action(struct sigma_firmware *ssfw, struct sigma_action *sa) return 1; } -static int -process_sigma_actions(struct sigma_firmware *ssfw) +static int sigmadsp_fw_load_v1(struct sigmadsp *sigmadsp, + const struct firmware *fw) { struct sigma_action *sa; - size_t size; + size_t size, pos; int ret; - while (ssfw->pos + sizeof(*sa) <= ssfw->fw->size) { - sa = (struct sigma_action *)(ssfw->fw->data + ssfw->pos); + pos = sizeof(struct sigma_firmware_header); + + while (pos + sizeof(*sa) <= fw->size) { + sa = (struct sigma_action *)(fw->data + pos); size = sigma_action_size(sa); - ssfw->pos += size; - if (ssfw->pos > ssfw->fw->size || size == 0) + pos += size; + if (pos > fw->size || size == 0) break; - ret = process_sigma_action(ssfw, sa); + ret = process_sigma_action(sigmadsp, sa); pr_debug("%s: action returned %i\n", __func__, ret); @@ -115,29 +446,47 @@ process_sigma_actions(struct sigma_firmware *ssfw) return ret; } - if (ssfw->pos != ssfw->fw->size) + if (pos != fw->size) return -EINVAL; return 0; } -int _process_sigma_firmware(struct device *dev, - struct sigma_firmware *ssfw, const char *name) +static void sigmadsp_firmware_release(struct sigmadsp *sigmadsp) { - int ret; - struct sigma_firmware_header *ssfw_head; + struct sigmadsp_control *ctrl, *_ctrl; + struct sigmadsp_data *data, *_data; + + list_for_each_entry_safe(ctrl, _ctrl, &sigmadsp->ctrl_list, head) { + kfree(ctrl->name); + kfree(ctrl); + } + + list_for_each_entry_safe(data, _data, &sigmadsp->data_list, head) + kfree(data); + + INIT_LIST_HEAD(&sigmadsp->ctrl_list); + INIT_LIST_HEAD(&sigmadsp->data_list); +} + +static void devm_sigmadsp_release(struct device *dev, void *res) +{ + sigmadsp_firmware_release((struct sigmadsp *)res); +} + +static int sigmadsp_firmware_load(struct sigmadsp *sigmadsp, const char *name) +{ + const struct sigma_firmware_header *ssfw_head; const struct firmware *fw; + int ret; u32 crc; - pr_debug("%s: loading firmware %s\n", __func__, name); - /* first load the blob */ - ret = request_firmware(&fw, name, dev); + ret = request_firmware(&fw, name, sigmadsp->dev); if (ret) { pr_debug("%s: request_firmware() failed with %i\n", __func__, ret); - return ret; + goto done; } - ssfw->fw = fw; /* then verify the header */ ret = -EINVAL; @@ -149,20 +498,13 @@ int _process_sigma_firmware(struct device *dev, * overflows later in the loading process. */ if (fw->size < sizeof(*ssfw_head) || fw->size >= 0x4000000) { - dev_err(dev, "Failed to load firmware: Invalid size\n"); + dev_err(sigmadsp->dev, "Failed to load firmware: Invalid size\n"); goto done; } ssfw_head = (void *)fw->data; if (memcmp(ssfw_head->magic, SIGMA_MAGIC, ARRAY_SIZE(ssfw_head->magic))) { - dev_err(dev, "Failed to load firmware: Invalid magic\n"); - goto done; - } - - if (ssfw_head->version != 1) { - dev_err(dev, - "Failed to load firmware: Invalid version %d. Supported firmware versions: 1\n", - ssfw_head->version); + dev_err(sigmadsp->dev, "Failed to load firmware: Invalid magic\n"); goto done; } @@ -170,23 +512,303 @@ int _process_sigma_firmware(struct device *dev, fw->size - sizeof(*ssfw_head)); pr_debug("%s: crc=%x\n", __func__, crc); if (crc != le32_to_cpu(ssfw_head->crc)) { - dev_err(dev, "Failed to load firmware: Wrong crc checksum: expected %x got %x\n", + dev_err(sigmadsp->dev, "Failed to load firmware: Wrong crc checksum: expected %x got %x\n", le32_to_cpu(ssfw_head->crc), crc); goto done; } - ssfw->pos = sizeof(*ssfw_head); + switch (ssfw_head->version) { + case 1: + ret = sigmadsp_fw_load_v1(sigmadsp, fw); + break; + case 2: + ret = sigmadsp_fw_load_v2(sigmadsp, fw); + break; + default: + dev_err(sigmadsp->dev, + "Failed to load firmware: Invalid version %d. Supported firmware versions: 1, 2\n", + ssfw_head->version); + ret = -EINVAL; + break; + } - /* finally process all of the actions */ - ret = process_sigma_actions(ssfw); + if (ret) + sigmadsp_firmware_release(sigmadsp); - done: +done: release_firmware(fw); - pr_debug("%s: loaded %s\n", __func__, name); + return ret; +} + +static int sigmadsp_init(struct sigmadsp *sigmadsp, struct device *dev, + const struct sigmadsp_ops *ops, const char *firmware_name) +{ + sigmadsp->ops = ops; + sigmadsp->dev = dev; + + INIT_LIST_HEAD(&sigmadsp->ctrl_list); + INIT_LIST_HEAD(&sigmadsp->data_list); + mutex_init(&sigmadsp->lock); + + return sigmadsp_firmware_load(sigmadsp, firmware_name); +} + +/** + * devm_sigmadsp_init() - Initialize SigmaDSP instance + * @dev: The parent device + * @ops: The sigmadsp_ops to use for this instance + * @firmware_name: Name of the firmware file to load + * + * Allocates a SigmaDSP instance and loads the specified firmware file. + * + * Returns a pointer to a struct sigmadsp on success, or a PTR_ERR() on error. + */ +struct sigmadsp *devm_sigmadsp_init(struct device *dev, + const struct sigmadsp_ops *ops, const char *firmware_name) +{ + struct sigmadsp *sigmadsp; + int ret; + + sigmadsp = devres_alloc(devm_sigmadsp_release, sizeof(*sigmadsp), + GFP_KERNEL); + if (!sigmadsp) + return ERR_PTR(-ENOMEM); + + ret = sigmadsp_init(sigmadsp, dev, ops, firmware_name); + if (ret) { + devres_free(sigmadsp); + return ERR_PTR(ret); + } + + devres_add(dev, sigmadsp); + + return sigmadsp; +} +EXPORT_SYMBOL_GPL(devm_sigmadsp_init); + +static int sigmadsp_rate_to_index(struct sigmadsp *sigmadsp, unsigned int rate) +{ + unsigned int i; + + for (i = 0; i < sigmadsp->rate_constraints.count; i++) { + if (sigmadsp->rate_constraints.list[i] == rate) + return i; + } + + return -EINVAL; +} + +static unsigned int sigmadsp_get_samplerate_mask(struct sigmadsp *sigmadsp, + unsigned int samplerate) +{ + int samplerate_index; + + if (samplerate == 0) + return 0; + + if (sigmadsp->rate_constraints.count) { + samplerate_index = sigmadsp_rate_to_index(sigmadsp, samplerate); + if (samplerate_index < 0) + return 0; + + return BIT(samplerate_index); + } else { + return ~0; + } +} + +static bool sigmadsp_samplerate_valid(unsigned int supported, + unsigned int requested) +{ + /* All samplerates are supported */ + if (!supported) + return true; + + return supported & requested; +} + +static int sigmadsp_alloc_control(struct sigmadsp *sigmadsp, + struct sigmadsp_control *ctrl, unsigned int samplerate_mask) +{ + struct snd_kcontrol_new template; + struct snd_kcontrol *kcontrol; + + memset(&template, 0, sizeof(template)); + template.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + template.name = ctrl->name; + template.info = sigmadsp_ctrl_info; + template.get = sigmadsp_ctrl_get; + template.put = sigmadsp_ctrl_put; + template.private_value = (unsigned long)ctrl; + template.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; + if (!sigmadsp_samplerate_valid(ctrl->samplerates, samplerate_mask)) + template.access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; + + kcontrol = snd_ctl_new1(&template, sigmadsp); + if (!kcontrol) + return -ENOMEM; + + kcontrol->private_free = sigmadsp_control_free; + ctrl->kcontrol = kcontrol; + + return snd_ctl_add(sigmadsp->component->card->snd_card, kcontrol); +} + +static void sigmadsp_activate_ctrl(struct sigmadsp *sigmadsp, + struct sigmadsp_control *ctrl, unsigned int samplerate_mask) +{ + struct snd_card *card = sigmadsp->component->card->snd_card; + struct snd_kcontrol_volatile *vd; + struct snd_ctl_elem_id id; + bool active; + bool changed = false; + + active = sigmadsp_samplerate_valid(ctrl->samplerates, samplerate_mask); + + down_write(&card->controls_rwsem); + if (!ctrl->kcontrol) { + up_write(&card->controls_rwsem); + return; + } + + id = ctrl->kcontrol->id; + vd = &ctrl->kcontrol->vd[0]; + if (active == (bool)(vd->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE)) { + vd->access ^= SNDRV_CTL_ELEM_ACCESS_INACTIVE; + changed = true; + } + up_write(&card->controls_rwsem); + + if (active && changed) { + mutex_lock(&sigmadsp->lock); + if (ctrl->cached) + sigmadsp_ctrl_write(sigmadsp, ctrl, ctrl->cache); + mutex_unlock(&sigmadsp->lock); + } + + if (changed) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, &id); +} + +/** + * sigmadsp_attach() - Attach a sigmadsp instance to a ASoC component + * @sigmadsp: The sigmadsp instance to attach + * @component: The component to attach to + * + * Typically called in the components probe callback. + * + * Note, once this function has been called the firmware must not be released + * until after the ALSA snd_card that the component belongs to has been + * disconnected, even if sigmadsp_attach() returns an error. + */ +int sigmadsp_attach(struct sigmadsp *sigmadsp, + struct snd_soc_component *component) +{ + struct sigmadsp_control *ctrl; + unsigned int samplerate_mask; + int ret; + + sigmadsp->component = component; + + samplerate_mask = sigmadsp_get_samplerate_mask(sigmadsp, + sigmadsp->current_samplerate); + + list_for_each_entry(ctrl, &sigmadsp->ctrl_list, head) { + ret = sigmadsp_alloc_control(sigmadsp, ctrl, samplerate_mask); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(sigmadsp_attach); + +/** + * sigmadsp_setup() - Setup the DSP for the specified samplerate + * @sigmadsp: The sigmadsp instance to configure + * @samplerate: The samplerate the DSP should be configured for + * + * Loads the appropriate firmware program and parameter memory (if not already + * loaded) and enables the controls for the specified samplerate. Any control + * parameter changes that have been made previously will be restored. + * + * Returns 0 on success, a negative error code otherwise. + */ +int sigmadsp_setup(struct sigmadsp *sigmadsp, unsigned int samplerate) +{ + struct sigmadsp_control *ctrl; + unsigned int samplerate_mask; + struct sigmadsp_data *data; + int ret; + + if (sigmadsp->current_samplerate == samplerate) + return 0; + + samplerate_mask = sigmadsp_get_samplerate_mask(sigmadsp, samplerate); + if (samplerate_mask == 0) + return -EINVAL; + + list_for_each_entry(data, &sigmadsp->data_list, head) { + if (!sigmadsp_samplerate_valid(data->samplerates, + samplerate_mask)) + continue; + ret = sigmadsp_write(sigmadsp, data->addr, data->data, + data->length); + if (ret) + goto err; + } + + list_for_each_entry(ctrl, &sigmadsp->ctrl_list, head) + sigmadsp_activate_ctrl(sigmadsp, ctrl, samplerate_mask); + + sigmadsp->current_samplerate = samplerate; + + return 0; +err: + sigmadsp_reset(sigmadsp); return ret; } -EXPORT_SYMBOL_GPL(_process_sigma_firmware); +EXPORT_SYMBOL_GPL(sigmadsp_setup); + +/** + * sigmadsp_reset() - Notify the sigmadsp instance that the DSP has been reset + * @sigmadsp: The sigmadsp instance to reset + * + * Should be called whenever the DSP has been reset and parameter and program + * memory need to be re-loaded. + */ +void sigmadsp_reset(struct sigmadsp *sigmadsp) +{ + struct sigmadsp_control *ctrl; + + list_for_each_entry(ctrl, &sigmadsp->ctrl_list, head) + sigmadsp_activate_ctrl(sigmadsp, ctrl, false); + + sigmadsp->current_samplerate = 0; +} +EXPORT_SYMBOL_GPL(sigmadsp_reset); + +/** + * sigmadsp_restrict_params() - Applies DSP firmware specific constraints + * @sigmadsp: The sigmadsp instance + * @substream: The substream to restrict + * + * Applies samplerate constraints that may be required by the firmware Should + * typically be called from the CODEC/component drivers startup callback. + * + * Returns 0 on success, a negative error code otherwise. + */ +int sigmadsp_restrict_params(struct sigmadsp *sigmadsp, + struct snd_pcm_substream *substream) +{ + if (sigmadsp->rate_constraints.count == 0) + return 0; + + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &sigmadsp->rate_constraints); +} +EXPORT_SYMBOL_GPL(sigmadsp_restrict_params); MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/sigmadsp.h b/sound/soc/codecs/sigmadsp.h index c47cd23e9827..614475cbb823 100644 --- a/sound/soc/codecs/sigmadsp.h +++ b/sound/soc/codecs/sigmadsp.h @@ -11,31 +11,56 @@ #include <linux/device.h> #include <linux/regmap.h> +#include <linux/list.h> -struct sigma_action { - u8 instr; - u8 len_hi; - __le16 len; - __be16 addr; - unsigned char payload[]; -} __packed; +#include <sound/pcm.h> -struct sigma_firmware { - const struct firmware *fw; - size_t pos; +struct sigmadsp; +struct snd_soc_component; +struct snd_pcm_substream; + +struct sigmadsp_ops { + int (*safeload)(struct sigmadsp *sigmadsp, unsigned int addr, + const uint8_t *data, size_t len); +}; + +struct sigmadsp { + const struct sigmadsp_ops *ops; + + struct list_head ctrl_list; + struct list_head data_list; + + struct snd_pcm_hw_constraint_list rate_constraints; + + unsigned int current_samplerate; + struct snd_soc_component *component; + struct device *dev; + + struct mutex lock; void *control_data; - int (*write)(void *control_data, const struct sigma_action *sa, - size_t len); + int (*write)(void *, unsigned int, const uint8_t *, size_t); + int (*read)(void *, unsigned int, uint8_t *, size_t); }; -int _process_sigma_firmware(struct device *dev, - struct sigma_firmware *ssfw, const char *name); +struct sigmadsp *devm_sigmadsp_init(struct device *dev, + const struct sigmadsp_ops *ops, const char *firmware_name); +void sigmadsp_reset(struct sigmadsp *sigmadsp); + +int sigmadsp_restrict_params(struct sigmadsp *sigmadsp, + struct snd_pcm_substream *substream); struct i2c_client; -extern int process_sigma_firmware(struct i2c_client *client, const char *name); -extern int process_sigma_firmware_regmap(struct device *dev, - struct regmap *regmap, const char *name); +struct sigmadsp *devm_sigmadsp_init_regmap(struct device *dev, + struct regmap *regmap, const struct sigmadsp_ops *ops, + const char *firmware_name); +struct sigmadsp *devm_sigmadsp_init_i2c(struct i2c_client *client, + const struct sigmadsp_ops *ops, const char *firmware_name); + +int sigmadsp_attach(struct sigmadsp *sigmadsp, + struct snd_soc_component *component); +int sigmadsp_setup(struct sigmadsp *sigmadsp, unsigned int rate); +void sigmadsp_reset(struct sigmadsp *sigmadsp); #endif diff --git a/sound/soc/codecs/sirf-audio-codec.c b/sound/soc/codecs/sirf-audio-codec.c index 06ba4923fd5a..07eea20e6645 100644 --- a/sound/soc/codecs/sirf-audio-codec.c +++ b/sound/soc/codecs/sirf-audio-codec.c @@ -120,7 +120,8 @@ static int atlas6_codec_enable_and_reset_event(struct snd_soc_dapm_widget *w, { #define ATLAS6_CODEC_ENABLE_BITS (1 << 29) #define ATLAS6_CODEC_RESET_BITS (1 << 28) - struct sirf_audio_codec *sirf_audio_codec = dev_get_drvdata(w->codec->dev); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct sirf_audio_codec *sirf_audio_codec = snd_soc_codec_get_drvdata(codec); switch (event) { case SND_SOC_DAPM_PRE_PMU: enable_and_reset_codec(sirf_audio_codec->regmap, @@ -142,7 +143,8 @@ static int prima2_codec_enable_and_reset_event(struct snd_soc_dapm_widget *w, { #define PRIMA2_CODEC_ENABLE_BITS (1 << 27) #define PRIMA2_CODEC_RESET_BITS (1 << 26) - struct sirf_audio_codec *sirf_audio_codec = dev_get_drvdata(w->codec->dev); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct sirf_audio_codec *sirf_audio_codec = snd_soc_codec_get_drvdata(codec); switch (event) { case SND_SOC_DAPM_POST_PMU: enable_and_reset_codec(sirf_audio_codec->regmap, diff --git a/sound/soc/codecs/sn95031.c b/sound/soc/codecs/sn95031.c index cf8fa40662f0..31d97cd5e59b 100644 --- a/sound/soc/codecs/sn95031.c +++ b/sound/soc/codecs/sn95031.c @@ -867,25 +867,16 @@ static int sn95031_codec_probe(struct snd_soc_codec *codec) snd_soc_write(codec, SN95031_SSR2, 0x10); snd_soc_write(codec, SN95031_SSR3, 0x40); - snd_soc_add_codec_controls(codec, sn95031_snd_controls, - ARRAY_SIZE(sn95031_snd_controls)); - - return 0; -} - -static int sn95031_codec_remove(struct snd_soc_codec *codec) -{ - pr_debug("codec_remove called\n"); - sn95031_set_vaud_bias(codec, SND_SOC_BIAS_OFF); - return 0; } static struct snd_soc_codec_driver sn95031_codec = { .probe = sn95031_codec_probe, - .remove = sn95031_codec_remove, .set_bias_level = sn95031_set_vaud_bias, .idle_bias_off = true, + + .controls = sn95031_snd_controls, + .num_controls = ARRAY_SIZE(sn95031_snd_controls), .dapm_widgets = sn95031_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sn95031_dapm_widgets), .dapm_routes = sn95031_audio_map, diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 3e3fec4e5c4e..ece22d55ba82 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -232,7 +232,6 @@ asoc_simple_card_sub_parse_of(struct device_node *np, static int asoc_simple_card_parse_daifmt(struct device_node *node, struct simple_card_data *priv, - struct device_node *cpu, struct device_node *codec, char *prefix, int idx) { @@ -309,7 +308,7 @@ static int asoc_simple_card_dai_link_of(struct device_node *node, } ret = asoc_simple_card_parse_daifmt(node, priv, - cpu, codec, prefix, idx); + codec, prefix, idx); if (ret < 0) goto dai_link_of_err; |