blob: 89bb41f42a0632f75437486d7f966d83dfd5c545 [file] [log] [blame]
Tomi Valkeinen559d6702009-11-03 11:23:50 +02001/*
2 * linux/drivers/video/omap2/dss/dss.c
3 *
4 * Copyright (C) 2009 Nokia Corporation
5 * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
6 *
7 * Some code and ideas taken from drivers/video/omap/ driver
8 * by Imre Deak.
9 *
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License version 2 as published by
12 * the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 * more details.
18 *
19 * You should have received a copy of the GNU General Public License along with
20 * this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23#define DSS_SUBSYS_NAME "DSS"
24
Laurent Pinchart11765d12017-08-05 01:44:01 +030025#include <linux/debugfs.h>
Tomi Valkeinen559d6702009-11-03 11:23:50 +020026#include <linux/kernel.h>
Tomi Valkeinen2ecef242013-12-16 15:13:24 +020027#include <linux/module.h>
Tomi Valkeinen559d6702009-11-03 11:23:50 +020028#include <linux/io.h>
Paul Gortmakera8a35932011-07-10 13:20:26 -040029#include <linux/export.h>
Tomi Valkeinen559d6702009-11-03 11:23:50 +020030#include <linux/err.h>
31#include <linux/delay.h>
Tomi Valkeinen559d6702009-11-03 11:23:50 +020032#include <linux/seq_file.h>
33#include <linux/clk.h>
Arnd Bergmann2639d6b2016-05-09 23:51:27 +020034#include <linux/pinctrl/consumer.h>
Tomi Valkeinen24e62892011-05-23 11:51:18 +030035#include <linux/platform_device.h>
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +030036#include <linux/pm_runtime.h>
Chandrabhanu Mahapatra185bae12012-07-11 18:36:18 +053037#include <linux/gfp.h>
Tomi Valkeinen33366d02012-09-28 13:54:35 +030038#include <linux/sizes.h>
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +053039#include <linux/mfd/syscon.h>
40#include <linux/regmap.h>
Tomi Valkeinen2ecef242013-12-16 15:13:24 +020041#include <linux/of.h>
Laurent Pinchart18daeb82017-08-05 01:43:58 +030042#include <linux/of_device.h>
Rob Herring09bffa62017-03-22 08:26:08 -050043#include <linux/of_graph.h>
Tomi Valkeinen99767542014-07-04 13:38:27 +053044#include <linux/regulator/consumer.h>
Tomi Valkeinencb17a4a2015-02-25 12:08:14 +020045#include <linux/suspend.h>
Tomi Valkeinen736e60d2015-06-04 15:22:23 +030046#include <linux/component.h>
Laurent Pinchart18daeb82017-08-05 01:43:58 +030047#include <linux/sys_soc.h>
Tomi Valkeinen559d6702009-11-03 11:23:50 +020048
Peter Ujfalusi32043da2016-05-27 14:40:49 +030049#include "omapdss.h"
Tomi Valkeinen559d6702009-11-03 11:23:50 +020050#include "dss.h"
Tomi Valkeinen6ec549e2011-02-24 14:18:50 +020051#include "dss_features.h"
Tomi Valkeinen559d6702009-11-03 11:23:50 +020052
Tomi Valkeinen559d6702009-11-03 11:23:50 +020053#define DSS_SZ_REGS SZ_512
54
55struct dss_reg {
56 u16 idx;
57};
58
59#define DSS_REG(idx) ((const struct dss_reg) { idx })
60
61#define DSS_REVISION DSS_REG(0x0000)
62#define DSS_SYSCONFIG DSS_REG(0x0010)
63#define DSS_SYSSTATUS DSS_REG(0x0014)
Tomi Valkeinen559d6702009-11-03 11:23:50 +020064#define DSS_CONTROL DSS_REG(0x0040)
65#define DSS_SDI_CONTROL DSS_REG(0x0044)
66#define DSS_PLL_CONTROL DSS_REG(0x0048)
67#define DSS_SDI_STATUS DSS_REG(0x005C)
68
69#define REG_GET(idx, start, end) \
70 FLD_GET(dss_read_reg(idx), start, end)
71
72#define REG_FLD_MOD(idx, val, start, end) \
73 dss_write_reg(idx, FLD_MOD(dss_read_reg(idx), val, start, end))
74
Laurent Pinchartfecea252017-08-05 01:43:52 +030075struct dss_ops {
76 int (*dpi_select_source)(int port, enum omap_channel channel);
77 int (*select_lcd_source)(enum omap_channel channel,
78 enum dss_clk_source clk_src);
79};
80
Chandrabhanu Mahapatra185bae12012-07-11 18:36:18 +053081struct dss_features {
Laurent Pinchartb8dab2b2017-08-05 01:43:56 +030082 enum dss_model model;
Chandrabhanu Mahapatra185bae12012-07-11 18:36:18 +053083 u8 fck_div_max;
Laurent Pinchart9f0fbae2017-08-05 01:44:17 +030084 unsigned int fck_freq_max;
Chandrabhanu Mahapatra185bae12012-07-11 18:36:18 +053085 u8 dss_fck_multiplier;
Tomi Valkeinen64ad8462013-11-01 11:38:04 +020086 const char *parent_clk_name;
Tomi Valkeinen234f9a22014-12-11 15:59:31 +020087 const enum omap_display_type *ports;
Archit Taneja387ce9f2014-05-22 17:01:57 +053088 int num_ports;
Laurent Pinchartfecea252017-08-05 01:43:52 +030089 const struct dss_ops *ops;
Laurent Pinchart6d85d4a2017-08-05 01:44:07 +030090 struct dss_reg_field dispc_clk_switch;
Laurent Pinchart4569ab72017-08-05 01:44:13 +030091 bool has_lcd_clk_src;
Chandrabhanu Mahapatra185bae12012-07-11 18:36:18 +053092};
93
Tomi Valkeinen559d6702009-11-03 11:23:50 +020094static struct {
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +000095 struct platform_device *pdev;
Tomi Valkeinen559d6702009-11-03 11:23:50 +020096 void __iomem *base;
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +053097 struct regmap *syscon_pll_ctrl;
98 u32 syscon_pll_ctrl_offset;
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +030099
Tomi Valkeinen64ad8462013-11-01 11:38:04 +0200100 struct clk *parent_clk;
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300101 struct clk *dss_clk;
Tomi Valkeinen5aaee692012-12-12 10:37:03 +0200102 unsigned long dss_clk_rate;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200103
104 unsigned long cache_req_pck;
105 unsigned long cache_prate;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200106 struct dispc_clock_info cache_dispc_cinfo;
107
Tomi Valkeinendc0352d2016-05-17 13:45:09 +0300108 enum dss_clk_source dsi_clk_source[MAX_NUM_DSI];
109 enum dss_clk_source dispc_clk_source;
110 enum dss_clk_source lcd_clk_source[MAX_DSS_LCD_MANAGERS];
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +0200111
Tomi Valkeinen69f06052011-06-01 15:56:39 +0300112 bool ctx_valid;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200113 u32 ctx[DSS_SZ_REGS / sizeof(u32)];
Chandrabhanu Mahapatra185bae12012-07-11 18:36:18 +0530114
115 const struct dss_features *feat;
Tomi Valkeinen99767542014-07-04 13:38:27 +0530116
117 struct dss_pll *video1_pll;
118 struct dss_pll *video2_pll;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200119} dss;
120
Taneja, Archit235e7db2011-03-14 23:28:21 -0500121static const char * const dss_generic_clk_source_names[] = {
Tomi Valkeinen3b63ca72016-05-17 14:01:10 +0300122 [DSS_CLK_SRC_FCK] = "FCK",
123 [DSS_CLK_SRC_PLL1_1] = "PLL1:1",
124 [DSS_CLK_SRC_PLL1_2] = "PLL1:2",
Tomi Valkeinenb5d8c752016-05-17 14:12:35 +0300125 [DSS_CLK_SRC_PLL1_3] = "PLL1:3",
Tomi Valkeinen3b63ca72016-05-17 14:01:10 +0300126 [DSS_CLK_SRC_PLL2_1] = "PLL2:1",
127 [DSS_CLK_SRC_PLL2_2] = "PLL2:2",
Tomi Valkeinenb5d8c752016-05-17 14:12:35 +0300128 [DSS_CLK_SRC_PLL2_3] = "PLL2:3",
129 [DSS_CLK_SRC_HDMI_PLL] = "HDMI PLL",
Archit Taneja067a57e2011-03-02 11:57:25 +0530130};
131
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200132static inline void dss_write_reg(const struct dss_reg idx, u32 val)
133{
134 __raw_writel(val, dss.base + idx.idx);
135}
136
137static inline u32 dss_read_reg(const struct dss_reg idx)
138{
139 return __raw_readl(dss.base + idx.idx);
140}
141
142#define SR(reg) \
143 dss.ctx[(DSS_##reg).idx / sizeof(u32)] = dss_read_reg(DSS_##reg)
144#define RR(reg) \
145 dss_write_reg(DSS_##reg, dss.ctx[(DSS_##reg).idx / sizeof(u32)])
146
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300147static void dss_save_context(void)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200148{
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300149 DSSDBG("dss_save_context\n");
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200150
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200151 SR(CONTROL);
152
Laurent Pinchart24ab1df2017-08-05 01:43:59 +0300153 if (dss_feat_get_supported_outputs(OMAP_DSS_CHANNEL_LCD) &
154 OMAP_DSS_OUTPUT_SDI) {
Tomi Valkeinen6ec549e2011-02-24 14:18:50 +0200155 SR(SDI_CONTROL);
156 SR(PLL_CONTROL);
157 }
Tomi Valkeinen69f06052011-06-01 15:56:39 +0300158
159 dss.ctx_valid = true;
160
161 DSSDBG("context saved\n");
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200162}
163
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300164static void dss_restore_context(void)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200165{
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300166 DSSDBG("dss_restore_context\n");
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200167
Tomi Valkeinen69f06052011-06-01 15:56:39 +0300168 if (!dss.ctx_valid)
169 return;
170
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200171 RR(CONTROL);
172
Laurent Pinchart24ab1df2017-08-05 01:43:59 +0300173 if (dss_feat_get_supported_outputs(OMAP_DSS_CHANNEL_LCD) &
174 OMAP_DSS_OUTPUT_SDI) {
Tomi Valkeinen6ec549e2011-02-24 14:18:50 +0200175 RR(SDI_CONTROL);
176 RR(PLL_CONTROL);
177 }
Tomi Valkeinen69f06052011-06-01 15:56:39 +0300178
179 DSSDBG("context restored\n");
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200180}
181
182#undef SR
183#undef RR
184
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +0530185void dss_ctrl_pll_enable(enum dss_pll_id pll_id, bool enable)
186{
187 unsigned shift;
188 unsigned val;
189
190 if (!dss.syscon_pll_ctrl)
191 return;
192
193 val = !enable;
194
195 switch (pll_id) {
196 case DSS_PLL_VIDEO1:
197 shift = 0;
198 break;
199 case DSS_PLL_VIDEO2:
200 shift = 1;
201 break;
202 case DSS_PLL_HDMI:
203 shift = 2;
204 break;
205 default:
206 DSSERR("illegal DSS PLL ID %d\n", pll_id);
207 return;
208 }
209
210 regmap_update_bits(dss.syscon_pll_ctrl, dss.syscon_pll_ctrl_offset,
211 1 << shift, val << shift);
212}
213
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300214static int dss_ctrl_pll_set_control_mux(enum dss_clk_source clk_src,
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +0530215 enum omap_channel channel)
216{
217 unsigned shift, val;
218
219 if (!dss.syscon_pll_ctrl)
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300220 return -EINVAL;
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +0530221
222 switch (channel) {
223 case OMAP_DSS_CHANNEL_LCD:
224 shift = 3;
225
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300226 switch (clk_src) {
227 case DSS_CLK_SRC_PLL1_1:
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +0530228 val = 0; break;
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300229 case DSS_CLK_SRC_HDMI_PLL:
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +0530230 val = 1; break;
231 default:
232 DSSERR("error in PLL mux config for LCD\n");
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300233 return -EINVAL;
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +0530234 }
235
236 break;
237 case OMAP_DSS_CHANNEL_LCD2:
238 shift = 5;
239
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300240 switch (clk_src) {
241 case DSS_CLK_SRC_PLL1_3:
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +0530242 val = 0; break;
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300243 case DSS_CLK_SRC_PLL2_3:
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +0530244 val = 1; break;
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300245 case DSS_CLK_SRC_HDMI_PLL:
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +0530246 val = 2; break;
247 default:
248 DSSERR("error in PLL mux config for LCD2\n");
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300249 return -EINVAL;
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +0530250 }
251
252 break;
253 case OMAP_DSS_CHANNEL_LCD3:
254 shift = 7;
255
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300256 switch (clk_src) {
257 case DSS_CLK_SRC_PLL2_1:
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +0530258 val = 0; break;
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300259 case DSS_CLK_SRC_PLL1_3:
260 val = 1; break;
261 case DSS_CLK_SRC_HDMI_PLL:
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +0530262 val = 2; break;
263 default:
264 DSSERR("error in PLL mux config for LCD3\n");
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300265 return -EINVAL;
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +0530266 }
267
268 break;
269 default:
270 DSSERR("error in PLL mux config\n");
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300271 return -EINVAL;
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +0530272 }
273
274 regmap_update_bits(dss.syscon_pll_ctrl, dss.syscon_pll_ctrl_offset,
275 0x3 << shift, val << shift);
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300276
277 return 0;
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +0530278}
279
Archit Taneja889b4fd2012-07-20 17:18:49 +0530280void dss_sdi_init(int datapairs)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200281{
282 u32 l;
283
284 BUG_ON(datapairs > 3 || datapairs < 1);
285
286 l = dss_read_reg(DSS_SDI_CONTROL);
287 l = FLD_MOD(l, 0xf, 19, 15); /* SDI_PDIV */
288 l = FLD_MOD(l, datapairs-1, 3, 2); /* SDI_PRSEL */
289 l = FLD_MOD(l, 2, 1, 0); /* SDI_BWSEL */
290 dss_write_reg(DSS_SDI_CONTROL, l);
291
292 l = dss_read_reg(DSS_PLL_CONTROL);
293 l = FLD_MOD(l, 0x7, 25, 22); /* SDI_PLL_FREQSEL */
294 l = FLD_MOD(l, 0xb, 16, 11); /* SDI_PLL_REGN */
295 l = FLD_MOD(l, 0xb4, 10, 1); /* SDI_PLL_REGM */
296 dss_write_reg(DSS_PLL_CONTROL, l);
297}
298
299int dss_sdi_enable(void)
300{
301 unsigned long timeout;
302
303 dispc_pck_free_enable(1);
304
305 /* Reset SDI PLL */
306 REG_FLD_MOD(DSS_PLL_CONTROL, 1, 18, 18); /* SDI_PLL_SYSRESET */
307 udelay(1); /* wait 2x PCLK */
308
309 /* Lock SDI PLL */
310 REG_FLD_MOD(DSS_PLL_CONTROL, 1, 28, 28); /* SDI_PLL_GOBIT */
311
312 /* Waiting for PLL lock request to complete */
313 timeout = jiffies + msecs_to_jiffies(500);
314 while (dss_read_reg(DSS_SDI_STATUS) & (1 << 6)) {
315 if (time_after_eq(jiffies, timeout)) {
316 DSSERR("PLL lock request timed out\n");
317 goto err1;
318 }
319 }
320
321 /* Clearing PLL_GO bit */
322 REG_FLD_MOD(DSS_PLL_CONTROL, 0, 28, 28);
323
324 /* Waiting for PLL to lock */
325 timeout = jiffies + msecs_to_jiffies(500);
326 while (!(dss_read_reg(DSS_SDI_STATUS) & (1 << 5))) {
327 if (time_after_eq(jiffies, timeout)) {
328 DSSERR("PLL lock timed out\n");
329 goto err1;
330 }
331 }
332
333 dispc_lcd_enable_signal(1);
334
335 /* Waiting for SDI reset to complete */
336 timeout = jiffies + msecs_to_jiffies(500);
337 while (!(dss_read_reg(DSS_SDI_STATUS) & (1 << 2))) {
338 if (time_after_eq(jiffies, timeout)) {
339 DSSERR("SDI reset timed out\n");
340 goto err2;
341 }
342 }
343
344 return 0;
345
346 err2:
347 dispc_lcd_enable_signal(0);
348 err1:
349 /* Reset SDI PLL */
350 REG_FLD_MOD(DSS_PLL_CONTROL, 0, 18, 18); /* SDI_PLL_SYSRESET */
351
352 dispc_pck_free_enable(0);
353
354 return -ETIMEDOUT;
355}
356
357void dss_sdi_disable(void)
358{
359 dispc_lcd_enable_signal(0);
360
361 dispc_pck_free_enable(0);
362
363 /* Reset SDI PLL */
364 REG_FLD_MOD(DSS_PLL_CONTROL, 0, 18, 18); /* SDI_PLL_SYSRESET */
365}
366
Tomi Valkeinen407bd562016-05-17 13:50:55 +0300367const char *dss_get_clk_source_name(enum dss_clk_source clk_src)
Archit Taneja067a57e2011-03-02 11:57:25 +0530368{
Taneja, Archit235e7db2011-03-14 23:28:21 -0500369 return dss_generic_clk_source_names[clk_src];
Archit Taneja067a57e2011-03-02 11:57:25 +0530370}
371
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200372void dss_dump_clocks(struct seq_file *s)
373{
Tomi Valkeinen557a1542016-05-17 13:49:18 +0300374 const char *fclk_name;
Tomi Valkeinen0acf6592011-03-14 07:28:57 -0500375 unsigned long fclk_rate;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200376
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300377 if (dss_runtime_get())
378 return;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200379
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200380 seq_printf(s, "- DSS -\n");
381
Tomi Valkeinen3b63ca72016-05-17 14:01:10 +0300382 fclk_name = dss_get_clk_source_name(DSS_CLK_SRC_FCK);
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300383 fclk_rate = clk_get_rate(dss.dss_clk);
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200384
Tomi Valkeinen557a1542016-05-17 13:49:18 +0300385 seq_printf(s, "%s = %lu\n",
386 fclk_name,
Tomi Valkeinen9c15d762013-11-01 11:36:10 +0200387 fclk_rate);
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200388
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300389 dss_runtime_put();
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200390}
391
Tomi Valkeinene40402c2012-03-02 18:01:07 +0200392static void dss_dump_regs(struct seq_file *s)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200393{
394#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dss_read_reg(r))
395
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300396 if (dss_runtime_get())
397 return;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200398
399 DUMPREG(DSS_REVISION);
400 DUMPREG(DSS_SYSCONFIG);
401 DUMPREG(DSS_SYSSTATUS);
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200402 DUMPREG(DSS_CONTROL);
Tomi Valkeinen6ec549e2011-02-24 14:18:50 +0200403
Laurent Pinchart24ab1df2017-08-05 01:43:59 +0300404 if (dss_feat_get_supported_outputs(OMAP_DSS_CHANNEL_LCD) &
405 OMAP_DSS_OUTPUT_SDI) {
Tomi Valkeinen6ec549e2011-02-24 14:18:50 +0200406 DUMPREG(DSS_SDI_CONTROL);
407 DUMPREG(DSS_PLL_CONTROL);
408 DUMPREG(DSS_SDI_STATUS);
409 }
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200410
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300411 dss_runtime_put();
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200412#undef DUMPREG
413}
414
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300415static int dss_get_channel_index(enum omap_channel channel)
416{
417 switch (channel) {
418 case OMAP_DSS_CHANNEL_LCD:
419 return 0;
420 case OMAP_DSS_CHANNEL_LCD2:
421 return 1;
422 case OMAP_DSS_CHANNEL_LCD3:
423 return 2;
424 default:
425 WARN_ON(1);
426 return 0;
427 }
428}
429
Tomi Valkeinendc0352d2016-05-17 13:45:09 +0300430static void dss_select_dispc_clk_source(enum dss_clk_source clk_src)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200431{
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +0200432 int b;
433
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300434 /*
435 * We always use PRCM clock as the DISPC func clock, except on DSS3,
436 * where we don't have separate DISPC and LCD clock sources.
437 */
Laurent Pinchart4569ab72017-08-05 01:44:13 +0300438 if (WARN_ON(dss.feat->has_lcd_clk_src && clk_src != DSS_CLK_SRC_FCK))
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300439 return;
440
Taneja, Archit66534e82011-03-08 05:50:34 -0600441 switch (clk_src) {
Tomi Valkeinen3b63ca72016-05-17 14:01:10 +0300442 case DSS_CLK_SRC_FCK:
Taneja, Archit66534e82011-03-08 05:50:34 -0600443 b = 0;
444 break;
Tomi Valkeinen3b63ca72016-05-17 14:01:10 +0300445 case DSS_CLK_SRC_PLL1_1:
Taneja, Archit66534e82011-03-08 05:50:34 -0600446 b = 1;
Taneja, Archit66534e82011-03-08 05:50:34 -0600447 break;
Tomi Valkeinen3b63ca72016-05-17 14:01:10 +0300448 case DSS_CLK_SRC_PLL2_1:
Archit Taneja5a8b5722011-05-12 17:26:29 +0530449 b = 2;
Archit Taneja5a8b5722011-05-12 17:26:29 +0530450 break;
Taneja, Archit66534e82011-03-08 05:50:34 -0600451 default:
452 BUG();
Tomi Valkeinenc6eee962012-05-18 11:47:02 +0300453 return;
Taneja, Archit66534e82011-03-08 05:50:34 -0600454 }
Tomi Valkeinene406f902010-06-09 15:28:12 +0300455
Laurent Pinchart6d85d4a2017-08-05 01:44:07 +0300456 REG_FLD_MOD(DSS_CONTROL, b, /* DISPC_CLK_SWITCH */
457 dss.feat->dispc_clk_switch.start,
458 dss.feat->dispc_clk_switch.end);
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +0200459
460 dss.dispc_clk_source = clk_src;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200461}
462
Archit Taneja5a8b5722011-05-12 17:26:29 +0530463void dss_select_dsi_clk_source(int dsi_module,
Tomi Valkeinendc0352d2016-05-17 13:45:09 +0300464 enum dss_clk_source clk_src)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200465{
Archit Tanejaa2e5d822012-05-07 16:51:35 +0530466 int b, pos;
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +0200467
Taneja, Archit66534e82011-03-08 05:50:34 -0600468 switch (clk_src) {
Tomi Valkeinen3b63ca72016-05-17 14:01:10 +0300469 case DSS_CLK_SRC_FCK:
Taneja, Archit66534e82011-03-08 05:50:34 -0600470 b = 0;
471 break;
Tomi Valkeinen3b63ca72016-05-17 14:01:10 +0300472 case DSS_CLK_SRC_PLL1_2:
Archit Taneja5a8b5722011-05-12 17:26:29 +0530473 BUG_ON(dsi_module != 0);
Taneja, Archit66534e82011-03-08 05:50:34 -0600474 b = 1;
Taneja, Archit66534e82011-03-08 05:50:34 -0600475 break;
Tomi Valkeinen3b63ca72016-05-17 14:01:10 +0300476 case DSS_CLK_SRC_PLL2_2:
Archit Taneja5a8b5722011-05-12 17:26:29 +0530477 BUG_ON(dsi_module != 1);
478 b = 1;
Archit Taneja5a8b5722011-05-12 17:26:29 +0530479 break;
Taneja, Archit66534e82011-03-08 05:50:34 -0600480 default:
481 BUG();
Tomi Valkeinenc6eee962012-05-18 11:47:02 +0300482 return;
Taneja, Archit66534e82011-03-08 05:50:34 -0600483 }
Tomi Valkeinene406f902010-06-09 15:28:12 +0300484
Archit Tanejaa2e5d822012-05-07 16:51:35 +0530485 pos = dsi_module == 0 ? 1 : 10;
486 REG_FLD_MOD(DSS_CONTROL, b, pos, pos); /* DSIx_CLK_SWITCH */
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +0200487
Archit Taneja5a8b5722011-05-12 17:26:29 +0530488 dss.dsi_clk_source[dsi_module] = clk_src;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200489}
490
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300491static int dss_lcd_clk_mux_dra7(enum omap_channel channel,
492 enum dss_clk_source clk_src)
Taneja, Architea751592011-03-08 05:50:35 -0600493{
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300494 const u8 ctrl_bits[] = {
495 [OMAP_DSS_CHANNEL_LCD] = 0,
496 [OMAP_DSS_CHANNEL_LCD2] = 12,
497 [OMAP_DSS_CHANNEL_LCD3] = 19,
498 };
499
500 u8 ctrl_bit = ctrl_bits[channel];
501 int r;
502
503 if (clk_src == DSS_CLK_SRC_FCK) {
504 /* LCDx_CLK_SWITCH */
505 REG_FLD_MOD(DSS_CONTROL, 0, ctrl_bit, ctrl_bit);
506 return -EINVAL;
507 }
508
509 r = dss_ctrl_pll_set_control_mux(clk_src, channel);
510 if (r)
511 return r;
512
513 REG_FLD_MOD(DSS_CONTROL, 1, ctrl_bit, ctrl_bit);
514
515 return 0;
516}
517
518static int dss_lcd_clk_mux_omap5(enum omap_channel channel,
519 enum dss_clk_source clk_src)
520{
521 const u8 ctrl_bits[] = {
522 [OMAP_DSS_CHANNEL_LCD] = 0,
523 [OMAP_DSS_CHANNEL_LCD2] = 12,
524 [OMAP_DSS_CHANNEL_LCD3] = 19,
525 };
526 const enum dss_clk_source allowed_plls[] = {
527 [OMAP_DSS_CHANNEL_LCD] = DSS_CLK_SRC_PLL1_1,
528 [OMAP_DSS_CHANNEL_LCD2] = DSS_CLK_SRC_FCK,
529 [OMAP_DSS_CHANNEL_LCD3] = DSS_CLK_SRC_PLL2_1,
530 };
531
532 u8 ctrl_bit = ctrl_bits[channel];
533
534 if (clk_src == DSS_CLK_SRC_FCK) {
535 /* LCDx_CLK_SWITCH */
536 REG_FLD_MOD(DSS_CONTROL, 0, ctrl_bit, ctrl_bit);
537 return -EINVAL;
538 }
539
540 if (WARN_ON(allowed_plls[channel] != clk_src))
541 return -EINVAL;
542
543 REG_FLD_MOD(DSS_CONTROL, 1, ctrl_bit, ctrl_bit);
544
545 return 0;
546}
547
548static int dss_lcd_clk_mux_omap4(enum omap_channel channel,
549 enum dss_clk_source clk_src)
550{
551 const u8 ctrl_bits[] = {
552 [OMAP_DSS_CHANNEL_LCD] = 0,
553 [OMAP_DSS_CHANNEL_LCD2] = 12,
554 };
555 const enum dss_clk_source allowed_plls[] = {
556 [OMAP_DSS_CHANNEL_LCD] = DSS_CLK_SRC_PLL1_1,
557 [OMAP_DSS_CHANNEL_LCD2] = DSS_CLK_SRC_PLL2_1,
558 };
559
560 u8 ctrl_bit = ctrl_bits[channel];
561
562 if (clk_src == DSS_CLK_SRC_FCK) {
563 /* LCDx_CLK_SWITCH */
564 REG_FLD_MOD(DSS_CONTROL, 0, ctrl_bit, ctrl_bit);
565 return 0;
566 }
567
568 if (WARN_ON(allowed_plls[channel] != clk_src))
569 return -EINVAL;
570
571 REG_FLD_MOD(DSS_CONTROL, 1, ctrl_bit, ctrl_bit);
572
573 return 0;
574}
575
Taneja, Architea751592011-03-08 05:50:35 -0600576void dss_select_lcd_clk_source(enum omap_channel channel,
Tomi Valkeinendc0352d2016-05-17 13:45:09 +0300577 enum dss_clk_source clk_src)
Taneja, Architea751592011-03-08 05:50:35 -0600578{
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300579 int idx = dss_get_channel_index(channel);
580 int r;
Taneja, Architea751592011-03-08 05:50:35 -0600581
Laurent Pinchart4569ab72017-08-05 01:44:13 +0300582 if (!dss.feat->has_lcd_clk_src) {
Tomi Valkeinena5b83992012-10-22 16:58:36 +0300583 dss_select_dispc_clk_source(clk_src);
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300584 dss.lcd_clk_source[idx] = clk_src;
Taneja, Architea751592011-03-08 05:50:35 -0600585 return;
Tomi Valkeinena5b83992012-10-22 16:58:36 +0300586 }
Taneja, Architea751592011-03-08 05:50:35 -0600587
Laurent Pinchartfecea252017-08-05 01:43:52 +0300588 r = dss.feat->ops->select_lcd_source(channel, clk_src);
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300589 if (r)
Tomi Valkeinenc6eee962012-05-18 11:47:02 +0300590 return;
Taneja, Architea751592011-03-08 05:50:35 -0600591
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300592 dss.lcd_clk_source[idx] = clk_src;
Taneja, Architea751592011-03-08 05:50:35 -0600593}
594
Tomi Valkeinendc0352d2016-05-17 13:45:09 +0300595enum dss_clk_source dss_get_dispc_clk_source(void)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200596{
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +0200597 return dss.dispc_clk_source;
598}
599
Tomi Valkeinendc0352d2016-05-17 13:45:09 +0300600enum dss_clk_source dss_get_dsi_clk_source(int dsi_module)
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +0200601{
Archit Taneja5a8b5722011-05-12 17:26:29 +0530602 return dss.dsi_clk_source[dsi_module];
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200603}
604
Tomi Valkeinendc0352d2016-05-17 13:45:09 +0300605enum dss_clk_source dss_get_lcd_clk_source(enum omap_channel channel)
Taneja, Architea751592011-03-08 05:50:35 -0600606{
Laurent Pinchart4569ab72017-08-05 01:44:13 +0300607 if (dss.feat->has_lcd_clk_src) {
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300608 int idx = dss_get_channel_index(channel);
609 return dss.lcd_clk_source[idx];
Archit Taneja89976f22011-03-31 13:23:35 +0530610 } else {
611 /* LCD_CLK source is the same as DISPC_FCLK source for
612 * OMAP2 and OMAP3 */
613 return dss.dispc_clk_source;
614 }
Taneja, Architea751592011-03-08 05:50:35 -0600615}
616
Tomi Valkeinen688af022013-10-31 16:41:57 +0200617bool dss_div_calc(unsigned long pck, unsigned long fck_min,
618 dss_div_calc_func func, void *data)
Tomi Valkeinen43417822013-03-05 16:34:05 +0200619{
620 int fckd, fckd_start, fckd_stop;
621 unsigned long fck;
622 unsigned long fck_hw_max;
623 unsigned long fckd_hw_max;
624 unsigned long prate;
Tomi Valkeinen648a55e2013-04-10 14:47:38 +0300625 unsigned m;
Tomi Valkeinen43417822013-03-05 16:34:05 +0200626
Laurent Pinchart9f0fbae2017-08-05 01:44:17 +0300627 fck_hw_max = dss.feat->fck_freq_max;
Tomi Valkeinenfc1fe6e2013-10-31 16:42:13 +0200628
Tomi Valkeinen64ad8462013-11-01 11:38:04 +0200629 if (dss.parent_clk == NULL) {
Tomi Valkeinenfc1fe6e2013-10-31 16:42:13 +0200630 unsigned pckd;
631
632 pckd = fck_hw_max / pck;
633
634 fck = pck * pckd;
635
636 fck = clk_round_rate(dss.dss_clk, fck);
637
Tomi Valkeinend0f58bd2013-10-31 14:44:23 +0200638 return func(fck, data);
Tomi Valkeinen43417822013-03-05 16:34:05 +0200639 }
640
Tomi Valkeinen43417822013-03-05 16:34:05 +0200641 fckd_hw_max = dss.feat->fck_div_max;
642
Tomi Valkeinen648a55e2013-04-10 14:47:38 +0300643 m = dss.feat->dss_fck_multiplier;
Tomi Valkeinenada94432013-10-31 16:06:38 +0200644 prate = clk_get_rate(dss.parent_clk);
Tomi Valkeinen43417822013-03-05 16:34:05 +0200645
646 fck_min = fck_min ? fck_min : 1;
647
Tomi Valkeinen648a55e2013-04-10 14:47:38 +0300648 fckd_start = min(prate * m / fck_min, fckd_hw_max);
649 fckd_stop = max(DIV_ROUND_UP(prate * m, fck_hw_max), 1ul);
Tomi Valkeinen43417822013-03-05 16:34:05 +0200650
651 for (fckd = fckd_start; fckd >= fckd_stop; --fckd) {
Tomi Valkeinend0e224f2014-02-13 11:36:22 +0200652 fck = DIV_ROUND_UP(prate, fckd) * m;
Tomi Valkeinen43417822013-03-05 16:34:05 +0200653
Tomi Valkeinend0f58bd2013-10-31 14:44:23 +0200654 if (func(fck, data))
Tomi Valkeinen43417822013-03-05 16:34:05 +0200655 return true;
656 }
657
658 return false;
659}
660
Tomi Valkeinend0f58bd2013-10-31 14:44:23 +0200661int dss_set_fck_rate(unsigned long rate)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200662{
Tomi Valkeinenada94432013-10-31 16:06:38 +0200663 int r;
664
Tomi Valkeinend0f58bd2013-10-31 14:44:23 +0200665 DSSDBG("set fck to %lu\n", rate);
666
Tomi Valkeinenada94432013-10-31 16:06:38 +0200667 r = clk_set_rate(dss.dss_clk, rate);
668 if (r)
669 return r;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200670
Tomi Valkeinen5aaee692012-12-12 10:37:03 +0200671 dss.dss_clk_rate = clk_get_rate(dss.dss_clk);
672
Tomi Valkeinend0f58bd2013-10-31 14:44:23 +0200673 WARN_ONCE(dss.dss_clk_rate != rate,
Tomi Valkeinen648a55e2013-04-10 14:47:38 +0300674 "clk rate mismatch: %lu != %lu", dss.dss_clk_rate,
Tomi Valkeinend0f58bd2013-10-31 14:44:23 +0200675 rate);
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200676
677 return 0;
678}
679
Tomi Valkeinen5aaee692012-12-12 10:37:03 +0200680unsigned long dss_get_dispc_clk_rate(void)
681{
682 return dss.dss_clk_rate;
683}
684
Laurent Pinchart9f0fbae2017-08-05 01:44:17 +0300685unsigned long dss_get_max_fck_rate(void)
686{
687 return dss.feat->fck_freq_max;
688}
689
Tomi Valkeinen13a1a2b2012-10-22 16:35:41 +0300690static int dss_setup_default_clock(void)
691{
692 unsigned long max_dss_fck, prate;
Tomi Valkeinend0f58bd2013-10-31 14:44:23 +0200693 unsigned long fck;
Tomi Valkeinen13a1a2b2012-10-22 16:35:41 +0300694 unsigned fck_div;
Tomi Valkeinen13a1a2b2012-10-22 16:35:41 +0300695 int r;
696
Laurent Pinchart9f0fbae2017-08-05 01:44:17 +0300697 max_dss_fck = dss.feat->fck_freq_max;
Tomi Valkeinen13a1a2b2012-10-22 16:35:41 +0300698
Tomi Valkeinenfc1fe6e2013-10-31 16:42:13 +0200699 if (dss.parent_clk == NULL) {
700 fck = clk_round_rate(dss.dss_clk, max_dss_fck);
701 } else {
702 prate = clk_get_rate(dss.parent_clk);
Tomi Valkeinen13a1a2b2012-10-22 16:35:41 +0300703
Tomi Valkeinenfc1fe6e2013-10-31 16:42:13 +0200704 fck_div = DIV_ROUND_UP(prate * dss.feat->dss_fck_multiplier,
705 max_dss_fck);
Tomi Valkeinend0e224f2014-02-13 11:36:22 +0200706 fck = DIV_ROUND_UP(prate, fck_div) * dss.feat->dss_fck_multiplier;
Tomi Valkeinenfc1fe6e2013-10-31 16:42:13 +0200707 }
Tomi Valkeinen13a1a2b2012-10-22 16:35:41 +0300708
Tomi Valkeinend0f58bd2013-10-31 14:44:23 +0200709 r = dss_set_fck_rate(fck);
Tomi Valkeinen13a1a2b2012-10-22 16:35:41 +0300710 if (r)
711 return r;
712
713 return 0;
714}
715
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200716void dss_set_venc_output(enum omap_dss_venc_type type)
717{
718 int l = 0;
719
720 if (type == OMAP_DSS_VENC_TYPE_COMPOSITE)
721 l = 0;
722 else if (type == OMAP_DSS_VENC_TYPE_SVIDEO)
723 l = 1;
724 else
725 BUG();
726
727 /* venc out selection. 0 = comp, 1 = svideo */
728 REG_FLD_MOD(DSS_CONTROL, l, 6, 6);
729}
730
731void dss_set_dac_pwrdn_bgz(bool enable)
732{
733 REG_FLD_MOD(DSS_CONTROL, enable, 5, 5); /* DAC Power-Down Control */
734}
735
Ricardo Neri8aa2eed2012-08-01 07:56:40 -0500736void dss_select_hdmi_venc_clk_source(enum dss_hdmi_venc_clk_source_select src)
Mythri P K7ed024a2011-03-09 16:31:38 +0530737{
Laurent Pinchart24ab1df2017-08-05 01:43:59 +0300738 enum omap_dss_output_id outputs;
739
740 outputs = dss_feat_get_supported_outputs(OMAP_DSS_CHANNEL_DIGIT);
Ricardo Neri8aa2eed2012-08-01 07:56:40 -0500741
742 /* Complain about invalid selections */
Laurent Pinchart24ab1df2017-08-05 01:43:59 +0300743 WARN_ON((src == DSS_VENC_TV_CLK) && !(outputs & OMAP_DSS_OUTPUT_VENC));
744 WARN_ON((src == DSS_HDMI_M_PCLK) && !(outputs & OMAP_DSS_OUTPUT_HDMI));
Ricardo Neri8aa2eed2012-08-01 07:56:40 -0500745
746 /* Select only if we have options */
Laurent Pinchart24ab1df2017-08-05 01:43:59 +0300747 if ((outputs & OMAP_DSS_OUTPUT_VENC) &&
748 (outputs & OMAP_DSS_OUTPUT_HDMI))
Ricardo Neri8aa2eed2012-08-01 07:56:40 -0500749 REG_FLD_MOD(DSS_CONTROL, src, 15, 15); /* VENC_HDMI_SWITCH */
Mythri P K7ed024a2011-03-09 16:31:38 +0530750}
751
Tomi Valkeinen4a61e262011-08-31 14:33:31 +0300752enum dss_hdmi_venc_clk_source_select dss_get_hdmi_venc_clk_source(void)
753{
Laurent Pinchart24ab1df2017-08-05 01:43:59 +0300754 enum omap_dss_output_id outputs;
Tomi Valkeinen4a61e262011-08-31 14:33:31 +0300755
Laurent Pinchart24ab1df2017-08-05 01:43:59 +0300756 outputs = dss_feat_get_supported_outputs(OMAP_DSS_CHANNEL_DIGIT);
757 if ((outputs & OMAP_DSS_OUTPUT_HDMI) == 0)
Tomi Valkeinen4a61e262011-08-31 14:33:31 +0300758 return DSS_VENC_TV_CLK;
759
Laurent Pinchart24ab1df2017-08-05 01:43:59 +0300760 if ((outputs & OMAP_DSS_OUTPUT_VENC) == 0)
Ricardo Neri8aa2eed2012-08-01 07:56:40 -0500761 return DSS_HDMI_M_PCLK;
762
Tomi Valkeinen4a61e262011-08-31 14:33:31 +0300763 return REG_GET(DSS_CONTROL, 15, 15);
764}
765
Archit Taneja064c2a42014-04-23 18:00:18 +0530766static int dss_dpi_select_source_omap2_omap3(int port, enum omap_channel channel)
Tomi Valkeinende09e452012-09-21 12:09:54 +0300767{
768 if (channel != OMAP_DSS_CHANNEL_LCD)
769 return -EINVAL;
770
771 return 0;
772}
773
Archit Taneja064c2a42014-04-23 18:00:18 +0530774static int dss_dpi_select_source_omap4(int port, enum omap_channel channel)
Tomi Valkeinende09e452012-09-21 12:09:54 +0300775{
776 int val;
777
778 switch (channel) {
779 case OMAP_DSS_CHANNEL_LCD2:
780 val = 0;
781 break;
782 case OMAP_DSS_CHANNEL_DIGIT:
783 val = 1;
784 break;
785 default:
786 return -EINVAL;
787 }
788
789 REG_FLD_MOD(DSS_CONTROL, val, 17, 17);
790
791 return 0;
792}
793
Archit Taneja064c2a42014-04-23 18:00:18 +0530794static int dss_dpi_select_source_omap5(int port, enum omap_channel channel)
Tomi Valkeinende09e452012-09-21 12:09:54 +0300795{
796 int val;
797
798 switch (channel) {
799 case OMAP_DSS_CHANNEL_LCD:
800 val = 1;
801 break;
802 case OMAP_DSS_CHANNEL_LCD2:
803 val = 2;
804 break;
805 case OMAP_DSS_CHANNEL_LCD3:
806 val = 3;
807 break;
808 case OMAP_DSS_CHANNEL_DIGIT:
809 val = 0;
810 break;
811 default:
812 return -EINVAL;
813 }
814
815 REG_FLD_MOD(DSS_CONTROL, val, 17, 16);
816
817 return 0;
818}
819
Tomi Valkeinen6d817882014-12-31 11:23:31 +0200820static int dss_dpi_select_source_dra7xx(int port, enum omap_channel channel)
821{
822 switch (port) {
823 case 0:
824 return dss_dpi_select_source_omap5(port, channel);
825 case 1:
826 if (channel != OMAP_DSS_CHANNEL_LCD2)
827 return -EINVAL;
828 break;
829 case 2:
830 if (channel != OMAP_DSS_CHANNEL_LCD3)
831 return -EINVAL;
832 break;
833 default:
834 return -EINVAL;
835 }
836
837 return 0;
838}
839
Archit Taneja064c2a42014-04-23 18:00:18 +0530840int dss_dpi_select_source(int port, enum omap_channel channel)
Tomi Valkeinende09e452012-09-21 12:09:54 +0300841{
Laurent Pinchartfecea252017-08-05 01:43:52 +0300842 return dss.feat->ops->dpi_select_source(port, channel);
Tomi Valkeinende09e452012-09-21 12:09:54 +0300843}
844
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000845static int dss_get_clocks(void)
846{
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300847 struct clk *clk;
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000848
Archit Tanejab2c9c8e2013-04-08 11:55:00 +0300849 clk = devm_clk_get(&dss.pdev->dev, "fck");
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300850 if (IS_ERR(clk)) {
851 DSSERR("can't get clock fck\n");
Archit Tanejab2c9c8e2013-04-08 11:55:00 +0300852 return PTR_ERR(clk);
Semwal, Sumita1a0dcc2011-03-01 02:42:14 -0600853 }
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000854
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300855 dss.dss_clk = clk;
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000856
Tomi Valkeinen64ad8462013-11-01 11:38:04 +0200857 if (dss.feat->parent_clk_name) {
858 clk = clk_get(NULL, dss.feat->parent_clk_name);
Aaro Koskinen8ad93752012-11-21 21:48:51 +0200859 if (IS_ERR(clk)) {
Tomi Valkeinen64ad8462013-11-01 11:38:04 +0200860 DSSERR("Failed to get %s\n", dss.feat->parent_clk_name);
Archit Tanejab2c9c8e2013-04-08 11:55:00 +0300861 return PTR_ERR(clk);
Aaro Koskinen8ad93752012-11-21 21:48:51 +0200862 }
863 } else {
864 clk = NULL;
Tomi Valkeinen94c042c2011-05-16 13:43:04 +0300865 }
866
Tomi Valkeinen64ad8462013-11-01 11:38:04 +0200867 dss.parent_clk = clk;
Tomi Valkeinen94c042c2011-05-16 13:43:04 +0300868
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000869 return 0;
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000870}
871
872static void dss_put_clocks(void)
873{
Tomi Valkeinen64ad8462013-11-01 11:38:04 +0200874 if (dss.parent_clk)
875 clk_put(dss.parent_clk);
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000876}
877
Tomi Valkeinen99767542014-07-04 13:38:27 +0530878int dss_runtime_get(void)
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000879{
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300880 int r;
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000881
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300882 DSSDBG("dss_runtime_get\n");
883
884 r = pm_runtime_get_sync(&dss.pdev->dev);
885 WARN_ON(r < 0);
886 return r < 0 ? r : 0;
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000887}
888
Tomi Valkeinen99767542014-07-04 13:38:27 +0530889void dss_runtime_put(void)
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000890{
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300891 int r;
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000892
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300893 DSSDBG("dss_runtime_put\n");
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000894
Tomi Valkeinen0eaf9f52012-01-23 13:23:08 +0200895 r = pm_runtime_put_sync(&dss.pdev->dev);
Tomi Valkeinen5be3aeb2012-06-27 16:37:18 +0300896 WARN_ON(r < 0 && r != -ENOSYS && r != -EBUSY);
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000897}
898
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000899/* DEBUGFS */
Chandrabhanu Mahapatra1b3bcb32012-09-29 11:25:42 +0530900#if defined(CONFIG_OMAP2_DSS_DEBUGFS)
Laurent Pinchart11765d12017-08-05 01:44:01 +0300901static void dss_debug_dump_clocks(struct seq_file *s)
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000902{
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000903 dss_dump_clocks(s);
904 dispc_dump_clocks(s);
905#ifdef CONFIG_OMAP2_DSS_DSI
906 dsi_dump_clocks(s);
907#endif
908}
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000909
Laurent Pinchart11765d12017-08-05 01:44:01 +0300910static int dss_debug_show(struct seq_file *s, void *unused)
911{
912 void (*func)(struct seq_file *) = s->private;
913
914 func(s);
915 return 0;
916}
917
918static int dss_debug_open(struct inode *inode, struct file *file)
919{
920 return single_open(file, dss_debug_show, inode->i_private);
921}
922
923static const struct file_operations dss_debug_fops = {
924 .open = dss_debug_open,
925 .read = seq_read,
926 .llseek = seq_lseek,
927 .release = single_release,
928};
929
930static struct dentry *dss_debugfs_dir;
931
932static int dss_initialize_debugfs(void)
933{
934 dss_debugfs_dir = debugfs_create_dir("omapdss", NULL);
935 if (IS_ERR(dss_debugfs_dir)) {
936 int err = PTR_ERR(dss_debugfs_dir);
937
938 dss_debugfs_dir = NULL;
939 return err;
940 }
941
942 debugfs_create_file("clk", S_IRUGO, dss_debugfs_dir,
943 &dss_debug_dump_clocks, &dss_debug_fops);
944
945 return 0;
946}
947
948static void dss_uninitialize_debugfs(void)
949{
950 if (dss_debugfs_dir)
951 debugfs_remove_recursive(dss_debugfs_dir);
952}
953
954int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *))
955{
956 struct dentry *d;
957
958 d = debugfs_create_file(name, S_IRUGO, dss_debugfs_dir,
959 write, &dss_debug_fops);
960
961 return PTR_ERR_OR_ZERO(d);
962}
963#else /* CONFIG_OMAP2_DSS_DEBUGFS */
964static inline int dss_initialize_debugfs(void)
965{
966 return 0;
967}
968static inline void dss_uninitialize_debugfs(void)
969{
970}
971#endif /* CONFIG_OMAP2_DSS_DEBUGFS */
Archit Taneja387ce9f2014-05-22 17:01:57 +0530972
Laurent Pinchartfecea252017-08-05 01:43:52 +0300973static const struct dss_ops dss_ops_omap2_omap3 = {
974 .dpi_select_source = &dss_dpi_select_source_omap2_omap3,
975};
976
977static const struct dss_ops dss_ops_omap4 = {
978 .dpi_select_source = &dss_dpi_select_source_omap4,
979 .select_lcd_source = &dss_lcd_clk_mux_omap4,
980};
981
982static const struct dss_ops dss_ops_omap5 = {
983 .dpi_select_source = &dss_dpi_select_source_omap5,
984 .select_lcd_source = &dss_lcd_clk_mux_omap5,
985};
986
987static const struct dss_ops dss_ops_dra7 = {
988 .dpi_select_source = &dss_dpi_select_source_dra7xx,
989 .select_lcd_source = &dss_lcd_clk_mux_dra7,
990};
991
Tomi Valkeinen234f9a22014-12-11 15:59:31 +0200992static const enum omap_display_type omap2plus_ports[] = {
Archit Taneja387ce9f2014-05-22 17:01:57 +0530993 OMAP_DISPLAY_TYPE_DPI,
994};
995
Tomi Valkeinen234f9a22014-12-11 15:59:31 +0200996static const enum omap_display_type omap34xx_ports[] = {
Archit Taneja387ce9f2014-05-22 17:01:57 +0530997 OMAP_DISPLAY_TYPE_DPI,
998 OMAP_DISPLAY_TYPE_SDI,
999};
1000
Tomi Valkeinen6d817882014-12-31 11:23:31 +02001001static const enum omap_display_type dra7xx_ports[] = {
1002 OMAP_DISPLAY_TYPE_DPI,
1003 OMAP_DISPLAY_TYPE_DPI,
1004 OMAP_DISPLAY_TYPE_DPI,
1005};
1006
Tomi Valkeinenede92692015-06-04 14:12:16 +03001007static const struct dss_features omap24xx_dss_feats = {
Laurent Pinchartb8dab2b2017-08-05 01:43:56 +03001008 .model = DSS_MODEL_OMAP2,
Tomi Valkeinen6e555e22013-11-01 11:26:43 +02001009 /*
1010 * fck div max is really 16, but the divider range has gaps. The range
1011 * from 1 to 6 has no gaps, so let's use that as a max.
1012 */
1013 .fck_div_max = 6,
Laurent Pinchart9f0fbae2017-08-05 01:44:17 +03001014 .fck_freq_max = 133000000,
Tomi Valkeinen84273a92012-09-21 12:03:31 +03001015 .dss_fck_multiplier = 2,
Tomi Valkeinenada94432013-10-31 16:06:38 +02001016 .parent_clk_name = "core_ck",
Archit Taneja387ce9f2014-05-22 17:01:57 +05301017 .ports = omap2plus_ports,
1018 .num_ports = ARRAY_SIZE(omap2plus_ports),
Laurent Pinchartfecea252017-08-05 01:43:52 +03001019 .ops = &dss_ops_omap2_omap3,
Laurent Pinchart6d85d4a2017-08-05 01:44:07 +03001020 .dispc_clk_switch = { 0, 0 },
Laurent Pinchart4569ab72017-08-05 01:44:13 +03001021 .has_lcd_clk_src = false,
Tomi Valkeinen84273a92012-09-21 12:03:31 +03001022};
1023
Tomi Valkeinenede92692015-06-04 14:12:16 +03001024static const struct dss_features omap34xx_dss_feats = {
Laurent Pinchartb8dab2b2017-08-05 01:43:56 +03001025 .model = DSS_MODEL_OMAP3,
Tomi Valkeinen84273a92012-09-21 12:03:31 +03001026 .fck_div_max = 16,
Laurent Pinchart9f0fbae2017-08-05 01:44:17 +03001027 .fck_freq_max = 173000000,
Tomi Valkeinen84273a92012-09-21 12:03:31 +03001028 .dss_fck_multiplier = 2,
Tomi Valkeinenada94432013-10-31 16:06:38 +02001029 .parent_clk_name = "dpll4_ck",
Archit Taneja387ce9f2014-05-22 17:01:57 +05301030 .ports = omap34xx_ports,
1031 .num_ports = ARRAY_SIZE(omap34xx_ports),
Laurent Pinchartfecea252017-08-05 01:43:52 +03001032 .ops = &dss_ops_omap2_omap3,
Laurent Pinchart6d85d4a2017-08-05 01:44:07 +03001033 .dispc_clk_switch = { 0, 0 },
Laurent Pinchart4569ab72017-08-05 01:44:13 +03001034 .has_lcd_clk_src = false,
Tomi Valkeinen84273a92012-09-21 12:03:31 +03001035};
1036
Tomi Valkeinenede92692015-06-04 14:12:16 +03001037static const struct dss_features omap3630_dss_feats = {
Laurent Pinchartb8dab2b2017-08-05 01:43:56 +03001038 .model = DSS_MODEL_OMAP3,
Tomi Valkeinen84273a92012-09-21 12:03:31 +03001039 .fck_div_max = 32,
Laurent Pinchart9f0fbae2017-08-05 01:44:17 +03001040 .fck_freq_max = 173000000,
Tomi Valkeinen84273a92012-09-21 12:03:31 +03001041 .dss_fck_multiplier = 1,
Tomi Valkeinenada94432013-10-31 16:06:38 +02001042 .parent_clk_name = "dpll4_ck",
Archit Taneja387ce9f2014-05-22 17:01:57 +05301043 .ports = omap2plus_ports,
1044 .num_ports = ARRAY_SIZE(omap2plus_ports),
Laurent Pinchartfecea252017-08-05 01:43:52 +03001045 .ops = &dss_ops_omap2_omap3,
Laurent Pinchart6d85d4a2017-08-05 01:44:07 +03001046 .dispc_clk_switch = { 0, 0 },
Laurent Pinchart4569ab72017-08-05 01:44:13 +03001047 .has_lcd_clk_src = false,
Tomi Valkeinen84273a92012-09-21 12:03:31 +03001048};
1049
Tomi Valkeinenede92692015-06-04 14:12:16 +03001050static const struct dss_features omap44xx_dss_feats = {
Laurent Pinchartb8dab2b2017-08-05 01:43:56 +03001051 .model = DSS_MODEL_OMAP4,
Tomi Valkeinen84273a92012-09-21 12:03:31 +03001052 .fck_div_max = 32,
Laurent Pinchart9f0fbae2017-08-05 01:44:17 +03001053 .fck_freq_max = 186000000,
Tomi Valkeinen84273a92012-09-21 12:03:31 +03001054 .dss_fck_multiplier = 1,
Tomi Valkeinenada94432013-10-31 16:06:38 +02001055 .parent_clk_name = "dpll_per_x2_ck",
Archit Taneja387ce9f2014-05-22 17:01:57 +05301056 .ports = omap2plus_ports,
1057 .num_ports = ARRAY_SIZE(omap2plus_ports),
Laurent Pinchartfecea252017-08-05 01:43:52 +03001058 .ops = &dss_ops_omap4,
Laurent Pinchart6d85d4a2017-08-05 01:44:07 +03001059 .dispc_clk_switch = { 9, 8 },
Laurent Pinchart4569ab72017-08-05 01:44:13 +03001060 .has_lcd_clk_src = true,
Tomi Valkeinen84273a92012-09-21 12:03:31 +03001061};
1062
Tomi Valkeinenede92692015-06-04 14:12:16 +03001063static const struct dss_features omap54xx_dss_feats = {
Laurent Pinchartb8dab2b2017-08-05 01:43:56 +03001064 .model = DSS_MODEL_OMAP5,
Tomi Valkeinen84273a92012-09-21 12:03:31 +03001065 .fck_div_max = 64,
Laurent Pinchart9f0fbae2017-08-05 01:44:17 +03001066 .fck_freq_max = 209250000,
Tomi Valkeinen84273a92012-09-21 12:03:31 +03001067 .dss_fck_multiplier = 1,
Tomi Valkeinenada94432013-10-31 16:06:38 +02001068 .parent_clk_name = "dpll_per_x2_ck",
Archit Taneja387ce9f2014-05-22 17:01:57 +05301069 .ports = omap2plus_ports,
1070 .num_ports = ARRAY_SIZE(omap2plus_ports),
Laurent Pinchartfecea252017-08-05 01:43:52 +03001071 .ops = &dss_ops_omap5,
Laurent Pinchart6d85d4a2017-08-05 01:44:07 +03001072 .dispc_clk_switch = { 9, 7 },
Laurent Pinchart4569ab72017-08-05 01:44:13 +03001073 .has_lcd_clk_src = true,
Tomi Valkeinen84273a92012-09-21 12:03:31 +03001074};
1075
Tomi Valkeinenede92692015-06-04 14:12:16 +03001076static const struct dss_features am43xx_dss_feats = {
Laurent Pinchartb8dab2b2017-08-05 01:43:56 +03001077 .model = DSS_MODEL_OMAP3,
Sathya Prakash M Rd6279d42014-03-24 16:31:51 +05301078 .fck_div_max = 0,
Laurent Pinchart9f0fbae2017-08-05 01:44:17 +03001079 .fck_freq_max = 200000000,
Sathya Prakash M Rd6279d42014-03-24 16:31:51 +05301080 .dss_fck_multiplier = 0,
1081 .parent_clk_name = NULL,
Archit Taneja387ce9f2014-05-22 17:01:57 +05301082 .ports = omap2plus_ports,
1083 .num_ports = ARRAY_SIZE(omap2plus_ports),
Laurent Pinchartfecea252017-08-05 01:43:52 +03001084 .ops = &dss_ops_omap2_omap3,
Laurent Pinchart6d85d4a2017-08-05 01:44:07 +03001085 .dispc_clk_switch = { 0, 0 },
Laurent Pinchart4569ab72017-08-05 01:44:13 +03001086 .has_lcd_clk_src = true,
Sathya Prakash M Rd6279d42014-03-24 16:31:51 +05301087};
1088
Tomi Valkeinenede92692015-06-04 14:12:16 +03001089static const struct dss_features dra7xx_dss_feats = {
Laurent Pinchartb8dab2b2017-08-05 01:43:56 +03001090 .model = DSS_MODEL_DRA7,
Tomi Valkeinen6d817882014-12-31 11:23:31 +02001091 .fck_div_max = 64,
Laurent Pinchart9f0fbae2017-08-05 01:44:17 +03001092 .fck_freq_max = 209250000,
Tomi Valkeinen6d817882014-12-31 11:23:31 +02001093 .dss_fck_multiplier = 1,
1094 .parent_clk_name = "dpll_per_x2_ck",
Tomi Valkeinen6d817882014-12-31 11:23:31 +02001095 .ports = dra7xx_ports,
1096 .num_ports = ARRAY_SIZE(dra7xx_ports),
Laurent Pinchartfecea252017-08-05 01:43:52 +03001097 .ops = &dss_ops_dra7,
Laurent Pinchart6d85d4a2017-08-05 01:44:07 +03001098 .dispc_clk_switch = { 9, 7 },
Laurent Pinchart4569ab72017-08-05 01:44:13 +03001099 .has_lcd_clk_src = true,
Tomi Valkeinen6d817882014-12-31 11:23:31 +02001100};
1101
Tomi Valkeinenede92692015-06-04 14:12:16 +03001102static int dss_init_ports(struct platform_device *pdev)
Tomi Valkeinen2ecef242013-12-16 15:13:24 +02001103{
1104 struct device_node *parent = pdev->dev.of_node;
1105 struct device_node *port;
Rob Herring09bffa62017-03-22 08:26:08 -05001106 int i;
Tomi Valkeinen2ecef242013-12-16 15:13:24 +02001107
Rob Herring09bffa62017-03-22 08:26:08 -05001108 for (i = 0; i < dss.feat->num_ports; i++) {
1109 port = of_graph_get_port_by_id(parent, i);
1110 if (!port)
Archit Taneja387ce9f2014-05-22 17:01:57 +05301111 continue;
1112
Rob Herring09bffa62017-03-22 08:26:08 -05001113 switch (dss.feat->ports[i]) {
Archit Taneja387ce9f2014-05-22 17:01:57 +05301114 case OMAP_DISPLAY_TYPE_DPI:
Laurent Pinchartb8dab2b2017-08-05 01:43:56 +03001115 dpi_init_port(pdev, port, dss.feat->model);
Archit Taneja387ce9f2014-05-22 17:01:57 +05301116 break;
1117 case OMAP_DISPLAY_TYPE_SDI:
Tomi Valkeinen2ecef242013-12-16 15:13:24 +02001118 sdi_init_port(pdev, port);
Archit Taneja387ce9f2014-05-22 17:01:57 +05301119 break;
1120 default:
1121 break;
1122 }
Rob Herring09bffa62017-03-22 08:26:08 -05001123 }
Tomi Valkeinen2ecef242013-12-16 15:13:24 +02001124
1125 return 0;
1126}
1127
Tomi Valkeinenede92692015-06-04 14:12:16 +03001128static void dss_uninit_ports(struct platform_device *pdev)
Tomi Valkeinen2ecef242013-12-16 15:13:24 +02001129{
Archit Taneja80eb6752014-06-02 14:11:51 +05301130 struct device_node *parent = pdev->dev.of_node;
1131 struct device_node *port;
Rob Herring09bffa62017-03-22 08:26:08 -05001132 int i;
Archit Taneja80eb6752014-06-02 14:11:51 +05301133
Rob Herring09bffa62017-03-22 08:26:08 -05001134 for (i = 0; i < dss.feat->num_ports; i++) {
1135 port = of_graph_get_port_by_id(parent, i);
1136 if (!port)
Archit Taneja387ce9f2014-05-22 17:01:57 +05301137 continue;
1138
Rob Herring09bffa62017-03-22 08:26:08 -05001139 switch (dss.feat->ports[i]) {
Archit Taneja387ce9f2014-05-22 17:01:57 +05301140 case OMAP_DISPLAY_TYPE_DPI:
1141 dpi_uninit_port(port);
1142 break;
1143 case OMAP_DISPLAY_TYPE_SDI:
1144 sdi_uninit_port(port);
1145 break;
1146 default:
1147 break;
1148 }
Rob Herring09bffa62017-03-22 08:26:08 -05001149 }
Tomi Valkeinen2ecef242013-12-16 15:13:24 +02001150}
1151
Tomi Valkeinen7e328f52015-06-04 13:02:52 +03001152static int dss_video_pll_probe(struct platform_device *pdev)
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +00001153{
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +05301154 struct device_node *np = pdev->dev.of_node;
Tomi Valkeinen99767542014-07-04 13:38:27 +05301155 struct regulator *pll_regulator;
Tomi Valkeinen7e328f52015-06-04 13:02:52 +03001156 int r;
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +00001157
Tomi Valkeinen7e328f52015-06-04 13:02:52 +03001158 if (!np)
1159 return 0;
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +00001160
Tomi Valkeinen7e328f52015-06-04 13:02:52 +03001161 if (of_property_read_bool(np, "syscon-pll-ctrl")) {
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +05301162 dss.syscon_pll_ctrl = syscon_regmap_lookup_by_phandle(np,
1163 "syscon-pll-ctrl");
1164 if (IS_ERR(dss.syscon_pll_ctrl)) {
1165 dev_err(&pdev->dev,
1166 "failed to get syscon-pll-ctrl regmap\n");
1167 return PTR_ERR(dss.syscon_pll_ctrl);
1168 }
1169
1170 if (of_property_read_u32_index(np, "syscon-pll-ctrl", 1,
1171 &dss.syscon_pll_ctrl_offset)) {
1172 dev_err(&pdev->dev,
1173 "failed to get syscon-pll-ctrl offset\n");
1174 return -EINVAL;
1175 }
1176 }
1177
Tomi Valkeinen99767542014-07-04 13:38:27 +05301178 pll_regulator = devm_regulator_get(&pdev->dev, "vdda_video");
1179 if (IS_ERR(pll_regulator)) {
1180 r = PTR_ERR(pll_regulator);
1181
1182 switch (r) {
1183 case -ENOENT:
1184 pll_regulator = NULL;
1185 break;
1186
1187 case -EPROBE_DEFER:
1188 return -EPROBE_DEFER;
1189
1190 default:
1191 DSSERR("can't get DPLL VDDA regulator\n");
1192 return r;
1193 }
1194 }
1195
1196 if (of_property_match_string(np, "reg-names", "pll1") >= 0) {
1197 dss.video1_pll = dss_video_pll_init(pdev, 0, pll_regulator);
Tomi Valkeinen7e328f52015-06-04 13:02:52 +03001198 if (IS_ERR(dss.video1_pll))
1199 return PTR_ERR(dss.video1_pll);
Tomi Valkeinen99767542014-07-04 13:38:27 +05301200 }
1201
1202 if (of_property_match_string(np, "reg-names", "pll2") >= 0) {
1203 dss.video2_pll = dss_video_pll_init(pdev, 1, pll_regulator);
1204 if (IS_ERR(dss.video2_pll)) {
Tomi Valkeinen7e328f52015-06-04 13:02:52 +03001205 dss_video_pll_uninit(dss.video1_pll);
1206 return PTR_ERR(dss.video2_pll);
Tomi Valkeinen99767542014-07-04 13:38:27 +05301207 }
1208 }
1209
Tomi Valkeinen7e328f52015-06-04 13:02:52 +03001210 return 0;
1211}
1212
1213/* DSS HW IP initialisation */
Laurent Pinchart18daeb82017-08-05 01:43:58 +03001214static const struct of_device_id dss_of_match[] = {
1215 { .compatible = "ti,omap2-dss", .data = &omap24xx_dss_feats },
1216 { .compatible = "ti,omap3-dss", .data = &omap3630_dss_feats },
1217 { .compatible = "ti,omap4-dss", .data = &omap44xx_dss_feats },
1218 { .compatible = "ti,omap5-dss", .data = &omap54xx_dss_feats },
1219 { .compatible = "ti,dra7-dss", .data = &dra7xx_dss_feats },
1220 {},
1221};
1222MODULE_DEVICE_TABLE(of, dss_of_match);
1223
1224static const struct soc_device_attribute dss_soc_devices[] = {
1225 { .machine = "OMAP3430/3530", .data = &omap34xx_dss_feats },
1226 { .machine = "AM35??", .data = &omap34xx_dss_feats },
1227 { .family = "AM43xx", .data = &am43xx_dss_feats },
1228 { /* sentinel */ }
1229};
1230
Tomi Valkeinen736e60d2015-06-04 15:22:23 +03001231static int dss_bind(struct device *dev)
Tomi Valkeinen7e328f52015-06-04 13:02:52 +03001232{
Tomi Valkeinen736e60d2015-06-04 15:22:23 +03001233 struct platform_device *pdev = to_platform_device(dev);
Tomi Valkeinen7e328f52015-06-04 13:02:52 +03001234 struct resource *dss_mem;
1235 u32 rev;
1236 int r;
1237
Tomi Valkeinen7e328f52015-06-04 13:02:52 +03001238 dss_mem = platform_get_resource(dss.pdev, IORESOURCE_MEM, 0);
Laurent Pinchartb22622f2017-05-07 00:29:09 +03001239 dss.base = devm_ioremap_resource(&pdev->dev, dss_mem);
1240 if (IS_ERR(dss.base))
1241 return PTR_ERR(dss.base);
Tomi Valkeinen7e328f52015-06-04 13:02:52 +03001242
1243 r = dss_get_clocks();
1244 if (r)
1245 return r;
1246
1247 r = dss_setup_default_clock();
1248 if (r)
1249 goto err_setup_clocks;
1250
1251 r = dss_video_pll_probe(pdev);
1252 if (r)
1253 goto err_pll_init;
1254
Tomi Valkeinenf5a1a1f82015-06-04 13:06:37 +03001255 r = dss_init_ports(pdev);
1256 if (r)
1257 goto err_init_ports;
1258
Tomi Valkeinen7e328f52015-06-04 13:02:52 +03001259 pm_runtime_enable(&pdev->dev);
1260
1261 r = dss_runtime_get();
1262 if (r)
1263 goto err_runtime_get;
1264
1265 dss.dss_clk_rate = clk_get_rate(dss.dss_clk);
1266
1267 /* Select DPLL */
1268 REG_FLD_MOD(DSS_CONTROL, 0, 0, 0);
1269
Tomi Valkeinen3b63ca72016-05-17 14:01:10 +03001270 dss_select_dispc_clk_source(DSS_CLK_SRC_FCK);
Tomi Valkeinen7e328f52015-06-04 13:02:52 +03001271
1272#ifdef CONFIG_OMAP2_DSS_VENC
1273 REG_FLD_MOD(DSS_CONTROL, 1, 4, 4); /* venc dac demen */
1274 REG_FLD_MOD(DSS_CONTROL, 1, 3, 3); /* venc clock 4x enable */
1275 REG_FLD_MOD(DSS_CONTROL, 0, 2, 2); /* venc clock mode = normal */
1276#endif
Tomi Valkeinen3b63ca72016-05-17 14:01:10 +03001277 dss.dsi_clk_source[0] = DSS_CLK_SRC_FCK;
1278 dss.dsi_clk_source[1] = DSS_CLK_SRC_FCK;
1279 dss.dispc_clk_source = DSS_CLK_SRC_FCK;
1280 dss.lcd_clk_source[0] = DSS_CLK_SRC_FCK;
1281 dss.lcd_clk_source[1] = DSS_CLK_SRC_FCK;
Tomi Valkeinen7e328f52015-06-04 13:02:52 +03001282
Tomi Valkeinenb98482e2011-05-16 13:52:51 +03001283 rev = dss_read_reg(DSS_REVISION);
Joe Perches8dfe1622017-02-28 04:55:54 -08001284 pr_info("OMAP DSS rev %d.%d\n", FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));
Tomi Valkeinenb98482e2011-05-16 13:52:51 +03001285
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001286 dss_runtime_put();
Tomi Valkeinenb98482e2011-05-16 13:52:51 +03001287
Tomi Valkeinen736e60d2015-06-04 15:22:23 +03001288 r = component_bind_all(&pdev->dev, NULL);
1289 if (r)
1290 goto err_component;
1291
Tomi Valkeinene40402c2012-03-02 18:01:07 +02001292 dss_debugfs_create_file("dss", dss_dump_regs);
1293
Tomi Valkeinencb17a4a2015-02-25 12:08:14 +02001294 pm_set_vt_switch(0);
1295
Peter Ujfalusi1e08c822016-05-03 22:07:10 +03001296 omapdss_gather_components(dev);
Tomi Valkeinen7c299712015-11-05 17:23:14 +02001297 omapdss_set_is_initialized(true);
Tomi Valkeinenf99467b2015-06-04 12:35:42 +03001298
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +00001299 return 0;
Tomi Valkeinena57dd4f2012-02-20 16:57:37 +02001300
Tomi Valkeinen736e60d2015-06-04 15:22:23 +03001301err_component:
Tomi Valkeinen7e328f52015-06-04 13:02:52 +03001302err_runtime_get:
1303 pm_runtime_disable(&pdev->dev);
Tomi Valkeinenf5a1a1f82015-06-04 13:06:37 +03001304 dss_uninit_ports(pdev);
1305err_init_ports:
Tomi Valkeinen99767542014-07-04 13:38:27 +05301306 if (dss.video1_pll)
1307 dss_video_pll_uninit(dss.video1_pll);
1308
1309 if (dss.video2_pll)
1310 dss_video_pll_uninit(dss.video2_pll);
Tomi Valkeinen7e328f52015-06-04 13:02:52 +03001311err_pll_init:
Tomi Valkeinen13a1a2b2012-10-22 16:35:41 +03001312err_setup_clocks:
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +00001313 dss_put_clocks();
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +00001314 return r;
1315}
1316
Tomi Valkeinen736e60d2015-06-04 15:22:23 +03001317static void dss_unbind(struct device *dev)
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +00001318{
Tomi Valkeinen736e60d2015-06-04 15:22:23 +03001319 struct platform_device *pdev = to_platform_device(dev);
1320
Tomi Valkeinen7c299712015-11-05 17:23:14 +02001321 omapdss_set_is_initialized(false);
Tomi Valkeinenf99467b2015-06-04 12:35:42 +03001322
Tomi Valkeinen736e60d2015-06-04 15:22:23 +03001323 component_unbind_all(&pdev->dev, NULL);
1324
Tomi Valkeinen99767542014-07-04 13:38:27 +05301325 if (dss.video1_pll)
1326 dss_video_pll_uninit(dss.video1_pll);
1327
1328 if (dss.video2_pll)
1329 dss_video_pll_uninit(dss.video2_pll);
1330
Archit Taneja2ac6a1a2014-06-01 12:47:44 +05301331 dss_uninit_ports(pdev);
Tomi Valkeinen2ecef242013-12-16 15:13:24 +02001332
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001333 pm_runtime_disable(&pdev->dev);
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +00001334
1335 dss_put_clocks();
Tomi Valkeinen736e60d2015-06-04 15:22:23 +03001336}
Tomi Valkeinenb98482e2011-05-16 13:52:51 +03001337
Tomi Valkeinen736e60d2015-06-04 15:22:23 +03001338static const struct component_master_ops dss_component_ops = {
1339 .bind = dss_bind,
1340 .unbind = dss_unbind,
1341};
1342
1343static int dss_component_compare(struct device *dev, void *data)
1344{
1345 struct device *child = data;
1346 return dev == child;
1347}
1348
1349static int dss_add_child_component(struct device *dev, void *data)
1350{
1351 struct component_match **match = data;
1352
Tomi Valkeinen0438ec92015-06-30 12:23:45 +03001353 /*
1354 * HACK
1355 * We don't have a working driver for rfbi, so skip it here always.
1356 * Otherwise dss will never get probed successfully, as it will wait
1357 * for rfbi to get probed.
1358 */
1359 if (strstr(dev_name(dev), "rfbi"))
1360 return 0;
1361
Tomi Valkeinen736e60d2015-06-04 15:22:23 +03001362 component_match_add(dev->parent, match, dss_component_compare, dev);
1363
1364 return 0;
1365}
1366
1367static int dss_probe(struct platform_device *pdev)
1368{
Laurent Pinchart4a9fab32017-08-05 01:44:00 +03001369 const struct soc_device_attribute *soc;
Tomi Valkeinen736e60d2015-06-04 15:22:23 +03001370 struct component_match *match = NULL;
1371 int r;
1372
Laurent Pinchart4a9fab32017-08-05 01:44:00 +03001373 dss.pdev = pdev;
1374
1375 /*
1376 * The various OMAP3-based SoCs can't be told apart using the compatible
1377 * string, use SoC device matching.
1378 */
1379 soc = soc_device_match(dss_soc_devices);
1380 if (soc)
1381 dss.feat = soc->data;
1382 else
1383 dss.feat = of_match_device(dss_of_match, &pdev->dev)->data;
1384
Laurent Pinchart11765d12017-08-05 01:44:01 +03001385 r = dss_initialize_debugfs();
1386 if (r)
1387 return r;
1388
Tomi Valkeinen736e60d2015-06-04 15:22:23 +03001389 /* add all the child devices as components */
1390 device_for_each_child(&pdev->dev, &match, dss_add_child_component);
1391
1392 r = component_master_add_with_match(&pdev->dev, &dss_component_ops, match);
Laurent Pinchart11765d12017-08-05 01:44:01 +03001393 if (r) {
1394 dss_uninitialize_debugfs();
Tomi Valkeinen736e60d2015-06-04 15:22:23 +03001395 return r;
Laurent Pinchart11765d12017-08-05 01:44:01 +03001396 }
Tomi Valkeinen736e60d2015-06-04 15:22:23 +03001397
1398 return 0;
1399}
1400
1401static int dss_remove(struct platform_device *pdev)
1402{
1403 component_master_del(&pdev->dev, &dss_component_ops);
Laurent Pinchart11765d12017-08-05 01:44:01 +03001404
1405 dss_uninitialize_debugfs();
1406
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +00001407 return 0;
1408}
1409
Laurent Pinchart74592ee2017-08-05 01:44:02 +03001410static void dss_shutdown(struct platform_device *pdev)
1411{
1412 struct omap_dss_device *dssdev = NULL;
1413
1414 DSSDBG("shutdown\n");
1415
1416 for_each_dss_dev(dssdev) {
1417 if (!dssdev->driver)
1418 continue;
1419
1420 if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
1421 dssdev->driver->disable(dssdev);
1422 }
1423}
1424
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001425static int dss_runtime_suspend(struct device *dev)
1426{
1427 dss_save_context();
Tomi Valkeinena8081d32012-03-08 12:52:38 +02001428 dss_set_min_bus_tput(dev, 0);
Dave Gerlach5038bb82014-10-31 16:28:57 -05001429
1430 pinctrl_pm_select_sleep_state(dev);
1431
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001432 return 0;
1433}
1434
1435static int dss_runtime_resume(struct device *dev)
1436{
Tomi Valkeinena8081d32012-03-08 12:52:38 +02001437 int r;
Dave Gerlach5038bb82014-10-31 16:28:57 -05001438
1439 pinctrl_pm_select_default_state(dev);
1440
Tomi Valkeinena8081d32012-03-08 12:52:38 +02001441 /*
1442 * Set an arbitrarily high tput request to ensure OPP100.
1443 * What we should really do is to make a request to stay in OPP100,
1444 * without any tput requirements, but that is not currently possible
1445 * via the PM layer.
1446 */
1447
1448 r = dss_set_min_bus_tput(dev, 1000000000);
1449 if (r)
1450 return r;
1451
Tomi Valkeinen39020712011-05-26 14:54:05 +03001452 dss_restore_context();
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001453 return 0;
1454}
1455
1456static const struct dev_pm_ops dss_pm_ops = {
1457 .runtime_suspend = dss_runtime_suspend,
1458 .runtime_resume = dss_runtime_resume,
1459};
1460
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +00001461static struct platform_driver omap_dsshw_driver = {
Tomi Valkeinen736e60d2015-06-04 15:22:23 +03001462 .probe = dss_probe,
1463 .remove = dss_remove,
Laurent Pinchart74592ee2017-08-05 01:44:02 +03001464 .shutdown = dss_shutdown,
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +00001465 .driver = {
1466 .name = "omapdss_dss",
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001467 .pm = &dss_pm_ops,
Tomi Valkeinen2ecef242013-12-16 15:13:24 +02001468 .of_match_table = dss_of_match,
Tomi Valkeinen422ccbd2014-10-16 09:54:25 +03001469 .suppress_bind_attrs = true,
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +00001470 },
1471};
1472
Tomi Valkeinen6e7e8f02012-02-17 17:41:13 +02001473int __init dss_init_platform_driver(void)
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +00001474{
Tomi Valkeinen736e60d2015-06-04 15:22:23 +03001475 return platform_driver_register(&omap_dsshw_driver);
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +00001476}
1477
1478void dss_uninit_platform_driver(void)
1479{
Tomi Valkeinen04c742c2012-02-23 15:32:37 +02001480 platform_driver_unregister(&omap_dsshw_driver);
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +00001481}