diff options
author | John Stultz <john.stultz@linaro.org> | 2016-11-28 20:22:31 -0500 |
---|---|---|
committer | Archit Taneja <architt@codeaurora.org> | 2016-11-28 23:18:46 -0500 |
commit | 53c515befe2864173ac5acb5c248587ce24d245e (patch) | |
tree | f527b1f69024b9cab762177d2c4f15574eb879dd | |
parent | c45a4e46570aa18f6128a2a44243c5d142a8e411 (diff) |
drm/bridge: adv7511: Add Audio support
This patch adds support to Audio for both adv7511 and adv7533
bridge chips.
This patch was originally from [1] by Lars-Peter Clausen <lars@metafoo.de>
and was adapted by Archit Taneja <architt@codeaurora.org> and
Srinivas Kandagatla <srinivas.kandagatla@linaro.org>.
Then I heavily reworked it to use the hdmi-codec driver. And also
folded in some audio packet initialization done by Andy Green
<andy.green@linaro.org>. So credit to them, but blame to me.
[1] https://github.com/analogdevicesinc/linux/blob/xcomm_zynq/drivers/gpu/drm/i2c/adv7511_audio.c
Cc: David Airlie <airlied@linux.ie>
Cc: Archit Taneja <architt@codeaurora.org>
Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Cc: Wolfram Sang <wsa+renesas@sang-engineering.com>
Cc: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Cc: "Ville Syrjälä" <ville.syrjala@linux.intel.com>
Cc: Boris Brezillon <boris.brezillon@free-electrons.com>
Cc: Andy Green <andy@warmcat.com>
Cc: Dave Long <dave.long@linaro.org>
Cc: Guodong Xu <guodong.xu@linaro.org>
Cc: Zhangfei Gao <zhangfei.gao@linaro.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Lars-Peter Clausen <lars@metafoo.de>
Cc: Jose Abreu <joabreu@synopsys.com>
Cc: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Cc: dri-devel@lists.freedesktop.org
Acked-by: Lars-Peter Clausen <lars@metafoo.de>
Signed-off-by: John Stultz <john.stultz@linaro.org>
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Link: http://patchwork.freedesktop.org/patch/msgid/1480382552-28219-2-git-send-email-john.stultz@linaro.org
-rw-r--r-- | drivers/gpu/drm/bridge/adv7511/Kconfig | 8 | ||||
-rw-r--r-- | drivers/gpu/drm/bridge/adv7511/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/bridge/adv7511/adv7511.h | 16 | ||||
-rw-r--r-- | drivers/gpu/drm/bridge/adv7511/adv7511_audio.c | 213 | ||||
-rw-r--r-- | drivers/gpu/drm/bridge/adv7511/adv7511_drv.c | 4 |
5 files changed, 242 insertions, 0 deletions
diff --git a/drivers/gpu/drm/bridge/adv7511/Kconfig b/drivers/gpu/drm/bridge/adv7511/Kconfig index d2b0499ab7d7..2fed567f9943 100644 --- a/drivers/gpu/drm/bridge/adv7511/Kconfig +++ b/drivers/gpu/drm/bridge/adv7511/Kconfig | |||
@@ -6,6 +6,14 @@ config DRM_I2C_ADV7511 | |||
6 | help | 6 | help |
7 | Support for the Analog Device ADV7511(W) and ADV7513 HDMI encoders. | 7 | Support for the Analog Device ADV7511(W) and ADV7513 HDMI encoders. |
8 | 8 | ||
9 | config DRM_I2C_ADV7511_AUDIO | ||
10 | bool "ADV7511 HDMI Audio driver" | ||
11 | depends on DRM_I2C_ADV7511 && SND_SOC | ||
12 | select SND_SOC_HDMI_CODEC | ||
13 | help | ||
14 | Support the ADV7511 HDMI Audio interface. This is used in | ||
15 | conjunction with the AV7511 HDMI driver. | ||
16 | |||
9 | config DRM_I2C_ADV7533 | 17 | config DRM_I2C_ADV7533 |
10 | bool "ADV7533 encoder" | 18 | bool "ADV7533 encoder" |
11 | depends on DRM_I2C_ADV7511 | 19 | depends on DRM_I2C_ADV7511 |
diff --git a/drivers/gpu/drm/bridge/adv7511/Makefile b/drivers/gpu/drm/bridge/adv7511/Makefile index 9019327fff4c..5ba675534f6e 100644 --- a/drivers/gpu/drm/bridge/adv7511/Makefile +++ b/drivers/gpu/drm/bridge/adv7511/Makefile | |||
@@ -1,3 +1,4 @@ | |||
1 | adv7511-y := adv7511_drv.o | 1 | adv7511-y := adv7511_drv.o |
2 | adv7511-$(CONFIG_DRM_I2C_ADV7511_AUDIO) += adv7511_audio.o | ||
2 | adv7511-$(CONFIG_DRM_I2C_ADV7533) += adv7533.o | 3 | adv7511-$(CONFIG_DRM_I2C_ADV7533) += adv7533.o |
3 | obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511.o | 4 | obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511.o |
diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511.h b/drivers/gpu/drm/bridge/adv7511/adv7511.h index 161c923d6162..992d76ce02bb 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511.h +++ b/drivers/gpu/drm/bridge/adv7511/adv7511.h | |||
@@ -309,6 +309,8 @@ struct adv7511 { | |||
309 | struct drm_display_mode curr_mode; | 309 | struct drm_display_mode curr_mode; |
310 | 310 | ||
311 | unsigned int f_tmds; | 311 | unsigned int f_tmds; |
312 | unsigned int f_audio; | ||
313 | unsigned int audio_source; | ||
312 | 314 | ||
313 | unsigned int current_edid_segment; | 315 | unsigned int current_edid_segment; |
314 | uint8_t edid_buf[256]; | 316 | uint8_t edid_buf[256]; |
@@ -334,6 +336,7 @@ struct adv7511 { | |||
334 | bool use_timing_gen; | 336 | bool use_timing_gen; |
335 | 337 | ||
336 | enum adv7511_type type; | 338 | enum adv7511_type type; |
339 | struct platform_device *audio_pdev; | ||
337 | }; | 340 | }; |
338 | 341 | ||
339 | #ifdef CONFIG_DRM_I2C_ADV7533 | 342 | #ifdef CONFIG_DRM_I2C_ADV7533 |
@@ -389,4 +392,17 @@ static inline int adv7533_parse_dt(struct device_node *np, struct adv7511 *adv) | |||
389 | } | 392 | } |
390 | #endif | 393 | #endif |
391 | 394 | ||
395 | #ifdef CONFIG_DRM_I2C_ADV7511_AUDIO | ||
396 | int adv7511_audio_init(struct device *dev, struct adv7511 *adv7511); | ||
397 | void adv7511_audio_exit(struct adv7511 *adv7511); | ||
398 | #else /*CONFIG_DRM_I2C_ADV7511_AUDIO */ | ||
399 | static inline int adv7511_audio_init(struct device *dev, struct adv7511 *adv7511) | ||
400 | { | ||
401 | return 0; | ||
402 | } | ||
403 | static inline void adv7511_audio_exit(struct adv7511 *adv7511) | ||
404 | { | ||
405 | } | ||
406 | #endif /* CONFIG_DRM_I2C_ADV7511_AUDIO */ | ||
407 | |||
392 | #endif /* __DRM_I2C_ADV7511_H__ */ | 408 | #endif /* __DRM_I2C_ADV7511_H__ */ |
diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_audio.c b/drivers/gpu/drm/bridge/adv7511/adv7511_audio.c new file mode 100644 index 000000000000..cf92ebfe6ab7 --- /dev/null +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_audio.c | |||
@@ -0,0 +1,213 @@ | |||
1 | /* | ||
2 | * Analog Devices ADV7511 HDMI transmitter driver | ||
3 | * | ||
4 | * Copyright 2012 Analog Devices Inc. | ||
5 | * Copyright (c) 2016, Linaro Limited | ||
6 | * | ||
7 | * Licensed under the GPL-2. | ||
8 | */ | ||
9 | |||
10 | #include <sound/core.h> | ||
11 | #include <sound/hdmi-codec.h> | ||
12 | #include <sound/pcm.h> | ||
13 | #include <sound/soc.h> | ||
14 | |||
15 | #include "adv7511.h" | ||
16 | |||
17 | static void adv7511_calc_cts_n(unsigned int f_tmds, unsigned int fs, | ||
18 | unsigned int *cts, unsigned int *n) | ||
19 | { | ||
20 | switch (fs) { | ||
21 | case 32000: | ||
22 | *n = 4096; | ||
23 | break; | ||
24 | case 44100: | ||
25 | *n = 6272; | ||
26 | break; | ||
27 | case 48000: | ||
28 | *n = 6144; | ||
29 | break; | ||
30 | } | ||
31 | |||
32 | *cts = ((f_tmds * *n) / (128 * fs)) * 1000; | ||
33 | } | ||
34 | |||
35 | static int adv7511_update_cts_n(struct adv7511 *adv7511) | ||
36 | { | ||
37 | unsigned int cts = 0; | ||
38 | unsigned int n = 0; | ||
39 | |||
40 | adv7511_calc_cts_n(adv7511->f_tmds, adv7511->f_audio, &cts, &n); | ||
41 | |||
42 | regmap_write(adv7511->regmap, ADV7511_REG_N0, (n >> 16) & 0xf); | ||
43 | regmap_write(adv7511->regmap, ADV7511_REG_N1, (n >> 8) & 0xff); | ||
44 | regmap_write(adv7511->regmap, ADV7511_REG_N2, n & 0xff); | ||
45 | |||
46 | regmap_write(adv7511->regmap, ADV7511_REG_CTS_MANUAL0, | ||
47 | (cts >> 16) & 0xf); | ||
48 | regmap_write(adv7511->regmap, ADV7511_REG_CTS_MANUAL1, | ||
49 | (cts >> 8) & 0xff); | ||
50 | regmap_write(adv7511->regmap, ADV7511_REG_CTS_MANUAL2, | ||
51 | cts & 0xff); | ||
52 | |||
53 | return 0; | ||
54 | } | ||
55 | |||
56 | int adv7511_hdmi_hw_params(struct device *dev, void *data, | ||
57 | struct hdmi_codec_daifmt *fmt, | ||
58 | struct hdmi_codec_params *hparms) | ||
59 | { | ||
60 | struct adv7511 *adv7511 = dev_get_drvdata(dev); | ||
61 | unsigned int audio_source, i2s_format = 0; | ||
62 | unsigned int invert_clock; | ||
63 | unsigned int rate; | ||
64 | unsigned int len; | ||
65 | |||
66 | switch (hparms->sample_rate) { | ||
67 | case 32000: | ||
68 | rate = ADV7511_SAMPLE_FREQ_32000; | ||
69 | break; | ||
70 | case 44100: | ||
71 | rate = ADV7511_SAMPLE_FREQ_44100; | ||
72 | break; | ||
73 | case 48000: | ||
74 | rate = ADV7511_SAMPLE_FREQ_48000; | ||
75 | break; | ||
76 | case 88200: | ||
77 | rate = ADV7511_SAMPLE_FREQ_88200; | ||
78 | break; | ||
79 | case 96000: | ||
80 | rate = ADV7511_SAMPLE_FREQ_96000; | ||
81 | break; | ||
82 | case 176400: | ||
83 | rate = ADV7511_SAMPLE_FREQ_176400; | ||
84 | break; | ||
85 | case 192000: | ||
86 | rate = ADV7511_SAMPLE_FREQ_192000; | ||
87 | break; | ||
88 | default: | ||
89 | return -EINVAL; | ||
90 | } | ||
91 | |||
92 | switch (hparms->sample_width) { | ||
93 | case 16: | ||
94 | len = ADV7511_I2S_SAMPLE_LEN_16; | ||
95 | break; | ||
96 | case 18: | ||
97 | len = ADV7511_I2S_SAMPLE_LEN_18; | ||
98 | break; | ||
99 | case 20: | ||
100 | len = ADV7511_I2S_SAMPLE_LEN_20; | ||
101 | break; | ||
102 | case 24: | ||
103 | len = ADV7511_I2S_SAMPLE_LEN_24; | ||
104 | break; | ||
105 | default: | ||
106 | return -EINVAL; | ||
107 | } | ||
108 | |||
109 | switch (fmt->fmt) { | ||
110 | case HDMI_I2S: | ||
111 | audio_source = ADV7511_AUDIO_SOURCE_I2S; | ||
112 | i2s_format = ADV7511_I2S_FORMAT_I2S; | ||
113 | break; | ||
114 | case HDMI_RIGHT_J: | ||
115 | audio_source = ADV7511_AUDIO_SOURCE_I2S; | ||
116 | i2s_format = ADV7511_I2S_FORMAT_RIGHT_J; | ||
117 | break; | ||
118 | case HDMI_LEFT_J: | ||
119 | audio_source = ADV7511_AUDIO_SOURCE_I2S; | ||
120 | i2s_format = ADV7511_I2S_FORMAT_LEFT_J; | ||
121 | break; | ||
122 | default: | ||
123 | return -EINVAL; | ||
124 | } | ||
125 | |||
126 | invert_clock = fmt->bit_clk_inv; | ||
127 | |||
128 | regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_SOURCE, 0x70, | ||
129 | audio_source << 4); | ||
130 | regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CONFIG, BIT(6), | ||
131 | invert_clock << 6); | ||
132 | regmap_update_bits(adv7511->regmap, ADV7511_REG_I2S_CONFIG, 0x03, | ||
133 | i2s_format); | ||
134 | |||
135 | adv7511->audio_source = audio_source; | ||
136 | |||
137 | adv7511->f_audio = hparms->sample_rate; | ||
138 | |||
139 | adv7511_update_cts_n(adv7511); | ||
140 | |||
141 | regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CFG3, | ||
142 | ADV7511_AUDIO_CFG3_LEN_MASK, len); | ||
143 | regmap_update_bits(adv7511->regmap, ADV7511_REG_I2C_FREQ_ID_CFG, | ||
144 | ADV7511_I2C_FREQ_ID_CFG_RATE_MASK, rate << 4); | ||
145 | regmap_write(adv7511->regmap, 0x73, 0x1); | ||
146 | |||
147 | return 0; | ||
148 | } | ||
149 | |||
150 | static int audio_startup(struct device *dev, void *data) | ||
151 | { | ||
152 | struct adv7511 *adv7511 = dev_get_drvdata(dev); | ||
153 | |||
154 | regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CONFIG, | ||
155 | BIT(7), 0); | ||
156 | |||
157 | /* hide Audio infoframe updates */ | ||
158 | regmap_update_bits(adv7511->regmap, ADV7511_REG_INFOFRAME_UPDATE, | ||
159 | BIT(5), BIT(5)); | ||
160 | /* enable N/CTS, enable Audio sample packets */ | ||
161 | regmap_update_bits(adv7511->regmap, ADV7511_REG_PACKET_ENABLE1, | ||
162 | BIT(5), BIT(5)); | ||
163 | /* enable N/CTS */ | ||
164 | regmap_update_bits(adv7511->regmap, ADV7511_REG_PACKET_ENABLE1, | ||
165 | BIT(6), BIT(6)); | ||
166 | /* not copyrighted */ | ||
167 | regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CFG1, | ||
168 | BIT(5), BIT(5)); | ||
169 | /* enable audio infoframes */ | ||
170 | regmap_update_bits(adv7511->regmap, ADV7511_REG_PACKET_ENABLE1, | ||
171 | BIT(3), BIT(3)); | ||
172 | /* AV mute disable */ | ||
173 | regmap_update_bits(adv7511->regmap, ADV7511_REG_GC(0), | ||
174 | BIT(7) | BIT(6), BIT(7)); | ||
175 | /* use Audio infoframe updated info */ | ||
176 | regmap_update_bits(adv7511->regmap, ADV7511_REG_GC(1), | ||
177 | BIT(5), 0); | ||
178 | return 0; | ||
179 | } | ||
180 | |||
181 | static void audio_shutdown(struct device *dev, void *data) | ||
182 | { | ||
183 | } | ||
184 | |||
185 | static const struct hdmi_codec_ops adv7511_codec_ops = { | ||
186 | .hw_params = adv7511_hdmi_hw_params, | ||
187 | .audio_shutdown = audio_shutdown, | ||
188 | .audio_startup = audio_startup, | ||
189 | }; | ||
190 | |||
191 | static struct hdmi_codec_pdata codec_data = { | ||
192 | .ops = &adv7511_codec_ops, | ||
193 | .max_i2s_channels = 2, | ||
194 | .i2s = 1, | ||
195 | }; | ||
196 | |||
197 | int adv7511_audio_init(struct device *dev, struct adv7511 *adv7511) | ||
198 | { | ||
199 | adv7511->audio_pdev = platform_device_register_data(dev, | ||
200 | HDMI_CODEC_DRV_NAME, | ||
201 | PLATFORM_DEVID_AUTO, | ||
202 | &codec_data, | ||
203 | sizeof(codec_data)); | ||
204 | return PTR_ERR_OR_ZERO(adv7511->audio_pdev); | ||
205 | } | ||
206 | |||
207 | void adv7511_audio_exit(struct adv7511 *adv7511) | ||
208 | { | ||
209 | if (adv7511->audio_pdev) { | ||
210 | platform_device_unregister(adv7511->audio_pdev); | ||
211 | adv7511->audio_pdev = NULL; | ||
212 | } | ||
213 | } | ||
diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c index 8ed3906dd411..8dba729f6ef9 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c | |||
@@ -1037,6 +1037,8 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) | |||
1037 | goto err_unregister_cec; | 1037 | goto err_unregister_cec; |
1038 | } | 1038 | } |
1039 | 1039 | ||
1040 | adv7511_audio_init(dev, adv7511); | ||
1041 | |||
1040 | return 0; | 1042 | return 0; |
1041 | 1043 | ||
1042 | err_unregister_cec: | 1044 | err_unregister_cec: |
@@ -1058,6 +1060,8 @@ static int adv7511_remove(struct i2c_client *i2c) | |||
1058 | 1060 | ||
1059 | drm_bridge_remove(&adv7511->bridge); | 1061 | drm_bridge_remove(&adv7511->bridge); |
1060 | 1062 | ||
1063 | adv7511_audio_exit(adv7511); | ||
1064 | |||
1061 | i2c_unregister_device(adv7511->i2c_edid); | 1065 | i2c_unregister_device(adv7511->i2c_edid); |
1062 | 1066 | ||
1063 | kfree(adv7511->edid); | 1067 | kfree(adv7511->edid); |