Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
f382d9eb AT |
2 | /* |
3 | * HDMI wrapper | |
4 | * | |
1b409fda | 5 | * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com/ |
f382d9eb AT |
6 | */ |
7 | ||
ac9f2421 TV |
8 | #define DSS_SUBSYS_NAME "HDMIWP" |
9 | ||
f382d9eb | 10 | #include <linux/kernel.h> |
f382d9eb AT |
11 | #include <linux/err.h> |
12 | #include <linux/io.h> | |
13 | #include <linux/platform_device.h> | |
2d802453 | 14 | #include <linux/seq_file.h> |
f382d9eb | 15 | |
32043da7 | 16 | #include "omapdss.h" |
f382d9eb | 17 | #include "dss.h" |
ef26958a | 18 | #include "hdmi.h" |
f382d9eb AT |
19 | |
20 | void hdmi_wp_dump(struct hdmi_wp_data *wp, struct seq_file *s) | |
21 | { | |
22 | #define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, hdmi_read_reg(wp->base, r)) | |
23 | ||
24 | DUMPREG(HDMI_WP_REVISION); | |
25 | DUMPREG(HDMI_WP_SYSCONFIG); | |
26 | DUMPREG(HDMI_WP_IRQSTATUS_RAW); | |
27 | DUMPREG(HDMI_WP_IRQSTATUS); | |
28 | DUMPREG(HDMI_WP_IRQENABLE_SET); | |
29 | DUMPREG(HDMI_WP_IRQENABLE_CLR); | |
30 | DUMPREG(HDMI_WP_IRQWAKEEN); | |
31 | DUMPREG(HDMI_WP_PWR_CTRL); | |
32 | DUMPREG(HDMI_WP_DEBOUNCE); | |
33 | DUMPREG(HDMI_WP_VIDEO_CFG); | |
34 | DUMPREG(HDMI_WP_VIDEO_SIZE); | |
35 | DUMPREG(HDMI_WP_VIDEO_TIMING_H); | |
36 | DUMPREG(HDMI_WP_VIDEO_TIMING_V); | |
42116517 | 37 | DUMPREG(HDMI_WP_CLK); |
f382d9eb AT |
38 | DUMPREG(HDMI_WP_AUDIO_CFG); |
39 | DUMPREG(HDMI_WP_AUDIO_CFG2); | |
40 | DUMPREG(HDMI_WP_AUDIO_CTRL); | |
41 | DUMPREG(HDMI_WP_AUDIO_DATA); | |
42 | } | |
43 | ||
44 | u32 hdmi_wp_get_irqstatus(struct hdmi_wp_data *wp) | |
45 | { | |
46 | return hdmi_read_reg(wp->base, HDMI_WP_IRQSTATUS); | |
47 | } | |
48 | ||
49 | void hdmi_wp_set_irqstatus(struct hdmi_wp_data *wp, u32 irqstatus) | |
50 | { | |
51 | hdmi_write_reg(wp->base, HDMI_WP_IRQSTATUS, irqstatus); | |
52 | /* flush posted write */ | |
53 | hdmi_read_reg(wp->base, HDMI_WP_IRQSTATUS); | |
54 | } | |
55 | ||
56 | void hdmi_wp_set_irqenable(struct hdmi_wp_data *wp, u32 mask) | |
57 | { | |
58 | hdmi_write_reg(wp->base, HDMI_WP_IRQENABLE_SET, mask); | |
59 | } | |
60 | ||
61 | void hdmi_wp_clear_irqenable(struct hdmi_wp_data *wp, u32 mask) | |
62 | { | |
63 | hdmi_write_reg(wp->base, HDMI_WP_IRQENABLE_CLR, mask); | |
64 | } | |
65 | ||
66 | /* PHY_PWR_CMD */ | |
67 | int hdmi_wp_set_phy_pwr(struct hdmi_wp_data *wp, enum hdmi_phy_pwr val) | |
68 | { | |
69 | /* Return if already the state */ | |
70 | if (REG_GET(wp->base, HDMI_WP_PWR_CTRL, 5, 4) == val) | |
71 | return 0; | |
72 | ||
73 | /* Command for power control of HDMI PHY */ | |
74 | REG_FLD_MOD(wp->base, HDMI_WP_PWR_CTRL, val, 7, 6); | |
75 | ||
76 | /* Status of the power control of HDMI PHY */ | |
77 | if (hdmi_wait_for_bit_change(wp->base, HDMI_WP_PWR_CTRL, 5, 4, val) | |
78 | != val) { | |
ac9f2421 | 79 | DSSERR("Failed to set PHY power mode to %d\n", val); |
f382d9eb AT |
80 | return -ETIMEDOUT; |
81 | } | |
82 | ||
83 | return 0; | |
84 | } | |
85 | ||
86 | /* PLL_PWR_CMD */ | |
87 | int hdmi_wp_set_pll_pwr(struct hdmi_wp_data *wp, enum hdmi_pll_pwr val) | |
88 | { | |
89 | /* Command for power control of HDMI PLL */ | |
90 | REG_FLD_MOD(wp->base, HDMI_WP_PWR_CTRL, val, 3, 2); | |
91 | ||
92 | /* wait till PHY_PWR_STATUS is set */ | |
93 | if (hdmi_wait_for_bit_change(wp->base, HDMI_WP_PWR_CTRL, 1, 0, val) | |
94 | != val) { | |
ac9f2421 | 95 | DSSERR("Failed to set PLL_PWR_STATUS\n"); |
f382d9eb AT |
96 | return -ETIMEDOUT; |
97 | } | |
98 | ||
99 | return 0; | |
100 | } | |
101 | ||
102 | int hdmi_wp_video_start(struct hdmi_wp_data *wp) | |
103 | { | |
104 | REG_FLD_MOD(wp->base, HDMI_WP_VIDEO_CFG, true, 31, 31); | |
105 | ||
106 | return 0; | |
107 | } | |
108 | ||
109 | void hdmi_wp_video_stop(struct hdmi_wp_data *wp) | |
110 | { | |
a9fad688 TV |
111 | int i; |
112 | ||
113 | hdmi_write_reg(wp->base, HDMI_WP_IRQSTATUS, HDMI_IRQ_VIDEO_FRAME_DONE); | |
114 | ||
f382d9eb | 115 | REG_FLD_MOD(wp->base, HDMI_WP_VIDEO_CFG, false, 31, 31); |
a9fad688 TV |
116 | |
117 | for (i = 0; i < 50; ++i) { | |
118 | u32 v; | |
119 | ||
120 | msleep(20); | |
121 | ||
122 | v = hdmi_read_reg(wp->base, HDMI_WP_IRQSTATUS_RAW); | |
123 | if (v & HDMI_IRQ_VIDEO_FRAME_DONE) | |
124 | return; | |
125 | } | |
126 | ||
127 | DSSERR("no HDMI FRAMEDONE when disabling output\n"); | |
f382d9eb AT |
128 | } |
129 | ||
130 | void hdmi_wp_video_config_format(struct hdmi_wp_data *wp, | |
95e472da | 131 | const struct hdmi_video_format *video_fmt) |
f382d9eb AT |
132 | { |
133 | u32 l = 0; | |
134 | ||
135 | REG_FLD_MOD(wp->base, HDMI_WP_VIDEO_CFG, video_fmt->packing_mode, | |
136 | 10, 8); | |
137 | ||
138 | l |= FLD_VAL(video_fmt->y_res, 31, 16); | |
139 | l |= FLD_VAL(video_fmt->x_res, 15, 0); | |
140 | hdmi_write_reg(wp->base, HDMI_WP_VIDEO_SIZE, l); | |
141 | } | |
142 | ||
143 | void hdmi_wp_video_config_interface(struct hdmi_wp_data *wp, | |
95e472da | 144 | const struct videomode *vm) |
f382d9eb AT |
145 | { |
146 | u32 r; | |
d5e7efad | 147 | bool vsync_inv, hsync_inv; |
ac9f2421 | 148 | DSSDBG("Enter hdmi_wp_video_config_interface\n"); |
f382d9eb | 149 | |
d5e7efad TV |
150 | vsync_inv = !!(vm->flags & DISPLAY_FLAGS_VSYNC_LOW); |
151 | hsync_inv = !!(vm->flags & DISPLAY_FLAGS_HSYNC_LOW); | |
f382d9eb AT |
152 | |
153 | r = hdmi_read_reg(wp->base, HDMI_WP_VIDEO_CFG); | |
d5e7efad TV |
154 | r = FLD_MOD(r, 1, 7, 7); /* VSYNC_POL to dispc active high */ |
155 | r = FLD_MOD(r, 1, 6, 6); /* HSYNC_POL to dispc active high */ | |
156 | r = FLD_MOD(r, vsync_inv, 5, 5); /* CORE_VSYNC_INV */ | |
157 | r = FLD_MOD(r, hsync_inv, 4, 4); /* CORE_HSYNC_INV */ | |
da11bbbb | 158 | r = FLD_MOD(r, !!(vm->flags & DISPLAY_FLAGS_INTERLACED), 3, 3); |
f382d9eb AT |
159 | r = FLD_MOD(r, 1, 1, 0); /* HDMI_TIMING_MASTER_24BIT */ |
160 | hdmi_write_reg(wp->base, HDMI_WP_VIDEO_CFG, r); | |
161 | } | |
162 | ||
163 | void hdmi_wp_video_config_timing(struct hdmi_wp_data *wp, | |
95e472da | 164 | const struct videomode *vm) |
f382d9eb AT |
165 | { |
166 | u32 timing_h = 0; | |
167 | u32 timing_v = 0; | |
d11e5c82 | 168 | unsigned int hsync_len_offset = 1; |
f382d9eb | 169 | |
ac9f2421 | 170 | DSSDBG("Enter hdmi_wp_video_config_timing\n"); |
f382d9eb | 171 | |
c92e8727 TV |
172 | /* |
173 | * On OMAP4 and OMAP5 ES1 the HSW field is programmed as is. On OMAP5 | |
4dc2250d | 174 | * ES2+ (including DRA7/AM5 SoCs) HSW field is programmed to hsync_len-1. |
c92e8727 TV |
175 | * However, we don't support OMAP5 ES1 at all, so we can just check for |
176 | * OMAP4 here. | |
177 | */ | |
fe16bc51 | 178 | if (wp->version == 4) |
4dc2250d | 179 | hsync_len_offset = 0; |
c92e8727 | 180 | |
da11bbbb PU |
181 | timing_h |= FLD_VAL(vm->hback_porch, 31, 20); |
182 | timing_h |= FLD_VAL(vm->hfront_porch, 19, 8); | |
183 | timing_h |= FLD_VAL(vm->hsync_len - hsync_len_offset, 7, 0); | |
f382d9eb AT |
184 | hdmi_write_reg(wp->base, HDMI_WP_VIDEO_TIMING_H, timing_h); |
185 | ||
da11bbbb PU |
186 | timing_v |= FLD_VAL(vm->vback_porch, 31, 20); |
187 | timing_v |= FLD_VAL(vm->vfront_porch, 19, 8); | |
188 | timing_v |= FLD_VAL(vm->vsync_len, 7, 0); | |
f382d9eb AT |
189 | hdmi_write_reg(wp->base, HDMI_WP_VIDEO_TIMING_V, timing_v); |
190 | } | |
191 | ||
192 | void hdmi_wp_init_vid_fmt_timings(struct hdmi_video_format *video_fmt, | |
95e472da | 193 | struct videomode *vm, const struct hdmi_config *param) |
f382d9eb | 194 | { |
ac9f2421 | 195 | DSSDBG("Enter hdmi_wp_video_init_format\n"); |
f382d9eb AT |
196 | |
197 | video_fmt->packing_mode = HDMI_PACK_10b_RGB_YUV444; | |
da11bbbb PU |
198 | video_fmt->y_res = param->vm.vactive; |
199 | video_fmt->x_res = param->vm.hactive; | |
f382d9eb | 200 | |
da11bbbb PU |
201 | vm->hback_porch = param->vm.hback_porch; |
202 | vm->hfront_porch = param->vm.hfront_porch; | |
203 | vm->hsync_len = param->vm.hsync_len; | |
204 | vm->vback_porch = param->vm.vback_porch; | |
205 | vm->vfront_porch = param->vm.vfront_porch; | |
206 | vm->vsync_len = param->vm.vsync_len; | |
b2af8092 | 207 | |
da11bbbb | 208 | vm->flags = param->vm.flags; |
b2af8092 | 209 | |
da11bbbb | 210 | if (param->vm.flags & DISPLAY_FLAGS_INTERLACED) { |
b2af8092 | 211 | video_fmt->y_res /= 2; |
da11bbbb PU |
212 | vm->vback_porch /= 2; |
213 | vm->vfront_porch /= 2; | |
214 | vm->vsync_len /= 2; | |
b2af8092 TV |
215 | } |
216 | ||
da11bbbb | 217 | if (param->vm.flags & DISPLAY_FLAGS_DOUBLECLK) { |
b2af8092 | 218 | video_fmt->x_res *= 2; |
da11bbbb PU |
219 | vm->hfront_porch *= 2; |
220 | vm->hsync_len *= 2; | |
221 | vm->hback_porch *= 2; | |
b2af8092 | 222 | } |
f382d9eb AT |
223 | } |
224 | ||
f382d9eb AT |
225 | void hdmi_wp_audio_config_format(struct hdmi_wp_data *wp, |
226 | struct hdmi_audio_format *aud_fmt) | |
227 | { | |
228 | u32 r; | |
229 | ||
230 | DSSDBG("Enter hdmi_wp_audio_config_format\n"); | |
231 | ||
232 | r = hdmi_read_reg(wp->base, HDMI_WP_AUDIO_CFG); | |
fe16bc51 | 233 | if (wp->version == 4) { |
086f8281 JS |
234 | r = FLD_MOD(r, aud_fmt->stereo_channels, 26, 24); |
235 | r = FLD_MOD(r, aud_fmt->active_chnnls_msk, 23, 16); | |
236 | } | |
f382d9eb AT |
237 | r = FLD_MOD(r, aud_fmt->en_sig_blk_strt_end, 5, 5); |
238 | r = FLD_MOD(r, aud_fmt->type, 4, 4); | |
239 | r = FLD_MOD(r, aud_fmt->justification, 3, 3); | |
240 | r = FLD_MOD(r, aud_fmt->sample_order, 2, 2); | |
241 | r = FLD_MOD(r, aud_fmt->samples_per_word, 1, 1); | |
242 | r = FLD_MOD(r, aud_fmt->sample_size, 0, 0); | |
243 | hdmi_write_reg(wp->base, HDMI_WP_AUDIO_CFG, r); | |
244 | } | |
245 | ||
246 | void hdmi_wp_audio_config_dma(struct hdmi_wp_data *wp, | |
247 | struct hdmi_audio_dma *aud_dma) | |
248 | { | |
249 | u32 r; | |
250 | ||
251 | DSSDBG("Enter hdmi_wp_audio_config_dma\n"); | |
252 | ||
253 | r = hdmi_read_reg(wp->base, HDMI_WP_AUDIO_CFG2); | |
254 | r = FLD_MOD(r, aud_dma->transfer_size, 15, 8); | |
255 | r = FLD_MOD(r, aud_dma->block_size, 7, 0); | |
256 | hdmi_write_reg(wp->base, HDMI_WP_AUDIO_CFG2, r); | |
257 | ||
258 | r = hdmi_read_reg(wp->base, HDMI_WP_AUDIO_CTRL); | |
259 | r = FLD_MOD(r, aud_dma->mode, 9, 9); | |
260 | r = FLD_MOD(r, aud_dma->fifo_threshold, 8, 0); | |
261 | hdmi_write_reg(wp->base, HDMI_WP_AUDIO_CTRL, r); | |
262 | } | |
263 | ||
264 | int hdmi_wp_audio_enable(struct hdmi_wp_data *wp, bool enable) | |
265 | { | |
266 | REG_FLD_MOD(wp->base, HDMI_WP_AUDIO_CTRL, enable, 31, 31); | |
267 | ||
268 | return 0; | |
269 | } | |
270 | ||
271 | int hdmi_wp_audio_core_req_enable(struct hdmi_wp_data *wp, bool enable) | |
272 | { | |
273 | REG_FLD_MOD(wp->base, HDMI_WP_AUDIO_CTRL, enable, 30, 30); | |
274 | ||
275 | return 0; | |
276 | } | |
f382d9eb | 277 | |
fe16bc51 LP |
278 | int hdmi_wp_init(struct platform_device *pdev, struct hdmi_wp_data *wp, |
279 | unsigned int version) | |
f382d9eb AT |
280 | { |
281 | struct resource *res; | |
f382d9eb | 282 | |
77601507 | 283 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "wp"); |
fc2daf3b | 284 | wp->base = devm_ioremap_resource(&pdev->dev, res); |
b22622f0 | 285 | if (IS_ERR(wp->base)) |
2b22df83 | 286 | return PTR_ERR(wp->base); |
b22622f0 LP |
287 | |
288 | wp->phys_base = res->start; | |
fe16bc51 | 289 | wp->version = version; |
f382d9eb AT |
290 | |
291 | return 0; | |
292 | } | |
58652163 JS |
293 | |
294 | phys_addr_t hdmi_wp_get_audio_dma_addr(struct hdmi_wp_data *wp) | |
295 | { | |
296 | return wp->phys_base + HDMI_WP_AUDIO_DATA; | |
297 | } |