drm: msm: Add ASoC generic hdmi audio codec support.
authorSrinivas Kandagatla <srinivas.kandagatla@linaro.org>
Fri, 10 Jun 2016 09:45:56 +0000 (10:45 +0100)
committerRob Clark <robdclark@gmail.com>
Sat, 16 Jul 2016 14:29:00 +0000 (10:29 -0400)
This patch adds support to generic audio codec via
ASoC hdmi-codec infrastucture which is merged recently.

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
[rebased on efc9194]
Signed-off-by: Rob Clark <robdclark@gmail.com>
drivers/gpu/drm/msm/Kconfig
drivers/gpu/drm/msm/hdmi/hdmi.c
drivers/gpu/drm/msm/hdmi/hdmi.h

index 167a4971f47c59d20a29b86b9a98a651426055ab..7c7a0314a7563f46a62b1ef4f156aba7615ebc03 100644 (file)
@@ -10,6 +10,7 @@ config DRM_MSM
        select SHMEM
        select TMPFS
        select QCOM_SCM
+       select SND_SOC_HDMI_CODEC if SND_SOC
        default y
        help
          DRM/KMS driver for MSM/snapdragon.
index 51b9ea552f9762c91c13f6a399b7314e2e9854c4..33bf52c6b312ccfc792ef3010e74a854df0554d1 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/of_irq.h>
 #include <linux/of_gpio.h>
 
+#include <sound/hdmi-codec.h>
 #include "hdmi.h"
 
 void msm_hdmi_set_mode(struct hdmi *hdmi, bool power_on)
@@ -434,6 +435,114 @@ static int msm_hdmi_get_gpio(struct device_node *of_node, const char *name)
        return gpio;
 }
 
