]>
Commit | Line | Data |
---|---|---|
caab277b | 1 | // SPDX-License-Identifier: GPL-2.0-only |
559d6701 | 2 | /* |
559d6701 | 3 | * Copyright (C) 2009 Nokia Corporation |
6505d75c | 4 | * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> |
559d6701 TV |
5 | * |
6 | * Some code and ideas taken from drivers/video/omap/ driver | |
7 | * by Imre Deak. | |
559d6701 TV |
8 | */ |
9 | ||
10 | #define DSS_SUBSYS_NAME "DSS" | |
11 | ||
11765d16 | 12 | #include <linux/debugfs.h> |
a921c1a8 | 13 | #include <linux/dma-mapping.h> |
559d6701 | 14 | #include <linux/kernel.h> |
2ecef246 | 15 | #include <linux/module.h> |
559d6701 | 16 | #include <linux/io.h> |
a8a35931 | 17 | #include <linux/export.h> |
559d6701 TV |
18 | #include <linux/err.h> |
19 | #include <linux/delay.h> | |
559d6701 TV |
20 | #include <linux/seq_file.h> |
21 | #include <linux/clk.h> | |
2639d6b9 | 22 | #include <linux/pinctrl/consumer.h> |
24e6289c | 23 | #include <linux/platform_device.h> |
4fbafaf3 | 24 | #include <linux/pm_runtime.h> |
185bae10 | 25 | #include <linux/gfp.h> |
33366d0e | 26 | #include <linux/sizes.h> |
be40eecf TV |
27 | #include <linux/mfd/syscon.h> |
28 | #include <linux/regmap.h> | |
2ecef246 | 29 | #include <linux/of.h> |
18daeb8e | 30 | #include <linux/of_device.h> |
09bffa6e | 31 | #include <linux/of_graph.h> |
99767548 | 32 | #include <linux/regulator/consumer.h> |
cb17a4ae | 33 | #include <linux/suspend.h> |
736e60dd | 34 | #include <linux/component.h> |
18daeb8e | 35 | #include <linux/sys_soc.h> |
559d6701 | 36 | |
32043da7 | 37 | #include "omapdss.h" |
559d6701 TV |
38 | #include "dss.h" |
39 | ||
559d6701 TV |
40 | struct dss_reg { |
41 | u16 idx; | |
42 | }; | |
43 | ||
44 | #define DSS_REG(idx) ((const struct dss_reg) { idx }) | |
45 | ||
46 | #define DSS_REVISION DSS_REG(0x0000) | |
47 | #define DSS_SYSCONFIG DSS_REG(0x0010) | |
48 | #define DSS_SYSSTATUS DSS_REG(0x0014) | |
559d6701 TV |
49 | #define DSS_CONTROL DSS_REG(0x0040) |
50 | #define DSS_SDI_CONTROL DSS_REG(0x0044) | |
51 | #define DSS_PLL_CONTROL DSS_REG(0x0048) | |
52 | #define DSS_SDI_STATUS DSS_REG(0x005C) | |
53 | ||
360c2153 LP |
54 | #define REG_GET(dss, idx, start, end) \ |
55 | FLD_GET(dss_read_reg(dss, idx), start, end) | |
559d6701 | 56 | |
360c2153 LP |
57 | #define REG_FLD_MOD(dss, idx, val, start, end) \ |
58 | dss_write_reg(dss, idx, \ | |
59 | FLD_MOD(dss_read_reg(dss, idx), val, start, end)) | |
559d6701 | 60 | |
fecea252 | 61 | struct dss_ops { |
8aea8e6a LP |
62 | int (*dpi_select_source)(struct dss_device *dss, int port, |
63 | enum omap_channel channel); | |
64 | int (*select_lcd_source)(struct dss_device *dss, | |
65 | enum omap_channel channel, | |
66 | enum dss_clk_source clk_src); | |
fecea252 LP |
67 | }; |
68 | ||
185bae10 | 69 | struct dss_features { |
b8dab2bd | 70 | enum dss_model model; |
185bae10 | 71 | u8 fck_div_max; |
9f0fbaea | 72 | unsigned int fck_freq_max; |
185bae10 | 73 | u8 dss_fck_multiplier; |
64ad846f | 74 | const char *parent_clk_name; |
234f9a22 | 75 | const enum omap_display_type *ports; |
387ce9f2 | 76 | int num_ports; |
51919572 | 77 | const enum omap_dss_output_id *outputs; |
fecea252 | 78 | const struct dss_ops *ops; |
6d85d4ad | 79 | struct dss_reg_field dispc_clk_switch; |
4569ab75 | 80 | bool has_lcd_clk_src; |
185bae10 CM |
81 | }; |
82 | ||
235e7dba | 83 | static const char * const dss_generic_clk_source_names[] = { |
3b63ca75 TV |
84 | [DSS_CLK_SRC_FCK] = "FCK", |
85 | [DSS_CLK_SRC_PLL1_1] = "PLL1:1", | |
86 | [DSS_CLK_SRC_PLL1_2] = "PLL1:2", | |
b5d8c757 | 87 | [DSS_CLK_SRC_PLL1_3] = "PLL1:3", |
3b63ca75 TV |
88 | [DSS_CLK_SRC_PLL2_1] = "PLL2:1", |
89 | [DSS_CLK_SRC_PLL2_2] = "PLL2:2", | |
b5d8c757 TV |
90 | [DSS_CLK_SRC_PLL2_3] = "PLL2:3", |
91 | [DSS_CLK_SRC_HDMI_PLL] = "HDMI PLL", | |
067a57e4 AT |
92 | }; |
93 | ||
360c2153 LP |
94 | static inline void dss_write_reg(struct dss_device *dss, |
95 | const struct dss_reg idx, u32 val) | |
559d6701 | 96 | { |
360c2153 | 97 | __raw_writel(val, dss->base + idx.idx); |
559d6701 TV |
98 | } |
99 | ||
360c2153 | 100 | static inline u32 dss_read_reg(struct dss_device *dss, const struct dss_reg idx) |
559d6701 | 101 | { |
360c2153 | 102 | return __raw_readl(dss->base + idx.idx); |
559d6701 TV |
103 | } |
104 | ||
360c2153 LP |
105 | #define SR(dss, reg) \ |
106 | dss->ctx[(DSS_##reg).idx / sizeof(u32)] = dss_read_reg(dss, DSS_##reg) | |
107 | #define RR(dss, reg) \ | |
108 | dss_write_reg(dss, DSS_##reg, dss->ctx[(DSS_##reg).idx / sizeof(u32)]) | |
559d6701 | 109 | |
360c2153 | 110 | static void dss_save_context(struct dss_device *dss) |
559d6701 | 111 | { |
4fbafaf3 | 112 | DSSDBG("dss_save_context\n"); |
559d6701 | 113 | |
360c2153 | 114 | SR(dss, CONTROL); |
559d6701 | 115 | |
360c2153 LP |
116 | if (dss->feat->outputs[OMAP_DSS_CHANNEL_LCD] & OMAP_DSS_OUTPUT_SDI) { |
117 | SR(dss, SDI_CONTROL); | |
118 | SR(dss, PLL_CONTROL); | |
6ec549e5 | 119 | } |
69f06054 | 120 | |
360c2153 | 121 | dss->ctx_valid = true; |
69f06054 TV |
122 | |
123 | DSSDBG("context saved\n"); | |
559d6701 TV |
124 | } |
125 | ||
360c2153 | 126 | static void dss_restore_context(struct dss_device *dss) |
559d6701 | 127 | { |
4fbafaf3 | 128 | DSSDBG("dss_restore_context\n"); |
559d6701 | 129 | |
360c2153 | 130 | if (!dss->ctx_valid) |
69f06054 TV |
131 | return; |
132 | ||
360c2153 | 133 | RR(dss, CONTROL); |
559d6701 | 134 | |
360c2153 LP |
135 | if (dss->feat->outputs[OMAP_DSS_CHANNEL_LCD] & OMAP_DSS_OUTPUT_SDI) { |
136 | RR(dss, SDI_CONTROL); | |
137 | RR(dss, PLL_CONTROL); | |
6ec549e5 | 138 | } |
69f06054 TV |
139 | |
140 | DSSDBG("context restored\n"); | |
559d6701 TV |
141 | } |
142 | ||
143 | #undef SR | |
144 | #undef RR | |
145 | ||
27260999 | 146 | void dss_ctrl_pll_enable(struct dss_pll *pll, bool enable) |
be40eecf | 147 | { |
d11e5c82 LP |
148 | unsigned int shift; |
149 | unsigned int val; | |
be40eecf | 150 | |
27260999 | 151 | if (!pll->dss->syscon_pll_ctrl) |
be40eecf TV |
152 | return; |
153 | ||
154 | val = !enable; | |
155 | ||
27260999 | 156 | switch (pll->id) { |
be40eecf TV |
157 | case DSS_PLL_VIDEO1: |
158 | shift = 0; | |
159 | break; | |
160 | case DSS_PLL_VIDEO2: | |
161 | shift = 1; | |
162 | break; | |
163 | case DSS_PLL_HDMI: | |
164 | shift = 2; | |
165 | break; | |
166 | default: | |
27260999 | 167 | DSSERR("illegal DSS PLL ID %d\n", pll->id); |
be40eecf TV |
168 | return; |
169 | } | |
170 | ||
27260999 LP |
171 | regmap_update_bits(pll->dss->syscon_pll_ctrl, |
172 | pll->dss->syscon_pll_ctrl_offset, | |
173 | 1 << shift, val << shift); | |
be40eecf TV |
174 | } |
175 | ||
360c2153 LP |
176 | static int dss_ctrl_pll_set_control_mux(struct dss_device *dss, |
177 | enum dss_clk_source clk_src, | |
178 | enum omap_channel channel) | |
be40eecf | 179 | { |
d11e5c82 | 180 | unsigned int shift, val; |
be40eecf | 181 | |
360c2153 | 182 | if (!dss->syscon_pll_ctrl) |
c63b1ec0 | 183 | return -EINVAL; |
be40eecf TV |
184 | |
185 | switch (channel) { | |
186 | case OMAP_DSS_CHANNEL_LCD: | |
187 | shift = 3; | |
188 | ||
c63b1ec0 TV |
189 | switch (clk_src) { |
190 | case DSS_CLK_SRC_PLL1_1: | |
be40eecf | 191 | val = 0; break; |
c63b1ec0 | 192 | case DSS_CLK_SRC_HDMI_PLL: |
be40eecf TV |
193 | val = 1; break; |
194 | default: | |
195 | DSSERR("error in PLL mux config for LCD\n"); | |
c63b1ec0 | 196 | return -EINVAL; |
be40eecf TV |
197 | } |
198 | ||
199 | break; | |
200 | case OMAP_DSS_CHANNEL_LCD2: | |
201 | shift = 5; | |
202 | ||
c63b1ec0 TV |
203 | switch (clk_src) { |
204 | case DSS_CLK_SRC_PLL1_3: | |
be40eecf | 205 | val = 0; break; |
c63b1ec0 | 206 | case DSS_CLK_SRC_PLL2_3: |
be40eecf | 207 | val = 1; break; |
c63b1ec0 | 208 | case DSS_CLK_SRC_HDMI_PLL: |
be40eecf TV |
209 | val = 2; break; |
210 | default: | |
211 | DSSERR("error in PLL mux config for LCD2\n"); | |
c63b1ec0 | 212 | return -EINVAL; |
be40eecf TV |
213 | } |
214 | ||
215 | break; | |
216 | case OMAP_DSS_CHANNEL_LCD3: | |
217 | shift = 7; | |
218 | ||
c63b1ec0 TV |
219 | switch (clk_src) { |
220 | case DSS_CLK_SRC_PLL2_1: | |
be40eecf | 221 | val = 0; break; |
c63b1ec0 TV |
222 | case DSS_CLK_SRC_PLL1_3: |
223 | val = 1; break; | |
224 | case DSS_CLK_SRC_HDMI_PLL: | |
be40eecf TV |
225 | val = 2; break; |
226 | default: | |
227 | DSSERR("error in PLL mux config for LCD3\n"); | |
c63b1ec0 | 228 | return -EINVAL; |
be40eecf TV |
229 | } |
230 | ||
231 | break; | |
232 | default: | |
233 | DSSERR("error in PLL mux config\n"); | |
c63b1ec0 | 234 | return -EINVAL; |
be40eecf TV |
235 | } |
236 | ||
360c2153 | 237 | regmap_update_bits(dss->syscon_pll_ctrl, dss->syscon_pll_ctrl_offset, |
be40eecf | 238 | 0x3 << shift, val << shift); |
c63b1ec0 TV |
239 | |
240 | return 0; | |
be40eecf TV |
241 | } |
242 | ||
d7157dfe | 243 | void dss_sdi_init(struct dss_device *dss, int datapairs) |
559d6701 TV |
244 | { |
245 | u32 l; | |
246 | ||
247 | BUG_ON(datapairs > 3 || datapairs < 1); | |
248 | ||
360c2153 | 249 | l = dss_read_reg(dss, DSS_SDI_CONTROL); |
559d6701 TV |
250 | l = FLD_MOD(l, 0xf, 19, 15); /* SDI_PDIV */ |
251 | l = FLD_MOD(l, datapairs-1, 3, 2); /* SDI_PRSEL */ | |
252 | l = FLD_MOD(l, 2, 1, 0); /* SDI_BWSEL */ | |
360c2153 | 253 | dss_write_reg(dss, DSS_SDI_CONTROL, l); |
559d6701 | 254 | |
360c2153 | 255 | l = dss_read_reg(dss, DSS_PLL_CONTROL); |
559d6701 TV |
256 | l = FLD_MOD(l, 0x7, 25, 22); /* SDI_PLL_FREQSEL */ |
257 | l = FLD_MOD(l, 0xb, 16, 11); /* SDI_PLL_REGN */ | |
258 | l = FLD_MOD(l, 0xb4, 10, 1); /* SDI_PLL_REGM */ | |
360c2153 | 259 | dss_write_reg(dss, DSS_PLL_CONTROL, l); |
559d6701 TV |
260 | } |
261 | ||
d7157dfe | 262 | int dss_sdi_enable(struct dss_device *dss) |
559d6701 TV |
263 | { |
264 | unsigned long timeout; | |
265 | ||
8a7eda76 | 266 | dispc_pck_free_enable(dss->dispc, 1); |
559d6701 TV |
267 | |
268 | /* Reset SDI PLL */ | |
360c2153 | 269 | REG_FLD_MOD(dss, DSS_PLL_CONTROL, 1, 18, 18); /* SDI_PLL_SYSRESET */ |
559d6701 TV |
270 | udelay(1); /* wait 2x PCLK */ |
271 | ||
272 | /* Lock SDI PLL */ | |
360c2153 | 273 | REG_FLD_MOD(dss, DSS_PLL_CONTROL, 1, 28, 28); /* SDI_PLL_GOBIT */ |
559d6701 TV |
274 | |
275 | /* Waiting for PLL lock request to complete */ | |
276 | timeout = jiffies + msecs_to_jiffies(500); | |
360c2153 | 277 | while (dss_read_reg(dss, DSS_SDI_STATUS) & (1 << 6)) { |
559d6701 TV |
278 | if (time_after_eq(jiffies, timeout)) { |
279 | DSSERR("PLL lock request timed out\n"); | |
280 | goto err1; | |
281 | } | |
282 | } | |
283 | ||
284 | /* Clearing PLL_GO bit */ | |
360c2153 | 285 | REG_FLD_MOD(dss, DSS_PLL_CONTROL, 0, 28, 28); |
559d6701 TV |
286 | |
287 | /* Waiting for PLL to lock */ | |
288 | timeout = jiffies + msecs_to_jiffies(500); | |
360c2153 | 289 | while (!(dss_read_reg(dss, DSS_SDI_STATUS) & (1 << 5))) { |
559d6701 TV |
290 | if (time_after_eq(jiffies, timeout)) { |
291 | DSSERR("PLL lock timed out\n"); | |
292 | goto err1; | |
293 | } | |
294 | } | |
295 | ||
8a7eda76 | 296 | dispc_lcd_enable_signal(dss->dispc, 1); |
559d6701 TV |
297 | |
298 | /* Waiting for SDI reset to complete */ | |
299 | timeout = jiffies + msecs_to_jiffies(500); | |
360c2153 | 300 | while (!(dss_read_reg(dss, DSS_SDI_STATUS) & (1 << 2))) { |
559d6701 TV |
301 | if (time_after_eq(jiffies, timeout)) { |
302 | DSSERR("SDI reset timed out\n"); | |
303 | goto err2; | |
304 | } | |
305 | } | |
306 | ||
307 | return 0; | |
308 | ||
309 | err2: | |
8a7eda76 | 310 | dispc_lcd_enable_signal(dss->dispc, 0); |
559d6701 TV |
311 | err1: |
312 | /* Reset SDI PLL */ | |
360c2153 | 313 | REG_FLD_MOD(dss, DSS_PLL_CONTROL, 0, 18, 18); /* SDI_PLL_SYSRESET */ |
559d6701 | 314 | |
8a7eda76 | 315 | dispc_pck_free_enable(dss->dispc, 0); |
559d6701 TV |
316 | |
317 | return -ETIMEDOUT; | |
318 | } | |
319 | ||
d7157dfe | 320 | void dss_sdi_disable(struct dss_device *dss) |
559d6701 | 321 | { |
8a7eda76 | 322 | dispc_lcd_enable_signal(dss->dispc, 0); |
559d6701 | 323 | |
8a7eda76 | 324 | dispc_pck_free_enable(dss->dispc, 0); |
559d6701 TV |
325 | |
326 | /* Reset SDI PLL */ | |
360c2153 | 327 | REG_FLD_MOD(dss, DSS_PLL_CONTROL, 0, 18, 18); /* SDI_PLL_SYSRESET */ |
559d6701 TV |
328 | } |
329 | ||
407bd564 | 330 | const char *dss_get_clk_source_name(enum dss_clk_source clk_src) |
067a57e4 | 331 | { |
235e7dba | 332 | return dss_generic_clk_source_names[clk_src]; |
067a57e4 AT |
333 | } |
334 | ||
360c2153 | 335 | static void dss_dump_clocks(struct dss_device *dss, struct seq_file *s) |
559d6701 | 336 | { |
557a1544 | 337 | const char *fclk_name; |
0acf659f | 338 | unsigned long fclk_rate; |
559d6701 | 339 | |
360c2153 | 340 | if (dss_runtime_get(dss)) |
4fbafaf3 | 341 | return; |
559d6701 | 342 | |
559d6701 TV |
343 | seq_printf(s, "- DSS -\n"); |
344 | ||
3b63ca75 | 345 | fclk_name = dss_get_clk_source_name(DSS_CLK_SRC_FCK); |
360c2153 | 346 | fclk_rate = clk_get_rate(dss->dss_clk); |
559d6701 | 347 | |
557a1544 TV |
348 | seq_printf(s, "%s = %lu\n", |
349 | fclk_name, | |
9c15d762 | 350 | fclk_rate); |
559d6701 | 351 | |
360c2153 | 352 | dss_runtime_put(dss); |
559d6701 TV |
353 | } |
354 | ||
f33656e1 | 355 | static int dss_dump_regs(struct seq_file *s, void *p) |
559d6701 | 356 | { |
360c2153 LP |
357 | struct dss_device *dss = s->private; |
358 | ||
359 | #define DUMPREG(dss, r) seq_printf(s, "%-35s %08x\n", #r, dss_read_reg(dss, r)) | |
559d6701 | 360 | |
360c2153 | 361 | if (dss_runtime_get(dss)) |
f33656e1 | 362 | return 0; |
559d6701 | 363 | |
360c2153 LP |
364 | DUMPREG(dss, DSS_REVISION); |
365 | DUMPREG(dss, DSS_SYSCONFIG); | |
366 | DUMPREG(dss, DSS_SYSSTATUS); | |
367 | DUMPREG(dss, DSS_CONTROL); | |
6ec549e5 | 368 | |
360c2153 LP |
369 | if (dss->feat->outputs[OMAP_DSS_CHANNEL_LCD] & OMAP_DSS_OUTPUT_SDI) { |
370 | DUMPREG(dss, DSS_SDI_CONTROL); | |
371 | DUMPREG(dss, DSS_PLL_CONTROL); | |
372 | DUMPREG(dss, DSS_SDI_STATUS); | |
6ec549e5 | 373 | } |
559d6701 | 374 | |
360c2153 | 375 | dss_runtime_put(dss); |
559d6701 | 376 | #undef DUMPREG |
f33656e1 | 377 | return 0; |
559d6701 TV |
378 | } |
379 | ||
83df2d4e TV |
380 | static int dss_debug_dump_clocks(struct seq_file *s, void *p) |
381 | { | |
382 | struct dss_device *dss = s->private; | |
383 | ||
384 | dss_dump_clocks(dss, s); | |
385 | dispc_dump_clocks(dss->dispc, s); | |
83df2d4e TV |
386 | return 0; |
387 | } | |
388 | ||
c63b1ec0 TV |
389 | static int dss_get_channel_index(enum omap_channel channel) |
390 | { | |
391 | switch (channel) { | |
392 | case OMAP_DSS_CHANNEL_LCD: | |
393 | return 0; | |
394 | case OMAP_DSS_CHANNEL_LCD2: | |
395 | return 1; | |
396 | case OMAP_DSS_CHANNEL_LCD3: | |
397 | return 2; | |
398 | default: | |
399 | WARN_ON(1); | |
400 | return 0; | |
401 | } | |
402 | } | |
403 | ||
360c2153 LP |
404 | static void dss_select_dispc_clk_source(struct dss_device *dss, |
405 | enum dss_clk_source clk_src) | |
2f18c4d8 TV |
406 | { |
407 | int b; | |
408 | ||
c63b1ec0 TV |
409 | /* |
410 | * We always use PRCM clock as the DISPC func clock, except on DSS3, | |
411 | * where we don't have separate DISPC and LCD clock sources. | |
412 | */ | |
360c2153 | 413 | if (WARN_ON(dss->feat->has_lcd_clk_src && clk_src != DSS_CLK_SRC_FCK)) |
c63b1ec0 TV |
414 | return; |
415 | ||
66534e8e | 416 | switch (clk_src) { |
3b63ca75 | 417 | case DSS_CLK_SRC_FCK: |
66534e8e AT |
418 | b = 0; |
419 | break; | |
3b63ca75 | 420 | case DSS_CLK_SRC_PLL1_1: |
66534e8e | 421 | b = 1; |
66534e8e | 422 | break; |
3b63ca75 | 423 | case DSS_CLK_SRC_PLL2_1: |
5a8b572d | 424 | b = 2; |
5a8b572d | 425 | break; |
66534e8e AT |
426 | default: |
427 | BUG(); | |
c6eee968 | 428 | return; |
66534e8e | 429 | } |
e406f907 | 430 | |
360c2153 LP |
431 | REG_FLD_MOD(dss, DSS_CONTROL, b, /* DISPC_CLK_SWITCH */ |
432 | dss->feat->dispc_clk_switch.start, | |
433 | dss->feat->dispc_clk_switch.end); | |
2f18c4d8 | 434 | |
360c2153 | 435 | dss->dispc_clk_source = clk_src; |
2f18c4d8 TV |
436 | } |
437 | ||
8aea8e6a LP |
438 | void dss_select_dsi_clk_source(struct dss_device *dss, int dsi_module, |
439 | enum dss_clk_source clk_src) | |
559d6701 | 440 | { |
a2e5d827 | 441 | int b, pos; |
2f18c4d8 | 442 | |
66534e8e | 443 | switch (clk_src) { |
3b63ca75 | 444 | case DSS_CLK_SRC_FCK: |
66534e8e AT |
445 | b = 0; |
446 | break; | |
3b63ca75 | 447 | case DSS_CLK_SRC_PLL1_2: |
5a8b572d | 448 | BUG_ON(dsi_module != 0); |
66534e8e | 449 | b = 1; |
66534e8e | 450 | break; |
3b63ca75 | 451 | case DSS_CLK_SRC_PLL2_2: |
5a8b572d AT |
452 | BUG_ON(dsi_module != 1); |
453 | b = 1; | |
5a8b572d | 454 | break; |
66534e8e AT |
455 | default: |
456 | BUG(); | |
c6eee968 | 457 | return; |
66534e8e | 458 | } |
e406f907 | 459 | |
a2e5d827 | 460 | pos = dsi_module == 0 ? 1 : 10; |
360c2153 | 461 | REG_FLD_MOD(dss, DSS_CONTROL, b, pos, pos); /* DSIx_CLK_SWITCH */ |
2f18c4d8 | 462 | |
8aea8e6a | 463 | dss->dsi_clk_source[dsi_module] = clk_src; |
559d6701 TV |
464 | } |
465 | ||
8aea8e6a LP |
466 | static int dss_lcd_clk_mux_dra7(struct dss_device *dss, |
467 | enum omap_channel channel, | |
468 | enum dss_clk_source clk_src) | |
c63b1ec0 TV |
469 | { |
470 | const u8 ctrl_bits[] = { | |
471 | [OMAP_DSS_CHANNEL_LCD] = 0, | |
472 | [OMAP_DSS_CHANNEL_LCD2] = 12, | |
473 | [OMAP_DSS_CHANNEL_LCD3] = 19, | |
474 | }; | |
475 | ||
476 | u8 ctrl_bit = ctrl_bits[channel]; | |
477 | int r; | |
478 | ||
479 | if (clk_src == DSS_CLK_SRC_FCK) { | |
480 | /* LCDx_CLK_SWITCH */ | |
360c2153 | 481 | REG_FLD_MOD(dss, DSS_CONTROL, 0, ctrl_bit, ctrl_bit); |
c63b1ec0 TV |
482 | return -EINVAL; |
483 | } | |
484 | ||
360c2153 | 485 | r = dss_ctrl_pll_set_control_mux(dss, clk_src, channel); |
c63b1ec0 TV |
486 | if (r) |
487 | return r; | |
488 | ||
360c2153 | 489 | REG_FLD_MOD(dss, DSS_CONTROL, 1, ctrl_bit, ctrl_bit); |
c63b1ec0 TV |
490 | |
491 | return 0; | |
492 | } | |
493 | ||
8aea8e6a LP |
494 | static int dss_lcd_clk_mux_omap5(struct dss_device *dss, |
495 | enum omap_channel channel, | |
496 | enum dss_clk_source clk_src) | |
c63b1ec0 TV |
497 | { |
498 | const u8 ctrl_bits[] = { | |
499 | [OMAP_DSS_CHANNEL_LCD] = 0, | |
500 | [OMAP_DSS_CHANNEL_LCD2] = 12, | |
501 | [OMAP_DSS_CHANNEL_LCD3] = 19, | |
502 | }; | |
503 | const enum dss_clk_source allowed_plls[] = { | |
504 | [OMAP_DSS_CHANNEL_LCD] = DSS_CLK_SRC_PLL1_1, | |
505 | [OMAP_DSS_CHANNEL_LCD2] = DSS_CLK_SRC_FCK, | |
506 | [OMAP_DSS_CHANNEL_LCD3] = DSS_CLK_SRC_PLL2_1, | |
507 | }; | |
508 | ||
509 | u8 ctrl_bit = ctrl_bits[channel]; | |
510 | ||
511 | if (clk_src == DSS_CLK_SRC_FCK) { | |
512 | /* LCDx_CLK_SWITCH */ | |
360c2153 | 513 | REG_FLD_MOD(dss, DSS_CONTROL, 0, ctrl_bit, ctrl_bit); |
c63b1ec0 TV |
514 | return -EINVAL; |
515 | } | |
516 | ||
517 | if (WARN_ON(allowed_plls[channel] != clk_src)) | |
518 | return -EINVAL; | |
519 | ||
360c2153 | 520 | REG_FLD_MOD(dss, DSS_CONTROL, 1, ctrl_bit, ctrl_bit); |
c63b1ec0 TV |
521 | |
522 | return 0; | |
523 | } | |
524 | ||
8aea8e6a LP |
525 | static int dss_lcd_clk_mux_omap4(struct dss_device *dss, |
526 | enum omap_channel channel, | |
527 | enum dss_clk_source clk_src) | |
c63b1ec0 TV |
528 | { |
529 | const u8 ctrl_bits[] = { | |
530 | [OMAP_DSS_CHANNEL_LCD] = 0, | |
531 | [OMAP_DSS_CHANNEL_LCD2] = 12, | |
532 | }; | |
533 | const enum dss_clk_source allowed_plls[] = { | |
534 | [OMAP_DSS_CHANNEL_LCD] = DSS_CLK_SRC_PLL1_1, | |
535 | [OMAP_DSS_CHANNEL_LCD2] = DSS_CLK_SRC_PLL2_1, | |
536 | }; | |
537 | ||
538 | u8 ctrl_bit = ctrl_bits[channel]; | |
539 | ||
540 | if (clk_src == DSS_CLK_SRC_FCK) { | |
541 | /* LCDx_CLK_SWITCH */ | |
360c2153 | 542 | REG_FLD_MOD(dss, DSS_CONTROL, 0, ctrl_bit, ctrl_bit); |
c63b1ec0 TV |
543 | return 0; |
544 | } | |
545 | ||
546 | if (WARN_ON(allowed_plls[channel] != clk_src)) | |
547 | return -EINVAL; | |
548 | ||
360c2153 | 549 | REG_FLD_MOD(dss, DSS_CONTROL, 1, ctrl_bit, ctrl_bit); |
c63b1ec0 TV |
550 | |
551 | return 0; | |
552 | } | |
553 | ||
8aea8e6a LP |
554 | void dss_select_lcd_clk_source(struct dss_device *dss, |
555 | enum omap_channel channel, | |
556 | enum dss_clk_source clk_src) | |
ea75159e | 557 | { |
c63b1ec0 TV |
558 | int idx = dss_get_channel_index(channel); |
559 | int r; | |
ea75159e | 560 | |
8aea8e6a | 561 | if (!dss->feat->has_lcd_clk_src) { |
360c2153 | 562 | dss_select_dispc_clk_source(dss, clk_src); |
8aea8e6a | 563 | dss->lcd_clk_source[idx] = clk_src; |
ea75159e | 564 | return; |
a5b8399f | 565 | } |
ea75159e | 566 | |
8aea8e6a | 567 | r = dss->feat->ops->select_lcd_source(dss, channel, clk_src); |
c63b1ec0 | 568 | if (r) |
c6eee968 | 569 | return; |
ea75159e | 570 | |
8aea8e6a | 571 | dss->lcd_clk_source[idx] = clk_src; |
ea75159e AT |
572 | } |
573 | ||
3cc62aad | 574 | enum dss_clk_source dss_get_dispc_clk_source(struct dss_device *dss) |
559d6701 | 575 | { |
3cc62aad | 576 | return dss->dispc_clk_source; |
559d6701 TV |
577 | } |
578 | ||
3cc62aad LP |
579 | enum dss_clk_source dss_get_dsi_clk_source(struct dss_device *dss, |
580 | int dsi_module) | |
559d6701 | 581 | { |
3cc62aad | 582 | return dss->dsi_clk_source[dsi_module]; |
559d6701 TV |
583 | } |
584 | ||
3cc62aad LP |
585 | enum dss_clk_source dss_get_lcd_clk_source(struct dss_device *dss, |
586 | enum omap_channel channel) | |
ea75159e | 587 | { |
3cc62aad | 588 | if (dss->feat->has_lcd_clk_src) { |
c63b1ec0 | 589 | int idx = dss_get_channel_index(channel); |
3cc62aad | 590 | return dss->lcd_clk_source[idx]; |
89976f29 AT |
591 | } else { |
592 | /* LCD_CLK source is the same as DISPC_FCLK source for | |
593 | * OMAP2 and OMAP3 */ | |
3cc62aad | 594 | return dss->dispc_clk_source; |
89976f29 | 595 | } |
ea75159e AT |
596 | } |
597 | ||
60f9c59f LP |
598 | bool dss_div_calc(struct dss_device *dss, unsigned long pck, |
599 | unsigned long fck_min, dss_div_calc_func func, void *data) | |
43417823 TV |
600 | { |
601 | int fckd, fckd_start, fckd_stop; | |
602 | unsigned long fck; | |
603 | unsigned long fck_hw_max; | |
604 | unsigned long fckd_hw_max; | |
605 | unsigned long prate; | |
d11e5c82 | 606 | unsigned int m; |
43417823 | 607 | |
60f9c59f | 608 | fck_hw_max = dss->feat->fck_freq_max; |
fc1fe6e7 | 609 | |
60f9c59f | 610 | if (dss->parent_clk == NULL) { |
d11e5c82 | 611 | unsigned int pckd; |
fc1fe6e7 TV |
612 | |
613 | pckd = fck_hw_max / pck; | |
614 | ||
615 | fck = pck * pckd; | |
616 | ||
60f9c59f | 617 | fck = clk_round_rate(dss->dss_clk, fck); |
fc1fe6e7 | 618 | |
d0f58bd3 | 619 | return func(fck, data); |
43417823 TV |
620 | } |
621 | ||
60f9c59f | 622 | fckd_hw_max = dss->feat->fck_div_max; |
43417823 | 623 | |
60f9c59f LP |
624 | m = dss->feat->dss_fck_multiplier; |
625 | prate = clk_get_rate(dss->parent_clk); | |
43417823 TV |
626 | |
627 | fck_min = fck_min ? fck_min : 1; | |
628 | ||
648a55e1 TV |
629 | fckd_start = min(prate * m / fck_min, fckd_hw_max); |
630 | fckd_stop = max(DIV_ROUND_UP(prate * m, fck_hw_max), 1ul); | |
43417823 TV |
631 | |
632 | for (fckd = fckd_start; fckd >= fckd_stop; --fckd) { | |
d0e224f9 | 633 | fck = DIV_ROUND_UP(prate, fckd) * m; |
43417823 | 634 | |
d0f58bd3 | 635 | if (func(fck, data)) |
43417823 TV |
636 | return true; |
637 | } | |
638 | ||
639 | return false; | |
640 | } | |
641 | ||
60f9c59f | 642 | int dss_set_fck_rate(struct dss_device *dss, unsigned long rate) |
559d6701 | 643 | { |
ada9443f | 644 | int r; |
559d6701 | 645 | |
ada9443f | 646 | DSSDBG("set fck to %lu\n", rate); |
559d6701 | 647 | |
60f9c59f | 648 | r = clk_set_rate(dss->dss_clk, rate); |
ada9443f TV |
649 | if (r) |
650 | return r; | |
559d6701 | 651 | |
60f9c59f | 652 | dss->dss_clk_rate = clk_get_rate(dss->dss_clk); |
5aaee69d | 653 | |
60f9c59f LP |
654 | WARN_ONCE(dss->dss_clk_rate != rate, "clk rate mismatch: %lu != %lu", |
655 | dss->dss_clk_rate, rate); | |
559d6701 TV |
656 | |
657 | return 0; | |
658 | } | |
659 | ||
60f9c59f | 660 | unsigned long dss_get_dispc_clk_rate(struct dss_device *dss) |
5aaee69d | 661 | { |
60f9c59f | 662 | return dss->dss_clk_rate; |
5aaee69d TV |
663 | } |
664 | ||
60f9c59f | 665 | unsigned long dss_get_max_fck_rate(struct dss_device *dss) |
9f0fbaea | 666 | { |
60f9c59f | 667 | return dss->feat->fck_freq_max; |
9f0fbaea LP |
668 | } |
669 | ||
360c2153 | 670 | static int dss_setup_default_clock(struct dss_device *dss) |
13a1a2b2 TV |
671 | { |
672 | unsigned long max_dss_fck, prate; | |
d0f58bd3 | 673 | unsigned long fck; |
d11e5c82 | 674 | unsigned int fck_div; |
13a1a2b2 TV |
675 | int r; |
676 | ||
360c2153 | 677 | max_dss_fck = dss->feat->fck_freq_max; |
13a1a2b2 | 678 | |
360c2153 LP |
679 | if (dss->parent_clk == NULL) { |
680 | fck = clk_round_rate(dss->dss_clk, max_dss_fck); | |
fc1fe6e7 | 681 | } else { |
360c2153 | 682 | prate = clk_get_rate(dss->parent_clk); |
13a1a2b2 | 683 | |
360c2153 | 684 | fck_div = DIV_ROUND_UP(prate * dss->feat->dss_fck_multiplier, |
fc1fe6e7 | 685 | max_dss_fck); |
360c2153 LP |
686 | fck = DIV_ROUND_UP(prate, fck_div) |
687 | * dss->feat->dss_fck_multiplier; | |
fc1fe6e7 | 688 | } |
13a1a2b2 | 689 | |
360c2153 | 690 | r = dss_set_fck_rate(dss, fck); |
13a1a2b2 TV |
691 | if (r) |
692 | return r; | |
693 | ||
694 | return 0; | |
695 | } | |
696 | ||
1ef904e1 | 697 | void dss_set_venc_output(struct dss_device *dss, enum omap_dss_venc_type type) |
559d6701 TV |
698 | { |
699 | int l = 0; | |
700 | ||
701 | if (type == OMAP_DSS_VENC_TYPE_COMPOSITE) | |
702 | l = 0; | |
703 | else if (type == OMAP_DSS_VENC_TYPE_SVIDEO) | |
704 | l = 1; | |
705 | else | |
706 | BUG(); | |
707 | ||
708 | /* venc out selection. 0 = comp, 1 = svideo */ | |
360c2153 | 709 | REG_FLD_MOD(dss, DSS_CONTROL, l, 6, 6); |
559d6701 TV |
710 | } |
711 | ||
1ef904e1 | 712 | void dss_set_dac_pwrdn_bgz(struct dss_device *dss, bool enable) |
559d6701 | 713 | { |
360c2153 LP |
714 | /* DAC Power-Down Control */ |
715 | REG_FLD_MOD(dss, DSS_CONTROL, enable, 5, 5); | |
559d6701 TV |
716 | } |
717 | ||
8aea8e6a LP |
718 | void dss_select_hdmi_venc_clk_source(struct dss_device *dss, |
719 | enum dss_hdmi_venc_clk_source_select src) | |
7ed024aa | 720 | { |
24ab1df3 LP |
721 | enum omap_dss_output_id outputs; |
722 | ||
8aea8e6a | 723 | outputs = dss->feat->outputs[OMAP_DSS_CHANNEL_DIGIT]; |
8aa2eed1 RN |
724 | |
725 | /* Complain about invalid selections */ | |
24ab1df3 LP |
726 | WARN_ON((src == DSS_VENC_TV_CLK) && !(outputs & OMAP_DSS_OUTPUT_VENC)); |
727 | WARN_ON((src == DSS_HDMI_M_PCLK) && !(outputs & OMAP_DSS_OUTPUT_HDMI)); | |
8aa2eed1 RN |
728 | |
729 | /* Select only if we have options */ | |
24ab1df3 LP |
730 | if ((outputs & OMAP_DSS_OUTPUT_VENC) && |
731 | (outputs & OMAP_DSS_OUTPUT_HDMI)) | |
360c2153 LP |
732 | /* VENC_HDMI_SWITCH */ |
733 | REG_FLD_MOD(dss, DSS_CONTROL, src, 15, 15); | |
7ed024aa M |
734 | } |
735 | ||
8aea8e6a LP |
736 | static int dss_dpi_select_source_omap2_omap3(struct dss_device *dss, int port, |
737 | enum omap_channel channel) | |
de09e455 TV |
738 | { |
739 | if (channel != OMAP_DSS_CHANNEL_LCD) | |
740 | return -EINVAL; | |
741 | ||
742 | return 0; | |
743 | } | |
744 | ||
8aea8e6a LP |
745 | static int dss_dpi_select_source_omap4(struct dss_device *dss, int port, |
746 | enum omap_channel channel) | |
de09e455 TV |
747 | { |
748 | int val; | |
749 | ||
750 | switch (channel) { | |
751 | case OMAP_DSS_CHANNEL_LCD2: | |
752 | val = 0; | |
753 | break; | |
754 | case OMAP_DSS_CHANNEL_DIGIT: | |
755 | val = 1; | |
756 | break; | |
757 | default: | |
758 | return -EINVAL; | |
759 | } | |
760 | ||
360c2153 | 761 | REG_FLD_MOD(dss, DSS_CONTROL, val, 17, 17); |
de09e455 TV |
762 | |
763 | return 0; | |
764 | } | |
765 | ||
8aea8e6a LP |
766 | static int dss_dpi_select_source_omap5(struct dss_device *dss, int port, |
767 | enum omap_channel channel) | |
de09e455 TV |
768 | { |
769 | int val; | |
770 | ||
771 | switch (channel) { | |
772 | case OMAP_DSS_CHANNEL_LCD: | |
773 | val = 1; | |
774 | break; | |
775 | case OMAP_DSS_CHANNEL_LCD2: | |
776 | val = 2; | |
777 | break; | |
778 | case OMAP_DSS_CHANNEL_LCD3: | |
779 | val = 3; | |
780 | break; | |
781 | case OMAP_DSS_CHANNEL_DIGIT: | |
782 | val = 0; | |
783 | break; | |
784 | default: | |
785 | return -EINVAL; | |
786 | } | |
787 | ||
360c2153 | 788 | REG_FLD_MOD(dss, DSS_CONTROL, val, 17, 16); |
de09e455 TV |
789 | |
790 | return 0; | |
791 | } | |
792 | ||
8aea8e6a LP |
793 | static int dss_dpi_select_source_dra7xx(struct dss_device *dss, int port, |
794 | enum omap_channel channel) | |
6d817880 TV |
795 | { |
796 | switch (port) { | |
797 | case 0: | |
8aea8e6a | 798 | return dss_dpi_select_source_omap5(dss, port, channel); |
6d817880 TV |
799 | case 1: |
800 | if (channel != OMAP_DSS_CHANNEL_LCD2) | |
801 | return -EINVAL; | |
802 | break; | |
803 | case 2: | |
804 | if (channel != OMAP_DSS_CHANNEL_LCD3) | |
805 | return -EINVAL; | |
806 | break; | |
807 | default: | |
808 | return -EINVAL; | |
809 | } | |
810 | ||
811 | return 0; | |
812 | } | |
813 | ||
8aea8e6a LP |
814 | int dss_dpi_select_source(struct dss_device *dss, int port, |
815 | enum omap_channel channel) | |
de09e455 | 816 | { |
8aea8e6a | 817 | return dss->feat->ops->dpi_select_source(dss, port, channel); |
de09e455 TV |
818 | } |
819 | ||
360c2153 | 820 | static int dss_get_clocks(struct dss_device *dss) |
8b9cb3a8 | 821 | { |
4fbafaf3 | 822 | struct clk *clk; |
8b9cb3a8 | 823 | |
360c2153 | 824 | clk = devm_clk_get(&dss->pdev->dev, "fck"); |
4fbafaf3 TV |
825 | if (IS_ERR(clk)) { |
826 | DSSERR("can't get clock fck\n"); | |
b2c9c8ee | 827 | return PTR_ERR(clk); |
a1a0dcca | 828 | } |
8b9cb3a8 | 829 | |
360c2153 | 830 | dss->dss_clk = clk; |
8b9cb3a8 | 831 | |
360c2153 LP |
832 | if (dss->feat->parent_clk_name) { |
833 | clk = clk_get(NULL, dss->feat->parent_clk_name); | |
8ad9375f | 834 | if (IS_ERR(clk)) { |
360c2153 LP |
835 | DSSERR("Failed to get %s\n", |
836 | dss->feat->parent_clk_name); | |
b2c9c8ee | 837 | return PTR_ERR(clk); |
8ad9375f AK |
838 | } |
839 | } else { | |
840 | clk = NULL; | |
94c042ce TV |
841 | } |
842 | ||
360c2153 | 843 | dss->parent_clk = clk; |
94c042ce | 844 | |
8b9cb3a8 | 845 | return 0; |
8b9cb3a8 SG |
846 | } |
847 | ||
360c2153 | 848 | static void dss_put_clocks(struct dss_device *dss) |
8b9cb3a8 | 849 | { |
360c2153 LP |
850 | if (dss->parent_clk) |
851 | clk_put(dss->parent_clk); | |
8b9cb3a8 SG |
852 | } |
853 | ||
7b295257 | 854 | int dss_runtime_get(struct dss_device *dss) |
8b9cb3a8 | 855 | { |
4fbafaf3 | 856 | int r; |
8b9cb3a8 | 857 | |
4fbafaf3 | 858 | DSSDBG("dss_runtime_get\n"); |
8b9cb3a8 | 859 | |
7b295257 | 860 | r = pm_runtime_get_sync(&dss->pdev->dev); |
4fbafaf3 TV |
861 | WARN_ON(r < 0); |
862 | return r < 0 ? r : 0; | |
8b9cb3a8 SG |
863 | } |
864 | ||
7b295257 | 865 | void dss_runtime_put(struct dss_device *dss) |
8b9cb3a8 | 866 | { |
4fbafaf3 | 867 | int r; |
8b9cb3a8 | 868 | |
4fbafaf3 | 869 | DSSDBG("dss_runtime_put\n"); |
8b9cb3a8 | 870 | |
7b295257 | 871 | r = pm_runtime_put_sync(&dss->pdev->dev); |
5be3aebd | 872 | WARN_ON(r < 0 && r != -ENOSYS && r != -EBUSY); |
8b9cb3a8 SG |
873 | } |
874 | ||
7b295257 LP |
875 | struct dss_device *dss_get_device(struct device *dev) |
876 | { | |
360c2153 | 877 | return dev_get_drvdata(dev); |
7b295257 LP |
878 | } |
879 | ||
8b9cb3a8 | 880 | /* DEBUGFS */ |
1b3bcb33 | 881 | #if defined(CONFIG_OMAP2_DSS_DEBUGFS) |
360c2153 | 882 | static int dss_initialize_debugfs(struct dss_device *dss) |
11765d16 | 883 | { |
1c4b92ee | 884 | struct dentry *dir; |
11765d16 | 885 | |
1c4b92ee LP |
886 | dir = debugfs_create_dir("omapdss", NULL); |
887 | if (IS_ERR(dir)) | |
888 | return PTR_ERR(dir); | |
889 | ||
890 | dss->debugfs.root = dir; | |
11765d16 | 891 | |
11765d16 LP |
892 | return 0; |
893 | } | |
894 | ||
1c4b92ee | 895 | static void dss_uninitialize_debugfs(struct dss_device *dss) |
11765d16 | 896 | { |
1c4b92ee | 897 | debugfs_remove_recursive(dss->debugfs.root); |
11765d16 LP |
898 | } |
899 | ||
f33656e1 LP |
900 | struct dss_debugfs_entry { |
901 | struct dentry *dentry; | |
902 | int (*show_fn)(struct seq_file *s, void *data); | |
903 | void *data; | |
904 | }; | |
905 | ||
906 | static int dss_debug_open(struct inode *inode, struct file *file) | |
907 | { | |
908 | struct dss_debugfs_entry *entry = inode->i_private; | |
909 | ||
910 | return single_open(file, entry->show_fn, entry->data); | |
911 | } | |
912 | ||
913 | static const struct file_operations dss_debug_fops = { | |
914 | .open = dss_debug_open, | |
915 | .read = seq_read, | |
916 | .llseek = seq_lseek, | |
917 | .release = single_release, | |
918 | }; | |
919 | ||
1c4b92ee LP |
920 | struct dss_debugfs_entry * |
921 | dss_debugfs_create_file(struct dss_device *dss, const char *name, | |
922 | int (*show_fn)(struct seq_file *s, void *data), | |
923 | void *data) | |
11765d16 | 924 | { |
f33656e1 | 925 | struct dss_debugfs_entry *entry; |
11765d16 | 926 | |
f33656e1 LP |
927 | entry = kzalloc(sizeof(*entry), GFP_KERNEL); |
928 | if (!entry) | |
929 | return ERR_PTR(-ENOMEM); | |
930 | ||
931 | entry->show_fn = show_fn; | |
932 | entry->data = data; | |
e26ae7c0 GKH |
933 | entry->dentry = debugfs_create_file(name, 0444, dss->debugfs.root, |
934 | entry, &dss_debug_fops); | |
11765d16 | 935 | |
f33656e1 LP |
936 | return entry; |
937 | } | |
938 | ||
939 | void dss_debugfs_remove_file(struct dss_debugfs_entry *entry) | |
940 | { | |
941 | if (IS_ERR_OR_NULL(entry)) | |
942 | return; | |
943 | ||
944 | debugfs_remove(entry->dentry); | |
945 | kfree(entry); | |
11765d16 | 946 | } |
f33656e1 | 947 | |
11765d16 | 948 | #else /* CONFIG_OMAP2_DSS_DEBUGFS */ |
360c2153 | 949 | static inline int dss_initialize_debugfs(struct dss_device *dss) |
11765d16 LP |
950 | { |
951 | return 0; | |
952 | } | |
1c4b92ee | 953 | static inline void dss_uninitialize_debugfs(struct dss_device *dss) |
11765d16 LP |
954 | { |
955 | } | |
956 | #endif /* CONFIG_OMAP2_DSS_DEBUGFS */ | |
387ce9f2 | 957 | |
fecea252 LP |
958 | static const struct dss_ops dss_ops_omap2_omap3 = { |
959 | .dpi_select_source = &dss_dpi_select_source_omap2_omap3, | |
960 | }; | |
961 | ||
962 | static const struct dss_ops dss_ops_omap4 = { | |
963 | .dpi_select_source = &dss_dpi_select_source_omap4, | |
964 | .select_lcd_source = &dss_lcd_clk_mux_omap4, | |
965 | }; | |
966 | ||
967 | static const struct dss_ops dss_ops_omap5 = { | |
968 | .dpi_select_source = &dss_dpi_select_source_omap5, | |
969 | .select_lcd_source = &dss_lcd_clk_mux_omap5, | |
970 | }; | |
971 | ||
972 | static const struct dss_ops dss_ops_dra7 = { | |
973 | .dpi_select_source = &dss_dpi_select_source_dra7xx, | |
974 | .select_lcd_source = &dss_lcd_clk_mux_dra7, | |
975 | }; | |
976 | ||
234f9a22 | 977 | static const enum omap_display_type omap2plus_ports[] = { |
387ce9f2 AT |
978 | OMAP_DISPLAY_TYPE_DPI, |
979 | }; | |
980 | ||
234f9a22 | 981 | static const enum omap_display_type omap34xx_ports[] = { |
387ce9f2 AT |
982 | OMAP_DISPLAY_TYPE_DPI, |
983 | OMAP_DISPLAY_TYPE_SDI, | |
984 | }; | |
985 | ||
6d817880 TV |
986 | static const enum omap_display_type dra7xx_ports[] = { |
987 | OMAP_DISPLAY_TYPE_DPI, | |
988 | OMAP_DISPLAY_TYPE_DPI, | |
989 | OMAP_DISPLAY_TYPE_DPI, | |
990 | }; | |
991 | ||
51919572 LP |
992 | static const enum omap_dss_output_id omap2_dss_supported_outputs[] = { |
993 | /* OMAP_DSS_CHANNEL_LCD */ | |
994 | OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI, | |
995 | ||
996 | /* OMAP_DSS_CHANNEL_DIGIT */ | |
997 | OMAP_DSS_OUTPUT_VENC, | |
998 | }; | |
999 | ||
1000 | static const enum omap_dss_output_id omap3430_dss_supported_outputs[] = { | |
1001 | /* OMAP_DSS_CHANNEL_LCD */ | |
1002 | OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI | | |
1003 | OMAP_DSS_OUTPUT_SDI | OMAP_DSS_OUTPUT_DSI1, | |
1004 | ||
1005 | /* OMAP_DSS_CHANNEL_DIGIT */ | |
1006 | OMAP_DSS_OUTPUT_VENC, | |
1007 | }; | |
1008 | ||
1009 | static const enum omap_dss_output_id omap3630_dss_supported_outputs[] = { | |
1010 | /* OMAP_DSS_CHANNEL_LCD */ | |
1011 | OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI | | |
1012 | OMAP_DSS_OUTPUT_DSI1, | |
1013 | ||
1014 | /* OMAP_DSS_CHANNEL_DIGIT */ | |
1015 | OMAP_DSS_OUTPUT_VENC, | |
1016 | }; | |
1017 | ||
1018 | static const enum omap_dss_output_id am43xx_dss_supported_outputs[] = { | |
1019 | /* OMAP_DSS_CHANNEL_LCD */ | |
1020 | OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI, | |
1021 | }; | |
1022 | ||
1023 | static const enum omap_dss_output_id omap4_dss_supported_outputs[] = { | |
1024 | /* OMAP_DSS_CHANNEL_LCD */ | |
1025 | OMAP_DSS_OUTPUT_DBI | OMAP_DSS_OUTPUT_DSI1, | |
1026 | ||
1027 | /* OMAP_DSS_CHANNEL_DIGIT */ | |
1028 | OMAP_DSS_OUTPUT_VENC | OMAP_DSS_OUTPUT_HDMI, | |
1029 | ||
1030 | /* OMAP_DSS_CHANNEL_LCD2 */ | |
1031 | OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI | | |
1032 | OMAP_DSS_OUTPUT_DSI2, | |
1033 | }; | |
1034 | ||
1035 | static const enum omap_dss_output_id omap5_dss_supported_outputs[] = { | |
1036 | /* OMAP_DSS_CHANNEL_LCD */ | |
1037 | OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI | | |
1038 | OMAP_DSS_OUTPUT_DSI1 | OMAP_DSS_OUTPUT_DSI2, | |
1039 | ||
1040 | /* OMAP_DSS_CHANNEL_DIGIT */ | |
1041 | OMAP_DSS_OUTPUT_HDMI, | |
1042 | ||
1043 | /* OMAP_DSS_CHANNEL_LCD2 */ | |
1044 | OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI | | |
1045 | OMAP_DSS_OUTPUT_DSI1, | |
1046 | ||
1047 | /* OMAP_DSS_CHANNEL_LCD3 */ | |
1048 | OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI | | |
1049 | OMAP_DSS_OUTPUT_DSI2, | |
1050 | }; | |
1051 | ||
ede92695 | 1052 | static const struct dss_features omap24xx_dss_feats = { |
b8dab2bd | 1053 | .model = DSS_MODEL_OMAP2, |
6e555e27 TV |
1054 | /* |
1055 | * fck div max is really 16, but the divider range has gaps. The range | |
1056 | * from 1 to 6 has no gaps, so let's use that as a max. | |
1057 | */ | |
1058 | .fck_div_max = 6, | |
9f0fbaea | 1059 | .fck_freq_max = 133000000, |
84273a95 | 1060 | .dss_fck_multiplier = 2, |
ada9443f | 1061 | .parent_clk_name = "core_ck", |
387ce9f2 AT |
1062 | .ports = omap2plus_ports, |
1063 | .num_ports = ARRAY_SIZE(omap2plus_ports), | |
51919572 | 1064 | .outputs = omap2_dss_supported_outputs, |
fecea252 | 1065 | .ops = &dss_ops_omap2_omap3, |
6d85d4ad | 1066 | .dispc_clk_switch = { 0, 0 }, |
4569ab75 | 1067 | .has_lcd_clk_src = false, |
84273a95 TV |
1068 | }; |
1069 | ||
ede92695 | 1070 | static const struct dss_features omap34xx_dss_feats = { |
b8dab2bd | 1071 | .model = DSS_MODEL_OMAP3, |
84273a95 | 1072 | .fck_div_max = 16, |
9f0fbaea | 1073 | .fck_freq_max = 173000000, |
84273a95 | 1074 | .dss_fck_multiplier = 2, |
ada9443f | 1075 | .parent_clk_name = "dpll4_ck", |
387ce9f2 | 1076 | .ports = omap34xx_ports, |
51919572 | 1077 | .outputs = omap3430_dss_supported_outputs, |
387ce9f2 | 1078 | .num_ports = ARRAY_SIZE(omap34xx_ports), |
fecea252 | 1079 | .ops = &dss_ops_omap2_omap3, |
6d85d4ad | 1080 | .dispc_clk_switch = { 0, 0 }, |
4569ab75 | 1081 | .has_lcd_clk_src = false, |
84273a95 TV |
1082 | }; |
1083 | ||
ede92695 | 1084 | static const struct dss_features omap3630_dss_feats = { |
b8dab2bd | 1085 | .model = DSS_MODEL_OMAP3, |
e2c4ed14 | 1086 | .fck_div_max = 31, |
9f0fbaea | 1087 | .fck_freq_max = 173000000, |
84273a95 | 1088 | .dss_fck_multiplier = 1, |
ada9443f | 1089 | .parent_clk_name = "dpll4_ck", |
387ce9f2 AT |
1090 | .ports = omap2plus_ports, |
1091 | .num_ports = ARRAY_SIZE(omap2plus_ports), | |
51919572 | 1092 | .outputs = omap3630_dss_supported_outputs, |
fecea252 | 1093 | .ops = &dss_ops_omap2_omap3, |
6d85d4ad | 1094 | .dispc_clk_switch = { 0, 0 }, |
4569ab75 | 1095 | .has_lcd_clk_src = false, |
84273a95 TV |
1096 | }; |
1097 | ||
ede92695 | 1098 | static const struct dss_features omap44xx_dss_feats = { |
b8dab2bd | 1099 | .model = DSS_MODEL_OMAP4, |
84273a95 | 1100 | .fck_div_max = 32, |
9f0fbaea | 1101 | .fck_freq_max = 186000000, |
84273a95 | 1102 | .dss_fck_multiplier = 1, |
ada9443f | 1103 | .parent_clk_name = "dpll_per_x2_ck", |
387ce9f2 AT |
1104 | .ports = omap2plus_ports, |
1105 | .num_ports = ARRAY_SIZE(omap2plus_ports), | |
51919572 | 1106 | .outputs = omap4_dss_supported_outputs, |
fecea252 | 1107 | .ops = &dss_ops_omap4, |
6d85d4ad | 1108 | .dispc_clk_switch = { 9, 8 }, |
4569ab75 | 1109 | .has_lcd_clk_src = true, |
84273a95 TV |
1110 | }; |
1111 | ||
ede92695 | 1112 | static const struct dss_features omap54xx_dss_feats = { |
b8dab2bd | 1113 | .model = DSS_MODEL_OMAP5, |
84273a95 | 1114 | .fck_div_max = 64, |
9f0fbaea | 1115 | .fck_freq_max = 209250000, |
84273a95 | 1116 | .dss_fck_multiplier = 1, |
ada9443f | 1117 | .parent_clk_name = "dpll_per_x2_ck", |
387ce9f2 AT |
1118 | .ports = omap2plus_ports, |
1119 | .num_ports = ARRAY_SIZE(omap2plus_ports), | |
51919572 | 1120 | .outputs = omap5_dss_supported_outputs, |
fecea252 | 1121 | .ops = &dss_ops_omap5, |
6d85d4ad | 1122 | .dispc_clk_switch = { 9, 7 }, |
4569ab75 | 1123 | .has_lcd_clk_src = true, |
84273a95 TV |
1124 | }; |
1125 | ||
ede92695 | 1126 | static const struct dss_features am43xx_dss_feats = { |
b8dab2bd | 1127 | .model = DSS_MODEL_OMAP3, |
d6279d4a | 1128 | .fck_div_max = 0, |
9f0fbaea | 1129 | .fck_freq_max = 200000000, |
d6279d4a SP |
1130 | .dss_fck_multiplier = 0, |
1131 | .parent_clk_name = NULL, | |
387ce9f2 AT |
1132 | .ports = omap2plus_ports, |
1133 | .num_ports = ARRAY_SIZE(omap2plus_ports), | |
51919572 | 1134 | .outputs = am43xx_dss_supported_outputs, |
fecea252 | 1135 | .ops = &dss_ops_omap2_omap3, |
6d85d4ad | 1136 | .dispc_clk_switch = { 0, 0 }, |
4569ab75 | 1137 | .has_lcd_clk_src = true, |
d6279d4a SP |
1138 | }; |
1139 | ||
ede92695 | 1140 | static const struct dss_features dra7xx_dss_feats = { |
b8dab2bd | 1141 | .model = DSS_MODEL_DRA7, |
6d817880 | 1142 | .fck_div_max = 64, |
9f0fbaea | 1143 | .fck_freq_max = 209250000, |
6d817880 TV |
1144 | .dss_fck_multiplier = 1, |
1145 | .parent_clk_name = "dpll_per_x2_ck", | |
6d817880 TV |
1146 | .ports = dra7xx_ports, |
1147 | .num_ports = ARRAY_SIZE(dra7xx_ports), | |
51919572 | 1148 | .outputs = omap5_dss_supported_outputs, |
fecea252 | 1149 | .ops = &dss_ops_dra7, |
6d85d4ad | 1150 | .dispc_clk_switch = { 9, 7 }, |
4569ab75 | 1151 | .has_lcd_clk_src = true, |
6d817880 TV |
1152 | }; |
1153 | ||
360c2153 | 1154 | static int dss_init_ports(struct dss_device *dss) |
2ecef246 | 1155 | { |
360c2153 | 1156 | struct platform_device *pdev = dss->pdev; |
2ecef246 TV |
1157 | struct device_node *parent = pdev->dev.of_node; |
1158 | struct device_node *port; | |
8023651b LP |
1159 | unsigned int i; |
1160 | int r; | |
2ecef246 | 1161 | |
360c2153 | 1162 | for (i = 0; i < dss->feat->num_ports; i++) { |
09bffa6e RH |
1163 | port = of_graph_get_port_by_id(parent, i); |
1164 | if (!port) | |
387ce9f2 | 1165 | continue; |
2ecef246 | 1166 | |
360c2153 | 1167 | switch (dss->feat->ports[i]) { |
387ce9f2 | 1168 | case OMAP_DISPLAY_TYPE_DPI: |
8023651b LP |
1169 | r = dpi_init_port(dss, pdev, port, dss->feat->model); |
1170 | if (r) | |
1171 | return r; | |
387ce9f2 | 1172 | break; |
8023651b | 1173 | |
387ce9f2 | 1174 | case OMAP_DISPLAY_TYPE_SDI: |
8023651b LP |
1175 | r = sdi_init_port(dss, pdev, port); |
1176 | if (r) | |
1177 | return r; | |
387ce9f2 | 1178 | break; |
8023651b | 1179 | |
387ce9f2 AT |
1180 | default: |
1181 | break; | |
1182 | } | |
09bffa6e | 1183 | } |
2ecef246 TV |
1184 | |
1185 | return 0; | |
1186 | } | |
1187 | ||
360c2153 | 1188 | static void dss_uninit_ports(struct dss_device *dss) |
2ecef246 | 1189 | { |
360c2153 | 1190 | struct platform_device *pdev = dss->pdev; |
80eb6751 AT |
1191 | struct device_node *parent = pdev->dev.of_node; |
1192 | struct device_node *port; | |
09bffa6e | 1193 | int i; |
80eb6751 | 1194 | |
360c2153 | 1195 | for (i = 0; i < dss->feat->num_ports; i++) { |
09bffa6e RH |
1196 | port = of_graph_get_port_by_id(parent, i); |
1197 | if (!port) | |
387ce9f2 AT |
1198 | continue; |
1199 | ||
360c2153 | 1200 | switch (dss->feat->ports[i]) { |
387ce9f2 AT |
1201 | case OMAP_DISPLAY_TYPE_DPI: |
1202 | dpi_uninit_port(port); | |
1203 | break; | |
1204 | case OMAP_DISPLAY_TYPE_SDI: | |
1205 | sdi_uninit_port(port); | |
1206 | break; | |
1207 | default: | |
1208 | break; | |
1209 | } | |
09bffa6e | 1210 | } |
2ecef246 TV |
1211 | } |
1212 | ||
360c2153 | 1213 | static int dss_video_pll_probe(struct dss_device *dss) |
7e328f5a | 1214 | { |
360c2153 | 1215 | struct platform_device *pdev = dss->pdev; |
7e328f5a TV |
1216 | struct device_node *np = pdev->dev.of_node; |
1217 | struct regulator *pll_regulator; | |
1218 | int r; | |
1219 | ||
1220 | if (!np) | |
1221 | return 0; | |
1222 | ||
1223 | if (of_property_read_bool(np, "syscon-pll-ctrl")) { | |
360c2153 | 1224 | dss->syscon_pll_ctrl = syscon_regmap_lookup_by_phandle(np, |
7e328f5a | 1225 | "syscon-pll-ctrl"); |
360c2153 | 1226 | if (IS_ERR(dss->syscon_pll_ctrl)) { |
7e328f5a TV |
1227 | dev_err(&pdev->dev, |
1228 | "failed to get syscon-pll-ctrl regmap\n"); | |
360c2153 | 1229 | return PTR_ERR(dss->syscon_pll_ctrl); |
7e328f5a TV |
1230 | } |
1231 | ||
1232 | if (of_property_read_u32_index(np, "syscon-pll-ctrl", 1, | |
360c2153 | 1233 | &dss->syscon_pll_ctrl_offset)) { |
7e328f5a TV |
1234 | dev_err(&pdev->dev, |
1235 | "failed to get syscon-pll-ctrl offset\n"); | |
1236 | return -EINVAL; | |
1237 | } | |
1238 | } | |
1239 | ||
1240 | pll_regulator = devm_regulator_get(&pdev->dev, "vdda_video"); | |
1241 | if (IS_ERR(pll_regulator)) { | |
1242 | r = PTR_ERR(pll_regulator); | |
1243 | ||
1244 | switch (r) { | |
1245 | case -ENOENT: | |
1246 | pll_regulator = NULL; | |
1247 | break; | |
1248 | ||
1249 | case -EPROBE_DEFER: | |
1250 | return -EPROBE_DEFER; | |
1251 | ||
1252 | default: | |
1253 | DSSERR("can't get DPLL VDDA regulator\n"); | |
1254 | return r; | |
1255 | } | |
1256 | } | |
1257 | ||
1258 | if (of_property_match_string(np, "reg-names", "pll1") >= 0) { | |
360c2153 LP |
1259 | dss->video1_pll = dss_video_pll_init(dss, pdev, 0, |
1260 | pll_regulator); | |
1261 | if (IS_ERR(dss->video1_pll)) | |
1262 | return PTR_ERR(dss->video1_pll); | |
7e328f5a TV |
1263 | } |
1264 | ||
1265 | if (of_property_match_string(np, "reg-names", "pll2") >= 0) { | |
360c2153 LP |
1266 | dss->video2_pll = dss_video_pll_init(dss, pdev, 1, |
1267 | pll_regulator); | |
1268 | if (IS_ERR(dss->video2_pll)) { | |
1269 | dss_video_pll_uninit(dss->video1_pll); | |
1270 | return PTR_ERR(dss->video2_pll); | |
7e328f5a TV |
1271 | } |
1272 | } | |
1273 | ||
1274 | return 0; | |
1275 | } | |
1276 | ||
96c401bc | 1277 | /* DSS HW IP initialisation */ |
18daeb8e LP |
1278 | static const struct of_device_id dss_of_match[] = { |
1279 | { .compatible = "ti,omap2-dss", .data = &omap24xx_dss_feats }, | |
1280 | { .compatible = "ti,omap3-dss", .data = &omap3630_dss_feats }, | |
1281 | { .compatible = "ti,omap4-dss", .data = &omap44xx_dss_feats }, | |
1282 | { .compatible = "ti,omap5-dss", .data = &omap54xx_dss_feats }, | |
1283 | { .compatible = "ti,dra7-dss", .data = &dra7xx_dss_feats }, | |
1284 | {}, | |
1285 | }; | |
1286 | MODULE_DEVICE_TABLE(of, dss_of_match); | |
1287 | ||
1288 | static const struct soc_device_attribute dss_soc_devices[] = { | |
1289 | { .machine = "OMAP3430/3530", .data = &omap34xx_dss_feats }, | |
1290 | { .machine = "AM35??", .data = &omap34xx_dss_feats }, | |
1291 | { .family = "AM43xx", .data = &am43xx_dss_feats }, | |
1292 | { /* sentinel */ } | |
1293 | }; | |
1294 | ||
736e60dd | 1295 | static int dss_bind(struct device *dev) |
96c401bc | 1296 | { |
72877cf3 | 1297 | struct dss_device *dss = dev_get_drvdata(dev); |
cc1876ce | 1298 | struct platform_device *drm_pdev; |
96c401bc | 1299 | int r; |
96c401bc | 1300 | |
215003b4 | 1301 | r = component_bind_all(dev, NULL); |
8b9cb3a8 | 1302 | if (r) |
cd3b3449 | 1303 | return r; |
8b9cb3a8 | 1304 | |
cb17a4ae TV |
1305 | pm_set_vt_switch(0); |
1306 | ||
72877cf3 | 1307 | omapdss_set_dss(dss); |
f99467b3 | 1308 | |
cc1876ce JS |
1309 | drm_pdev = platform_device_register_simple("omapdrm", 0, NULL, 0); |
1310 | if (IS_ERR(drm_pdev)) { | |
1311 | component_unbind_all(dev, NULL); | |
1312 | return PTR_ERR(drm_pdev); | |
1313 | } | |
1314 | ||
1315 | dss->drm_pdev = drm_pdev; | |
1316 | ||
8b9cb3a8 | 1317 | return 0; |
96c401bc SG |
1318 | } |
1319 | ||
736e60dd | 1320 | static void dss_unbind(struct device *dev) |
96c401bc | 1321 | { |
cc1876ce JS |
1322 | struct dss_device *dss = dev_get_drvdata(dev); |
1323 | ||
1324 | platform_device_unregister(dss->drm_pdev); | |
1325 | ||
72877cf3 | 1326 | omapdss_set_dss(NULL); |
f99467b3 | 1327 | |
b40d0ed6 | 1328 | component_unbind_all(dev, NULL); |
736e60dd TV |
1329 | } |
1330 | ||
1331 | static const struct component_master_ops dss_component_ops = { | |
1332 | .bind = dss_bind, | |
1333 | .unbind = dss_unbind, | |
1334 | }; | |
b98482ed | 1335 | |
736e60dd TV |
1336 | static int dss_component_compare(struct device *dev, void *data) |
1337 | { | |
1338 | struct device *child = data; | |
1339 | return dev == child; | |
1340 | } | |
1341 | ||
cef76630 TL |
1342 | struct dss_component_match_data { |
1343 | struct device *dev; | |
1344 | struct component_match **match; | |
1345 | }; | |
1346 | ||
736e60dd TV |
1347 | static int dss_add_child_component(struct device *dev, void *data) |
1348 | { | |
cef76630 TL |
1349 | struct dss_component_match_data *cmatch = data; |
1350 | struct component_match **match = cmatch->match; | |
736e60dd | 1351 | |
0438ec90 TV |
1352 | /* |
1353 | * HACK | |
1354 | * We don't have a working driver for rfbi, so skip it here always. | |
1355 | * Otherwise dss will never get probed successfully, as it will wait | |
1356 | * for rfbi to get probed. | |
1357 | */ | |
1358 | if (strstr(dev_name(dev), "rfbi")) | |
1359 | return 0; | |
1360 | ||
cef76630 TL |
1361 | /* |
1362 | * Handle possible interconnect target modules defined within the DSS. | |
1363 | * The DSS components can be children of an interconnect target module | |
1364 | * after the device tree has been updated for the module data. | |
1365 | * See also omapdss_boot_init() for compatible fixup. | |
1366 | */ | |
1367 | if (strstr(dev_name(dev), "target-module")) | |
1368 | return device_for_each_child(dev, cmatch, | |
1369 | dss_add_child_component); | |
1370 | ||
1371 | component_match_add(cmatch->dev, match, dss_component_compare, dev); | |
736e60dd TV |
1372 | |
1373 | return 0; | |
1374 | } | |
1375 | ||
7b295257 | 1376 | static int dss_probe_hardware(struct dss_device *dss) |
215003b4 LP |
1377 | { |
1378 | u32 rev; | |
1379 | int r; | |
1380 | ||
7b295257 | 1381 | r = dss_runtime_get(dss); |
215003b4 LP |
1382 | if (r) |
1383 | return r; | |
1384 | ||
7b295257 | 1385 | dss->dss_clk_rate = clk_get_rate(dss->dss_clk); |
215003b4 LP |
1386 | |
1387 | /* Select DPLL */ | |
360c2153 | 1388 | REG_FLD_MOD(dss, DSS_CONTROL, 0, 0, 0); |
215003b4 | 1389 | |
360c2153 | 1390 | dss_select_dispc_clk_source(dss, DSS_CLK_SRC_FCK); |
215003b4 LP |
1391 | |
1392 | #ifdef CONFIG_OMAP2_DSS_VENC | |
360c2153 LP |
1393 | REG_FLD_MOD(dss, DSS_CONTROL, 1, 4, 4); /* venc dac demen */ |
1394 | REG_FLD_MOD(dss, DSS_CONTROL, 1, 3, 3); /* venc clock 4x enable */ | |
1395 | REG_FLD_MOD(dss, DSS_CONTROL, 0, 2, 2); /* venc clock mode = normal */ | |
215003b4 | 1396 | #endif |
7b295257 LP |
1397 | dss->dsi_clk_source[0] = DSS_CLK_SRC_FCK; |
1398 | dss->dsi_clk_source[1] = DSS_CLK_SRC_FCK; | |
1399 | dss->dispc_clk_source = DSS_CLK_SRC_FCK; | |
1400 | dss->lcd_clk_source[0] = DSS_CLK_SRC_FCK; | |
1401 | dss->lcd_clk_source[1] = DSS_CLK_SRC_FCK; | |
215003b4 | 1402 | |
360c2153 | 1403 | rev = dss_read_reg(dss, DSS_REVISION); |
215003b4 LP |
1404 | pr_info("OMAP DSS rev %d.%d\n", FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); |
1405 | ||
7b295257 | 1406 | dss_runtime_put(dss); |
215003b4 LP |
1407 | |
1408 | return 0; | |
1409 | } | |
1410 | ||
736e60dd TV |
1411 | static int dss_probe(struct platform_device *pdev) |
1412 | { | |
4a9fab3d | 1413 | const struct soc_device_attribute *soc; |
cef76630 | 1414 | struct dss_component_match_data cmatch; |
736e60dd | 1415 | struct component_match *match = NULL; |
215003b4 | 1416 | struct resource *dss_mem; |
360c2153 | 1417 | struct dss_device *dss; |
736e60dd TV |
1418 | int r; |
1419 | ||
360c2153 LP |
1420 | dss = kzalloc(sizeof(*dss), GFP_KERNEL); |
1421 | if (!dss) | |
1422 | return -ENOMEM; | |
1423 | ||
1424 | dss->pdev = pdev; | |
1425 | platform_set_drvdata(pdev, dss); | |
4a9fab3d | 1426 | |
a921c1a8 LP |
1427 | r = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); |
1428 | if (r) { | |
1429 | dev_err(&pdev->dev, "Failed to set the DMA mask\n"); | |
360c2153 | 1430 | goto err_free_dss; |
a921c1a8 LP |
1431 | } |
1432 | ||
4a9fab3d LP |
1433 | /* |
1434 | * The various OMAP3-based SoCs can't be told apart using the compatible | |
1435 | * string, use SoC device matching. | |
1436 | */ | |
1437 | soc = soc_device_match(dss_soc_devices); | |
1438 | if (soc) | |
360c2153 | 1439 | dss->feat = soc->data; |
4a9fab3d | 1440 | else |
360c2153 | 1441 | dss->feat = of_match_device(dss_of_match, &pdev->dev)->data; |
4a9fab3d | 1442 | |
215003b4 LP |
1443 | /* Map I/O registers, get and setup clocks. */ |
1444 | dss_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
360c2153 LP |
1445 | dss->base = devm_ioremap_resource(&pdev->dev, dss_mem); |
1446 | if (IS_ERR(dss->base)) { | |
1447 | r = PTR_ERR(dss->base); | |
1448 | goto err_free_dss; | |
1449 | } | |
215003b4 | 1450 | |
360c2153 | 1451 | r = dss_get_clocks(dss); |
11765d16 | 1452 | if (r) |
360c2153 | 1453 | goto err_free_dss; |
11765d16 | 1454 | |
360c2153 | 1455 | r = dss_setup_default_clock(dss); |
215003b4 LP |
1456 | if (r) |
1457 | goto err_put_clocks; | |
1458 | ||
1459 | /* Setup the video PLLs and the DPI and SDI ports. */ | |
360c2153 | 1460 | r = dss_video_pll_probe(dss); |
215003b4 LP |
1461 | if (r) |
1462 | goto err_put_clocks; | |
1463 | ||
360c2153 | 1464 | r = dss_init_ports(dss); |
215003b4 LP |
1465 | if (r) |
1466 | goto err_uninit_plls; | |
1467 | ||
1468 | /* Enable runtime PM and probe the hardware. */ | |
1469 | pm_runtime_enable(&pdev->dev); | |
1470 | ||
360c2153 | 1471 | r = dss_probe_hardware(dss); |
215003b4 LP |
1472 | if (r) |
1473 | goto err_pm_runtime_disable; | |
1474 | ||
1475 | /* Initialize debugfs. */ | |
360c2153 | 1476 | r = dss_initialize_debugfs(dss); |
215003b4 LP |
1477 | if (r) |
1478 | goto err_pm_runtime_disable; | |
1479 | ||
1c4b92ee LP |
1480 | dss->debugfs.clk = dss_debugfs_create_file(dss, "clk", |
1481 | dss_debug_dump_clocks, dss); | |
1482 | dss->debugfs.dss = dss_debugfs_create_file(dss, "dss", dss_dump_regs, | |
f33656e1 | 1483 | dss); |
215003b4 LP |
1484 | |
1485 | /* Add all the child devices as components. */ | |
e0c827ac LP |
1486 | r = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); |
1487 | if (r) | |
1488 | goto err_uninit_debugfs; | |
1489 | ||
f13e97cf LP |
1490 | omapdss_gather_components(&pdev->dev); |
1491 | ||
cef76630 TL |
1492 | cmatch.dev = &pdev->dev; |
1493 | cmatch.match = &match; | |
1494 | device_for_each_child(&pdev->dev, &cmatch, dss_add_child_component); | |
736e60dd TV |
1495 | |
1496 | r = component_master_add_with_match(&pdev->dev, &dss_component_ops, match); | |
215003b4 | 1497 | if (r) |
e0c827ac | 1498 | goto err_of_depopulate; |
736e60dd TV |
1499 | |
1500 | return 0; | |
215003b4 | 1501 | |
e0c827ac LP |
1502 | err_of_depopulate: |
1503 | of_platform_depopulate(&pdev->dev); | |
1504 | ||
215003b4 | 1505 | err_uninit_debugfs: |
f33656e1 LP |
1506 | dss_debugfs_remove_file(dss->debugfs.clk); |
1507 | dss_debugfs_remove_file(dss->debugfs.dss); | |
1c4b92ee | 1508 | dss_uninitialize_debugfs(dss); |
215003b4 LP |
1509 | |
1510 | err_pm_runtime_disable: | |
1511 | pm_runtime_disable(&pdev->dev); | |
360c2153 | 1512 | dss_uninit_ports(dss); |
215003b4 LP |
1513 | |
1514 | err_uninit_plls: | |
360c2153 LP |
1515 | if (dss->video1_pll) |
1516 | dss_video_pll_uninit(dss->video1_pll); | |
1517 | if (dss->video2_pll) | |
1518 | dss_video_pll_uninit(dss->video2_pll); | |
215003b4 LP |
1519 | |
1520 | err_put_clocks: | |
360c2153 LP |
1521 | dss_put_clocks(dss); |
1522 | ||
1523 | err_free_dss: | |
1524 | kfree(dss); | |
215003b4 LP |
1525 | |
1526 | return r; | |
736e60dd TV |
1527 | } |
1528 | ||
1529 | static int dss_remove(struct platform_device *pdev) | |
1530 | { | |
360c2153 LP |
1531 | struct dss_device *dss = platform_get_drvdata(pdev); |
1532 | ||
e0c827ac LP |
1533 | of_platform_depopulate(&pdev->dev); |
1534 | ||
736e60dd | 1535 | component_master_del(&pdev->dev, &dss_component_ops); |
11765d16 | 1536 | |
f33656e1 LP |
1537 | dss_debugfs_remove_file(dss->debugfs.clk); |
1538 | dss_debugfs_remove_file(dss->debugfs.dss); | |
1c4b92ee | 1539 | dss_uninitialize_debugfs(dss); |
11765d16 | 1540 | |
215003b4 LP |
1541 | pm_runtime_disable(&pdev->dev); |
1542 | ||
360c2153 LP |
1543 | dss_uninit_ports(dss); |
1544 | ||
1545 | if (dss->video1_pll) | |
1546 | dss_video_pll_uninit(dss->video1_pll); | |
215003b4 | 1547 | |
360c2153 LP |
1548 | if (dss->video2_pll) |
1549 | dss_video_pll_uninit(dss->video2_pll); | |
215003b4 | 1550 | |
360c2153 | 1551 | dss_put_clocks(dss); |
215003b4 | 1552 | |
360c2153 | 1553 | kfree(dss); |
215003b4 | 1554 | |
96c401bc SG |
1555 | return 0; |
1556 | } | |
1557 | ||
74592ee7 LP |
1558 | static void dss_shutdown(struct platform_device *pdev) |
1559 | { | |
1560 | struct omap_dss_device *dssdev = NULL; | |
1561 | ||
1562 | DSSDBG("shutdown\n"); | |
1563 | ||
19b4200d | 1564 | for_each_dss_output(dssdev) { |
74592ee7 | 1565 | if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) |
83910ad3 | 1566 | dssdev->ops->disable(dssdev); |
74592ee7 LP |
1567 | } |
1568 | } | |
1569 | ||
4fbafaf3 TV |
1570 | static int dss_runtime_suspend(struct device *dev) |
1571 | { | |
360c2153 LP |
1572 | struct dss_device *dss = dev_get_drvdata(dev); |
1573 | ||
1574 | dss_save_context(dss); | |
a8081d31 | 1575 | dss_set_min_bus_tput(dev, 0); |
5038bb8c DG |
1576 | |
1577 | pinctrl_pm_select_sleep_state(dev); | |
1578 | ||
4fbafaf3 TV |
1579 | return 0; |
1580 | } | |
1581 | ||
1582 | static int dss_runtime_resume(struct device *dev) | |
1583 | { | |
360c2153 | 1584 | struct dss_device *dss = dev_get_drvdata(dev); |
a8081d31 | 1585 | int r; |
5038bb8c DG |
1586 | |
1587 | pinctrl_pm_select_default_state(dev); | |
1588 | ||
a8081d31 TV |
1589 | /* |
1590 | * Set an arbitrarily high tput request to ensure OPP100. | |
1591 | * What we should really do is to make a request to stay in OPP100, | |
1592 | * without any tput requirements, but that is not currently possible | |
1593 | * via the PM layer. | |
1594 | */ | |
1595 | ||
1596 | r = dss_set_min_bus_tput(dev, 1000000000); | |
1597 | if (r) | |
1598 | return r; | |
1599 | ||
360c2153 | 1600 | dss_restore_context(dss); |
4fbafaf3 TV |
1601 | return 0; |
1602 | } | |
1603 | ||
1604 | static const struct dev_pm_ops dss_pm_ops = { | |
1605 | .runtime_suspend = dss_runtime_suspend, | |
1606 | .runtime_resume = dss_runtime_resume, | |
1607 | }; | |
1608 | ||
d66c36a3 | 1609 | struct platform_driver omap_dsshw_driver = { |
736e60dd TV |
1610 | .probe = dss_probe, |
1611 | .remove = dss_remove, | |
74592ee7 | 1612 | .shutdown = dss_shutdown, |
96c401bc SG |
1613 | .driver = { |
1614 | .name = "omapdss_dss", | |
4fbafaf3 | 1615 | .pm = &dss_pm_ops, |
2ecef246 | 1616 | .of_match_table = dss_of_match, |
422ccbd5 | 1617 | .suppress_bind_attrs = true, |
96c401bc SG |
1618 | }, |
1619 | }; | |
34d71136 JS |
1620 | |
1621 | /* INIT */ | |
1622 | static struct platform_driver * const omap_dss_drivers[] = { | |
1623 | &omap_dsshw_driver, | |
1624 | &omap_dispchw_driver, | |
1625 | #ifdef CONFIG_OMAP2_DSS_DSI | |
1626 | &omap_dsihw_driver, | |
1627 | #endif | |
1628 | #ifdef CONFIG_OMAP2_DSS_VENC | |
1629 | &omap_venchw_driver, | |
1630 | #endif | |
1631 | #ifdef CONFIG_OMAP4_DSS_HDMI | |
1632 | &omapdss_hdmi4hw_driver, | |
1633 | #endif | |
1634 | #ifdef CONFIG_OMAP5_DSS_HDMI | |
1635 | &omapdss_hdmi5hw_driver, | |
1636 | #endif | |
1637 | }; | |
1638 | ||
1639 | static int __init omap_dss_init(void) | |
1640 | { | |
1641 | return platform_register_drivers(omap_dss_drivers, | |
1642 | ARRAY_SIZE(omap_dss_drivers)); | |
1643 | } | |
1644 | ||
1645 | static void __exit omap_dss_exit(void) | |
1646 | { | |
1647 | platform_unregister_drivers(omap_dss_drivers, | |
1648 | ARRAY_SIZE(omap_dss_drivers)); | |
1649 | } | |
1650 | ||
1651 | module_init(omap_dss_init); | |
1652 | module_exit(omap_dss_exit); | |
1653 | ||
1654 | MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); | |
1655 | MODULE_DESCRIPTION("OMAP2/3/4/5 Display Subsystem"); | |
1656 | MODULE_LICENSE("GPL v2"); |