diff options
Diffstat (limited to 'sound/soc/codecs/rt1308-sdw.c')
| -rw-r--r-- | sound/soc/codecs/rt1308-sdw.c | 273 |
1 files changed, 169 insertions, 104 deletions
diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c index 149a76075c76..b6c224832a43 100644 --- a/sound/soc/codecs/rt1308-sdw.c +++ b/sound/soc/codecs/rt1308-sdw.c @@ -17,6 +17,7 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> +#include <sound/sdw.h> #include <sound/soc.h> #include <sound/soc-dapm.h> #include <sound/initval.h> @@ -50,6 +51,10 @@ static bool rt1308_volatile_register(struct device *dev, unsigned int reg) case 0x3008: case 0x300a: case 0xc000: + case 0xc710: + case 0xcf01: + case 0xc860 ... 0xc863: + case 0xc870 ... 0xc873: return true; default: return false; @@ -64,7 +69,7 @@ static const struct regmap_config rt1308_sdw_regmap = { .max_register = 0xcfff, .reg_defaults = rt1308_reg_defaults, .num_reg_defaults = ARRAY_SIZE(rt1308_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -159,46 +164,12 @@ static int rt1308_read_prop(struct sdw_slave *slave) return 0; } -static int rt1308_io_init(struct device *dev, struct sdw_slave *slave) +static void rt1308_apply_calib_params(struct rt1308_sdw_priv *rt1308) { - struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(dev); - int ret = 0; unsigned int efuse_m_btl_l, efuse_m_btl_r, tmp; unsigned int efuse_c_btl_l, efuse_c_btl_r; - if (rt1308->hw_init) - return 0; - - if (rt1308->first_hw_init) { - regcache_cache_only(rt1308->regmap, false); - regcache_cache_bypass(rt1308->regmap, true); - } - - /* - * PM runtime is only enabled when a Slave reports as Attached - */ - if (!rt1308->first_hw_init) { - /* set autosuspend parameters */ - pm_runtime_set_autosuspend_delay(&slave->dev, 3000); - pm_runtime_use_autosuspend(&slave->dev); - - /* update count of parent 'active' children */ - pm_runtime_set_active(&slave->dev); - - /* make sure the device does not suspend immediately */ - pm_runtime_mark_last_busy(&slave->dev); - - pm_runtime_enable(&slave->dev); - } - - pm_runtime_get_noresume(&slave->dev); - - /* sw reset */ - regmap_write(rt1308->regmap, RT1308_SDW_RESET, 0); - - /* read efuse */ - regmap_write(rt1308->regmap, 0xc360, 0x01); - regmap_write(rt1308->regmap, 0xc361, 0x80); + /* read efuse to apply calibration parameters */ regmap_write(rt1308->regmap, 0xc7f0, 0x04); regmap_write(rt1308->regmap, 0xc7f1, 0xfe); msleep(100); @@ -222,10 +193,55 @@ static int rt1308_io_init(struct device *dev, struct sdw_slave *slave) efuse_c_btl_r = tmp; regmap_read(rt1308->regmap, 0xc872, &tmp); efuse_c_btl_r = efuse_c_btl_r | (tmp << 8); - dev_dbg(&slave->dev, "%s m_btl_l=0x%x, m_btl_r=0x%x\n", __func__, + dev_dbg(&rt1308->sdw_slave->dev, "%s m_btl_l=0x%x, m_btl_r=0x%x\n", __func__, efuse_m_btl_l, efuse_m_btl_r); - dev_dbg(&slave->dev, "%s c_btl_l=0x%x, c_btl_r=0x%x\n", __func__, + dev_dbg(&rt1308->sdw_slave->dev, "%s c_btl_l=0x%x, c_btl_r=0x%x\n", __func__, efuse_c_btl_l, efuse_c_btl_r); +} + +static void rt1308_apply_bq_params(struct rt1308_sdw_priv *rt1308) +{ + unsigned int i, reg, data; + + for (i = 0; i < rt1308->bq_params_cnt; i += 3) { + reg = rt1308->bq_params[i] | (rt1308->bq_params[i + 1] << 8); + data = rt1308->bq_params[i + 2]; + regmap_write(rt1308->regmap, reg, data); + } +} + +static int rt1308_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(dev); + int ret = 0; + unsigned int tmp, hibernation_flag; + + if (rt1308->hw_init) + return 0; + + regcache_cache_only(rt1308->regmap, false); + if (rt1308->first_hw_init) + regcache_cache_bypass(rt1308->regmap, true); + + /* + * PM runtime status is marked as 'active' only when a Slave reports as Attached + */ + if (!rt1308->first_hw_init) + /* update count of parent 'active' children */ + pm_runtime_set_active(&slave->dev); + + pm_runtime_get_noresume(&slave->dev); + + regmap_read(rt1308->regmap, 0xcf01, &hibernation_flag); + if ((hibernation_flag != 0x00) && rt1308->first_hw_init) + goto _preset_ready_; + + /* sw reset */ + regmap_write(rt1308->regmap, RT1308_SDW_RESET, 0); + + regmap_read(rt1308->regmap, 0xc710, &tmp); + rt1308->hw_ver = tmp; + dev_dbg(dev, "%s, hw_ver=0x%x\n", __func__, rt1308->hw_ver); /* initial settings */ regmap_write(rt1308->regmap, 0xc103, 0xc0); @@ -242,8 +258,14 @@ static int rt1308_io_init(struct device *dev, struct sdw_slave *slave) regmap_write(rt1308->regmap, 0xc062, 0x05); regmap_write(rt1308->regmap, 0xc171, 0x07); regmap_write(rt1308->regmap, 0xc173, 0x0d); - regmap_write(rt1308->regmap, 0xc311, 0x7f); - regmap_write(rt1308->regmap, 0xc900, 0x90); + if (rt1308->hw_ver == RT1308_VER_C) { + regmap_write(rt1308->regmap, 0xc311, 0x7f); + regmap_write(rt1308->regmap, 0xc300, 0x09); + } else { + regmap_write(rt1308->regmap, 0xc311, 0x4f); + regmap_write(rt1308->regmap, 0xc300, 0x0b); + } + regmap_write(rt1308->regmap, 0xc900, 0x5a); regmap_write(rt1308->regmap, 0xc1a0, 0x84); regmap_write(rt1308->regmap, 0xc1a1, 0x01); regmap_write(rt1308->regmap, 0xc360, 0x78); @@ -253,8 +275,13 @@ static int rt1308_io_init(struct device *dev, struct sdw_slave *slave) regmap_write(rt1308->regmap, 0xc070, 0x00); regmap_write(rt1308->regmap, 0xc100, 0xd7); regmap_write(rt1308->regmap, 0xc101, 0xd7); - regmap_write(rt1308->regmap, 0xc300, 0x09); + /* apply BQ params */ + rt1308_apply_bq_params(rt1308); + + regmap_write(rt1308->regmap, 0xcf01, 0x01); + +_preset_ready_: if (rt1308->first_hw_init) { regcache_cache_bypass(rt1308->regmap, false); regcache_mark_dirty(rt1308->regmap); @@ -264,7 +291,6 @@ static int rt1308_io_init(struct device *dev, struct sdw_slave *slave) /* Mark Slave initialization complete */ rt1308->hw_init = true; - pm_runtime_mark_last_busy(&slave->dev); pm_runtime_put_autosuspend(&slave->dev); dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); @@ -277,9 +303,6 @@ static int rt1308_update_status(struct sdw_slave *slave, { struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(&slave->dev); - /* Update the status */ - rt1308->status = status; - if (status == SDW_SLAVE_UNATTACHED) rt1308->hw_init = false; @@ -287,7 +310,7 @@ static int rt1308_update_status(struct sdw_slave *slave, * Perform initialization only if slave status is present and * hw_init flag is false */ - if (rt1308->hw_init || rt1308->status != SDW_SLAVE_ATTACHED) + if (rt1308->hw_init || status != SDW_SLAVE_ATTACHED) return 0; /* perform I/O transfers required for Slave initialization */ @@ -323,6 +346,8 @@ static int rt1308_classd_event(struct snd_soc_dapm_widget *w, { struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt1308_sdw_priv *rt1308 = + snd_soc_component_get_drvdata(component); switch (event) { case SND_SOC_DAPM_POST_PMU: @@ -331,6 +356,7 @@ static int rt1308_classd_event(struct snd_soc_dapm_widget *w, RT1308_SDW_OFFSET | (RT1308_POWER_STATUS << 4), 0x3, 0x3); msleep(40); + rt1308_apply_calib_params(rt1308); break; case SND_SOC_DAPM_PRE_PMD: snd_soc_component_update_bits(component, @@ -466,22 +492,7 @@ static const struct snd_soc_dapm_route rt1308_dapm_routes[] = { static int rt1308_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, int direction) { - struct sdw_stream_data *stream; - - if (!sdw_stream) - return 0; - - stream = kzalloc(sizeof(*stream), GFP_KERNEL); - if (!stream) - return -ENOMEM; - - stream->sdw_stream = sdw_stream; - - /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ - if (direction == SNDRV_PCM_STREAM_PLAYBACK) - dai->playback_dma_data = stream; - else - dai->capture_dma_data = stream; + snd_soc_dai_dma_data_set(dai, direction, sdw_stream); return 0; } @@ -489,11 +500,7 @@ static int rt1308_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, static void rt1308_sdw_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct sdw_stream_data *stream; - - stream = snd_soc_dai_get_dma_data(dai, substream); snd_soc_dai_set_dma_data(dai, substream, NULL); - kfree(stream); } static int rt1308_sdw_set_tdm_slot(struct snd_soc_dai *dai, @@ -524,48 +531,36 @@ static int rt1308_sdw_hw_params(struct snd_pcm_substream *substream, struct snd_soc_component *component = dai->component; struct rt1308_sdw_priv *rt1308 = snd_soc_component_get_drvdata(component); - struct sdw_stream_config stream_config; - struct sdw_port_config port_config; - enum sdw_data_direction direction; - struct sdw_stream_data *stream; - int retval, port, num_channels, ch_mask; + struct sdw_stream_config stream_config = {0}; + struct sdw_port_config port_config = {0}; + struct sdw_stream_runtime *sdw_stream; + int retval; dev_dbg(dai->dev, "%s %s", __func__, dai->name); - stream = snd_soc_dai_get_dma_data(dai, substream); + sdw_stream = snd_soc_dai_get_dma_data(dai, substream); - if (!stream) + if (!sdw_stream) return -EINVAL; if (!rt1308->sdw_slave) return -EINVAL; /* SoundWire specific configuration */ + snd_sdw_params_to_config(substream, params, &stream_config, &port_config); + /* port 1 for playback */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - direction = SDW_DATA_DIR_RX; - port = 1; - } else { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + port_config.num = 1; + else return -EINVAL; - } if (rt1308->slots) { - num_channels = rt1308->slots; - ch_mask = rt1308->rx_mask; - } else { - num_channels = params_channels(params); - ch_mask = (1 << num_channels) - 1; + stream_config.ch_count = rt1308->slots; + port_config.ch_mask = rt1308->rx_mask; } - stream_config.frame_rate = params_rate(params); - stream_config.ch_count = num_channels; - stream_config.bps = snd_pcm_format_width(params_format(params)); - stream_config.direction = direction; - - port_config.ch_mask = ch_mask; - port_config.num = port; - retval = sdw_stream_add_slave(rt1308->sdw_slave, &stream_config, - &port_config, 1, stream->sdw_stream); + &port_config, 1, sdw_stream); if (retval) { dev_err(dai->dev, "Unable to configure port\n"); return retval; @@ -580,13 +575,13 @@ static int rt1308_sdw_pcm_hw_free(struct snd_pcm_substream *substream, struct snd_soc_component *component = dai->component; struct rt1308_sdw_priv *rt1308 = snd_soc_component_get_drvdata(component); - struct sdw_stream_data *stream = + struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream); if (!rt1308->sdw_slave) return -EINVAL; - sdw_stream_remove_slave(rt1308->sdw_slave, stream->sdw_stream); + sdw_stream_remove_slave(rt1308->sdw_slave, sdw_stream); return 0; } @@ -601,13 +596,57 @@ static const struct sdw_slave_ops rt1308_slave_ops = { .bus_config = rt1308_bus_config, }; +static int rt1308_sdw_parse_dt(struct rt1308_sdw_priv *rt1308, struct device *dev) +{ + int ret = 0; + + device_property_read_u32(dev, "realtek,bq-params-cnt", &rt1308->bq_params_cnt); + if (rt1308->bq_params_cnt) { + rt1308->bq_params = devm_kzalloc(dev, rt1308->bq_params_cnt, GFP_KERNEL); + if (!rt1308->bq_params) { + dev_err(dev, "Could not allocate bq_params memory\n"); + ret = -ENOMEM; + } else { + ret = device_property_read_u8_array(dev, "realtek,bq-params", rt1308->bq_params, rt1308->bq_params_cnt); + if (ret < 0) + dev_err(dev, "Could not read list of realtek,bq-params\n"); + } + } + + dev_dbg(dev, "bq_params_cnt=%d\n", rt1308->bq_params_cnt); + return ret; +} + +static int rt1308_sdw_component_probe(struct snd_soc_component *component) +{ + struct rt1308_sdw_priv *rt1308 = snd_soc_component_get_drvdata(component); + int ret; + + rt1308->component = component; + rt1308_sdw_parse_dt(rt1308, &rt1308->sdw_slave->dev); + + if (!rt1308->first_hw_init) + return 0; + + ret = pm_runtime_resume(component->dev); + if (ret < 0 && ret != -EACCES) + return ret; + + /* apply BQ params */ + rt1308_apply_bq_params(rt1308); + + return 0; +} + static const struct snd_soc_component_driver soc_component_sdw_rt1308 = { + .probe = rt1308_sdw_component_probe, .controls = rt1308_snd_controls, .num_controls = ARRAY_SIZE(rt1308_snd_controls), .dapm_widgets = rt1308_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(rt1308_dapm_widgets), .dapm_routes = rt1308_dapm_routes, .num_dapm_routes = ARRAY_SIZE(rt1308_dapm_routes), + .endianness = 1, }; static const struct snd_soc_dai_ops rt1308_aif_dai_ops = { @@ -651,6 +690,8 @@ static int rt1308_sdw_init(struct device *dev, struct regmap *regmap, rt1308->sdw_slave = slave; rt1308->regmap = regmap; + regcache_cache_only(rt1308->regmap, true); + /* * Mark hw_init to false * HW init will be performed when device reports present @@ -662,10 +703,27 @@ static int rt1308_sdw_init(struct device *dev, struct regmap *regmap, &soc_component_sdw_rt1308, rt1308_sdw_dai, ARRAY_SIZE(rt1308_sdw_dai)); + if (ret < 0) + return ret; - dev_dbg(&slave->dev, "%s\n", __func__); + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); - return ret; + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + + dev_dbg(dev, "%s\n", __func__); + + return 0; } static int rt1308_sdw_probe(struct sdw_slave *slave, @@ -678,7 +736,12 @@ static int rt1308_sdw_probe(struct sdw_slave *slave, if (IS_ERR(regmap)) return PTR_ERR(regmap); - rt1308_sdw_init(&slave->dev, regmap, slave); + return rt1308_sdw_init(&slave->dev, regmap, slave); +} + +static int rt1308_sdw_remove(struct sdw_slave *slave) +{ + pm_runtime_disable(&slave->dev); return 0; } @@ -689,7 +752,7 @@ static const struct sdw_device_id rt1308_id[] = { }; MODULE_DEVICE_TABLE(sdw, rt1308_id); -static int __maybe_unused rt1308_dev_suspend(struct device *dev) +static int rt1308_dev_suspend(struct device *dev) { struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(dev); @@ -703,7 +766,7 @@ static int __maybe_unused rt1308_dev_suspend(struct device *dev) #define RT1308_PROBE_TIMEOUT 5000 -static int __maybe_unused rt1308_dev_resume(struct device *dev) +static int rt1308_dev_resume(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(dev); @@ -719,6 +782,8 @@ static int __maybe_unused rt1308_dev_resume(struct device *dev) msecs_to_jiffies(RT1308_PROBE_TIMEOUT)); if (!time) { dev_err(&slave->dev, "Initialization not complete, timed out\n"); + sdw_show_ping_status(slave->bus, true); + return -ETIMEDOUT; } @@ -731,17 +796,17 @@ regmap_sync: } static const struct dev_pm_ops rt1308_pm = { - SET_SYSTEM_SLEEP_PM_OPS(rt1308_dev_suspend, rt1308_dev_resume) - SET_RUNTIME_PM_OPS(rt1308_dev_suspend, rt1308_dev_resume, NULL) + SYSTEM_SLEEP_PM_OPS(rt1308_dev_suspend, rt1308_dev_resume) + RUNTIME_PM_OPS(rt1308_dev_suspend, rt1308_dev_resume, NULL) }; static struct sdw_driver rt1308_sdw_driver = { .driver = { .name = "rt1308", - .owner = THIS_MODULE, - .pm = &rt1308_pm, + .pm = pm_ptr(&rt1308_pm), }, .probe = rt1308_sdw_probe, + .remove = rt1308_sdw_remove, .ops = &rt1308_slave_ops, .id_table = rt1308_id, }; |
