MLK-18368-4: hdmi_rx: add generic hdmi audio rx driver
authorShengjiu Wang <shengjiu.wang@nxp.com>
Tue, 22 May 2018 07:32:51 +0000 (15:32 +0800)
committerSandor Yu <Sandor.yu@nxp.com>
Fri, 19 Apr 2019 02:40:50 +0000 (10:40 +0800)
Register generic hdmi audio rx driver.

Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
drivers/media/platform/imx8/hdmi/Makefile
drivers/media/platform/imx8/hdmi/mxc-hdmi-rx-audio.c [new file with mode: 0644]
drivers/media/platform/imx8/hdmi/mxc-hdmi-rx.c
drivers/media/platform/imx8/hdmi/mxc-hdmi-rx.h

index 9a466a5..93c0b3f 100644 (file)
@@ -1 +1,2 @@
-obj-$(CONFIG_IMX8_HDMI_RX) += mxc-hdmi-rx.o mxc-hdmi-hw.o API_AFE_ss28fdsoi_hdmirx.o
+obj-$(CONFIG_IMX8_HDMI_RX) += mxc-hdmi-rx.o mxc-hdmi-hw.o API_AFE_ss28fdsoi_hdmirx.o \
+                               mxc-hdmi-rx-audio.o
diff --git a/drivers/media/platform/imx8/hdmi/mxc-hdmi-rx-audio.c b/drivers/media/platform/imx8/hdmi/mxc-hdmi-rx-audio.c
new file mode 100644 (file)
index 0000000..d7d9a5e
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ * Copyright 2018 NXP
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/component.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of.h>
+#include <linux/irq.h>
+#include <linux/of_device.h>
+#include <sound/hdmi-codec.h>
+
+#include "mxc-hdmi-rx.h"
+#include "../../../../mxc/hdp/API_Audio.h"
+#include "../../../../mxc/hdp/API_HDMI_RX_Audio.h"
+#include "../../../../mxc/hdp/sink_pif.h"
+#include "../../../../mxc/hdp/aif_pckt2smp.h"
+
+
+static int get_audio_infoframe(state_struct *state, unsigned int *chan)
+{
+
+       unsigned int regread;
+       int ret = 0;
+       int times = 0;
+
+       cdn_apb_write(state, ADDR_SINK_PIF + (PKT_INFO_TYPE_CFG1 << 2), F_INFO_TYPE1(0x84));
+
+       cdn_apb_write(state, ADDR_SINK_PIF + (PKT_INT_MASK << 2), 0x1FFFE);
+
+       do {
+               cdn_apb_read(state, ADDR_SINK_PIF + (PKT_INT_STATUS << 2), &regread);
+               udelay(10);
+               times++;
+       } while (!(regread & (1 << 0)) && times < 100);
+
+
+       if (times == 100) {
+               ret = -EINVAL;
+               return ret;
+       }
+
+       cdn_apb_write(state, ADDR_SINK_PIF + (PKT_INFO_CTRL << 2), F_PACKET_RDN_WR(0x0) | F_PACKET_NUM(0x0));
+
+       times = 0;
+
+       do {
+               cdn_apb_read(state, ADDR_SINK_PIF + (PKT_INT_STATUS << 2), &regread);
+               udelay(10);
+               times++;
+       } while (!(regread & (1 << 16)) && times < 100);
+
+       if (times == 100) {
+               ret = -EINVAL;
+               return ret;
+       }
+
+       cdn_apb_read(state, ADDR_SINK_PIF + (PKT_INFO_DATA1 << 2), &regread);
+
+       cdn_apb_write(state, ADDR_SINK_PIF + (PKT_INFO_TYPE_CFG1 << 2), F_INFO_TYPE1(0x00));
+
+       *chan = ((regread & 0x700) >> 8) + 1;
+
+       cdn_apb_write(state, ADDR_SINK_PIF + (PKT_INFO_HEADER << 2), 0);
+       cdn_apb_write(state, ADDR_SINK_PIF + (PKT_INFO_DATA1 << 2), 0);
+       cdn_apb_write(state, ADDR_SINK_PIF + (PKT_INFO_DATA2 << 2), 0);
+       cdn_apb_write(state, ADDR_SINK_PIF + (PKT_INFO_DATA3 << 2), 0);
+       cdn_apb_write(state, ADDR_SINK_PIF + (PKT_INFO_DATA4 << 2), 0);
+       cdn_apb_write(state, ADDR_SINK_PIF + (PKT_INFO_DATA5 << 2), 0);
+       cdn_apb_write(state, ADDR_SINK_PIF + (PKT_INFO_DATA6 << 2), 0);
+       cdn_apb_write(state, ADDR_SINK_PIF + (PKT_INFO_DATA7 << 2), 0);
+
+       cdn_apb_write(state, ADDR_SINK_PIF + (PKT_INFO_CTRL << 2), F_PACKET_RDN_WR(0x1) | F_PACKET_NUM(0x0));
+
+       times = 0;
+       do {
+               cdn_apb_read(state, ADDR_SINK_PIF + (PKT_INT_STATUS << 2), &regread);
+               udelay(10);
+               times++;
+       } while (!(regread & (1 << 16)) && times < 100);
+
+       if (times == 100) {
+               ret = -EINVAL;
+               return ret;
+       }
+
+       return ret;
+}
+
+static u32 TMDS_rate_table[7] = {
+25200, 27000, 54000, 74250, 148500, 297000, 594000,
+};
+
+static u32 N_table_32k[8] = {
+/*25200, 27000, 54000, 74250, 148500, 297000, 594000,*/
+4096, 4096, 4096, 4096, 4096, 3072, 3072, 4096,
+};
+
+static u32 N_table_44k[8] = {
+6272, 6272, 6272, 6272, 6272, 4704, 9408, 6272,
+};
+
+static u32 N_table_48k[8] = {
+6144, 6144, 6144, 6144, 6144, 5120, 6144, 6144,
+};
+
+static int select_rate(u32 pclk, u32 N)
+{
+       int i = 0;
+       int rate = 0;
+
+       for (i = 0; i < 7; i++) {
+               if (pclk == TMDS_rate_table[i])
+                       break;
+       }
+
+       if (i == 7)
+               DRM_WARN("pclkc %d is not supported!\n", pclk);
+
+       if (N_table_32k[i] == N)
+               rate = 32000;
+
+       if (N_table_44k[i] == N)
+               rate = 44100;
+
+       if (N_table_44k[i] * 2 == N)
+               rate = 44100 * 2;
+
+       if (N_table_44k[i] * 4 == N)
+               rate = 44100 * 4;
+
+       if (N_table_48k[i] == N)
+               rate = 48000;
+
+       if (N_table_48k[i] * 2 == N)
+               rate = 48000 * 2;
+
+       if (N_table_48k[i] * 4 == N)
+               rate = 48000 * 4;
+
+       return rate;
+}
+
+
+
+static int mxc_hdmi_rx_audio(struct mxc_hdmi_rx_dev *hdmi)
+{
+       state_struct *state = &hdmi->state;
+       u32 regread;
+       u32 rate;
+       u32 chan = 2;
+       CDN_API_STATUS status;
+       int ret;
+
+       ret = get_audio_infoframe(state, &chan);
+       if (ret)
+               return ret;
+
+       status = CDN_API_RX_AudioAutoConfig(state, chan, chan/2, 0, 32, 32);
+       if (status != CDN_OK)
+               return -EINVAL;
+
+       if (cdn_apb_read(state, ADDR_AIF_ENCODER + (AIF_ACR_N_ST << 2), &regread))
+               return -EINVAL;
+
+       rate = select_rate(hdmi->timings->timings.bt.pixelclock/1000, regread);
+
+       hdmi->channels = chan;
+       hdmi->sample_rate = rate;
+
+       return 0;
+}
+
+/*
+ * HDMI audio codec callbacks
+ */
+static int mxc_hdmi_rx_audio_hw_params(struct device *dev, void *data,
+                                   struct hdmi_codec_daifmt *daifmt,
+                                   struct hdmi_codec_params *params)
+{
+       return 0;
+}
+
+static void mxc_hdmi_rx_audio_shutdown(struct device *dev, void *data)
+{
+       pm_runtime_put_sync(dev);
+}
+
+static int mxc_hdmi_rx_audio_startup(struct device *dev, void *data)
+{
+       struct mxc_hdmi_rx_dev *hdmi = dev_get_drvdata(dev);
+       int ret;
+
+       pm_runtime_get_sync(dev);
+       ret = mxc_hdmi_rx_audio(hdmi);
+
+       return ret;
+}
+
+static int mxc_hdmi_rx_audio_get_eld(struct device *dev, void *data, uint8_t *buf, size_t len)
+{
+       struct mxc_hdmi_rx_dev *hdmi = dev_get_drvdata(dev);
+
+       if (len < 8)
+               return -EINVAL;
+
+       memcpy(buf, &hdmi->sample_rate, 4);
+       memcpy(buf + 4, &hdmi->channels, 4);
+
+       return 0;
+}
+
+static const struct hdmi_codec_ops mxc_hdmi_rx_audio_codec_ops = {
+       .hw_params = mxc_hdmi_rx_audio_hw_params,
+       .audio_shutdown = mxc_hdmi_rx_audio_shutdown,
+       .audio_startup = mxc_hdmi_rx_audio_startup,
+       .get_eld = mxc_hdmi_rx_audio_get_eld,
+};
+
+void mxc_hdmi_rx_register_audio_driver(struct device *dev)
+{
+       struct hdmi_codec_pdata codec_data = {
+               .ops = &mxc_hdmi_rx_audio_codec_ops,
+               .max_i2s_channels = 8,
+               .i2s = 1,
+       };
+       struct platform_device *pdev;
+
+       pdev = platform_device_register_data(dev, HDMI_CODEC_DRV_NAME,
+                                            2, &codec_data,
+                                            sizeof(codec_data));
+       if (IS_ERR(pdev))
+               return;
+
+       DRM_INFO("%s driver bound to HDMI\n", HDMI_CODEC_DRV_NAME);
+}
+
+
index 2ec1366..0ab1308 100644 (file)
@@ -777,6 +777,8 @@ static int mxc_hdmi_probe(struct platform_device *pdev)
        }
 #endif
 
+       mxc_hdmi_rx_register_audio_driver(dev);
+
        dev_info(dev, "mxc hdmi rx probe successfully\n");
 
        return ret;
index 777d623..85baaff 100644 (file)
@@ -118,6 +118,9 @@ struct mxc_hdmi_rx_dev {
 
        u8 is_cec;
        struct imx_cec_dev cec;
+       u32 sample_rate;
+       u32 sample_width;
+       u32 channels;
 };
 
 enum mxc_hdmi_rx_power_state {
@@ -130,5 +133,6 @@ int hdmirx_startup(state_struct *state);
 void imx8qm_hdmi_phy_reset(state_struct *state, u8 reset);
 int hdmi_rx_init(state_struct *state);
 int mxc_hdmi_frame_timing(struct mxc_hdmi_rx_dev *hdmi_rx);
+void mxc_hdmi_rx_register_audio_driver(struct device *dev);
 
 #endif