blob: 29c3a0dba698f8570e74dbff51276d5925afac76 [file] [log] [blame]
Tomi Valkeinen559d6702009-11-03 11:23:50 +02001/*
Tomi Valkeinen559d6702009-11-03 11:23:50 +02002 * Copyright (C) 2009 Nokia Corporation
3 * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
4 *
5 * Some code and ideas taken from drivers/video/omap/ driver
6 * by Imre Deak.
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License version 2 as published by
10 * the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
16 *
17 * You should have received a copy of the GNU General Public License along with
18 * this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#define DSS_SUBSYS_NAME "DSS"
22
Laurent Pinchart11765d12017-08-05 01:44:01 +030023#include <linux/debugfs.h>
Laurent Pincharta921c1a2017-10-13 17:59:01 +030024#include <linux/dma-mapping.h>
Tomi Valkeinen559d6702009-11-03 11:23:50 +020025#include <linux/kernel.h>
Tomi Valkeinen2ecef242013-12-16 15:13:24 +020026#include <linux/module.h>
Tomi Valkeinen559d6702009-11-03 11:23:50 +020027#include <linux/io.h>
Paul Gortmakera8a35932011-07-10 13:20:26 -040028#include <linux/export.h>
Tomi Valkeinen559d6702009-11-03 11:23:50 +020029#include <linux/err.h>
30#include <linux/delay.h>
Tomi Valkeinen559d6702009-11-03 11:23:50 +020031#include <linux/seq_file.h>
32#include <linux/clk.h>
Arnd Bergmann2639d6b2016-05-09 23:51:27 +020033#include <linux/pinctrl/consumer.h>
Tomi Valkeinen24e62892011-05-23 11:51:18 +030034#include <linux/platform_device.h>
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +030035#include <linux/pm_runtime.h>
Chandrabhanu Mahapatra185bae12012-07-11 18:36:18 +053036#include <linux/gfp.h>
Tomi Valkeinen33366d02012-09-28 13:54:35 +030037#include <linux/sizes.h>
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +053038#include <linux/mfd/syscon.h>
39#include <linux/regmap.h>
Tomi Valkeinen2ecef242013-12-16 15:13:24 +020040#include <linux/of.h>
Laurent Pinchart18daeb82017-08-05 01:43:58 +030041#include <linux/of_device.h>
Rob Herring09bffa62017-03-22 08:26:08 -050042#include <linux/of_graph.h>
Tomi Valkeinen99767542014-07-04 13:38:27 +053043#include <linux/regulator/consumer.h>
Tomi Valkeinencb17a4a2015-02-25 12:08:14 +020044#include <linux/suspend.h>
Tomi Valkeinen736e60d2015-06-04 15:22:23 +030045#include <linux/component.h>
Laurent Pinchart18daeb82017-08-05 01:43:58 +030046#include <linux/sys_soc.h>
Tomi Valkeinen559d6702009-11-03 11:23:50 +020047
Peter Ujfalusi32043da2016-05-27 14:40:49 +030048#include "omapdss.h"
Tomi Valkeinen559d6702009-11-03 11:23:50 +020049#include "dss.h"
50
Tomi Valkeinen559d6702009-11-03 11:23:50 +020051struct dss_reg {
52 u16 idx;
53};
54
55#define DSS_REG(idx) ((const struct dss_reg) { idx })
56
57#define DSS_REVISION DSS_REG(0x0000)
58#define DSS_SYSCONFIG DSS_REG(0x0010)
59#define DSS_SYSSTATUS DSS_REG(0x0014)
Tomi Valkeinen559d6702009-11-03 11:23:50 +020060#define DSS_CONTROL DSS_REG(0x0040)
61#define DSS_SDI_CONTROL DSS_REG(0x0044)
62#define DSS_PLL_CONTROL DSS_REG(0x0048)
63#define DSS_SDI_STATUS DSS_REG(0x005C)
64
65#define REG_GET(idx, start, end) \
66 FLD_GET(dss_read_reg(idx), start, end)
67
68#define REG_FLD_MOD(idx, val, start, end) \
69 dss_write_reg(idx, FLD_MOD(dss_read_reg(idx), val, start, end))
70
Laurent Pinchartfecea252017-08-05 01:43:52 +030071struct dss_ops {
72 int (*dpi_select_source)(int port, enum omap_channel channel);
73 int (*select_lcd_source)(enum omap_channel channel,
74 enum dss_clk_source clk_src);
75};
76
Chandrabhanu Mahapatra185bae12012-07-11 18:36:18 +053077struct dss_features {
Laurent Pinchartb8dab2b2017-08-05 01:43:56 +030078 enum dss_model model;
Chandrabhanu Mahapatra185bae12012-07-11 18:36:18 +053079 u8 fck_div_max;
Laurent Pinchart9f0fbae2017-08-05 01:44:17 +030080 unsigned int fck_freq_max;
Chandrabhanu Mahapatra185bae12012-07-11 18:36:18 +053081 u8 dss_fck_multiplier;
Tomi Valkeinen64ad8462013-11-01 11:38:04 +020082 const char *parent_clk_name;
Tomi Valkeinen234f9a22014-12-11 15:59:31 +020083 const enum omap_display_type *ports;
Archit Taneja387ce9f2014-05-22 17:01:57 +053084 int num_ports;
Laurent Pinchart51919572017-08-05 01:44:18 +030085 const enum omap_dss_output_id *outputs;
Laurent Pinchartfecea252017-08-05 01:43:52 +030086 const struct dss_ops *ops;
Laurent Pinchart6d85d4a2017-08-05 01:44:07 +030087 struct dss_reg_field dispc_clk_switch;
Laurent Pinchart4569ab72017-08-05 01:44:13 +030088 bool has_lcd_clk_src;
Chandrabhanu Mahapatra185bae12012-07-11 18:36:18 +053089};
90
Laurent Pinchart0e546df2018-02-13 14:00:20 +020091static struct dss_device dss;
Tomi Valkeinen559d6702009-11-03 11:23:50 +020092
Taneja, Archit235e7db2011-03-14 23:28:21 -050093static const char * const dss_generic_clk_source_names[] = {
Tomi Valkeinen3b63ca72016-05-17 14:01:10 +030094 [DSS_CLK_SRC_FCK] = "FCK",
95 [DSS_CLK_SRC_PLL1_1] = "PLL1:1",
96 [DSS_CLK_SRC_PLL1_2] = "PLL1:2",
Tomi Valkeinenb5d8c752016-05-17 14:12:35 +030097 [DSS_CLK_SRC_PLL1_3] = "PLL1:3",
Tomi Valkeinen3b63ca72016-05-17 14:01:10 +030098 [DSS_CLK_SRC_PLL2_1] = "PLL2:1",
99 [DSS_CLK_SRC_PLL2_2] = "PLL2:2",
Tomi Valkeinenb5d8c752016-05-17 14:12:35 +0300100 [DSS_CLK_SRC_PLL2_3] = "PLL2:3",
101 [DSS_CLK_SRC_HDMI_PLL] = "HDMI PLL",
Archit Taneja067a57e2011-03-02 11:57:25 +0530102};
103
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200104static inline void dss_write_reg(const struct dss_reg idx, u32 val)
105{
106 __raw_writel(val, dss.base + idx.idx);
107}
108
109static inline u32 dss_read_reg(const struct dss_reg idx)
110{
111 return __raw_readl(dss.base + idx.idx);
112}
113
114#define SR(reg) \
115 dss.ctx[(DSS_##reg).idx / sizeof(u32)] = dss_read_reg(DSS_##reg)
116#define RR(reg) \
117 dss_write_reg(DSS_##reg, dss.ctx[(DSS_##reg).idx / sizeof(u32)])
118
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300119static void dss_save_context(void)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200120{
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300121 DSSDBG("dss_save_context\n");
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200122
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200123 SR(CONTROL);
124
Laurent Pinchart51919572017-08-05 01:44:18 +0300125 if (dss.feat->outputs[OMAP_DSS_CHANNEL_LCD] & OMAP_DSS_OUTPUT_SDI) {
Tomi Valkeinen6ec549e2011-02-24 14:18:50 +0200126 SR(SDI_CONTROL);
127 SR(PLL_CONTROL);
128 }
Tomi Valkeinen69f06052011-06-01 15:56:39 +0300129
130 dss.ctx_valid = true;
131
132 DSSDBG("context saved\n");
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200133}
134
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300135static void dss_restore_context(void)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200136{
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300137 DSSDBG("dss_restore_context\n");
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200138
Tomi Valkeinen69f06052011-06-01 15:56:39 +0300139 if (!dss.ctx_valid)
140 return;
141
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200142 RR(CONTROL);
143
Laurent Pinchart51919572017-08-05 01:44:18 +0300144 if (dss.feat->outputs[OMAP_DSS_CHANNEL_LCD] & OMAP_DSS_OUTPUT_SDI) {
Tomi Valkeinen6ec549e2011-02-24 14:18:50 +0200145 RR(SDI_CONTROL);
146 RR(PLL_CONTROL);
147 }
Tomi Valkeinen69f06052011-06-01 15:56:39 +0300148
149 DSSDBG("context restored\n");
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200150}
151
152#undef SR
153#undef RR
154
Laurent Pinchart27260992018-02-13 14:00:22 +0200155void dss_ctrl_pll_enable(struct dss_pll *pll, bool enable)
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +0530156{
Laurent Pinchartd11e5c82018-02-11 15:07:34 +0200157 unsigned int shift;
158 unsigned int val;
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +0530159
Laurent Pinchart27260992018-02-13 14:00:22 +0200160 if (!pll->dss->syscon_pll_ctrl)
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +0530161 return;
162
163 val = !enable;
164
Laurent Pinchart27260992018-02-13 14:00:22 +0200165 switch (pll->id) {
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +0530166 case DSS_PLL_VIDEO1:
167 shift = 0;
168 break;
169 case DSS_PLL_VIDEO2:
170 shift = 1;
171 break;
172 case DSS_PLL_HDMI:
173 shift = 2;
174 break;
175 default:
Laurent Pinchart27260992018-02-13 14:00:22 +0200176 DSSERR("illegal DSS PLL ID %d\n", pll->id);
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +0530177 return;
178 }
179
Laurent Pinchart27260992018-02-13 14:00:22 +0200180 regmap_update_bits(pll->dss->syscon_pll_ctrl,
181 pll->dss->syscon_pll_ctrl_offset,
182 1 << shift, val << shift);
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +0530183}
184
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300185static int dss_ctrl_pll_set_control_mux(enum dss_clk_source clk_src,
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +0530186 enum omap_channel channel)
187{
Laurent Pinchartd11e5c82018-02-11 15:07:34 +0200188 unsigned int shift, val;
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +0530189
190 if (!dss.syscon_pll_ctrl)
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300191 return -EINVAL;
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +0530192
193 switch (channel) {
194 case OMAP_DSS_CHANNEL_LCD:
195 shift = 3;
196
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300197 switch (clk_src) {
198 case DSS_CLK_SRC_PLL1_1:
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +0530199 val = 0; break;
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300200 case DSS_CLK_SRC_HDMI_PLL:
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +0530201 val = 1; break;
202 default:
203 DSSERR("error in PLL mux config for LCD\n");
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300204 return -EINVAL;
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +0530205 }
206
207 break;
208 case OMAP_DSS_CHANNEL_LCD2:
209 shift = 5;
210
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300211 switch (clk_src) {
212 case DSS_CLK_SRC_PLL1_3:
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +0530213 val = 0; break;
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300214 case DSS_CLK_SRC_PLL2_3:
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +0530215 val = 1; break;
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300216 case DSS_CLK_SRC_HDMI_PLL:
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +0530217 val = 2; break;
218 default:
219 DSSERR("error in PLL mux config for LCD2\n");
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300220 return -EINVAL;
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +0530221 }
222
223 break;
224 case OMAP_DSS_CHANNEL_LCD3:
225 shift = 7;
226
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300227 switch (clk_src) {
228 case DSS_CLK_SRC_PLL2_1:
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +0530229 val = 0; break;
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300230 case DSS_CLK_SRC_PLL1_3:
231 val = 1; break;
232 case DSS_CLK_SRC_HDMI_PLL:
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +0530233 val = 2; break;
234 default:
235 DSSERR("error in PLL mux config for LCD3\n");
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300236 return -EINVAL;
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +0530237 }
238
239 break;
240 default:
241 DSSERR("error in PLL mux config\n");
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300242 return -EINVAL;
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +0530243 }
244
245 regmap_update_bits(dss.syscon_pll_ctrl, dss.syscon_pll_ctrl_offset,
246 0x3 << shift, val << shift);
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300247
248 return 0;
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +0530249}
250
Archit Taneja889b4fd2012-07-20 17:18:49 +0530251void dss_sdi_init(int datapairs)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200252{
253 u32 l;
254
255 BUG_ON(datapairs > 3 || datapairs < 1);
256
257 l = dss_read_reg(DSS_SDI_CONTROL);
258 l = FLD_MOD(l, 0xf, 19, 15); /* SDI_PDIV */
259 l = FLD_MOD(l, datapairs-1, 3, 2); /* SDI_PRSEL */
260 l = FLD_MOD(l, 2, 1, 0); /* SDI_BWSEL */
261 dss_write_reg(DSS_SDI_CONTROL, l);
262
263 l = dss_read_reg(DSS_PLL_CONTROL);
264 l = FLD_MOD(l, 0x7, 25, 22); /* SDI_PLL_FREQSEL */
265 l = FLD_MOD(l, 0xb, 16, 11); /* SDI_PLL_REGN */
266 l = FLD_MOD(l, 0xb4, 10, 1); /* SDI_PLL_REGM */
267 dss_write_reg(DSS_PLL_CONTROL, l);
268}
269
270int dss_sdi_enable(void)
271{
272 unsigned long timeout;
273
274 dispc_pck_free_enable(1);
275
276 /* Reset SDI PLL */
277 REG_FLD_MOD(DSS_PLL_CONTROL, 1, 18, 18); /* SDI_PLL_SYSRESET */
278 udelay(1); /* wait 2x PCLK */
279
280 /* Lock SDI PLL */
281 REG_FLD_MOD(DSS_PLL_CONTROL, 1, 28, 28); /* SDI_PLL_GOBIT */
282
283 /* Waiting for PLL lock request to complete */
284 timeout = jiffies + msecs_to_jiffies(500);
285 while (dss_read_reg(DSS_SDI_STATUS) & (1 << 6)) {
286 if (time_after_eq(jiffies, timeout)) {
287 DSSERR("PLL lock request timed out\n");
288 goto err1;
289 }
290 }
291
292 /* Clearing PLL_GO bit */
293 REG_FLD_MOD(DSS_PLL_CONTROL, 0, 28, 28);
294
295 /* Waiting for PLL to lock */
296 timeout = jiffies + msecs_to_jiffies(500);
297 while (!(dss_read_reg(DSS_SDI_STATUS) & (1 << 5))) {
298 if (time_after_eq(jiffies, timeout)) {
299 DSSERR("PLL lock timed out\n");
300 goto err1;
301 }
302 }
303
304 dispc_lcd_enable_signal(1);
305
306 /* Waiting for SDI reset to complete */
307 timeout = jiffies + msecs_to_jiffies(500);
308 while (!(dss_read_reg(DSS_SDI_STATUS) & (1 << 2))) {
309 if (time_after_eq(jiffies, timeout)) {
310 DSSERR("SDI reset timed out\n");
311 goto err2;
312 }
313 }
314
315 return 0;
316
317 err2:
318 dispc_lcd_enable_signal(0);
319 err1:
320 /* Reset SDI PLL */
321 REG_FLD_MOD(DSS_PLL_CONTROL, 0, 18, 18); /* SDI_PLL_SYSRESET */
322
323 dispc_pck_free_enable(0);
324
325 return -ETIMEDOUT;
326}
327
328void dss_sdi_disable(void)
329{
330 dispc_lcd_enable_signal(0);
331
332 dispc_pck_free_enable(0);
333
334 /* Reset SDI PLL */
335 REG_FLD_MOD(DSS_PLL_CONTROL, 0, 18, 18); /* SDI_PLL_SYSRESET */
336}
337
Tomi Valkeinen407bd562016-05-17 13:50:55 +0300338const char *dss_get_clk_source_name(enum dss_clk_source clk_src)
Archit Taneja067a57e2011-03-02 11:57:25 +0530339{
Taneja, Archit235e7db2011-03-14 23:28:21 -0500340 return dss_generic_clk_source_names[clk_src];
Archit Taneja067a57e2011-03-02 11:57:25 +0530341}
342
Laurent Pinchart9be9d7e2017-10-13 17:59:02 +0300343#if defined(CONFIG_OMAP2_DSS_DEBUGFS)
344static void dss_dump_clocks(struct seq_file *s)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200345{
Tomi Valkeinen557a1542016-05-17 13:49:18 +0300346 const char *fclk_name;
Tomi Valkeinen0acf6592011-03-14 07:28:57 -0500347 unsigned long fclk_rate;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200348
Laurent Pinchart7b295252018-02-13 14:00:21 +0200349 if (dss_runtime_get(&dss))
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300350 return;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200351
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200352 seq_printf(s, "- DSS -\n");
353
Tomi Valkeinen3b63ca72016-05-17 14:01:10 +0300354 fclk_name = dss_get_clk_source_name(DSS_CLK_SRC_FCK);
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300355 fclk_rate = clk_get_rate(dss.dss_clk);
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200356
Tomi Valkeinen557a1542016-05-17 13:49:18 +0300357 seq_printf(s, "%s = %lu\n",
358 fclk_name,
Tomi Valkeinen9c15d762013-11-01 11:36:10 +0200359 fclk_rate);
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200360
Laurent Pinchart7b295252018-02-13 14:00:21 +0200361 dss_runtime_put(&dss);
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200362}
Laurent Pinchart9be9d7e2017-10-13 17:59:02 +0300363#endif
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200364
Tomi Valkeinene40402c2012-03-02 18:01:07 +0200365static void dss_dump_regs(struct seq_file *s)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200366{
367#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dss_read_reg(r))
368
Laurent Pinchart7b295252018-02-13 14:00:21 +0200369 if (dss_runtime_get(&dss))
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300370 return;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200371
372 DUMPREG(DSS_REVISION);
373 DUMPREG(DSS_SYSCONFIG);
374 DUMPREG(DSS_SYSSTATUS);
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200375 DUMPREG(DSS_CONTROL);
Tomi Valkeinen6ec549e2011-02-24 14:18:50 +0200376
Laurent Pinchart51919572017-08-05 01:44:18 +0300377 if (dss.feat->outputs[OMAP_DSS_CHANNEL_LCD] & OMAP_DSS_OUTPUT_SDI) {
Tomi Valkeinen6ec549e2011-02-24 14:18:50 +0200378 DUMPREG(DSS_SDI_CONTROL);
379 DUMPREG(DSS_PLL_CONTROL);
380 DUMPREG(DSS_SDI_STATUS);
381 }
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200382
Laurent Pinchart7b295252018-02-13 14:00:21 +0200383 dss_runtime_put(&dss);
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200384#undef DUMPREG
385}
386
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300387static int dss_get_channel_index(enum omap_channel channel)
388{
389 switch (channel) {
390 case OMAP_DSS_CHANNEL_LCD:
391 return 0;
392 case OMAP_DSS_CHANNEL_LCD2:
393 return 1;
394 case OMAP_DSS_CHANNEL_LCD3:
395 return 2;
396 default:
397 WARN_ON(1);
398 return 0;
399 }
400}
401
Tomi Valkeinendc0352d2016-05-17 13:45:09 +0300402static void dss_select_dispc_clk_source(enum dss_clk_source clk_src)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200403{
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +0200404 int b;
405
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300406 /*
407 * We always use PRCM clock as the DISPC func clock, except on DSS3,
408 * where we don't have separate DISPC and LCD clock sources.
409 */
Laurent Pinchart4569ab72017-08-05 01:44:13 +0300410 if (WARN_ON(dss.feat->has_lcd_clk_src && clk_src != DSS_CLK_SRC_FCK))
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300411 return;
412
Taneja, Archit66534e82011-03-08 05:50:34 -0600413 switch (clk_src) {
Tomi Valkeinen3b63ca72016-05-17 14:01:10 +0300414 case DSS_CLK_SRC_FCK:
Taneja, Archit66534e82011-03-08 05:50:34 -0600415 b = 0;
416 break;
Tomi Valkeinen3b63ca72016-05-17 14:01:10 +0300417 case DSS_CLK_SRC_PLL1_1:
Taneja, Archit66534e82011-03-08 05:50:34 -0600418 b = 1;
Taneja, Archit66534e82011-03-08 05:50:34 -0600419 break;
Tomi Valkeinen3b63ca72016-05-17 14:01:10 +0300420 case DSS_CLK_SRC_PLL2_1:
Archit Taneja5a8b5722011-05-12 17:26:29 +0530421 b = 2;
Archit Taneja5a8b5722011-05-12 17:26:29 +0530422 break;
Taneja, Archit66534e82011-03-08 05:50:34 -0600423 default:
424 BUG();
Tomi Valkeinenc6eee962012-05-18 11:47:02 +0300425 return;
Taneja, Archit66534e82011-03-08 05:50:34 -0600426 }
Tomi Valkeinene406f902010-06-09 15:28:12 +0300427
Laurent Pinchart6d85d4a2017-08-05 01:44:07 +0300428 REG_FLD_MOD(DSS_CONTROL, b, /* DISPC_CLK_SWITCH */
429 dss.feat->dispc_clk_switch.start,
430 dss.feat->dispc_clk_switch.end);
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +0200431
432 dss.dispc_clk_source = clk_src;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200433}
434
Archit Taneja5a8b5722011-05-12 17:26:29 +0530435void dss_select_dsi_clk_source(int dsi_module,
Tomi Valkeinendc0352d2016-05-17 13:45:09 +0300436 enum dss_clk_source clk_src)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200437{
Archit Tanejaa2e5d822012-05-07 16:51:35 +0530438 int b, pos;
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +0200439
Taneja, Archit66534e82011-03-08 05:50:34 -0600440 switch (clk_src) {
Tomi Valkeinen3b63ca72016-05-17 14:01:10 +0300441 case DSS_CLK_SRC_FCK:
Taneja, Archit66534e82011-03-08 05:50:34 -0600442 b = 0;
443 break;
Tomi Valkeinen3b63ca72016-05-17 14:01:10 +0300444 case DSS_CLK_SRC_PLL1_2:
Archit Taneja5a8b5722011-05-12 17:26:29 +0530445 BUG_ON(dsi_module != 0);
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_2:
Archit Taneja5a8b5722011-05-12 17:26:29 +0530449 BUG_ON(dsi_module != 1);
450 b = 1;
Archit Taneja5a8b5722011-05-12 17:26:29 +0530451 break;
Taneja, Archit66534e82011-03-08 05:50:34 -0600452 default:
453 BUG();
Tomi Valkeinenc6eee962012-05-18 11:47:02 +0300454 return;
Taneja, Archit66534e82011-03-08 05:50:34 -0600455 }
Tomi Valkeinene406f902010-06-09 15:28:12 +0300456
Archit Tanejaa2e5d822012-05-07 16:51:35 +0530457 pos = dsi_module == 0 ? 1 : 10;
458 REG_FLD_MOD(DSS_CONTROL, b, pos, pos); /* DSIx_CLK_SWITCH */
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +0200459
Archit Taneja5a8b5722011-05-12 17:26:29 +0530460 dss.dsi_clk_source[dsi_module] = clk_src;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200461}
462
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300463static int dss_lcd_clk_mux_dra7(enum omap_channel channel,
464 enum dss_clk_source clk_src)
Taneja, Architea751592011-03-08 05:50:35 -0600465{
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300466 const u8 ctrl_bits[] = {
467 [OMAP_DSS_CHANNEL_LCD] = 0,
468 [OMAP_DSS_CHANNEL_LCD2] = 12,
469 [OMAP_DSS_CHANNEL_LCD3] = 19,
470 };
471
472 u8 ctrl_bit = ctrl_bits[channel];
473 int r;
474
475 if (clk_src == DSS_CLK_SRC_FCK) {
476 /* LCDx_CLK_SWITCH */
477 REG_FLD_MOD(DSS_CONTROL, 0, ctrl_bit, ctrl_bit);
478 return -EINVAL;
479 }
480
481 r = dss_ctrl_pll_set_control_mux(clk_src, channel);
482 if (r)
483 return r;
484
485 REG_FLD_MOD(DSS_CONTROL, 1, ctrl_bit, ctrl_bit);
486
487 return 0;
488}
489
490static int dss_lcd_clk_mux_omap5(enum omap_channel channel,
491 enum dss_clk_source clk_src)
492{
493 const u8 ctrl_bits[] = {
494 [OMAP_DSS_CHANNEL_LCD] = 0,
495 [OMAP_DSS_CHANNEL_LCD2] = 12,
496 [OMAP_DSS_CHANNEL_LCD3] = 19,
497 };
498 const enum dss_clk_source allowed_plls[] = {
499 [OMAP_DSS_CHANNEL_LCD] = DSS_CLK_SRC_PLL1_1,
500 [OMAP_DSS_CHANNEL_LCD2] = DSS_CLK_SRC_FCK,
501 [OMAP_DSS_CHANNEL_LCD3] = DSS_CLK_SRC_PLL2_1,
502 };
503
504 u8 ctrl_bit = ctrl_bits[channel];
505
506 if (clk_src == DSS_CLK_SRC_FCK) {
507 /* LCDx_CLK_SWITCH */
508 REG_FLD_MOD(DSS_CONTROL, 0, ctrl_bit, ctrl_bit);
509 return -EINVAL;
510 }
511
512 if (WARN_ON(allowed_plls[channel] != clk_src))
513 return -EINVAL;
514
515 REG_FLD_MOD(DSS_CONTROL, 1, ctrl_bit, ctrl_bit);
516
517 return 0;
518}
519
520static int dss_lcd_clk_mux_omap4(enum omap_channel channel,
521 enum dss_clk_source clk_src)
522{
523 const u8 ctrl_bits[] = {
524 [OMAP_DSS_CHANNEL_LCD] = 0,
525 [OMAP_DSS_CHANNEL_LCD2] = 12,
526 };
527 const enum dss_clk_source allowed_plls[] = {
528 [OMAP_DSS_CHANNEL_LCD] = DSS_CLK_SRC_PLL1_1,
529 [OMAP_DSS_CHANNEL_LCD2] = 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 0;
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
Taneja, Architea751592011-03-08 05:50:35 -0600548void dss_select_lcd_clk_source(enum omap_channel channel,
Tomi Valkeinendc0352d2016-05-17 13:45:09 +0300549 enum dss_clk_source clk_src)
Taneja, Architea751592011-03-08 05:50:35 -0600550{
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300551 int idx = dss_get_channel_index(channel);
552 int r;
Taneja, Architea751592011-03-08 05:50:35 -0600553
Laurent Pinchart4569ab72017-08-05 01:44:13 +0300554 if (!dss.feat->has_lcd_clk_src) {
Tomi Valkeinena5b83992012-10-22 16:58:36 +0300555 dss_select_dispc_clk_source(clk_src);
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300556 dss.lcd_clk_source[idx] = clk_src;
Taneja, Architea751592011-03-08 05:50:35 -0600557 return;
Tomi Valkeinena5b83992012-10-22 16:58:36 +0300558 }
Taneja, Architea751592011-03-08 05:50:35 -0600559
Laurent Pinchartfecea252017-08-05 01:43:52 +0300560 r = dss.feat->ops->select_lcd_source(channel, clk_src);
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300561 if (r)
Tomi Valkeinenc6eee962012-05-18 11:47:02 +0300562 return;
Taneja, Architea751592011-03-08 05:50:35 -0600563
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300564 dss.lcd_clk_source[idx] = clk_src;
Taneja, Architea751592011-03-08 05:50:35 -0600565}
566
Tomi Valkeinendc0352d2016-05-17 13:45:09 +0300567enum dss_clk_source dss_get_dispc_clk_source(void)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200568{
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +0200569 return dss.dispc_clk_source;
570}
571
Tomi Valkeinendc0352d2016-05-17 13:45:09 +0300572enum dss_clk_source dss_get_dsi_clk_source(int dsi_module)
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +0200573{
Archit Taneja5a8b5722011-05-12 17:26:29 +0530574 return dss.dsi_clk_source[dsi_module];
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200575}
576
Tomi Valkeinendc0352d2016-05-17 13:45:09 +0300577enum dss_clk_source dss_get_lcd_clk_source(enum omap_channel channel)
Taneja, Architea751592011-03-08 05:50:35 -0600578{
Laurent Pinchart4569ab72017-08-05 01:44:13 +0300579 if (dss.feat->has_lcd_clk_src) {
Tomi Valkeinenc63b1ec2016-05-17 15:46:19 +0300580 int idx = dss_get_channel_index(channel);
581 return dss.lcd_clk_source[idx];
Archit Taneja89976f22011-03-31 13:23:35 +0530582 } else {
583 /* LCD_CLK source is the same as DISPC_FCLK source for
584 * OMAP2 and OMAP3 */
585 return dss.dispc_clk_source;
586 }
Taneja, Architea751592011-03-08 05:50:35 -0600587}
588
Tomi Valkeinen688af022013-10-31 16:41:57 +0200589bool dss_div_calc(unsigned long pck, unsigned long fck_min,
590 dss_div_calc_func func, void *data)
Tomi Valkeinen43417822013-03-05 16:34:05 +0200591{
592 int fckd, fckd_start, fckd_stop;
593 unsigned long fck;
594 unsigned long fck_hw_max;
595 unsigned long fckd_hw_max;
596 unsigned long prate;
Laurent Pinchartd11e5c82018-02-11 15:07:34 +0200597 unsigned int m;
Tomi Valkeinen43417822013-03-05 16:34:05 +0200598
Laurent Pinchart9f0fbae2017-08-05 01:44:17 +0300599 fck_hw_max = dss.feat->fck_freq_max;
Tomi Valkeinenfc1fe6e2013-10-31 16:42:13 +0200600
Tomi Valkeinen64ad8462013-11-01 11:38:04 +0200601 if (dss.parent_clk == NULL) {
Laurent Pinchartd11e5c82018-02-11 15:07:34 +0200602 unsigned int pckd;
Tomi Valkeinenfc1fe6e2013-10-31 16:42:13 +0200603
604 pckd = fck_hw_max / pck;
605
606 fck = pck * pckd;
607
608 fck = clk_round_rate(dss.dss_clk, fck);
609
Tomi Valkeinend0f58bd2013-10-31 14:44:23 +0200610 return func(fck, data);
Tomi Valkeinen43417822013-03-05 16:34:05 +0200611 }
612
Tomi Valkeinen43417822013-03-05 16:34:05 +0200613 fckd_hw_max = dss.feat->fck_div_max;
614
Tomi Valkeinen648a55e2013-04-10 14:47:38 +0300615 m = dss.feat->dss_fck_multiplier;
Tomi Valkeinenada94432013-10-31 16:06:38 +0200616 prate = clk_get_rate(dss.parent_clk);
Tomi Valkeinen43417822013-03-05 16:34:05 +0200617
618 fck_min = fck_min ? fck_min : 1;
619
Tomi Valkeinen648a55e2013-04-10 14:47:38 +0300620 fckd_start = min(prate * m / fck_min, fckd_hw_max);
621 fckd_stop = max(DIV_ROUND_UP(prate * m, fck_hw_max), 1ul);
Tomi Valkeinen43417822013-03-05 16:34:05 +0200622
623 for (fckd = fckd_start; fckd >= fckd_stop; --fckd) {
Tomi Valkeinend0e224f2014-02-13 11:36:22 +0200624 fck = DIV_ROUND_UP(prate, fckd) * m;
Tomi Valkeinen43417822013-03-05 16:34:05 +0200625
Tomi Valkeinend0f58bd2013-10-31 14:44:23 +0200626 if (func(fck, data))
Tomi Valkeinen43417822013-03-05 16:34:05 +0200627 return true;
628 }
629
630 return false;
631}
632
Tomi Valkeinend0f58bd2013-10-31 14:44:23 +0200633int dss_set_fck_rate(unsigned long rate)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200634{
Tomi Valkeinenada94432013-10-31 16:06:38 +0200635 int r;
636
Tomi Valkeinend0f58bd2013-10-31 14:44:23 +0200637 DSSDBG("set fck to %lu\n", rate);
638
Tomi Valkeinenada94432013-10-31 16:06:38 +0200639 r = clk_set_rate(dss.dss_clk, rate);
640 if (r)
641 return r;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200642
Tomi Valkeinen5aaee692012-12-12 10:37:03 +0200643 dss.dss_clk_rate = clk_get_rate(dss.dss_clk);
644
Tomi Valkeinend0f58bd2013-10-31 14:44:23 +0200645 WARN_ONCE(dss.dss_clk_rate != rate,
Tomi Valkeinen648a55e2013-04-10 14:47:38 +0300646 "clk rate mismatch: %lu != %lu", dss.dss_clk_rate,
Tomi Valkeinend0f58bd2013-10-31 14:44:23 +0200647 rate);
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200648
649 return 0;
650}
651
Tomi Valkeinen5aaee692012-12-12 10:37:03 +0200652unsigned long dss_get_dispc_clk_rate(void)
653{
654 return dss.dss_clk_rate;
655}
656
Laurent Pinchart9f0fbae2017-08-05 01:44:17 +0300657unsigned long dss_get_max_fck_rate(void)
658{
659 return dss.feat->fck_freq_max;
660}
661
Laurent Pinchart51919572017-08-05 01:44:18 +0300662enum omap_dss_output_id dss_get_supported_outputs(enum omap_channel channel)
663{
664 return dss.feat->outputs[channel];
665}
666
Tomi Valkeinen13a1a2b2012-10-22 16:35:41 +0300667static int dss_setup_default_clock(void)
668{
669 unsigned long max_dss_fck, prate;
Tomi Valkeinend0f58bd2013-10-31 14:44:23 +0200670 unsigned long fck;
Laurent Pinchartd11e5c82018-02-11 15:07:34 +0200671 unsigned int fck_div;
Tomi Valkeinen13a1a2b2012-10-22 16:35:41 +0300672 int r;
673
Laurent Pinchart9f0fbae2017-08-05 01:44:17 +0300674 max_dss_fck = dss.feat->fck_freq_max;
Tomi Valkeinen13a1a2b2012-10-22 16:35:41 +0300675
Tomi Valkeinenfc1fe6e2013-10-31 16:42:13 +0200676 if (dss.parent_clk == NULL) {
677 fck = clk_round_rate(dss.dss_clk, max_dss_fck);
678 } else {
679 prate = clk_get_rate(dss.parent_clk);
Tomi Valkeinen13a1a2b2012-10-22 16:35:41 +0300680
Tomi Valkeinenfc1fe6e2013-10-31 16:42:13 +0200681 fck_div = DIV_ROUND_UP(prate * dss.feat->dss_fck_multiplier,
682 max_dss_fck);
Tomi Valkeinend0e224f2014-02-13 11:36:22 +0200683 fck = DIV_ROUND_UP(prate, fck_div) * dss.feat->dss_fck_multiplier;
Tomi Valkeinenfc1fe6e2013-10-31 16:42:13 +0200684 }
Tomi Valkeinen13a1a2b2012-10-22 16:35:41 +0300685
Tomi Valkeinend0f58bd2013-10-31 14:44:23 +0200686 r = dss_set_fck_rate(fck);
Tomi Valkeinen13a1a2b2012-10-22 16:35:41 +0300687 if (r)
688 return r;
689
690 return 0;
691}
692
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200693void dss_set_venc_output(enum omap_dss_venc_type type)
694{
695 int l = 0;
696
697 if (type == OMAP_DSS_VENC_TYPE_COMPOSITE)
698 l = 0;
699 else if (type == OMAP_DSS_VENC_TYPE_SVIDEO)
700 l = 1;
701 else
702 BUG();
703
704 /* venc out selection. 0 = comp, 1 = svideo */
705 REG_FLD_MOD(DSS_CONTROL, l, 6, 6);
706}
707
708void dss_set_dac_pwrdn_bgz(bool enable)
709{
710 REG_FLD_MOD(DSS_CONTROL, enable, 5, 5); /* DAC Power-Down Control */
711}
712
Ricardo Neri8aa2eed2012-08-01 07:56:40 -0500713void dss_select_hdmi_venc_clk_source(enum dss_hdmi_venc_clk_source_select src)
Mythri P K7ed024a2011-03-09 16:31:38 +0530714{
Laurent Pinchart24ab1df2017-08-05 01:43:59 +0300715 enum omap_dss_output_id outputs;
716
Laurent Pinchart51919572017-08-05 01:44:18 +0300717 outputs = dss.feat->outputs[OMAP_DSS_CHANNEL_DIGIT];
Ricardo Neri8aa2eed2012-08-01 07:56:40 -0500718
719 /* Complain about invalid selections */
Laurent Pinchart24ab1df2017-08-05 01:43:59 +0300720 WARN_ON((src == DSS_VENC_TV_CLK) && !(outputs & OMAP_DSS_OUTPUT_VENC));
721 WARN_ON((src == DSS_HDMI_M_PCLK) && !(outputs & OMAP_DSS_OUTPUT_HDMI));
Ricardo Neri8aa2eed2012-08-01 07:56:40 -0500722
723 /* Select only if we have options */
Laurent Pinchart24ab1df2017-08-05 01:43:59 +0300724 if ((outputs & OMAP_DSS_OUTPUT_VENC) &&
725 (outputs & OMAP_DSS_OUTPUT_HDMI))
Ricardo Neri8aa2eed2012-08-01 07:56:40 -0500726 REG_FLD_MOD(DSS_CONTROL, src, 15, 15); /* VENC_HDMI_SWITCH */
Mythri P K7ed024a2011-03-09 16:31:38 +0530727}
728
Archit Taneja064c2a42014-04-23 18:00:18 +0530729static int dss_dpi_select_source_omap2_omap3(int port, enum omap_channel channel)
Tomi Valkeinende09e452012-09-21 12:09:54 +0300730{
731 if (channel != OMAP_DSS_CHANNEL_LCD)
732 return -EINVAL;
733
734 return 0;
735}
736
Archit Taneja064c2a42014-04-23 18:00:18 +0530737static int dss_dpi_select_source_omap4(int port, enum omap_channel channel)
Tomi Valkeinende09e452012-09-21 12:09:54 +0300738{
739 int val;
740
741 switch (channel) {
742 case OMAP_DSS_CHANNEL_LCD2:
743 val = 0;
744 break;
745 case OMAP_DSS_CHANNEL_DIGIT:
746 val = 1;
747 break;
748 default:
749 return -EINVAL;
750 }
751
752 REG_FLD_MOD(DSS_CONTROL, val, 17, 17);
753
754 return 0;
755}
756
Archit Taneja064c2a42014-04-23 18:00:18 +0530757static int dss_dpi_select_source_omap5(int port, enum omap_channel channel)
Tomi Valkeinende09e452012-09-21 12:09:54 +0300758{
759 int val;
760
761 switch (channel) {
762 case OMAP_DSS_CHANNEL_LCD:
763 val = 1;
764 break;
765 case OMAP_DSS_CHANNEL_LCD2:
766 val = 2;
767 break;
768 case OMAP_DSS_CHANNEL_LCD3:
769 val = 3;
770 break;
771 case OMAP_DSS_CHANNEL_DIGIT:
772 val = 0;
773 break;
774 default:
775 return -EINVAL;
776 }
777
778 REG_FLD_MOD(DSS_CONTROL, val, 17, 16);
779
780 return 0;
781}
782
Tomi Valkeinen6d817882014-12-31 11:23:31 +0200783static int dss_dpi_select_source_dra7xx(int port, enum omap_channel channel)
784{
785 switch (port) {
786 case 0:
787 return dss_dpi_select_source_omap5(port, channel);
788 case 1:
789 if (channel != OMAP_DSS_CHANNEL_LCD2)
790 return -EINVAL;
791 break;
792 case 2:
793 if (channel != OMAP_DSS_CHANNEL_LCD3)
794 return -EINVAL;
795 break;
796 default:
797 return -EINVAL;
798 }
799
800 return 0;
801}
802
Archit Taneja064c2a42014-04-23 18:00:18 +0530803int dss_dpi_select_source(int port, enum omap_channel channel)
Tomi Valkeinende09e452012-09-21 12:09:54 +0300804{
Laurent Pinchartfecea252017-08-05 01:43:52 +0300805 return dss.feat->ops->dpi_select_source(port, channel);
Tomi Valkeinende09e452012-09-21 12:09:54 +0300806}
807
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000808static int dss_get_clocks(void)
809{
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300810 struct clk *clk;
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000811
Archit Tanejab2c9c8e2013-04-08 11:55:00 +0300812 clk = devm_clk_get(&dss.pdev->dev, "fck");
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300813 if (IS_ERR(clk)) {
814 DSSERR("can't get clock fck\n");
Archit Tanejab2c9c8e2013-04-08 11:55:00 +0300815 return PTR_ERR(clk);
Semwal, Sumita1a0dcc2011-03-01 02:42:14 -0600816 }
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000817
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300818 dss.dss_clk = clk;
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000819
Tomi Valkeinen64ad8462013-11-01 11:38:04 +0200820 if (dss.feat->parent_clk_name) {
821 clk = clk_get(NULL, dss.feat->parent_clk_name);
Aaro Koskinen8ad93752012-11-21 21:48:51 +0200822 if (IS_ERR(clk)) {
Tomi Valkeinen64ad8462013-11-01 11:38:04 +0200823 DSSERR("Failed to get %s\n", dss.feat->parent_clk_name);
Archit Tanejab2c9c8e2013-04-08 11:55:00 +0300824 return PTR_ERR(clk);
Aaro Koskinen8ad93752012-11-21 21:48:51 +0200825 }
826 } else {
827 clk = NULL;
Tomi Valkeinen94c042c2011-05-16 13:43:04 +0300828 }
829
Tomi Valkeinen64ad8462013-11-01 11:38:04 +0200830 dss.parent_clk = clk;
Tomi Valkeinen94c042c2011-05-16 13:43:04 +0300831
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000832 return 0;
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000833}
834
835static void dss_put_clocks(void)
836{
Tomi Valkeinen64ad8462013-11-01 11:38:04 +0200837 if (dss.parent_clk)
838 clk_put(dss.parent_clk);
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000839}
840
Laurent Pinchart7b295252018-02-13 14:00:21 +0200841int dss_runtime_get(struct dss_device *dss)
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000842{
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300843 int r;
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000844
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300845 DSSDBG("dss_runtime_get\n");
846
Laurent Pinchart7b295252018-02-13 14:00:21 +0200847 r = pm_runtime_get_sync(&dss->pdev->dev);
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300848 WARN_ON(r < 0);
849 return r < 0 ? r : 0;
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000850}
851
Laurent Pinchart7b295252018-02-13 14:00:21 +0200852void dss_runtime_put(struct dss_device *dss)
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000853{
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300854 int r;
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000855
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300856 DSSDBG("dss_runtime_put\n");
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000857
Laurent Pinchart7b295252018-02-13 14:00:21 +0200858 r = pm_runtime_put_sync(&dss->pdev->dev);
Tomi Valkeinen5be3aeb2012-06-27 16:37:18 +0300859 WARN_ON(r < 0 && r != -ENOSYS && r != -EBUSY);
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000860}
861
Laurent Pinchart7b295252018-02-13 14:00:21 +0200862struct dss_device *dss_get_device(struct device *dev)
863{
864 return &dss;
865}
866
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000867/* DEBUGFS */
Chandrabhanu Mahapatra1b3bcb32012-09-29 11:25:42 +0530868#if defined(CONFIG_OMAP2_DSS_DEBUGFS)
Laurent Pinchart11765d12017-08-05 01:44:01 +0300869static void dss_debug_dump_clocks(struct seq_file *s)
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000870{
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000871 dss_dump_clocks(s);
872 dispc_dump_clocks(s);
873#ifdef CONFIG_OMAP2_DSS_DSI
874 dsi_dump_clocks(s);
875#endif
876}
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000877
Laurent Pinchart11765d12017-08-05 01:44:01 +0300878static int dss_debug_show(struct seq_file *s, void *unused)
879{
880 void (*func)(struct seq_file *) = s->private;
881
882 func(s);
883 return 0;
884}
885
886static int dss_debug_open(struct inode *inode, struct file *file)
887{
888 return single_open(file, dss_debug_show, inode->i_private);
889}
890
891static const struct file_operations dss_debug_fops = {
892 .open = dss_debug_open,
893 .read = seq_read,
894 .llseek = seq_lseek,
895 .release = single_release,
896};
897
898static struct dentry *dss_debugfs_dir;
899
900static int dss_initialize_debugfs(void)
901{
902 dss_debugfs_dir = debugfs_create_dir("omapdss", NULL);
903 if (IS_ERR(dss_debugfs_dir)) {
904 int err = PTR_ERR(dss_debugfs_dir);
905
906 dss_debugfs_dir = NULL;
907 return err;
908 }
909
910 debugfs_create_file("clk", S_IRUGO, dss_debugfs_dir,
911 &dss_debug_dump_clocks, &dss_debug_fops);
912
913 return 0;
914}
915
916static void dss_uninitialize_debugfs(void)
917{
918 if (dss_debugfs_dir)
919 debugfs_remove_recursive(dss_debugfs_dir);
920}
921
922int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *))
923{
924 struct dentry *d;
925
926 d = debugfs_create_file(name, S_IRUGO, dss_debugfs_dir,
927 write, &dss_debug_fops);
928
929 return PTR_ERR_OR_ZERO(d);
930}
931#else /* CONFIG_OMAP2_DSS_DEBUGFS */
932static inline int dss_initialize_debugfs(void)
933{
934 return 0;
935}
936static inline void dss_uninitialize_debugfs(void)
937{
938}
939#endif /* CONFIG_OMAP2_DSS_DEBUGFS */
Archit Taneja387ce9f2014-05-22 17:01:57 +0530940
Laurent Pinchartfecea252017-08-05 01:43:52 +0300941static const struct dss_ops dss_ops_omap2_omap3 = {
942 .dpi_select_source = &dss_dpi_select_source_omap2_omap3,
943};
944
945static const struct dss_ops dss_ops_omap4 = {
946 .dpi_select_source = &dss_dpi_select_source_omap4,
947 .select_lcd_source = &dss_lcd_clk_mux_omap4,
948};
949
950static const struct dss_ops dss_ops_omap5 = {
951 .dpi_select_source = &dss_dpi_select_source_omap5,
952 .select_lcd_source = &dss_lcd_clk_mux_omap5,
953};
954
955static const struct dss_ops dss_ops_dra7 = {
956 .dpi_select_source = &dss_dpi_select_source_dra7xx,
957 .select_lcd_source = &dss_lcd_clk_mux_dra7,
958};
959
Tomi Valkeinen234f9a22014-12-11 15:59:31 +0200960static const enum omap_display_type omap2plus_ports[] = {
Archit Taneja387ce9f2014-05-22 17:01:57 +0530961 OMAP_DISPLAY_TYPE_DPI,
962};
963
Tomi Valkeinen234f9a22014-12-11 15:59:31 +0200964static const enum omap_display_type omap34xx_ports[] = {
Archit Taneja387ce9f2014-05-22 17:01:57 +0530965 OMAP_DISPLAY_TYPE_DPI,
966 OMAP_DISPLAY_TYPE_SDI,
967};
968
Tomi Valkeinen6d817882014-12-31 11:23:31 +0200969static const enum omap_display_type dra7xx_ports[] = {
970 OMAP_DISPLAY_TYPE_DPI,
971 OMAP_DISPLAY_TYPE_DPI,
972 OMAP_DISPLAY_TYPE_DPI,
973};
974
Laurent Pinchart51919572017-08-05 01:44:18 +0300975static const enum omap_dss_output_id omap2_dss_supported_outputs[] = {
976 /* OMAP_DSS_CHANNEL_LCD */
977 OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI,
978
979 /* OMAP_DSS_CHANNEL_DIGIT */
980 OMAP_DSS_OUTPUT_VENC,
981};
982
983static const enum omap_dss_output_id omap3430_dss_supported_outputs[] = {
984 /* OMAP_DSS_CHANNEL_LCD */
985 OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI |
986 OMAP_DSS_OUTPUT_SDI | OMAP_DSS_OUTPUT_DSI1,
987
988 /* OMAP_DSS_CHANNEL_DIGIT */
989 OMAP_DSS_OUTPUT_VENC,
990};
991
992static const enum omap_dss_output_id omap3630_dss_supported_outputs[] = {
993 /* OMAP_DSS_CHANNEL_LCD */
994 OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI |
995 OMAP_DSS_OUTPUT_DSI1,
996
997 /* OMAP_DSS_CHANNEL_DIGIT */
998 OMAP_DSS_OUTPUT_VENC,
999};
1000
1001static const enum omap_dss_output_id am43xx_dss_supported_outputs[] = {
1002 /* OMAP_DSS_CHANNEL_LCD */
1003 OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI,
1004};
1005
1006static const enum omap_dss_output_id omap4_dss_supported_outputs[] = {
1007 /* OMAP_DSS_CHANNEL_LCD */
1008 OMAP_DSS_OUTPUT_DBI | OMAP_DSS_OUTPUT_DSI1,
1009
1010 /* OMAP_DSS_CHANNEL_DIGIT */
1011 OMAP_DSS_OUTPUT_VENC | OMAP_DSS_OUTPUT_HDMI,
1012
1013 /* OMAP_DSS_CHANNEL_LCD2 */
1014 OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI |
1015 OMAP_DSS_OUTPUT_DSI2,
1016};
1017
1018static const enum omap_dss_output_id omap5_dss_supported_outputs[] = {
1019 /* OMAP_DSS_CHANNEL_LCD */
1020 OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI |
1021 OMAP_DSS_OUTPUT_DSI1 | OMAP_DSS_OUTPUT_DSI2,
1022
1023 /* OMAP_DSS_CHANNEL_DIGIT */
1024 OMAP_DSS_OUTPUT_HDMI,
1025
1026 /* OMAP_DSS_CHANNEL_LCD2 */
1027 OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI |
1028 OMAP_DSS_OUTPUT_DSI1,
1029
1030 /* OMAP_DSS_CHANNEL_LCD3 */
1031 OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI |
1032 OMAP_DSS_OUTPUT_DSI2,
1033};
1034
Tomi Valkeinenede92692015-06-04 14:12:16 +03001035static const struct dss_features omap24xx_dss_feats = {
Laurent Pinchartb8dab2b2017-08-05 01:43:56 +03001036 .model = DSS_MODEL_OMAP2,
Tomi Valkeinen6e555e22013-11-01 11:26:43 +02001037 /*
1038 * fck div max is really 16, but the divider range has gaps. The range
1039 * from 1 to 6 has no gaps, so let's use that as a max.
1040 */
1041 .fck_div_max = 6,
Laurent Pinchart9f0fbae2017-08-05 01:44:17 +03001042 .fck_freq_max = 133000000,
Tomi Valkeinen84273a92012-09-21 12:03:31 +03001043 .dss_fck_multiplier = 2,
Tomi Valkeinenada94432013-10-31 16:06:38 +02001044 .parent_clk_name = "core_ck",
Archit Taneja387ce9f2014-05-22 17:01:57 +05301045 .ports = omap2plus_ports,
1046 .num_ports = ARRAY_SIZE(omap2plus_ports),
Laurent Pinchart51919572017-08-05 01:44:18 +03001047 .outputs = omap2_dss_supported_outputs,
Laurent Pinchartfecea252017-08-05 01:43:52 +03001048 .ops = &dss_ops_omap2_omap3,
Laurent Pinchart6d85d4a2017-08-05 01:44:07 +03001049 .dispc_clk_switch = { 0, 0 },
Laurent Pinchart4569ab72017-08-05 01:44:13 +03001050 .has_lcd_clk_src = false,
Tomi Valkeinen84273a92012-09-21 12:03:31 +03001051};
1052
Tomi Valkeinenede92692015-06-04 14:12:16 +03001053static const struct dss_features omap34xx_dss_feats = {
Laurent Pinchartb8dab2b2017-08-05 01:43:56 +03001054 .model = DSS_MODEL_OMAP3,
Tomi Valkeinen84273a92012-09-21 12:03:31 +03001055 .fck_div_max = 16,
Laurent Pinchart9f0fbae2017-08-05 01:44:17 +03001056 .fck_freq_max = 173000000,
Tomi Valkeinen84273a92012-09-21 12:03:31 +03001057 .dss_fck_multiplier = 2,
Tomi Valkeinenada94432013-10-31 16:06:38 +02001058 .parent_clk_name = "dpll4_ck",
Archit Taneja387ce9f2014-05-22 17:01:57 +05301059 .ports = omap34xx_ports,
Laurent Pinchart51919572017-08-05 01:44:18 +03001060 .outputs = omap3430_dss_supported_outputs,
Archit Taneja387ce9f2014-05-22 17:01:57 +05301061 .num_ports = ARRAY_SIZE(omap34xx_ports),
Laurent Pinchartfecea252017-08-05 01:43:52 +03001062 .ops = &dss_ops_omap2_omap3,
Laurent Pinchart6d85d4a2017-08-05 01:44:07 +03001063 .dispc_clk_switch = { 0, 0 },
Laurent Pinchart4569ab72017-08-05 01:44:13 +03001064 .has_lcd_clk_src = false,
Tomi Valkeinen84273a92012-09-21 12:03:31 +03001065};
1066
Tomi Valkeinenede92692015-06-04 14:12:16 +03001067static const struct dss_features omap3630_dss_feats = {
Laurent Pinchartb8dab2b2017-08-05 01:43:56 +03001068 .model = DSS_MODEL_OMAP3,
Tomi Valkeinen84273a92012-09-21 12:03:31 +03001069 .fck_div_max = 32,
Laurent Pinchart9f0fbae2017-08-05 01:44:17 +03001070 .fck_freq_max = 173000000,
Tomi Valkeinen84273a92012-09-21 12:03:31 +03001071 .dss_fck_multiplier = 1,
Tomi Valkeinenada94432013-10-31 16:06:38 +02001072 .parent_clk_name = "dpll4_ck",
Archit Taneja387ce9f2014-05-22 17:01:57 +05301073 .ports = omap2plus_ports,
1074 .num_ports = ARRAY_SIZE(omap2plus_ports),
Laurent Pinchart51919572017-08-05 01:44:18 +03001075 .outputs = omap3630_dss_supported_outputs,
Laurent Pinchartfecea252017-08-05 01:43:52 +03001076 .ops = &dss_ops_omap2_omap3,
Laurent Pinchart6d85d4a2017-08-05 01:44:07 +03001077 .dispc_clk_switch = { 0, 0 },
Laurent Pinchart4569ab72017-08-05 01:44:13 +03001078 .has_lcd_clk_src = false,
Tomi Valkeinen84273a92012-09-21 12:03:31 +03001079};
1080
Tomi Valkeinenede92692015-06-04 14:12:16 +03001081static const struct dss_features omap44xx_dss_feats = {
Laurent Pinchartb8dab2b2017-08-05 01:43:56 +03001082 .model = DSS_MODEL_OMAP4,
Tomi Valkeinen84273a92012-09-21 12:03:31 +03001083 .fck_div_max = 32,
Laurent Pinchart9f0fbae2017-08-05 01:44:17 +03001084 .fck_freq_max = 186000000,
Tomi Valkeinen84273a92012-09-21 12:03:31 +03001085 .dss_fck_multiplier = 1,
Tomi Valkeinenada94432013-10-31 16:06:38 +02001086 .parent_clk_name = "dpll_per_x2_ck",
Archit Taneja387ce9f2014-05-22 17:01:57 +05301087 .ports = omap2plus_ports,
1088 .num_ports = ARRAY_SIZE(omap2plus_ports),
Laurent Pinchart51919572017-08-05 01:44:18 +03001089 .outputs = omap4_dss_supported_outputs,
Laurent Pinchartfecea252017-08-05 01:43:52 +03001090 .ops = &dss_ops_omap4,
Laurent Pinchart6d85d4a2017-08-05 01:44:07 +03001091 .dispc_clk_switch = { 9, 8 },
Laurent Pinchart4569ab72017-08-05 01:44:13 +03001092 .has_lcd_clk_src = true,
Tomi Valkeinen84273a92012-09-21 12:03:31 +03001093};
1094
Tomi Valkeinenede92692015-06-04 14:12:16 +03001095static const struct dss_features omap54xx_dss_feats = {
Laurent Pinchartb8dab2b2017-08-05 01:43:56 +03001096 .model = DSS_MODEL_OMAP5,
Tomi Valkeinen84273a92012-09-21 12:03:31 +03001097 .fck_div_max = 64,
Laurent Pinchart9f0fbae2017-08-05 01:44:17 +03001098 .fck_freq_max = 209250000,
Tomi Valkeinen84273a92012-09-21 12:03:31 +03001099 .dss_fck_multiplier = 1,
Tomi Valkeinenada94432013-10-31 16:06:38 +02001100 .parent_clk_name = "dpll_per_x2_ck",
Archit Taneja387ce9f2014-05-22 17:01:57 +05301101 .ports = omap2plus_ports,
1102 .num_ports = ARRAY_SIZE(omap2plus_ports),
Laurent Pinchart51919572017-08-05 01:44:18 +03001103 .outputs = omap5_dss_supported_outputs,
Laurent Pinchartfecea252017-08-05 01:43:52 +03001104 .ops = &dss_ops_omap5,
Laurent Pinchart6d85d4a2017-08-05 01:44:07 +03001105 .dispc_clk_switch = { 9, 7 },
Laurent Pinchart4569ab72017-08-05 01:44:13 +03001106 .has_lcd_clk_src = true,
Tomi Valkeinen84273a92012-09-21 12:03:31 +03001107};
1108
Tomi Valkeinenede92692015-06-04 14:12:16 +03001109static const struct dss_features am43xx_dss_feats = {
Laurent Pinchartb8dab2b2017-08-05 01:43:56 +03001110 .model = DSS_MODEL_OMAP3,
Sathya Prakash M Rd6279d42014-03-24 16:31:51 +05301111 .fck_div_max = 0,
Laurent Pinchart9f0fbae2017-08-05 01:44:17 +03001112 .fck_freq_max = 200000000,
Sathya Prakash M Rd6279d42014-03-24 16:31:51 +05301113 .dss_fck_multiplier = 0,
1114 .parent_clk_name = NULL,
Archit Taneja387ce9f2014-05-22 17:01:57 +05301115 .ports = omap2plus_ports,
1116 .num_ports = ARRAY_SIZE(omap2plus_ports),
Laurent Pinchart51919572017-08-05 01:44:18 +03001117 .outputs = am43xx_dss_supported_outputs,
Laurent Pinchartfecea252017-08-05 01:43:52 +03001118 .ops = &dss_ops_omap2_omap3,
Laurent Pinchart6d85d4a2017-08-05 01:44:07 +03001119 .dispc_clk_switch = { 0, 0 },
Laurent Pinchart4569ab72017-08-05 01:44:13 +03001120 .has_lcd_clk_src = true,
Sathya Prakash M Rd6279d42014-03-24 16:31:51 +05301121};
1122
Tomi Valkeinenede92692015-06-04 14:12:16 +03001123static const struct dss_features dra7xx_dss_feats = {
Laurent Pinchartb8dab2b2017-08-05 01:43:56 +03001124 .model = DSS_MODEL_DRA7,
Tomi Valkeinen6d817882014-12-31 11:23:31 +02001125 .fck_div_max = 64,
Laurent Pinchart9f0fbae2017-08-05 01:44:17 +03001126 .fck_freq_max = 209250000,
Tomi Valkeinen6d817882014-12-31 11:23:31 +02001127 .dss_fck_multiplier = 1,
1128 .parent_clk_name = "dpll_per_x2_ck",
Tomi Valkeinen6d817882014-12-31 11:23:31 +02001129 .ports = dra7xx_ports,
1130 .num_ports = ARRAY_SIZE(dra7xx_ports),
Laurent Pinchart51919572017-08-05 01:44:18 +03001131 .outputs = omap5_dss_supported_outputs,
Laurent Pinchartfecea252017-08-05 01:43:52 +03001132 .ops = &dss_ops_dra7,
Laurent Pinchart6d85d4a2017-08-05 01:44:07 +03001133 .dispc_clk_switch = { 9, 7 },
Laurent Pinchart4569ab72017-08-05 01:44:13 +03001134 .has_lcd_clk_src = true,
Tomi Valkeinen6d817882014-12-31 11:23:31 +02001135};
1136
Tomi Valkeinenede92692015-06-04 14:12:16 +03001137static int dss_init_ports(struct platform_device *pdev)
Tomi Valkeinen2ecef242013-12-16 15:13:24 +02001138{
1139 struct device_node *parent = pdev->dev.of_node;
1140 struct device_node *port;
Rob Herring09bffa62017-03-22 08:26:08 -05001141 int i;
Tomi Valkeinen2ecef242013-12-16 15:13:24 +02001142
Rob Herring09bffa62017-03-22 08:26:08 -05001143 for (i = 0; i < dss.feat->num_ports; i++) {
1144 port = of_graph_get_port_by_id(parent, i);
1145 if (!port)
Archit Taneja387ce9f2014-05-22 17:01:57 +05301146 continue;
1147
Rob Herring09bffa62017-03-22 08:26:08 -05001148 switch (dss.feat->ports[i]) {
Archit Taneja387ce9f2014-05-22 17:01:57 +05301149 case OMAP_DISPLAY_TYPE_DPI:
Laurent Pinchartb8dab2b2017-08-05 01:43:56 +03001150 dpi_init_port(pdev, port, dss.feat->model);
Archit Taneja387ce9f2014-05-22 17:01:57 +05301151 break;
1152 case OMAP_DISPLAY_TYPE_SDI:
Tomi Valkeinen2ecef242013-12-16 15:13:24 +02001153 sdi_init_port(pdev, port);
Archit Taneja387ce9f2014-05-22 17:01:57 +05301154 break;
1155 default:
1156 break;
1157 }
Rob Herring09bffa62017-03-22 08:26:08 -05001158 }
Tomi Valkeinen2ecef242013-12-16 15:13:24 +02001159
1160 return 0;
1161}
1162
Tomi Valkeinenede92692015-06-04 14:12:16 +03001163static void dss_uninit_ports(struct platform_device *pdev)
Tomi Valkeinen2ecef242013-12-16 15:13:24 +02001164{
Archit Taneja80eb6752014-06-02 14:11:51 +05301165 struct device_node *parent = pdev->dev.of_node;
1166 struct device_node *port;
Rob Herring09bffa62017-03-22 08:26:08 -05001167 int i;
Archit Taneja80eb6752014-06-02 14:11:51 +05301168
Rob Herring09bffa62017-03-22 08:26:08 -05001169 for (i = 0; i < dss.feat->num_ports; i++) {
1170 port = of_graph_get_port_by_id(parent, i);
1171 if (!port)
Archit Taneja387ce9f2014-05-22 17:01:57 +05301172 continue;
1173
Rob Herring09bffa62017-03-22 08:26:08 -05001174 switch (dss.feat->ports[i]) {
Archit Taneja387ce9f2014-05-22 17:01:57 +05301175 case OMAP_DISPLAY_TYPE_DPI:
1176 dpi_uninit_port(port);
1177 break;
1178 case OMAP_DISPLAY_TYPE_SDI:
1179 sdi_uninit_port(port);
1180 break;
1181 default:
1182 break;
1183 }
Rob Herring09bffa62017-03-22 08:26:08 -05001184 }
Tomi Valkeinen2ecef242013-12-16 15:13:24 +02001185}
1186
Tomi Valkeinen7e328f52015-06-04 13:02:52 +03001187static int dss_video_pll_probe(struct platform_device *pdev)
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +00001188{
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +05301189 struct device_node *np = pdev->dev.of_node;
Tomi Valkeinen99767542014-07-04 13:38:27 +05301190 struct regulator *pll_regulator;
Tomi Valkeinen7e328f52015-06-04 13:02:52 +03001191 int r;
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +00001192
Tomi Valkeinen7e328f52015-06-04 13:02:52 +03001193 if (!np)
1194 return 0;
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +00001195
Tomi Valkeinen7e328f52015-06-04 13:02:52 +03001196 if (of_property_read_bool(np, "syscon-pll-ctrl")) {
Tomi Valkeinenbe40eec2014-07-04 13:37:15 +05301197 dss.syscon_pll_ctrl = syscon_regmap_lookup_by_phandle(np,
1198 "syscon-pll-ctrl");
1199 if (IS_ERR(dss.syscon_pll_ctrl)) {
1200 dev_err(&pdev->dev,
1201 "failed to get syscon-pll-ctrl regmap\n");
1202 return PTR_ERR(dss.syscon_pll_ctrl);
1203 }
1204
1205 if (of_property_read_u32_index(np, "syscon-pll-ctrl", 1,
1206 &dss.syscon_pll_ctrl_offset)) {
1207 dev_err(&pdev->dev,
1208 "failed to get syscon-pll-ctrl offset\n");
1209 return -EINVAL;
1210 }
1211 }
1212
Tomi Valkeinen99767542014-07-04 13:38:27 +05301213 pll_regulator = devm_regulator_get(&pdev->dev, "vdda_video");
1214 if (IS_ERR(pll_regulator)) {
1215 r = PTR_ERR(pll_regulator);
1216
1217 switch (r) {
1218 case -ENOENT:
1219 pll_regulator = NULL;
1220 break;
1221
1222 case -EPROBE_DEFER:
1223 return -EPROBE_DEFER;
1224
1225 default:
1226 DSSERR("can't get DPLL VDDA regulator\n");
1227 return r;
1228 }
1229 }
1230
1231 if (of_property_match_string(np, "reg-names", "pll1") >= 0) {
Laurent Pinchart7b295252018-02-13 14:00:21 +02001232 dss.video1_pll = dss_video_pll_init(&dss, pdev, 0,
1233 pll_regulator);
Tomi Valkeinen7e328f52015-06-04 13:02:52 +03001234 if (IS_ERR(dss.video1_pll))
1235 return PTR_ERR(dss.video1_pll);
Tomi Valkeinen99767542014-07-04 13:38:27 +05301236 }
1237
1238 if (of_property_match_string(np, "reg-names", "pll2") >= 0) {
Laurent Pinchart7b295252018-02-13 14:00:21 +02001239 dss.video2_pll = dss_video_pll_init(&dss, pdev, 1,
1240 pll_regulator);
Tomi Valkeinen99767542014-07-04 13:38:27 +05301241 if (IS_ERR(dss.video2_pll)) {
Tomi Valkeinen7e328f52015-06-04 13:02:52 +03001242 dss_video_pll_uninit(dss.video1_pll);
1243 return PTR_ERR(dss.video2_pll);
Tomi Valkeinen99767542014-07-04 13:38:27 +05301244 }
1245 }
1246
Tomi Valkeinen7e328f52015-06-04 13:02:52 +03001247 return 0;
1248}
1249
1250/* DSS HW IP initialisation */
Laurent Pinchart18daeb82017-08-05 01:43:58 +03001251static const struct of_device_id dss_of_match[] = {
1252 { .compatible = "ti,omap2-dss", .data = &omap24xx_dss_feats },
1253 { .compatible = "ti,omap3-dss", .data = &omap3630_dss_feats },
1254 { .compatible = "ti,omap4-dss", .data = &omap44xx_dss_feats },
1255 { .compatible = "ti,omap5-dss", .data = &omap54xx_dss_feats },
1256 { .compatible = "ti,dra7-dss", .data = &dra7xx_dss_feats },
1257 {},
1258};
1259MODULE_DEVICE_TABLE(of, dss_of_match);
1260
1261static const struct soc_device_attribute dss_soc_devices[] = {
1262 { .machine = "OMAP3430/3530", .data = &omap34xx_dss_feats },
1263 { .machine = "AM35??", .data = &omap34xx_dss_feats },
1264 { .family = "AM43xx", .data = &am43xx_dss_feats },
1265 { /* sentinel */ }
1266};
1267
Tomi Valkeinen736e60d2015-06-04 15:22:23 +03001268static int dss_bind(struct device *dev)
Tomi Valkeinen7e328f52015-06-04 13:02:52 +03001269{
Tomi Valkeinen7e328f52015-06-04 13:02:52 +03001270 int r;
1271
Laurent Pinchart215003b2018-02-11 15:07:44 +02001272 r = component_bind_all(dev, NULL);
Tomi Valkeinen7e328f52015-06-04 13:02:52 +03001273 if (r)
1274 return r;
1275
Tomi Valkeinencb17a4a2015-02-25 12:08:14 +02001276 pm_set_vt_switch(0);
1277
Peter Ujfalusi1e08c822016-05-03 22:07:10 +03001278 omapdss_gather_components(dev);
Tomi Valkeinen7c299712015-11-05 17:23:14 +02001279 omapdss_set_is_initialized(true);
Tomi Valkeinenf99467b2015-06-04 12:35:42 +03001280
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +00001281 return 0;
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +00001282}
1283
Tomi Valkeinen736e60d2015-06-04 15:22:23 +03001284static void dss_unbind(struct device *dev)
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +00001285{
Tomi Valkeinen736e60d2015-06-04 15:22:23 +03001286 struct platform_device *pdev = to_platform_device(dev);
1287
Tomi Valkeinen7c299712015-11-05 17:23:14 +02001288 omapdss_set_is_initialized(false);
Tomi Valkeinenf99467b2015-06-04 12:35:42 +03001289
Tomi Valkeinen736e60d2015-06-04 15:22:23 +03001290 component_unbind_all(&pdev->dev, NULL);
Tomi Valkeinen736e60d2015-06-04 15:22:23 +03001291}
Tomi Valkeinenb98482e2011-05-16 13:52:51 +03001292
Tomi Valkeinen736e60d2015-06-04 15:22:23 +03001293static const struct component_master_ops dss_component_ops = {
1294 .bind = dss_bind,
1295 .unbind = dss_unbind,
1296};
1297
1298static int dss_component_compare(struct device *dev, void *data)
1299{
1300 struct device *child = data;
1301 return dev == child;
1302}
1303
1304static int dss_add_child_component(struct device *dev, void *data)
1305{
1306 struct component_match **match = data;
1307
Tomi Valkeinen0438ec92015-06-30 12:23:45 +03001308 /*
1309 * HACK
1310 * We don't have a working driver for rfbi, so skip it here always.
1311 * Otherwise dss will never get probed successfully, as it will wait
1312 * for rfbi to get probed.
1313 */
1314 if (strstr(dev_name(dev), "rfbi"))
1315 return 0;
1316
Tomi Valkeinen736e60d2015-06-04 15:22:23 +03001317 component_match_add(dev->parent, match, dss_component_compare, dev);
1318
1319 return 0;
1320}
1321
Laurent Pinchart7b295252018-02-13 14:00:21 +02001322static int dss_probe_hardware(struct dss_device *dss)
Laurent Pinchart215003b2018-02-11 15:07:44 +02001323{
1324 u32 rev;
1325 int r;
1326
Laurent Pinchart7b295252018-02-13 14:00:21 +02001327 r = dss_runtime_get(dss);
Laurent Pinchart215003b2018-02-11 15:07:44 +02001328 if (r)
1329 return r;
1330
Laurent Pinchart7b295252018-02-13 14:00:21 +02001331 dss->dss_clk_rate = clk_get_rate(dss->dss_clk);
Laurent Pinchart215003b2018-02-11 15:07:44 +02001332
1333 /* Select DPLL */
1334 REG_FLD_MOD(DSS_CONTROL, 0, 0, 0);
1335
1336 dss_select_dispc_clk_source(DSS_CLK_SRC_FCK);
1337
1338#ifdef CONFIG_OMAP2_DSS_VENC
1339 REG_FLD_MOD(DSS_CONTROL, 1, 4, 4); /* venc dac demen */
1340 REG_FLD_MOD(DSS_CONTROL, 1, 3, 3); /* venc clock 4x enable */
1341 REG_FLD_MOD(DSS_CONTROL, 0, 2, 2); /* venc clock mode = normal */
1342#endif
Laurent Pinchart7b295252018-02-13 14:00:21 +02001343 dss->dsi_clk_source[0] = DSS_CLK_SRC_FCK;
1344 dss->dsi_clk_source[1] = DSS_CLK_SRC_FCK;
1345 dss->dispc_clk_source = DSS_CLK_SRC_FCK;
1346 dss->lcd_clk_source[0] = DSS_CLK_SRC_FCK;
1347 dss->lcd_clk_source[1] = DSS_CLK_SRC_FCK;
Laurent Pinchart215003b2018-02-11 15:07:44 +02001348
1349 rev = dss_read_reg(DSS_REVISION);
1350 pr_info("OMAP DSS rev %d.%d\n", FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));
1351
Laurent Pinchart7b295252018-02-13 14:00:21 +02001352 dss_runtime_put(dss);
Laurent Pinchart215003b2018-02-11 15:07:44 +02001353
1354 return 0;
1355}
1356
Tomi Valkeinen736e60d2015-06-04 15:22:23 +03001357static int dss_probe(struct platform_device *pdev)
1358{
Laurent Pinchart4a9fab32017-08-05 01:44:00 +03001359 const struct soc_device_attribute *soc;
Tomi Valkeinen736e60d2015-06-04 15:22:23 +03001360 struct component_match *match = NULL;
Laurent Pinchart215003b2018-02-11 15:07:44 +02001361 struct resource *dss_mem;
Tomi Valkeinen736e60d2015-06-04 15:22:23 +03001362 int r;
1363
Laurent Pinchart4a9fab32017-08-05 01:44:00 +03001364 dss.pdev = pdev;
1365
Laurent Pincharta921c1a2017-10-13 17:59:01 +03001366 r = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
1367 if (r) {
1368 dev_err(&pdev->dev, "Failed to set the DMA mask\n");
1369 return r;
1370 }
1371
Laurent Pinchart4a9fab32017-08-05 01:44:00 +03001372 /*
1373 * The various OMAP3-based SoCs can't be told apart using the compatible
1374 * string, use SoC device matching.
1375 */
1376 soc = soc_device_match(dss_soc_devices);
1377 if (soc)
1378 dss.feat = soc->data;
1379 else
1380 dss.feat = of_match_device(dss_of_match, &pdev->dev)->data;
1381
Laurent Pinchart215003b2018-02-11 15:07:44 +02001382 /* Map I/O registers, get and setup clocks. */
1383 dss_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1384 dss.base = devm_ioremap_resource(&pdev->dev, dss_mem);
1385 if (IS_ERR(dss.base))
1386 return PTR_ERR(dss.base);
1387
1388 r = dss_get_clocks();
Laurent Pinchart11765d12017-08-05 01:44:01 +03001389 if (r)
1390 return r;
1391
Laurent Pinchart215003b2018-02-11 15:07:44 +02001392 r = dss_setup_default_clock();
1393 if (r)
1394 goto err_put_clocks;
1395
1396 /* Setup the video PLLs and the DPI and SDI ports. */
1397 r = dss_video_pll_probe(pdev);
1398 if (r)
1399 goto err_put_clocks;
1400
1401 r = dss_init_ports(pdev);
1402 if (r)
1403 goto err_uninit_plls;
1404
1405 /* Enable runtime PM and probe the hardware. */
1406 pm_runtime_enable(&pdev->dev);
1407
Laurent Pinchart7b295252018-02-13 14:00:21 +02001408 r = dss_probe_hardware(&dss);
Laurent Pinchart215003b2018-02-11 15:07:44 +02001409 if (r)
1410 goto err_pm_runtime_disable;
1411
1412 /* Initialize debugfs. */
1413 r = dss_initialize_debugfs();
1414 if (r)
1415 goto err_pm_runtime_disable;
1416
1417 dss_debugfs_create_file("dss", dss_dump_regs);
1418
1419 /* Add all the child devices as components. */
Tomi Valkeinen736e60d2015-06-04 15:22:23 +03001420 device_for_each_child(&pdev->dev, &match, dss_add_child_component);
1421
1422 r = component_master_add_with_match(&pdev->dev, &dss_component_ops, match);
Laurent Pinchart215003b2018-02-11 15:07:44 +02001423 if (r)
1424 goto err_uninit_debugfs;
Tomi Valkeinen736e60d2015-06-04 15:22:23 +03001425
1426 return 0;
Laurent Pinchart215003b2018-02-11 15:07:44 +02001427
1428err_uninit_debugfs:
1429 dss_uninitialize_debugfs();
1430
1431err_pm_runtime_disable:
1432 pm_runtime_disable(&pdev->dev);
1433 dss_uninit_ports(pdev);
1434
1435err_uninit_plls:
1436 if (dss.video1_pll)
1437 dss_video_pll_uninit(dss.video1_pll);
1438 if (dss.video2_pll)
1439 dss_video_pll_uninit(dss.video2_pll);
1440
1441err_put_clocks:
1442 dss_put_clocks();
1443
1444 return r;
Tomi Valkeinen736e60d2015-06-04 15:22:23 +03001445}
1446
1447static int dss_remove(struct platform_device *pdev)
1448{
1449 component_master_del(&pdev->dev, &dss_component_ops);
Laurent Pinchart11765d12017-08-05 01:44:01 +03001450
1451 dss_uninitialize_debugfs();
1452
Laurent Pinchart215003b2018-02-11 15:07:44 +02001453 pm_runtime_disable(&pdev->dev);
1454
1455 dss_uninit_ports(pdev);
1456
1457 if (dss.video1_pll)
1458 dss_video_pll_uninit(dss.video1_pll);
1459
1460 if (dss.video2_pll)
1461 dss_video_pll_uninit(dss.video2_pll);
1462
1463 dss_put_clocks();
1464
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +00001465 return 0;
1466}
1467
Laurent Pinchart74592ee2017-08-05 01:44:02 +03001468static void dss_shutdown(struct platform_device *pdev)
1469{
1470 struct omap_dss_device *dssdev = NULL;
1471
1472 DSSDBG("shutdown\n");
1473
1474 for_each_dss_dev(dssdev) {
1475 if (!dssdev->driver)
1476 continue;
1477
1478 if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
1479 dssdev->driver->disable(dssdev);
1480 }
1481}
1482
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001483static int dss_runtime_suspend(struct device *dev)
1484{
1485 dss_save_context();
Tomi Valkeinena8081d32012-03-08 12:52:38 +02001486 dss_set_min_bus_tput(dev, 0);
Dave Gerlach5038bb82014-10-31 16:28:57 -05001487
1488 pinctrl_pm_select_sleep_state(dev);
1489
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001490 return 0;
1491}
1492
1493static int dss_runtime_resume(struct device *dev)
1494{
Tomi Valkeinena8081d32012-03-08 12:52:38 +02001495 int r;
Dave Gerlach5038bb82014-10-31 16:28:57 -05001496
1497 pinctrl_pm_select_default_state(dev);
1498
Tomi Valkeinena8081d32012-03-08 12:52:38 +02001499 /*
1500 * Set an arbitrarily high tput request to ensure OPP100.
1501 * What we should really do is to make a request to stay in OPP100,
1502 * without any tput requirements, but that is not currently possible
1503 * via the PM layer.
1504 */
1505
1506 r = dss_set_min_bus_tput(dev, 1000000000);
1507 if (r)
1508 return r;
1509
Tomi Valkeinen39020712011-05-26 14:54:05 +03001510 dss_restore_context();
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001511 return 0;
1512}
1513
1514static const struct dev_pm_ops dss_pm_ops = {
1515 .runtime_suspend = dss_runtime_suspend,
1516 .runtime_resume = dss_runtime_resume,
1517};
1518
Andrew F. Davisd66c36a2017-12-05 14:29:32 -06001519struct platform_driver omap_dsshw_driver = {
Tomi Valkeinen736e60d2015-06-04 15:22:23 +03001520 .probe = dss_probe,
1521 .remove = dss_remove,
Laurent Pinchart74592ee2017-08-05 01:44:02 +03001522 .shutdown = dss_shutdown,
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +00001523 .driver = {
1524 .name = "omapdss_dss",
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001525 .pm = &dss_pm_ops,
Tomi Valkeinen2ecef242013-12-16 15:13:24 +02001526 .of_match_table = dss_of_match,
Tomi Valkeinen422ccbd2014-10-16 09:54:25 +03001527 .suppress_bind_attrs = true,
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +00001528 },
1529};