Loading sound/soc/codecs/Kconfig +1 −0 Original line number Diff line number Diff line Loading @@ -236,6 +236,7 @@ config SND_SOC_CX20442 tristate config SND_SOC_JZ4740_CODEC select REGMAP_MMIO tristate config SND_SOC_L3 Loading sound/soc/codecs/jz4740.c +59 −83 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/io.h> #include <linux/regmap.h> #include <linux/delay.h> Loading @@ -24,9 +25,10 @@ #include <sound/pcm_params.h> #include <sound/initval.h> #include <sound/soc.h> #include <sound/tlv.h> #define JZ4740_REG_CODEC_1 0x0 #define JZ4740_REG_CODEC_2 0x1 #define JZ4740_REG_CODEC_2 0x4 #define JZ4740_CODEC_1_LINE_ENABLE BIT(29) #define JZ4740_CODEC_1_MIC_ENABLE BIT(28) Loading Loading @@ -67,43 +69,36 @@ #define JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET 4 #define JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET 0 static const uint32_t jz4740_codec_regs[] = { 0x021b2302, 0x00170803, static const struct reg_default jz4740_codec_reg_defaults[] = { { JZ4740_REG_CODEC_1, 0x021b2302 }, { JZ4740_REG_CODEC_2, 0x00170803 }, }; struct jz4740_codec { void __iomem *base; struct resource *mem; struct regmap *regmap; }; static unsigned int jz4740_codec_read(struct snd_soc_codec *codec, unsigned int reg) { struct jz4740_codec *jz4740_codec = snd_soc_codec_get_drvdata(codec); return readl(jz4740_codec->base + (reg << 2)); } static int jz4740_codec_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int val) { struct jz4740_codec *jz4740_codec = snd_soc_codec_get_drvdata(codec); u32 *cache = codec->reg_cache; cache[reg] = val; writel(val, jz4740_codec->base + (reg << 2)); static const unsigned int jz4740_mic_tlv[] = { TLV_DB_RANGE_HEAD(2), 0, 2, TLV_DB_SCALE_ITEM(0, 600, 0), 3, 3, TLV_DB_SCALE_ITEM(2000, 0, 0), }; return 0; } static const DECLARE_TLV_DB_SCALE(jz4740_out_tlv, 0, 200, 0); static const DECLARE_TLV_DB_SCALE(jz4740_in_tlv, -3450, 150, 0); static const struct snd_kcontrol_new jz4740_codec_controls[] = { SOC_SINGLE("Master Playback Volume", JZ4740_REG_CODEC_2, JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET, 3, 0), SOC_SINGLE("Master Capture Volume", JZ4740_REG_CODEC_2, JZ4740_CODEC_2_INPUT_VOLUME_OFFSET, 31, 0), SOC_SINGLE_TLV("Master Playback Volume", JZ4740_REG_CODEC_2, JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET, 3, 0, jz4740_out_tlv), SOC_SINGLE_TLV("Master Capture Volume", JZ4740_REG_CODEC_2, JZ4740_CODEC_2_INPUT_VOLUME_OFFSET, 31, 0, jz4740_in_tlv), SOC_SINGLE("Master Playback Switch", JZ4740_REG_CODEC_1, JZ4740_CODEC_1_HEADPHONE_DISABLE_OFFSET, 1, 1), SOC_SINGLE("Mic Capture Volume", JZ4740_REG_CODEC_2, JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET, 3, 0), SOC_SINGLE_TLV("Mic Capture Volume", JZ4740_REG_CODEC_2, JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET, 3, 0, jz4740_mic_tlv), }; static const struct snd_kcontrol_new jz4740_codec_output_controls[] = { Loading Loading @@ -163,8 +158,8 @@ static const struct snd_soc_dapm_route jz4740_codec_dapm_routes[] = { static int jz4740_codec_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct jz4740_codec *jz4740_codec = snd_soc_codec_get_drvdata(dai->codec); uint32_t val; struct snd_soc_codec *codec = dai->codec; switch (params_rate(params)) { case 8000: Loading Loading @@ -200,7 +195,7 @@ static int jz4740_codec_hw_params(struct snd_pcm_substream *substream, val <<= JZ4740_CODEC_2_SAMPLE_RATE_OFFSET; snd_soc_update_bits(codec, JZ4740_REG_CODEC_2, regmap_update_bits(jz4740_codec->regmap, JZ4740_REG_CODEC_2, JZ4740_CODEC_2_SAMPLE_RATE_MASK, val); return 0; Loading Loading @@ -230,25 +225,23 @@ static struct snd_soc_dai_driver jz4740_codec_dai = { .symmetric_rates = 1, }; static void jz4740_codec_wakeup(struct snd_soc_codec *codec) static void jz4740_codec_wakeup(struct regmap *regmap) { int i; uint32_t *cache = codec->reg_cache; snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, regmap_update_bits(regmap, JZ4740_REG_CODEC_1, JZ4740_CODEC_1_RESET, JZ4740_CODEC_1_RESET); udelay(2); snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, regmap_update_bits(regmap, JZ4740_REG_CODEC_1, JZ4740_CODEC_1_SUSPEND | JZ4740_CODEC_1_RESET, 0); for (i = 0; i < ARRAY_SIZE(jz4740_codec_regs); ++i) jz4740_codec_write(codec, i, cache[i]); regcache_sync(regmap); } static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { struct jz4740_codec *jz4740_codec = snd_soc_codec_get_drvdata(codec); struct regmap *regmap = jz4740_codec->regmap; unsigned int mask; unsigned int value; Loading @@ -261,12 +254,12 @@ static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec, JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M; value = 0; snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, mask, value); regmap_update_bits(regmap, JZ4740_REG_CODEC_1, mask, value); break; case SND_SOC_BIAS_STANDBY: /* The only way to clear the suspend flag is to reset the codec */ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) jz4740_codec_wakeup(codec); jz4740_codec_wakeup(regmap); mask = JZ4740_CODEC_1_VREF_DISABLE | JZ4740_CODEC_1_VREF_AMP_DISABLE | Loading @@ -275,13 +268,14 @@ static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec, JZ4740_CODEC_1_VREF_AMP_DISABLE | JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M; snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, mask, value); regmap_update_bits(regmap, JZ4740_REG_CODEC_1, mask, value); break; case SND_SOC_BIAS_OFF: mask = JZ4740_CODEC_1_SUSPEND; value = JZ4740_CODEC_1_SUSPEND; snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, mask, value); regmap_update_bits(regmap, JZ4740_REG_CODEC_1, mask, value); regcache_mark_dirty(regmap); break; default: break; Loading @@ -294,7 +288,9 @@ static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec, static int jz4740_codec_dev_probe(struct snd_soc_codec *codec) { snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, struct jz4740_codec *jz4740_codec = snd_soc_codec_get_drvdata(codec); regmap_update_bits(jz4740_codec->regmap, JZ4740_REG_CODEC_1, JZ4740_CODEC_1_SW2_ENABLE, JZ4740_CODEC_1_SW2_ENABLE); jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY); Loading Loading @@ -331,12 +327,7 @@ static struct snd_soc_codec_driver soc_codec_dev_jz4740_codec = { .remove = jz4740_codec_dev_remove, .suspend = jz4740_codec_suspend, .resume = jz4740_codec_resume, .read = jz4740_codec_read, .write = jz4740_codec_write, .set_bias_level = jz4740_codec_set_bias_level, .reg_cache_default = jz4740_codec_regs, .reg_word_size = sizeof(u32), .reg_cache_size = 2, .controls = jz4740_codec_controls, .num_controls = ARRAY_SIZE(jz4740_codec_controls), Loading @@ -346,11 +337,23 @@ static struct snd_soc_codec_driver soc_codec_dev_jz4740_codec = { .num_dapm_routes = ARRAY_SIZE(jz4740_codec_dapm_routes), }; static const struct regmap_config jz4740_codec_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, .max_register = JZ4740_REG_CODEC_2, .reg_defaults = jz4740_codec_reg_defaults, .num_reg_defaults = ARRAY_SIZE(jz4740_codec_reg_defaults), .cache_type = REGCACHE_RBTREE, }; static int __devinit jz4740_codec_probe(struct platform_device *pdev) { int ret; struct jz4740_codec *jz4740_codec; struct resource *mem; void __iomem *base; jz4740_codec = devm_kzalloc(&pdev->dev, sizeof(*jz4740_codec), GFP_KERNEL); Loading @@ -358,56 +361,29 @@ static int __devinit jz4740_codec_probe(struct platform_device *pdev) return -ENOMEM; mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mem) { dev_err(&pdev->dev, "Failed to get mmio memory resource\n"); ret = -ENOENT; goto err_out; } mem = request_mem_region(mem->start, resource_size(mem), pdev->name); if (!mem) { dev_err(&pdev->dev, "Failed to request mmio memory region\n"); ret = -EBUSY; goto err_out; } base = devm_request_and_ioremap(&pdev->dev, mem); if (!base) return -EBUSY; jz4740_codec->base = ioremap(mem->start, resource_size(mem)); if (!jz4740_codec->base) { dev_err(&pdev->dev, "Failed to ioremap mmio memory\n"); ret = -EBUSY; goto err_release_mem_region; } jz4740_codec->mem = mem; jz4740_codec->regmap = devm_regmap_init_mmio(&pdev->dev, base, &jz4740_codec_regmap_config); if (IS_ERR(jz4740_codec->regmap)) return PTR_ERR(jz4740_codec->regmap); platform_set_drvdata(pdev, jz4740_codec); ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_jz4740_codec, &jz4740_codec_dai, 1); if (ret) { if (ret) dev_err(&pdev->dev, "Failed to register codec\n"); goto err_iounmap; } return 0; err_iounmap: iounmap(jz4740_codec->base); err_release_mem_region: release_mem_region(mem->start, resource_size(mem)); err_out: return ret; } static int __devexit jz4740_codec_remove(struct platform_device *pdev) { struct jz4740_codec *jz4740_codec = platform_get_drvdata(pdev); struct resource *mem = jz4740_codec->mem; snd_soc_unregister_codec(&pdev->dev); iounmap(jz4740_codec->base); release_mem_region(mem->start, resource_size(mem)); platform_set_drvdata(pdev, NULL); return 0; Loading Loading
sound/soc/codecs/Kconfig +1 −0 Original line number Diff line number Diff line Loading @@ -236,6 +236,7 @@ config SND_SOC_CX20442 tristate config SND_SOC_JZ4740_CODEC select REGMAP_MMIO tristate config SND_SOC_L3 Loading
sound/soc/codecs/jz4740.c +59 −83 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/io.h> #include <linux/regmap.h> #include <linux/delay.h> Loading @@ -24,9 +25,10 @@ #include <sound/pcm_params.h> #include <sound/initval.h> #include <sound/soc.h> #include <sound/tlv.h> #define JZ4740_REG_CODEC_1 0x0 #define JZ4740_REG_CODEC_2 0x1 #define JZ4740_REG_CODEC_2 0x4 #define JZ4740_CODEC_1_LINE_ENABLE BIT(29) #define JZ4740_CODEC_1_MIC_ENABLE BIT(28) Loading Loading @@ -67,43 +69,36 @@ #define JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET 4 #define JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET 0 static const uint32_t jz4740_codec_regs[] = { 0x021b2302, 0x00170803, static const struct reg_default jz4740_codec_reg_defaults[] = { { JZ4740_REG_CODEC_1, 0x021b2302 }, { JZ4740_REG_CODEC_2, 0x00170803 }, }; struct jz4740_codec { void __iomem *base; struct resource *mem; struct regmap *regmap; }; static unsigned int jz4740_codec_read(struct snd_soc_codec *codec, unsigned int reg) { struct jz4740_codec *jz4740_codec = snd_soc_codec_get_drvdata(codec); return readl(jz4740_codec->base + (reg << 2)); } static int jz4740_codec_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int val) { struct jz4740_codec *jz4740_codec = snd_soc_codec_get_drvdata(codec); u32 *cache = codec->reg_cache; cache[reg] = val; writel(val, jz4740_codec->base + (reg << 2)); static const unsigned int jz4740_mic_tlv[] = { TLV_DB_RANGE_HEAD(2), 0, 2, TLV_DB_SCALE_ITEM(0, 600, 0), 3, 3, TLV_DB_SCALE_ITEM(2000, 0, 0), }; return 0; } static const DECLARE_TLV_DB_SCALE(jz4740_out_tlv, 0, 200, 0); static const DECLARE_TLV_DB_SCALE(jz4740_in_tlv, -3450, 150, 0); static const struct snd_kcontrol_new jz4740_codec_controls[] = { SOC_SINGLE("Master Playback Volume", JZ4740_REG_CODEC_2, JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET, 3, 0), SOC_SINGLE("Master Capture Volume", JZ4740_REG_CODEC_2, JZ4740_CODEC_2_INPUT_VOLUME_OFFSET, 31, 0), SOC_SINGLE_TLV("Master Playback Volume", JZ4740_REG_CODEC_2, JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET, 3, 0, jz4740_out_tlv), SOC_SINGLE_TLV("Master Capture Volume", JZ4740_REG_CODEC_2, JZ4740_CODEC_2_INPUT_VOLUME_OFFSET, 31, 0, jz4740_in_tlv), SOC_SINGLE("Master Playback Switch", JZ4740_REG_CODEC_1, JZ4740_CODEC_1_HEADPHONE_DISABLE_OFFSET, 1, 1), SOC_SINGLE("Mic Capture Volume", JZ4740_REG_CODEC_2, JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET, 3, 0), SOC_SINGLE_TLV("Mic Capture Volume", JZ4740_REG_CODEC_2, JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET, 3, 0, jz4740_mic_tlv), }; static const struct snd_kcontrol_new jz4740_codec_output_controls[] = { Loading Loading @@ -163,8 +158,8 @@ static const struct snd_soc_dapm_route jz4740_codec_dapm_routes[] = { static int jz4740_codec_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct jz4740_codec *jz4740_codec = snd_soc_codec_get_drvdata(dai->codec); uint32_t val; struct snd_soc_codec *codec = dai->codec; switch (params_rate(params)) { case 8000: Loading Loading @@ -200,7 +195,7 @@ static int jz4740_codec_hw_params(struct snd_pcm_substream *substream, val <<= JZ4740_CODEC_2_SAMPLE_RATE_OFFSET; snd_soc_update_bits(codec, JZ4740_REG_CODEC_2, regmap_update_bits(jz4740_codec->regmap, JZ4740_REG_CODEC_2, JZ4740_CODEC_2_SAMPLE_RATE_MASK, val); return 0; Loading Loading @@ -230,25 +225,23 @@ static struct snd_soc_dai_driver jz4740_codec_dai = { .symmetric_rates = 1, }; static void jz4740_codec_wakeup(struct snd_soc_codec *codec) static void jz4740_codec_wakeup(struct regmap *regmap) { int i; uint32_t *cache = codec->reg_cache; snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, regmap_update_bits(regmap, JZ4740_REG_CODEC_1, JZ4740_CODEC_1_RESET, JZ4740_CODEC_1_RESET); udelay(2); snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, regmap_update_bits(regmap, JZ4740_REG_CODEC_1, JZ4740_CODEC_1_SUSPEND | JZ4740_CODEC_1_RESET, 0); for (i = 0; i < ARRAY_SIZE(jz4740_codec_regs); ++i) jz4740_codec_write(codec, i, cache[i]); regcache_sync(regmap); } static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { struct jz4740_codec *jz4740_codec = snd_soc_codec_get_drvdata(codec); struct regmap *regmap = jz4740_codec->regmap; unsigned int mask; unsigned int value; Loading @@ -261,12 +254,12 @@ static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec, JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M; value = 0; snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, mask, value); regmap_update_bits(regmap, JZ4740_REG_CODEC_1, mask, value); break; case SND_SOC_BIAS_STANDBY: /* The only way to clear the suspend flag is to reset the codec */ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) jz4740_codec_wakeup(codec); jz4740_codec_wakeup(regmap); mask = JZ4740_CODEC_1_VREF_DISABLE | JZ4740_CODEC_1_VREF_AMP_DISABLE | Loading @@ -275,13 +268,14 @@ static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec, JZ4740_CODEC_1_VREF_AMP_DISABLE | JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M; snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, mask, value); regmap_update_bits(regmap, JZ4740_REG_CODEC_1, mask, value); break; case SND_SOC_BIAS_OFF: mask = JZ4740_CODEC_1_SUSPEND; value = JZ4740_CODEC_1_SUSPEND; snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, mask, value); regmap_update_bits(regmap, JZ4740_REG_CODEC_1, mask, value); regcache_mark_dirty(regmap); break; default: break; Loading @@ -294,7 +288,9 @@ static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec, static int jz4740_codec_dev_probe(struct snd_soc_codec *codec) { snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, struct jz4740_codec *jz4740_codec = snd_soc_codec_get_drvdata(codec); regmap_update_bits(jz4740_codec->regmap, JZ4740_REG_CODEC_1, JZ4740_CODEC_1_SW2_ENABLE, JZ4740_CODEC_1_SW2_ENABLE); jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY); Loading Loading @@ -331,12 +327,7 @@ static struct snd_soc_codec_driver soc_codec_dev_jz4740_codec = { .remove = jz4740_codec_dev_remove, .suspend = jz4740_codec_suspend, .resume = jz4740_codec_resume, .read = jz4740_codec_read, .write = jz4740_codec_write, .set_bias_level = jz4740_codec_set_bias_level, .reg_cache_default = jz4740_codec_regs, .reg_word_size = sizeof(u32), .reg_cache_size = 2, .controls = jz4740_codec_controls, .num_controls = ARRAY_SIZE(jz4740_codec_controls), Loading @@ -346,11 +337,23 @@ static struct snd_soc_codec_driver soc_codec_dev_jz4740_codec = { .num_dapm_routes = ARRAY_SIZE(jz4740_codec_dapm_routes), }; static const struct regmap_config jz4740_codec_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, .max_register = JZ4740_REG_CODEC_2, .reg_defaults = jz4740_codec_reg_defaults, .num_reg_defaults = ARRAY_SIZE(jz4740_codec_reg_defaults), .cache_type = REGCACHE_RBTREE, }; static int __devinit jz4740_codec_probe(struct platform_device *pdev) { int ret; struct jz4740_codec *jz4740_codec; struct resource *mem; void __iomem *base; jz4740_codec = devm_kzalloc(&pdev->dev, sizeof(*jz4740_codec), GFP_KERNEL); Loading @@ -358,56 +361,29 @@ static int __devinit jz4740_codec_probe(struct platform_device *pdev) return -ENOMEM; mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mem) { dev_err(&pdev->dev, "Failed to get mmio memory resource\n"); ret = -ENOENT; goto err_out; } mem = request_mem_region(mem->start, resource_size(mem), pdev->name); if (!mem) { dev_err(&pdev->dev, "Failed to request mmio memory region\n"); ret = -EBUSY; goto err_out; } base = devm_request_and_ioremap(&pdev->dev, mem); if (!base) return -EBUSY; jz4740_codec->base = ioremap(mem->start, resource_size(mem)); if (!jz4740_codec->base) { dev_err(&pdev->dev, "Failed to ioremap mmio memory\n"); ret = -EBUSY; goto err_release_mem_region; } jz4740_codec->mem = mem; jz4740_codec->regmap = devm_regmap_init_mmio(&pdev->dev, base, &jz4740_codec_regmap_config); if (IS_ERR(jz4740_codec->regmap)) return PTR_ERR(jz4740_codec->regmap); platform_set_drvdata(pdev, jz4740_codec); ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_jz4740_codec, &jz4740_codec_dai, 1); if (ret) { if (ret) dev_err(&pdev->dev, "Failed to register codec\n"); goto err_iounmap; } return 0; err_iounmap: iounmap(jz4740_codec->base); err_release_mem_region: release_mem_region(mem->start, resource_size(mem)); err_out: return ret; } static int __devexit jz4740_codec_remove(struct platform_device *pdev) { struct jz4740_codec *jz4740_codec = platform_get_drvdata(pdev); struct resource *mem = jz4740_codec->mem; snd_soc_unregister_codec(&pdev->dev); iounmap(jz4740_codec->base); release_mem_region(mem->start, resource_size(mem)); platform_set_drvdata(pdev, NULL); return 0; Loading