Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm: bridge: add DesignWare HDMI I2S audio support

Current dw-hdmi is supporting sound via AHB bus, but it has
I2S audio feature too. This patch adds I2S audio support to dw-hdmi.
This HDMI I2S is supported by using ALSA SoC common HDMI encoder
driver.

Tested-by: Jose Abreu <joabreu@synopsys.com>
Acked-by: Russell King <rmk+kernel@armlinux.org.uk>
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Link: http://patchwork.freedesktop.org/patch/msgid/8737j2bxba.wl%kuninori.morimoto.gx@renesas.com

authored by

Kuninori Morimoto and committed by
Archit Taneja
2761ba6c 2db86dfc

+197 -2
+8
drivers/gpu/drm/bridge/Kconfig
··· 39 39 Designware HDMI block. This is used in conjunction with 40 40 the i.MX6 HDMI driver. 41 41 42 + config DRM_DW_HDMI_I2S_AUDIO 43 + tristate "Synopsis Designware I2S Audio interface" 44 + depends on DRM_DW_HDMI 45 + select SND_SOC_HDMI_CODEC 46 + help 47 + Support the I2S Audio interface which is part of the Synopsis 48 + Designware HDMI block. 49 + 42 50 config DRM_NXP_PTN3460 43 51 tristate "NXP PTN3460 DP/LVDS bridge" 44 52 depends on OF
+1
drivers/gpu/drm/bridge/Makefile
··· 4 4 obj-$(CONFIG_DRM_DUMB_VGA_DAC) += dumb-vga-dac.o 5 5 obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o 6 6 obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o 7 + obj-$(CONFIG_DRM_DW_HDMI_I2S_AUDIO) += dw-hdmi-i2s-audio.o 7 8 obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o 8 9 obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o 9 10 obj-$(CONFIG_DRM_SIL_SII8620) += sil-sii8620.o
+7
drivers/gpu/drm/bridge/dw-hdmi-audio.h
··· 11 11 u8 *eld; 12 12 }; 13 13 14 + struct dw_hdmi_i2s_audio_data { 15 + struct dw_hdmi *hdmi; 16 + 17 + void (*write)(struct dw_hdmi *hdmi, u8 val, int offset); 18 + u8 (*read)(struct dw_hdmi *hdmi, int offset); 19 + }; 20 + 14 21 #endif
+141
drivers/gpu/drm/bridge/dw-hdmi-i2s-audio.c
··· 1 + /* 2 + * dw-hdmi-i2s-audio.c 3 + * 4 + * Copyright (c) 2016 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License version 2 as 8 + * published by the Free Software Foundation. 9 + */ 10 + #include <drm/bridge/dw_hdmi.h> 11 + 12 + #include <sound/hdmi-codec.h> 13 + 14 + #include "dw-hdmi.h" 15 + #include "dw-hdmi-audio.h" 16 + 17 + #define DRIVER_NAME "dw-hdmi-i2s-audio" 18 + 19 + static inline void hdmi_write(struct dw_hdmi_i2s_audio_data *audio, 20 + u8 val, int offset) 21 + { 22 + struct dw_hdmi *hdmi = audio->hdmi; 23 + 24 + audio->write(hdmi, val, offset); 25 + } 26 + 27 + static inline u8 hdmi_read(struct dw_hdmi_i2s_audio_data *audio, int offset) 28 + { 29 + struct dw_hdmi *hdmi = audio->hdmi; 30 + 31 + return audio->read(hdmi, offset); 32 + } 33 + 34 + static int dw_hdmi_i2s_hw_params(struct device *dev, void *data, 35 + struct hdmi_codec_daifmt *fmt, 36 + struct hdmi_codec_params *hparms) 37 + { 38 + struct dw_hdmi_i2s_audio_data *audio = data; 39 + struct dw_hdmi *hdmi = audio->hdmi; 40 + u8 conf0 = 0; 41 + u8 conf1 = 0; 42 + u8 inputclkfs = 0; 43 + 44 + /* it cares I2S only */ 45 + if ((fmt->fmt != HDMI_I2S) || 46 + (fmt->bit_clk_master | fmt->frame_clk_master)) { 47 + dev_err(dev, "unsupported format/settings\n"); 48 + return -EINVAL; 49 + } 50 + 51 + inputclkfs = HDMI_AUD_INPUTCLKFS_64FS; 52 + conf0 = HDMI_AUD_CONF0_I2S_ALL_ENABLE; 53 + 54 + switch (hparms->sample_width) { 55 + case 16: 56 + conf1 = HDMI_AUD_CONF1_WIDTH_16; 57 + break; 58 + case 24: 59 + case 32: 60 + conf1 = HDMI_AUD_CONF1_WIDTH_24; 61 + break; 62 + } 63 + 64 + dw_hdmi_set_sample_rate(hdmi, hparms->sample_rate); 65 + 66 + hdmi_write(audio, inputclkfs, HDMI_AUD_INPUTCLKFS); 67 + hdmi_write(audio, conf0, HDMI_AUD_CONF0); 68 + hdmi_write(audio, conf1, HDMI_AUD_CONF1); 69 + 70 + dw_hdmi_audio_enable(hdmi); 71 + 72 + return 0; 73 + } 74 + 75 + static void dw_hdmi_i2s_audio_shutdown(struct device *dev, void *data) 76 + { 77 + struct dw_hdmi_i2s_audio_data *audio = data; 78 + struct dw_hdmi *hdmi = audio->hdmi; 79 + 80 + dw_hdmi_audio_disable(hdmi); 81 + 82 + hdmi_write(audio, HDMI_AUD_CONF0_SW_RESET, HDMI_AUD_CONF0); 83 + } 84 + 85 + static struct hdmi_codec_ops dw_hdmi_i2s_ops = { 86 + .hw_params = dw_hdmi_i2s_hw_params, 87 + .audio_shutdown = dw_hdmi_i2s_audio_shutdown, 88 + }; 89 + 90 + static int snd_dw_hdmi_probe(struct platform_device *pdev) 91 + { 92 + struct dw_hdmi_i2s_audio_data *audio = pdev->dev.platform_data; 93 + struct platform_device_info pdevinfo; 94 + struct hdmi_codec_pdata pdata; 95 + struct platform_device *platform; 96 + 97 + pdata.ops = &dw_hdmi_i2s_ops; 98 + pdata.i2s = 1; 99 + pdata.max_i2s_channels = 6; 100 + pdata.data = audio; 101 + 102 + memset(&pdevinfo, 0, sizeof(pdevinfo)); 103 + pdevinfo.parent = pdev->dev.parent; 104 + pdevinfo.id = PLATFORM_DEVID_AUTO; 105 + pdevinfo.name = HDMI_CODEC_DRV_NAME; 106 + pdevinfo.data = &pdata; 107 + pdevinfo.size_data = sizeof(pdata); 108 + pdevinfo.dma_mask = DMA_BIT_MASK(32); 109 + 110 + platform = platform_device_register_full(&pdevinfo); 111 + if (IS_ERR(platform)) 112 + return PTR_ERR(platform); 113 + 114 + dev_set_drvdata(&pdev->dev, platform); 115 + 116 + return 0; 117 + } 118 + 119 + static int snd_dw_hdmi_remove(struct platform_device *pdev) 120 + { 121 + struct platform_device *platform = dev_get_drvdata(&pdev->dev); 122 + 123 + platform_device_unregister(platform); 124 + 125 + return 0; 126 + } 127 + 128 + static struct platform_driver snd_dw_hdmi_driver = { 129 + .probe = snd_dw_hdmi_probe, 130 + .remove = snd_dw_hdmi_remove, 131 + .driver = { 132 + .name = DRIVER_NAME, 133 + .owner = THIS_MODULE, 134 + }, 135 + }; 136 + module_platform_driver(snd_dw_hdmi_driver); 137 + 138 + MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); 139 + MODULE_DESCRIPTION("Synopsis Designware HDMI I2S ALSA SoC interface"); 140 + MODULE_LICENSE("GPL v2"); 141 + MODULE_ALIAS("platform:" DRIVER_NAME);
+20 -2
drivers/gpu/drm/bridge/dw-hdmi.c
··· 1871 1871 struct device_node *np = dev->of_node; 1872 1872 struct platform_device_info pdevinfo; 1873 1873 struct device_node *ddc_node; 1874 - struct dw_hdmi_audio_data audio; 1875 1874 struct dw_hdmi *hdmi; 1876 1875 int ret; 1877 1876 u32 val = 1; 1877 + u8 config0; 1878 + u8 config1; 1878 1879 1879 1880 hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); 1880 1881 if (!hdmi) ··· 2012 2011 pdevinfo.parent = dev; 2013 2012 pdevinfo.id = PLATFORM_DEVID_AUTO; 2014 2013 2015 - if (hdmi_readb(hdmi, HDMI_CONFIG1_ID) & HDMI_CONFIG1_AHB) { 2014 + config0 = hdmi_readb(hdmi, HDMI_CONFIG0_ID); 2015 + config1 = hdmi_readb(hdmi, HDMI_CONFIG1_ID); 2016 + 2017 + if (config1 & HDMI_CONFIG1_AHB) { 2018 + struct dw_hdmi_audio_data audio; 2019 + 2016 2020 audio.phys = iores->start; 2017 2021 audio.base = hdmi->regs; 2018 2022 audio.irq = irq; ··· 2025 2019 audio.eld = hdmi->connector.eld; 2026 2020 2027 2021 pdevinfo.name = "dw-hdmi-ahb-audio"; 2022 + pdevinfo.data = &audio; 2023 + pdevinfo.size_data = sizeof(audio); 2024 + pdevinfo.dma_mask = DMA_BIT_MASK(32); 2025 + hdmi->audio = platform_device_register_full(&pdevinfo); 2026 + } else if (config0 & HDMI_CONFIG0_I2S) { 2027 + struct dw_hdmi_i2s_audio_data audio; 2028 + 2029 + audio.hdmi = hdmi; 2030 + audio.write = hdmi_writeb; 2031 + audio.read = hdmi_readb; 2032 + 2033 + pdevinfo.name = "dw-hdmi-i2s-audio"; 2028 2034 pdevinfo.data = &audio; 2029 2035 pdevinfo.size_data = sizeof(audio); 2030 2036 pdevinfo.dma_mask = DMA_BIT_MASK(32);
+20
drivers/gpu/drm/bridge/dw-hdmi.h
··· 545 545 #define HDMI_I2CM_FS_SCL_LCNT_0_ADDR 0x7E12 546 546 547 547 enum { 548 + /* CONFIG0_ID field values */ 549 + HDMI_CONFIG0_I2S = 0x10, 550 + 548 551 /* CONFIG1_ID field values */ 549 552 HDMI_CONFIG1_AHB = 0x01, 550 553 ··· 894 891 HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL = 0x08, 895 892 HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_MASK = 0x04, 896 893 894 + /* AUD_CONF0 field values */ 895 + HDMI_AUD_CONF0_SW_RESET = 0x80, 896 + HDMI_AUD_CONF0_I2S_ALL_ENABLE = 0x2F, 897 + 898 + /* AUD_CONF1 field values */ 899 + HDMI_AUD_CONF1_MODE_I2S = 0x00, 900 + HDMI_AUD_CONF1_MODE_RIGHT_J = 0x02, 901 + HDMI_AUD_CONF1_MODE_LEFT_J = 0x04, 902 + HDMI_AUD_CONF1_WIDTH_16 = 0x10, 903 + HDMI_AUD_CONF1_WIDTH_24 = 0x18, 904 + 897 905 /* AUD_CTS3 field values */ 898 906 HDMI_AUD_CTS3_N_SHIFT_OFFSET = 5, 899 907 HDMI_AUD_CTS3_N_SHIFT_MASK = 0xe0, ··· 918 904 from our part. Can't set it, will read as 0. */ 919 905 HDMI_AUD_CTS3_CTS_MANUAL = 0x10, 920 906 HDMI_AUD_CTS3_AUDCTS19_16_MASK = 0x0f, 907 + 908 + /* HDMI_AUD_INPUTCLKFS field values */ 909 + HDMI_AUD_INPUTCLKFS_128FS = 0, 910 + HDMI_AUD_INPUTCLKFS_256FS = 1, 911 + HDMI_AUD_INPUTCLKFS_512FS = 2, 912 + HDMI_AUD_INPUTCLKFS_64FS = 4, 921 913 922 914 /* AHB_DMA_CONF0 field values */ 923 915 HDMI_AHB_DMA_CONF0_SW_FIFO_RST_OFFSET = 7,