+/*
+ * HDMI audio codec callbacks
+ */
+static int msm_hdmi_audio_hw_params(struct device *dev, void *data,
+                                   struct hdmi_codec_daifmt *daifmt,
+                                   struct hdmi_codec_params *params)
+{
+       struct hdmi *hdmi = dev_get_drvdata(dev);
+       unsigned int chan;
+       unsigned int channel_allocation = 0;
+       unsigned int rate;
+       unsigned int level_shift  = 0; /* 0dB */
+       bool down_mix = false;
+
+       dev_dbg(dev, "%u Hz, %d bit, %d channels\n", params->sample_rate,
+                params->sample_width, params->cea.channels);
+
+       switch (params->cea.channels) {
+       case 2:
+               /* FR and FL speakers */
+               channel_allocation  = 0;
+               chan = MSM_HDMI_AUDIO_CHANNEL_2;
+               break;
+       case 4:
+               /* FC, LFE, FR and FL speakers */
+               channel_allocation  = 0x3;
+               chan = MSM_HDMI_AUDIO_CHANNEL_4;
+               break;
+       case 6:
+               /* RR, RL, FC, LFE, FR and FL speakers */
+               channel_allocation  = 0x0B;
+               chan = MSM_HDMI_AUDIO_CHANNEL_6;
+               break;
+       case 8:
+               /* FRC, FLC, RR, RL, FC, LFE, FR and FL speakers */
+               channel_allocation  = 0x1F;
+               chan = MSM_HDMI_AUDIO_CHANNEL_8;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (params->sample_rate) {
+       case 32000:
+               rate = HDMI_SAMPLE_RATE_32KHZ;
+               break;
+       case 44100:
+               rate = HDMI_SAMPLE_RATE_44_1KHZ;
+               break;
+       case 48000:
+               rate = HDMI_SAMPLE_RATE_48KHZ;
+               break;
+       case 88200:
+               rate = HDMI_SAMPLE_RATE_88_2KHZ;
+               break;
+       case 96000:
+               rate = HDMI_SAMPLE_RATE_96KHZ;
+               break;
+       case 176400:
+               rate = HDMI_SAMPLE_RATE_176_4KHZ;
+               break;
+       case 192000:
+               rate = HDMI_SAMPLE_RATE_192KHZ;
+               break;
+       default:
+               dev_err(dev, "rate[%d] not supported!\n",
+                       params->sample_rate);
+               return -EINVAL;
+       }
+
+       msm_hdmi_audio_set_sample_rate(hdmi, rate);
+       msm_hdmi_audio_info_setup(hdmi, 1, chan, channel_allocation,
+                             level_shift, down_mix);
+
+       return 0;
+}
+
+static void msm_hdmi_audio_shutdown(struct device *dev, void *data)
+{
+       struct hdmi *hdmi = dev_get_drvdata(dev);
+
+       msm_hdmi_audio_info_setup(hdmi, 0, 0, 0, 0, 0);
+}
+
+static const struct hdmi_codec_ops msm_hdmi_audio_codec_ops = {
+       .hw_params = msm_hdmi_audio_hw_params,
+       .audio_shutdown = msm_hdmi_audio_shutdown,
+};
+
+static struct hdmi_codec_pdata codec_data = {
+       .ops = &msm_hdmi_audio_codec_ops,
+       .max_i2s_channels = 8,
+       .i2s = 1,
+};
+
+static int msm_hdmi_register_audio_driver(struct hdmi *hdmi, struct device *dev)
+{
+       hdmi->audio_pdev = platform_device_register_data(dev,
+                                                        HDMI_CODEC_DRV_NAME,
+                                                        PLATFORM_DEVID_AUTO,
+                                                        &codec_data,
+                                                        sizeof(codec_data));
+       if (IS_ERR(hdmi->audio_pdev))
+               return PTR_ERR(hdmi->audio_pdev);
+
+       return 0;
+}
+
 static int msm_hdmi_bind(struct device *dev, struct device *master, void *data)
 {
        struct drm_device *drm = dev_get_drvdata(master);
@@ -441,7 +550,7 @@ static int msm_hdmi_bind(struct device *dev, struct device *master, void *data)
        static struct hdmi_platform_config *hdmi_cfg;
        struct hdmi *hdmi;
        struct device_node *of_node = dev->of_node;
-       int i;
+       int i, err;
 
        hdmi_cfg = (struct hdmi_platform_config *)
                        of_device_get_match_data(dev);
@@ -468,6 +577,12 @@ static int msm_hdmi_bind(struct device *dev, struct device *master, void *data)
                return PTR_ERR(hdmi);
        priv->hdmi = hdmi;
 
+       err = msm_hdmi_register_audio_driver(hdmi, dev);
+       if (err) {
+               DRM_ERROR("Failed to attach an audio codec %d\n", err);
+               hdmi->audio_pdev = NULL;
+       }
+
        return 0;
 }
 
@@ -477,6 +592,9 @@ static void msm_hdmi_unbind(struct device *dev, struct device *master,
        struct drm_device *drm = dev_get_drvdata(master);
        struct msm_drm_private *priv = drm->dev_private;
        if (priv->hdmi) {
+               if (priv->hdmi->audio_pdev)
+                       platform_device_unregister(priv->hdmi->audio_pdev);
+
                msm_hdmi_destroy(priv->hdmi);
                priv->hdmi = NULL;
        }
index bc7ba0bdee072056526f3f1da8cc88c981ec1150..accc9a61611d35bf9718d66fe09ce6c6e40a549f 100644 (file)
@@ -50,6 +50,7 @@ struct hdmi_hdcp_ctrl;
 struct hdmi {
        struct drm_device *dev;
        struct platform_device *pdev;
+       struct platform_device *audio_pdev;
 
        const struct hdmi_platform_config *config;
 
@@ -210,6 +211,19 @@ static inline int msm_hdmi_pll_8996_init(struct platform_device *pdev)
 /*
  * audio:
  */
+/* Supported HDMI Audio channels and rates */
+#define        MSM_HDMI_AUDIO_CHANNEL_2        0
+#define        MSM_HDMI_AUDIO_CHANNEL_4        1
+#define        MSM_HDMI_AUDIO_CHANNEL_6        2
+#define        MSM_HDMI_AUDIO_CHANNEL_8        3
+
+#define        HDMI_SAMPLE_RATE_32KHZ          0
+#define        HDMI_SAMPLE_RATE_44_1KHZ        1
+#define        HDMI_SAMPLE_RATE_48KHZ          2
+#define        HDMI_SAMPLE_RATE_88_2KHZ        3
+#define        HDMI_SAMPLE_RATE_96KHZ          4
+#define        HDMI_SAMPLE_RATE_176_4KHZ       5
+#define        HDMI_SAMPLE_RATE_192KHZ         6
 
 int msm_hdmi_audio_update(struct hdmi *hdmi);
 int msm_hdmi_audio_info_setup(struct hdmi *hdmi, bool enabled,