[alsa-devel] [PATCH v8 0/6] Add HDMI jack support on RK3288
This patch series supports HDMI jack reporting on RK3288, which uses DRM dw-hdmi driver and hdmi-codec codec driver.
The previous discussion about reporting jack status using hdmi-notifier and drm_audio_component is at
https://lore.kernel.org/patchwork/patch/1083027/
The new approach is to use a callback mechanism that is specific to hdmi-codec.
The dependent change on hdmi-codec.c
6fa5963c37a2 ASoC: hdmi-codec: Add an op to set callback function for plug event
has been merged to upstream.
Changes from v7 to v8:
1. rockchip_max98090: Allow three different use cases: max98090-only: For backward compatibility where DTS does not specify HDMI node. HDMI-only: For HDMI-only board like veyron-mickey. max98090 + HDMI: For other veyron boards. Pass different compatible string to specify the use case.
2. Add more maintainers to cc-list for new device property reviewing.
Cheng-Yi Chiang (6): drm: bridge: dw-hdmi: Report connector status using callback ASoC: rockchip-max98090: Support usage with and without HDMI ASoC: rockchip_max98090: Optionally support HDMI use case ASoC: rockchip_max98090: Add HDMI jack support ARM: dts: rockchip: Add HDMI support to rk3288-veyron-analog-audio ARM: dts: rockchip: Add HDMI audio support to rk3288-veyron-mickey.dts
.../bindings/sound/rockchip-max98090.txt | 38 +- .../boot/dts/rk3288-veyron-analog-audio.dtsi | 3 +- arch/arm/boot/dts/rk3288-veyron-mickey.dts | 7 + .../drm/bridge/synopsys/dw-hdmi-i2s-audio.c | 11 + drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 41 +- include/drm/bridge/dw_hdmi.h | 4 + sound/soc/rockchip/Kconfig | 3 +- sound/soc/rockchip/rockchip_max98090.c | 392 +++++++++++++++--- 8 files changed, 425 insertions(+), 74 deletions(-)
Allow codec driver register callback function for plug event.
The callback registration flow: dw-hdmi <--- hw-hdmi-i2s-audio <--- hdmi-codec
dw-hdmi-i2s-audio implements hook_plugged_cb op so codec driver can register the callback.
dw-hdmi exports a function dw_hdmi_set_plugged_cb so platform device can register the callback.
When connector plug/unplug event happens, report this event using the callback.
Make sure that audio and drm are using the single source of truth for connector status.
Signed-off-by: Cheng-Yi Chiang cychiang@chromium.org --- .../drm/bridge/synopsys/dw-hdmi-i2s-audio.c | 11 +++++ drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 41 ++++++++++++++++++- include/drm/bridge/dw_hdmi.h | 4 ++ 3 files changed, 55 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c index 1d15cf9b6821..6c2c44d0bdee 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c @@ -151,11 +151,22 @@ static int dw_hdmi_i2s_get_dai_id(struct snd_soc_component *component, return -EINVAL; }
+static int dw_hdmi_i2s_hook_plugged_cb(struct device *dev, void *data, + hdmi_codec_plugged_cb fn, + struct device *codec_dev) +{ + struct dw_hdmi_i2s_audio_data *audio = data; + struct dw_hdmi *hdmi = audio->hdmi; + + return dw_hdmi_set_plugged_cb(hdmi, fn, codec_dev); +} + static struct hdmi_codec_ops dw_hdmi_i2s_ops = { .hw_params = dw_hdmi_i2s_hw_params, .audio_shutdown = dw_hdmi_i2s_audio_shutdown, .get_eld = dw_hdmi_i2s_get_eld, .get_dai_id = dw_hdmi_i2s_get_dai_id, + .hook_plugged_cb = dw_hdmi_i2s_hook_plugged_cb, };
static int snd_dw_hdmi_probe(struct platform_device *pdev) diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index 521d689413c8..2102872bf43c 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -191,6 +191,10 @@ struct dw_hdmi {
struct mutex cec_notifier_mutex; struct cec_notifier *cec_notifier; + + hdmi_codec_plugged_cb plugged_cb; + struct device *codec_dev; + enum drm_connector_status last_connector_result; };
#define HDMI_IH_PHY_STAT0_RX_SENSE \ @@ -215,6 +219,28 @@ static inline u8 hdmi_readb(struct dw_hdmi *hdmi, int offset) return val; }
+static void handle_plugged_change(struct dw_hdmi *hdmi, bool plugged) +{ + if (hdmi->plugged_cb && hdmi->codec_dev) + hdmi->plugged_cb(hdmi->codec_dev, plugged); +} + +int dw_hdmi_set_plugged_cb(struct dw_hdmi *hdmi, hdmi_codec_plugged_cb fn, + struct device *codec_dev) +{ + bool plugged; + + mutex_lock(&hdmi->mutex); + hdmi->plugged_cb = fn; + hdmi->codec_dev = codec_dev; + plugged = hdmi->last_connector_result == connector_status_connected; + handle_plugged_change(hdmi, plugged); + mutex_unlock(&hdmi->mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(dw_hdmi_set_plugged_cb); + static void hdmi_modb(struct dw_hdmi *hdmi, u8 data, u8 mask, unsigned reg) { regmap_update_bits(hdmi->regm, reg << hdmi->reg_shift, mask, data); @@ -2161,6 +2187,7 @@ dw_hdmi_connector_detect(struct drm_connector *connector, bool force) { struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi, connector); + enum drm_connector_status result;
mutex_lock(&hdmi->mutex); hdmi->force = DRM_FORCE_UNSPECIFIED; @@ -2168,7 +2195,18 @@ dw_hdmi_connector_detect(struct drm_connector *connector, bool force) dw_hdmi_update_phy_mask(hdmi); mutex_unlock(&hdmi->mutex);
- return hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data); + result = hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data); + + mutex_lock(&hdmi->mutex); + if (result != hdmi->last_connector_result) { + dev_dbg(hdmi->dev, "read_hpd result: %d", result); + handle_plugged_change(hdmi, + result == connector_status_connected); + hdmi->last_connector_result = result; + } + mutex_unlock(&hdmi->mutex); + + return result; }
static int dw_hdmi_connector_get_modes(struct drm_connector *connector) @@ -2619,6 +2657,7 @@ __dw_hdmi_probe(struct platform_device *pdev, hdmi->rxsense = true; hdmi->phy_mask = (u8)~(HDMI_PHY_HPD | HDMI_PHY_RX_SENSE); hdmi->mc_clkdis = 0x7f; + hdmi->last_connector_result = connector_status_disconnected;
mutex_init(&hdmi->mutex); mutex_init(&hdmi->audio_mutex); diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h index cf528c289857..9a0c8381a069 100644 --- a/include/drm/bridge/dw_hdmi.h +++ b/include/drm/bridge/dw_hdmi.h @@ -6,6 +6,8 @@ #ifndef __DW_HDMI__ #define __DW_HDMI__
+#include <sound/hdmi-codec.h> + struct drm_connector; struct drm_display_mode; struct drm_encoder; @@ -154,6 +156,8 @@ void dw_hdmi_resume(struct dw_hdmi *hdmi);
void dw_hdmi_setup_rx_sense(struct dw_hdmi *hdmi, bool hpd, bool rx_sense);
+int dw_hdmi_set_plugged_cb(struct dw_hdmi *hdmi, hdmi_codec_plugged_cb fn, + struct device *codec_dev); void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate); void dw_hdmi_set_channel_count(struct dw_hdmi *hdmi, unsigned int cnt); void dw_hdmi_set_channel_allocation(struct dw_hdmi *hdmi, unsigned int ca);
There will be multiple boards sharing this machine driver. Use compatible string to specify the use case.
"rockchip,rockchip-audio-max98090" for max98090-only. "rockchip,rockchip-audio-hdmi" for HDMI-only "rockchip,rockchip-audio-max98090-hdmi" for max98090 plus
Move these properties to optional because they are not needed for HDMI-only use case. "rockchip,audio-codec": The phandle of the MAX98090 audio codec "rockchip,headset-codec": The phandle of Ext chip for jack detection
The machine driver change will add support for HDMI codec in rockchip-max98090. Add one optional property "rockchip,hdmi-codec" to let user specify HDMI device node in DTS so machine driver can find hdmi-codec device node for codec DAI.
Signed-off-by: Cheng-Yi Chiang cychiang@chromium.org --- .../bindings/sound/rockchip-max98090.txt | 38 +++++++++++++++++-- 1 file changed, 35 insertions(+), 3 deletions(-)
diff --git a/Documentation/devicetree/bindings/sound/rockchip-max98090.txt b/Documentation/devicetree/bindings/sound/rockchip-max98090.txt index a805aa99ad75..75f438741ca6 100644 --- a/Documentation/devicetree/bindings/sound/rockchip-max98090.txt +++ b/Documentation/devicetree/bindings/sound/rockchip-max98090.txt @@ -1,15 +1,29 @@ ROCKCHIP with MAX98090 CODEC
Required properties: -- compatible: "rockchip,rockchip-audio-max98090" +- compatible: Set this property to one of the strings below depending on the + usage on the board: + "rockchip,rockchip-audio-max98090" for max98090-only. + "rockchip,rockchip-audio-hdmi" for HDMI-only + "rockchip,rockchip-audio-max98090-hdmi" for max98090 plus HDMI. - rockchip,model: The user-visible name of this sound complex - rockchip,i2s-controller: The phandle of the Rockchip I2S controller that's connected to the CODEC -- rockchip,audio-codec: The phandle of the MAX98090 audio codec -- rockchip,headset-codec: The phandle of Ext chip for jack detection + +Optional properties: +- rockchip,audio-codec: The phandle of the MAX98090 audio codec. This is + required if compatible string is set to the one + with max98090. +- rockchip,headset-codec: The phandle of Ext chip for jack detection. This is + required if compatible string is set to the one + with max98090. +- rockchip,hdmi-codec: The phandle of HDMI device for HDMI codec. This is + required if compatible string is set to the one with + HDMI.
Example:
+/* For max98090-only board. */ sound { compatible = "rockchip,rockchip-audio-max98090"; rockchip,model = "ROCKCHIP-I2S"; @@ -17,3 +31,21 @@ sound { rockchip,audio-codec = <&max98090>; rockchip,headset-codec = <&headsetcodec>; }; + +/* For HDMI-only board. */ +sound { + compatible = "rockchip,rockchip-audio-hdmi"; + rockchip,model = "ROCKCHIP-I2S"; + rockchip,i2s-controller = <&i2s>; + rockchip,hdmi-codec = <&hdmi>; +}; + +/* For max98090 plus HDMI board. */ +sound { + compatible = "rockchip,rockchip-audio-max98090-hdmi"; + rockchip,model = "ROCKCHIP-I2S"; + rockchip,i2s-controller = <&i2s>; + rockchip,audio-codec = <&max98090>; + rockchip,headset-codec = <&headsetcodec>; + rockchip,hdmi-codec = <&hdmi>; +};
On Fri, Oct 25, 2019 at 09:30:03PM +0800, Cheng-Yi Chiang wrote:
There will be multiple boards sharing this machine driver. Use compatible string to specify the use case.
"rockchip,rockchip-audio-max98090" for max98090-only. "rockchip,rockchip-audio-hdmi" for HDMI-only "rockchip,rockchip-audio-max98090-hdmi" for max98090 plus
Move these properties to optional because they are not needed for HDMI-only use case. "rockchip,audio-codec": The phandle of the MAX98090 audio codec "rockchip,headset-codec": The phandle of Ext chip for jack detection
The machine driver change will add support for HDMI codec in rockchip-max98090. Add one optional property "rockchip,hdmi-codec" to let user specify HDMI device node in DTS so machine driver can find hdmi-codec device node for codec DAI.
Why not just use the presence of rockchip,hdmi-codec to enable HDMI or not. Maybe you still add rockchip,rockchip-audio-hdmi for HDMI only.
Really, the same should have been done for which codec is used too, but I guess someone wanted 2 machine drivers.
Signed-off-by: Cheng-Yi Chiang cychiang@chromium.org
.../bindings/sound/rockchip-max98090.txt | 38 +++++++++++++++++-- 1 file changed, 35 insertions(+), 3 deletions(-)
On Sat, Oct 26, 2019 at 12:22 AM Rob Herring robh@kernel.org wrote:
On Fri, Oct 25, 2019 at 09:30:03PM +0800, Cheng-Yi Chiang wrote:
There will be multiple boards sharing this machine driver. Use compatible string to specify the use case.
"rockchip,rockchip-audio-max98090" for max98090-only. "rockchip,rockchip-audio-hdmi" for HDMI-only "rockchip,rockchip-audio-max98090-hdmi" for max98090 plus
Move these properties to optional because they are not needed for HDMI-only use case. "rockchip,audio-codec": The phandle of the MAX98090 audio codec "rockchip,headset-codec": The phandle of Ext chip for jack detection
The machine driver change will add support for HDMI codec in rockchip-max98090. Add one optional property "rockchip,hdmi-codec" to let user specify HDMI device node in DTS so machine driver can find hdmi-codec device node for codec DAI.
Why not just use the presence of rockchip,hdmi-codec to enable HDMI or not. Maybe you still add rockchip,rockchip-audio-hdmi for HDMI only.
Really, the same should have been done for which codec is used too, but I guess someone wanted 2 machine drivers.
Hi Rob, Thanks for the quick reply. I can make change in machine driver so that - The presence of rockchip,audio-codec enable max98090 - The presence of rockchip,hdmi-codec enable HDMI.
With that, we don't need the three properties added in this patch: "rockchip,rockchip-audio-max98090" for max98090-only. "rockchip,rockchip-audio-hdmi" for HDMI-only "rockchip,rockchip-audio-max98090-hdmi" for max98090 plus HDMI.
I will post an update soon. Thanks!
Signed-off-by: Cheng-Yi Chiang cychiang@chromium.org
.../bindings/sound/rockchip-max98090.txt | 38 +++++++++++++++++-- 1 file changed, 35 insertions(+), 3 deletions(-)
Support three different use cases with rockchip_max98090 driver:
- max98090 only: Current usage, where HDMI link is missing. - HDMI only: Needed use case for veyron_mickey board. - max98090 + HDMI: Ideal use case for veyron_jerry, veyron_minnie..etc.
Note that we keep max98090 only usage for backward compatibility.
From devicetree, different compatible string can be passed to configure
the usage.
Signed-off-by: Cheng-Yi Chiang cychiang@chromium.org --- The checkpatch error "Macros with complex values should be enclosed in parentheses" seems not suitable for the macro usage for DAPM widget, route, and controls.
sound/soc/rockchip/rockchip_max98090.c | 371 ++++++++++++++++++++----- 1 file changed, 303 insertions(+), 68 deletions(-)
diff --git a/sound/soc/rockchip/rockchip_max98090.c b/sound/soc/rockchip/rockchip_max98090.c index 0097df1fae66..550e57789719 100644 --- a/sound/soc/rockchip/rockchip_max98090.c +++ b/sound/soc/rockchip/rockchip_max98090.c @@ -6,11 +6,13 @@ */
#include <linux/module.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/gpio.h> #include <linux/of_gpio.h> #include <sound/core.h> +#include <sound/hdmi-codec.h> #include <sound/jack.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -21,6 +23,17 @@
#define DRV_NAME "rockchip-snd-max98090"
+enum rk_snd_usage { + RK_USE_MAX98090, /* Only use max98090. */ + RK_USE_HDMI, /* Only use HDMI. */ + RK_USE_MAX98090_HDMI, /* Use both max98090 and HDMI. */ +}; + +struct rk_card_config { + enum rk_snd_usage usage; + struct snd_soc_card *card; +}; + static struct snd_soc_jack headset_jack;
/* Headset jack detection DAPM pins */ @@ -36,28 +49,73 @@ static struct snd_soc_jack_pin headset_jack_pins[] = {
};
-static const struct snd_soc_dapm_widget rk_dapm_widgets[] = { - SND_SOC_DAPM_HP("Headphone", NULL), - SND_SOC_DAPM_MIC("Headset Mic", NULL), - SND_SOC_DAPM_MIC("Int Mic", NULL), - SND_SOC_DAPM_SPK("Speaker", NULL), +#define RK_MAX98090_WIDGETS \ + SND_SOC_DAPM_HP("Headphone", NULL), \ + SND_SOC_DAPM_MIC("Headset Mic", NULL), \ + SND_SOC_DAPM_MIC("Int Mic", NULL), \ + SND_SOC_DAPM_SPK("Speaker", NULL) + +#define RK_HDMI_WIDGETS \ + SND_SOC_DAPM_LINE("HDMI", NULL) + +static const struct snd_soc_dapm_widget rk_max98090_dapm_widgets[] = { + RK_MAX98090_WIDGETS, +}; + +static const struct snd_soc_dapm_widget rk_hdmi_dapm_widgets[] = { + RK_HDMI_WIDGETS, +}; + +static const struct snd_soc_dapm_widget rk_max98090_hdmi_dapm_widgets[] = { + RK_MAX98090_WIDGETS, + RK_HDMI_WIDGETS, +}; + +#define RK_MAX98090_AUDIO_MAP \ + {"IN34", NULL, "Headset Mic"}, \ + {"Headset Mic", NULL, "MICBIAS"}, \ + {"DMICL", NULL, "Int Mic"}, \ + {"Headphone", NULL, "HPL"}, \ + {"Headphone", NULL, "HPR"}, \ + {"Speaker", NULL, "SPKL"}, \ + {"Speaker", NULL, "SPKR"} + +#define RK_HDMI_AUDIO_MAP \ + {"HDMI", NULL, "TX"} + +static const struct snd_soc_dapm_route rk_max98090_audio_map[] = { + RK_MAX98090_AUDIO_MAP, +}; + +static const struct snd_soc_dapm_route rk_hdmi_audio_map[] = { + RK_HDMI_AUDIO_MAP, +}; + +static const struct snd_soc_dapm_route rk_max98090_hdmi_audio_map[] = { + RK_MAX98090_AUDIO_MAP, + RK_HDMI_AUDIO_MAP, +}; + +#define RK_MAX98090_CONTROLS \ + SOC_DAPM_PIN_SWITCH("Headphone"), \ + SOC_DAPM_PIN_SWITCH("Headset Mic"), \ + SOC_DAPM_PIN_SWITCH("Int Mic"), \ + SOC_DAPM_PIN_SWITCH("Speaker") + +#define RK_HDMI_CONTROLS \ + SOC_DAPM_PIN_SWITCH("HDMI") + +static const struct snd_kcontrol_new rk_max98090_controls[] = { + RK_MAX98090_CONTROLS, };
-static const struct snd_soc_dapm_route rk_audio_map[] = { - {"IN34", NULL, "Headset Mic"}, - {"Headset Mic", NULL, "MICBIAS"}, - {"DMICL", NULL, "Int Mic"}, - {"Headphone", NULL, "HPL"}, - {"Headphone", NULL, "HPR"}, - {"Speaker", NULL, "SPKL"}, - {"Speaker", NULL, "SPKR"}, +static const struct snd_kcontrol_new rk_hdmi_controls[] = { + RK_HDMI_CONTROLS, };
-static const struct snd_kcontrol_new rk_mc_controls[] = { - SOC_DAPM_PIN_SWITCH("Headphone"), - SOC_DAPM_PIN_SWITCH("Headset Mic"), - SOC_DAPM_PIN_SWITCH("Int Mic"), - SOC_DAPM_PIN_SWITCH("Speaker"), +static const struct snd_kcontrol_new rk_max98090_hdmi_controls[] = { + RK_MAX98090_CONTROLS, + RK_HDMI_CONTROLS, };
static int rk_jack_event(struct notifier_block *nb, unsigned long event, @@ -122,15 +180,20 @@ static int rk_aif1_hw_params(struct snd_pcm_substream *substream,
ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk, SND_SOC_CLOCK_OUT); - if (ret < 0) { - dev_err(codec_dai->dev, "Can't set codec clock %d\n", ret); + if (ret) { + dev_err(cpu_dai->dev, "Can't set cpu dai clock %d\n", ret); return ret; }
ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, SND_SOC_CLOCK_IN); - if (ret < 0) { - dev_err(codec_dai->dev, "Can't set codec clock %d\n", ret); + + /* HDMI codec dai does not need to set sysclk. */ + if (!strcmp(rtd->dai_link->name, "HDMI")) + return 0; + + if (ret) { + dev_err(codec_dai->dev, "Can't set codec dai clock %d\n", ret); return ret; }
@@ -152,20 +215,67 @@ static const struct snd_soc_ops rk_aif1_ops = { .startup = rk_aif1_startup, };
-SND_SOC_DAILINK_DEFS(hifi, - DAILINK_COMP_ARRAY(COMP_EMPTY()), - DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "HiFi")), - DAILINK_COMP_ARRAY(COMP_EMPTY())); - -static struct snd_soc_dai_link rk_dailink = { - .name = "max98090", - .stream_name = "Audio", - .init = rk_init, - .ops = &rk_aif1_ops, - /* set max98090 as slave */ - .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, - SND_SOC_DAILINK_REG(hifi), +SND_SOC_DAILINK_DEFS(analog, + DAILINK_COMP_ARRAY(COMP_EMPTY()), + DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "HiFi")), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(hdmi, + DAILINK_COMP_ARRAY(COMP_EMPTY()), + DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "i2s-hifi")), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +enum { + DAILINK_MAX98090, + DAILINK_HDMI, +}; + +/* max98090 dai_link */ +static struct snd_soc_dai_link rk_max98090_dailinks[] = { + { + .name = "max98090", + .stream_name = "Analog", + .init = rk_init, + .ops = &rk_aif1_ops, + /* set max98090 as slave */ + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAILINK_REG(analog), + }, +}; + +/* HDMI codec dai_link */ +static struct snd_soc_dai_link rk_hdmi_dailinks[] = { + { + .name = "HDMI", + .stream_name = "HDMI", + .ops = &rk_aif1_ops, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAILINK_REG(hdmi), + } +}; + +/* max98090 and HDMI codec dai_link */ +static struct snd_soc_dai_link rk_max98090_hdmi_dailinks[] = { + [DAILINK_MAX98090] = { + .name = "max98090", + .stream_name = "Analog", + .init = rk_init, + .ops = &rk_aif1_ops, + /* set max98090 as slave */ + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAILINK_REG(analog), + }, + [DAILINK_HDMI] = { + .name = "HDMI", + .stream_name = "HDMI", + .ops = &rk_aif1_ops, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAILINK_REG(hdmi), + } };
static int rk_98090_headset_init(struct snd_soc_component *component); @@ -175,19 +285,47 @@ static struct snd_soc_aux_dev rk_98090_headset_dev = { .init = rk_98090_headset_init, };
-static struct snd_soc_card snd_soc_card_rk = { +static struct snd_soc_card rockchip_max98090_card = { .name = "ROCKCHIP-I2S", .owner = THIS_MODULE, - .dai_link = &rk_dailink, - .num_links = 1, + .dai_link = rk_max98090_dailinks, + .num_links = ARRAY_SIZE(rk_max98090_dailinks), + .aux_dev = &rk_98090_headset_dev, + .num_aux_devs = 1, + .dapm_widgets = rk_max98090_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rk_max98090_dapm_widgets), + .dapm_routes = rk_max98090_audio_map, + .num_dapm_routes = ARRAY_SIZE(rk_max98090_audio_map), + .controls = rk_max98090_controls, + .num_controls = ARRAY_SIZE(rk_max98090_controls), +}; + +static struct snd_soc_card rockchip_hdmi_card = { + .name = "ROCKCHIP-HDMI", + .owner = THIS_MODULE, + .dai_link = rk_hdmi_dailinks, + .num_links = ARRAY_SIZE(rk_hdmi_dailinks), + .dapm_widgets = rk_hdmi_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rk_hdmi_dapm_widgets), + .dapm_routes = rk_hdmi_audio_map, + .num_dapm_routes = ARRAY_SIZE(rk_hdmi_audio_map), + .controls = rk_hdmi_controls, + .num_controls = ARRAY_SIZE(rk_hdmi_controls), +}; + +static struct snd_soc_card rockchip_max98090_hdmi_card = { + .name = "ROCKCHIP-MAX98090-HDMI", + .owner = THIS_MODULE, + .dai_link = rk_max98090_hdmi_dailinks, + .num_links = ARRAY_SIZE(rk_max98090_hdmi_dailinks), .aux_dev = &rk_98090_headset_dev, .num_aux_devs = 1, - .dapm_widgets = rk_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(rk_dapm_widgets), - .dapm_routes = rk_audio_map, - .num_dapm_routes = ARRAY_SIZE(rk_audio_map), - .controls = rk_mc_controls, - .num_controls = ARRAY_SIZE(rk_mc_controls), + .dapm_widgets = rk_max98090_hdmi_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rk_max98090_hdmi_dapm_widgets), + .dapm_routes = rk_max98090_hdmi_audio_map, + .num_dapm_routes = ARRAY_SIZE(rk_max98090_hdmi_audio_map), + .controls = rk_max98090_hdmi_controls, + .num_controls = ARRAY_SIZE(rk_max98090_hdmi_controls), };
static int rk_98090_headset_init(struct snd_soc_component *component) @@ -195,7 +333,7 @@ static int rk_98090_headset_init(struct snd_soc_component *component) int ret;
/* Enable Headset and 4 Buttons Jack detection */ - ret = snd_soc_card_jack_new(&snd_soc_card_rk, "Headset Jack", + ret = snd_soc_card_jack_new(component->card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3, @@ -210,41 +348,112 @@ static int rk_98090_headset_init(struct snd_soc_component *component) return ret; }
+static int rk_parse_codec_from_of(struct device *dev, struct device_node *np, + const char *codec_property_name, + struct snd_soc_dai_link_component *codecs) +{ + struct device_node *np_codec; + + np_codec = of_parse_phandle(np, codec_property_name, 0); + if (!np_codec) { + dev_err(dev, + "Property %s missing or invalid\n", + codec_property_name); + return -EINVAL; + } + + codecs->of_node = np_codec; + return 0; +} + +static int rk_parse_headset_from_of(struct device *dev, struct device_node *np) +{ + rk_98090_headset_dev.dlc.of_node = of_parse_phandle( + np, "rockchip,headset-codec", 0); + if (!rk_98090_headset_dev.dlc.of_node) { + dev_err(dev, + "Property 'rockchip,headset-codec' missing/invalid\n"); + return -EINVAL; + } + return 0; +} + static int snd_rk_mc_probe(struct platform_device *pdev) { int ret = 0; - struct snd_soc_card *card = &snd_soc_card_rk; + struct snd_soc_card *card; + struct device *dev = &pdev->dev; struct device_node *np = pdev->dev.of_node; + struct device_node *np_cpu; + const struct of_device_id *match; + struct rk_card_config *config; + enum rk_snd_usage usage; + + /* Find the config and card to use based on match table. */ + match = of_match_device(dev->driver->of_match_table, dev); + if (!match || !match->data) + return -EINVAL;
- /* register the soc card */ - card->dev = &pdev->dev; + config = (struct rk_card_config *)match->data; + card = config->card; + usage = config->usage; + card->dev = dev;
- rk_dailink.codecs->of_node = of_parse_phandle(np, - "rockchip,audio-codec", 0); - if (!rk_dailink.codecs->of_node) { + /* Parse DTS for I2S controller. */ + np_cpu = of_parse_phandle(np, "rockchip,i2s-controller", 0); + + if (!np_cpu) { dev_err(&pdev->dev, - "Property 'rockchip,audio-codec' missing or invalid\n"); + "Property 'rockchip,i2s-controller missing or invalid\n"); return -EINVAL; }
- rk_dailink.cpus->of_node = of_parse_phandle(np, - "rockchip,i2s-controller", 0); - if (!rk_dailink.cpus->of_node) { - dev_err(&pdev->dev, - "Property 'rockchip,i2s-controller' missing or invalid\n"); - return -EINVAL; + /* Parse DTS for max98090-only card. */ + if (usage == RK_USE_MAX98090) { + ret = rk_parse_codec_from_of(dev, np, "rockchip,audio-codec", + card->dai_link[0].codecs); + if (ret) + return ret; + card->dai_link[0].cpus->of_node = np_cpu; + card->dai_link[0].platforms->of_node = np_cpu; }
- rk_dailink.platforms->of_node = rk_dailink.cpus->of_node; + /* Parse DTS for HDMI-only card. */ + if (usage == RK_USE_HDMI) { + ret = rk_parse_codec_from_of(dev, np, "rockchip,hdmi-codec", + card->dai_link[0].codecs); + if (ret) + return ret; + card->dai_link[0].cpus->of_node = np_cpu; + card->dai_link[0].platforms->of_node = np_cpu; + }
- rk_98090_headset_dev.dlc.of_node = of_parse_phandle(np, - "rockchip,headset-codec", 0); - if (!rk_98090_headset_dev.dlc.of_node) { - dev_err(&pdev->dev, - "Property 'rockchip,headset-codec' missing/invalid\n"); - return -EINVAL; + /* Parse DTS for max98090 plus HDMI card. */ + if (usage == RK_USE_MAX98090_HDMI) { + ret = rk_parse_codec_from_of( + dev, np, "rockchip,audio-codec", + card->dai_link[DAILINK_MAX98090].codecs); + if (ret) + return ret; + ret = rk_parse_codec_from_of( + dev, np, "rockchip,hdmi-codec", + card->dai_link[DAILINK_HDMI].codecs); + if (ret) + return ret; + card->dai_link[DAILINK_MAX98090].cpus->of_node = np_cpu; + card->dai_link[DAILINK_MAX98090].platforms->of_node = np_cpu; + card->dai_link[DAILINK_HDMI].cpus->of_node = np_cpu; + card->dai_link[DAILINK_HDMI].platforms->of_node = np_cpu; }
+ /* Parse headset detection codec. */ + if (usage == RK_USE_MAX98090 || usage == RK_USE_MAX98090_HDMI) { + ret = rk_parse_headset_from_of(dev, np); + if (ret) + return ret; + } + + /* Parse card name. */ ret = snd_soc_of_parse_card_name(card, "rockchip,model"); if (ret) { dev_err(&pdev->dev, @@ -252,6 +461,7 @@ static int snd_rk_mc_probe(struct platform_device *pdev) return ret; }
+ /* register the soc card */ ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { dev_err(&pdev->dev, @@ -262,9 +472,34 @@ static int snd_rk_mc_probe(struct platform_device *pdev) return ret; }
+struct rk_card_config rk_card_configs[] = { + [RK_USE_MAX98090] = { + .usage = RK_USE_MAX98090, + .card = &rockchip_max98090_card, + }, + [RK_USE_HDMI] = { + .usage = RK_USE_HDMI, + .card = &rockchip_hdmi_card, + }, + [RK_USE_MAX98090_HDMI] = { + .usage = RK_USE_MAX98090_HDMI, + .card = &rockchip_max98090_hdmi_card, + }, +}; + static const struct of_device_id rockchip_max98090_of_match[] = { - { .compatible = "rockchip,rockchip-audio-max98090", }, - {}, + { + .compatible = "rockchip,rockchip-audio-max98090", + .data = &rk_card_configs[RK_USE_MAX98090], + }, + { + .compatible = "rockchip,rockchip-audio-hdmi", + .data = &rk_card_configs[RK_USE_HDMI], + }, + { + .compatible = "rockchip,rockchip-audio-max98090-hdmi", + .data = &rk_card_configs[RK_USE_MAX98090_HDMI], + }, };
MODULE_DEVICE_TABLE(of, rockchip_max98090_of_match);
Hi Cheng-Yi,
I love your patch! Perhaps something to improve:
[auto build test WARNING on rockchip/for-next] [also build test WARNING on v5.4-rc5 next-20191028] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system. BTW, we also suggest to use '--base' option to specify the base tree in git format-patch, please see https://stackoverflow.com/a/37406982]
url: https://github.com/0day-ci/linux/commits/Cheng-Yi-Chiang/Add-HDMI-jack-suppo... base: https://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip.git for-next reproduce: # apt-get install sparse # sparse version: v0.6.1-dirty make ARCH=x86_64 allmodconfig make C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__'
If you fix the issue, kindly add following tag Reported-by: kbuild test robot lkp@intel.com
sparse warnings: (new ones prefixed by >>)
sound/soc/rockchip/rockchip_max98090.c:475:23: sparse: sparse: symbol 'rk_card_configs' was not declared. Should it be static?
Please review and possibly fold the followup patch.
--- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation
Fixes: 0cc0922ae849 ("ASoC: rockchip_max98090: Optionally support HDMI use case") Signed-off-by: kbuild test robot lkp@intel.com --- rockchip_max98090.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sound/soc/rockchip/rockchip_max98090.c b/sound/soc/rockchip/rockchip_max98090.c index 550e577897198..a476749931387 100644 --- a/sound/soc/rockchip/rockchip_max98090.c +++ b/sound/soc/rockchip/rockchip_max98090.c @@ -472,7 +472,7 @@ static int snd_rk_mc_probe(struct platform_device *pdev) return ret; }
-struct rk_card_config rk_card_configs[] = { +static struct rk_card_config rk_card_configs[] = { [RK_USE_MAX98090] = { .usage = RK_USE_MAX98090, .card = &rockchip_max98090_card,
Hi Cheng-Yi,
I love your patch! Yet something to improve:
[auto build test ERROR on rockchip/for-next] [also build test ERROR on v5.4-rc5 next-20191028] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system. BTW, we also suggest to use '--base' option to specify the base tree in git format-patch, please see https://stackoverflow.com/a/37406982]
url: https://github.com/0day-ci/linux/commits/Cheng-Yi-Chiang/Add-HDMI-jack-suppo... base: https://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip.git for-next config: i386-allmodconfig (attached as .config) compiler: gcc-7 (Debian 7.4.0-14) 7.4.0 reproduce: # save the attached .config to linux build tree make ARCH=i386
If you fix the issue, kindly add following tag Reported-by: kbuild test robot lkp@intel.com
All errors (new ones prefixed by >>):
sound/soc/rockchip/snd-soc-rockchip-max98090: struct of_device_id is 196 bytes. The last of 3 is: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x72 0x6f 0x63 0x6b 0x63 0x68 0x69 0x70 0x2c 0x72 0x6f 0x63 0x6b 0x63 0x68 0x69 0x70 0x2d 0x61 0x75 0x64 0x69 0x6f 0x2d 0x6d 0x61 0x78 0x39 0x38 0x30 0x39 0x30 0x2d 0x68 0x64 0x6d 0x69 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x10 0x00 0x00 0x00
FATAL: sound/soc/rockchip/snd-soc-rockchip-max98090: struct of_device_id is not terminated with a NULL entry!
--- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation
On Tue, Oct 29, 2019 at 3:10 AM kbuild test robot lkp@intel.com wrote:
Hi Cheng-Yi,
I love your patch! Yet something to improve:
[auto build test ERROR on rockchip/for-next] [also build test ERROR on v5.4-rc5 next-20191028] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system. BTW, we also suggest to use '--base' option to specify the base tree in git format-patch, please see https://stackoverflow.com/a/37406982]
url: https://github.com/0day-ci/linux/commits/Cheng-Yi-Chiang/Add-HDMI-jack-suppo... base: https://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip.git for-next config: i386-allmodconfig (attached as .config) compiler: gcc-7 (Debian 7.4.0-14) 7.4.0 reproduce: # save the attached .config to linux build tree make ARCH=i386
If you fix the issue, kindly add following tag Reported-by: kbuild test robot lkp@intel.com
All errors (new ones prefixed by >>):
sound/soc/rockchip/snd-soc-rockchip-max98090: struct of_device_id is 196 bytes. The last of 3 is: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x72 0x6f 0x63 0x6b 0x63 0x68 0x69 0x70 0x2c 0x72 0x6f 0x63 0x6b 0x63 0x68 0x69 0x70 0x2d 0x61 0x75 0x64 0x69 0x6f 0x2d 0x6d 0x61 0x78 0x39 0x38 0x30 0x39 0x30 0x2d 0x68 0x64 0x6d 0x69 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x10 0x00 0x00 0x00
FATAL: sound/soc/rockchip/snd-soc-rockchip-max98090: struct of_device_id is not terminated with a NULL entry!
Please ignore this error for v8 patch series because the change in rockchip_max98090.c of of_device_id is removed in v9 patch series. Thanks!
0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation
In machine driver, create a jack and let hdmi-codec report jack status.
Signed-off-by: Cheng-Yi Chiang cychiang@chromium.org --- sound/soc/rockchip/Kconfig | 3 ++- sound/soc/rockchip/rockchip_max98090.c | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-)
diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig index b43657e6e655..d610b553ea3b 100644 --- a/sound/soc/rockchip/Kconfig +++ b/sound/soc/rockchip/Kconfig @@ -40,9 +40,10 @@ config SND_SOC_ROCKCHIP_MAX98090 select SND_SOC_ROCKCHIP_I2S select SND_SOC_MAX98090 select SND_SOC_TS3A227E + select SND_SOC_HDMI_CODEC help Say Y or M here if you want to add support for SoC audio on Rockchip - boards using the MAX98090 codec, such as Veyron. + boards using the MAX98090 codec and HDMI codec, such as Veyron.
config SND_SOC_ROCKCHIP_RT5645 tristate "ASoC support for Rockchip boards using a RT5645/RT5650 codec" diff --git a/sound/soc/rockchip/rockchip_max98090.c b/sound/soc/rockchip/rockchip_max98090.c index 550e57789719..fdd1d9921a4e 100644 --- a/sound/soc/rockchip/rockchip_max98090.c +++ b/sound/soc/rockchip/rockchip_max98090.c @@ -230,6 +230,25 @@ enum { DAILINK_HDMI, };
+static struct snd_soc_jack rk_hdmi_jack; + +static int rk_hdmi_init(struct snd_soc_pcm_runtime *runtime) +{ + struct snd_soc_card *card = runtime->card; + struct snd_soc_component *component = runtime->codec_dai->component; + int ret; + + /* enable jack detection */ + ret = snd_soc_card_jack_new(card, "HDMI Jack", SND_JACK_LINEOUT, + &rk_hdmi_jack, NULL, 0); + if (ret) { + dev_err(card->dev, "Can't new HDMI Jack %d\n", ret); + return ret; + } + + return hdmi_codec_set_jack_detect(component, &rk_hdmi_jack); +} + /* max98090 dai_link */ static struct snd_soc_dai_link rk_max98090_dailinks[] = { { @@ -249,6 +268,7 @@ static struct snd_soc_dai_link rk_hdmi_dailinks[] = { { .name = "HDMI", .stream_name = "HDMI", + .init = rk_hdmi_init, .ops = &rk_aif1_ops, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, @@ -271,6 +291,7 @@ static struct snd_soc_dai_link rk_max98090_hdmi_dailinks[] = { [DAILINK_HDMI] = { .name = "HDMI", .stream_name = "HDMI", + .init = rk_hdmi_init, .ops = &rk_aif1_ops, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS,
All boards using rk3288-veyron-analog-audio.dtsi have HDMI audio. Specify the support of HDMI audio on machine driver using compatible string so machine driver creates HDMI audio device.
Signed-off-by: Cheng-Yi Chiang cychiang@chromium.org --- arch/arm/boot/dts/rk3288-veyron-analog-audio.dtsi | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/rk3288-veyron-analog-audio.dtsi b/arch/arm/boot/dts/rk3288-veyron-analog-audio.dtsi index 445270aa136e..92ea623401e9 100644 --- a/arch/arm/boot/dts/rk3288-veyron-analog-audio.dtsi +++ b/arch/arm/boot/dts/rk3288-veyron-analog-audio.dtsi @@ -8,7 +8,7 @@
/ { sound { - compatible = "rockchip,rockchip-audio-max98090"; + compatible = "rockchip,rockchip-audio-max98090-hdmi"; pinctrl-names = "default"; pinctrl-0 = <&mic_det>, <&hp_det>; rockchip,model = "VEYRON-I2S"; @@ -17,6 +17,7 @@ rockchip,hp-det-gpios = <&gpio6 RK_PA5 GPIO_ACTIVE_HIGH>; rockchip,mic-det-gpios = <&gpio6 RK_PB3 GPIO_ACTIVE_LOW>; rockchip,headset-codec = <&headsetcodec>; + rockchip,hdmi-codec = <&hdmi>; }; };
Add HDMI audio support to veyron-mickey. The sound card should expose one audio device for HDMI.
Signed-off-by: Cheng-Yi Chiang cychiang@chromium.org --- arch/arm/boot/dts/rk3288-veyron-mickey.dts | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/arch/arm/boot/dts/rk3288-veyron-mickey.dts b/arch/arm/boot/dts/rk3288-veyron-mickey.dts index aa352d40c991..98a2aee240f1 100644 --- a/arch/arm/boot/dts/rk3288-veyron-mickey.dts +++ b/arch/arm/boot/dts/rk3288-veyron-mickey.dts @@ -28,6 +28,13 @@ regulator-boot-on; vin-supply = <&vcc33_sys>; }; + + sound { + compatible = "rockchip,rockchip-audio-hdmi"; + rockchip,model = "VEYRON-HDMI"; + rockchip,i2s-controller = <&i2s>; + rockchip,hdmi-codec = <&hdmi>; + }; };
&cpu_thermal {
participants (3)
-
Cheng-Yi Chiang
-
kbuild test robot
-
Rob Herring