blob: ca1e3b489540fe8171b7eaa1d6839963a2533e08 [file] [log] [blame]
Tomi Valkeinen553c48c2009-08-07 13:15:50 +03001/*
2 * linux/drivers/video/omap2/dss/dpi.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 "DPI"
24
25#include <linux/kernel.h>
Tomi Valkeinen553c48c2009-08-07 13:15:50 +030026#include <linux/delay.h>
Paul Gortmakera8a35932011-07-10 13:20:26 -040027#include <linux/export.h>
Tomi Valkeinen8a2cfea2010-02-04 17:03:41 +020028#include <linux/err.h>
Tomi Valkeinen553c48c2009-08-07 13:15:50 +030029#include <linux/errno.h>
Tomi Valkeinen8a2cfea2010-02-04 17:03:41 +020030#include <linux/platform_device.h>
31#include <linux/regulator/consumer.h>
Tomi Valkeinen13b1ba72012-09-28 10:03:03 +030032#include <linux/string.h>
Tomi Valkeinen2ecef242013-12-16 15:13:24 +020033#include <linux/of.h>
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +030034#include <linux/clk.h>
Laurent Pinchartd178e032017-08-05 01:44:12 +030035#include <linux/sys_soc.h>
Tomi Valkeinen553c48c2009-08-07 13:15:50 +030036
Peter Ujfalusi32043da2016-05-27 14:40:49 +030037#include "omapdss.h"
Tomi Valkeinen553c48c2009-08-07 13:15:50 +030038#include "dss.h"
39
Archit Taneja630d2d02014-05-30 16:26:22 +053040struct dpi_data {
Tomi Valkeinen00df43b2013-03-19 11:33:52 +020041 struct platform_device *pdev;
Laurent Pinchartb8dab2b2017-08-05 01:43:56 +030042 enum dss_model dss_model;
Tomi Valkeinen00df43b2013-03-19 11:33:52 +020043
Tomi Valkeinen8a2cfea2010-02-04 17:03:41 +020044 struct regulator *vdds_dsi_reg;
Tomi Valkeinen331e6072016-05-17 16:08:54 +030045 enum dss_clk_source clk_src;
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +030046 struct dss_pll *pll;
Archit Taneja5cf9a262012-06-29 14:19:13 +053047
Archit Tanejac8a5e4e2012-07-05 12:52:46 +053048 struct mutex lock;
49
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +030050 struct videomode vm;
Archit Taneja5cf9a262012-06-29 14:19:13 +053051 struct dss_lcd_mgr_config mgr_config;
Archit Tanejac6b393d2012-07-06 15:30:52 +053052 int data_lines;
Archit Taneja81b87f52012-09-26 16:30:49 +053053
Tomi Valkeinen1f68d9c2013-04-19 15:09:34 +030054 struct omap_dss_device output;
Tomi Valkeinen2ecef242013-12-16 15:13:24 +020055
56 bool port_initialized;
Archit Taneja630d2d02014-05-30 16:26:22 +053057};
58
Archit Taneja2ac6a1a2014-06-01 12:47:44 +053059static struct dpi_data *dpi_get_data_from_dssdev(struct omap_dss_device *dssdev)
60{
61 return container_of(dssdev, struct dpi_data, output);
62}
63
Tomi Valkeinen7bbdef22016-08-10 11:04:29 +030064static enum dss_clk_source dpi_get_clk_src_dra7xx(enum omap_channel channel)
65{
66 /*
67 * Possible clock sources:
68 * LCD1: FCK/PLL1_1/HDMI_PLL
69 * LCD2: FCK/PLL1_3/HDMI_PLL (DRA74x: PLL2_3)
70 * LCD3: FCK/PLL1_3/HDMI_PLL (DRA74x: PLL2_1)
71 */
72
73 switch (channel) {
74 case OMAP_DSS_CHANNEL_LCD:
75 {
76 if (dss_pll_find_by_src(DSS_CLK_SRC_PLL1_1))
77 return DSS_CLK_SRC_PLL1_1;
78 break;
79 }
80 case OMAP_DSS_CHANNEL_LCD2:
81 {
82 if (dss_pll_find_by_src(DSS_CLK_SRC_PLL1_3))
83 return DSS_CLK_SRC_PLL1_3;
84 if (dss_pll_find_by_src(DSS_CLK_SRC_PLL2_3))
85 return DSS_CLK_SRC_PLL2_3;
86 break;
87 }
88 case OMAP_DSS_CHANNEL_LCD3:
89 {
90 if (dss_pll_find_by_src(DSS_CLK_SRC_PLL2_1))
91 return DSS_CLK_SRC_PLL2_1;
92 if (dss_pll_find_by_src(DSS_CLK_SRC_PLL1_3))
93 return DSS_CLK_SRC_PLL1_3;
94 break;
95 }
96 default:
97 break;
98 }
99
100 return DSS_CLK_SRC_FCK;
101}
102
Laurent Pinchartb8dab2b2017-08-05 01:43:56 +0300103static enum dss_clk_source dpi_get_clk_src(struct dpi_data *dpi)
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530104{
Laurent Pinchartb8dab2b2017-08-05 01:43:56 +0300105 enum omap_channel channel = dpi->output.dispc_channel;
106
Tomi Valkeinenbd0f5cc2012-12-13 14:21:30 +0200107 /*
108 * XXX we can't currently use DSI PLL for DPI with OMAP3, as the DSI PLL
109 * would also be used for DISPC fclk. Meaning, when the DPI output is
110 * disabled, DISPC clock will be disabled, and TV out will stop.
111 */
Laurent Pinchartb8dab2b2017-08-05 01:43:56 +0300112 switch (dpi->dss_model) {
113 case DSS_MODEL_OMAP2:
114 case DSS_MODEL_OMAP3:
Tomi Valkeinen331e6072016-05-17 16:08:54 +0300115 return DSS_CLK_SRC_FCK;
Tomi Valkeinenbd0f5cc2012-12-13 14:21:30 +0200116
Laurent Pinchartb8dab2b2017-08-05 01:43:56 +0300117 case DSS_MODEL_OMAP4:
Tomi Valkeinenf8ad9842013-03-11 13:57:38 +0200118 switch (channel) {
119 case OMAP_DSS_CHANNEL_LCD:
Tomi Valkeinen331e6072016-05-17 16:08:54 +0300120 return DSS_CLK_SRC_PLL1_1;
Tomi Valkeinenf8ad9842013-03-11 13:57:38 +0200121 case OMAP_DSS_CHANNEL_LCD2:
Tomi Valkeinen331e6072016-05-17 16:08:54 +0300122 return DSS_CLK_SRC_PLL2_1;
Tomi Valkeinenf8ad9842013-03-11 13:57:38 +0200123 default:
Tomi Valkeinen331e6072016-05-17 16:08:54 +0300124 return DSS_CLK_SRC_FCK;
Tomi Valkeinenf8ad9842013-03-11 13:57:38 +0200125 }
126
Laurent Pinchartb8dab2b2017-08-05 01:43:56 +0300127 case DSS_MODEL_OMAP5:
Tomi Valkeinenf8ad9842013-03-11 13:57:38 +0200128 switch (channel) {
129 case OMAP_DSS_CHANNEL_LCD:
Tomi Valkeinen331e6072016-05-17 16:08:54 +0300130 return DSS_CLK_SRC_PLL1_1;
Tomi Valkeinenf8ad9842013-03-11 13:57:38 +0200131 case OMAP_DSS_CHANNEL_LCD3:
Tomi Valkeinen331e6072016-05-17 16:08:54 +0300132 return DSS_CLK_SRC_PLL2_1;
133 case OMAP_DSS_CHANNEL_LCD2:
Tomi Valkeinenf8ad9842013-03-11 13:57:38 +0200134 default:
Tomi Valkeinen331e6072016-05-17 16:08:54 +0300135 return DSS_CLK_SRC_FCK;
Tomi Valkeinenf8ad9842013-03-11 13:57:38 +0200136 }
137
Laurent Pinchartb8dab2b2017-08-05 01:43:56 +0300138 case DSS_MODEL_DRA7:
Tomi Valkeinen7bbdef22016-08-10 11:04:29 +0300139 return dpi_get_clk_src_dra7xx(channel);
Tomi Valkeinena2408152014-12-31 11:26:06 +0200140
Tomi Valkeinen0e8276e2012-10-22 16:12:58 +0300141 default:
Tomi Valkeinen3b63ca72016-05-17 14:01:10 +0300142 return DSS_CLK_SRC_FCK;
Tomi Valkeinen0e8276e2012-10-22 16:12:58 +0300143 }
Archit Taneja7636b3b2011-04-12 13:52:26 +0530144}
145
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200146struct dpi_clk_calc_ctx {
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300147 struct dss_pll *pll;
Tomi Valkeinen13ece4d2016-05-17 16:20:07 +0300148 unsigned clkout_idx;
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200149
150 /* inputs */
151
152 unsigned long pck_min, pck_max;
153
154 /* outputs */
155
Tomi Valkeinen31dca072016-05-18 12:22:32 +0300156 struct dss_pll_clock_info pll_cinfo;
Tomi Valkeinenc56812f2014-01-28 08:50:47 +0200157 unsigned long fck;
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200158 struct dispc_clock_info dispc_cinfo;
159};
160
161static bool dpi_calc_dispc_cb(int lckd, int pckd, unsigned long lck,
162 unsigned long pck, void *data)
163{
164 struct dpi_clk_calc_ctx *ctx = data;
165
166 /*
167 * Odd dividers give us uneven duty cycle, causing problem when level
168 * shifted. So skip all odd dividers when the pixel clock is on the
169 * higher side.
170 */
Tomi Valkeinen72e55122013-06-12 09:44:52 +0300171 if (ctx->pck_min >= 100000000) {
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200172 if (lckd > 1 && lckd % 2 != 0)
173 return false;
174
175 if (pckd > 1 && pckd % 2 != 0)
176 return false;
177 }
178
179 ctx->dispc_cinfo.lck_div = lckd;
180 ctx->dispc_cinfo.pck_div = pckd;
181 ctx->dispc_cinfo.lck = lck;
182 ctx->dispc_cinfo.pck = pck;
183
184 return true;
185}
186
187
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300188static bool dpi_calc_hsdiv_cb(int m_dispc, unsigned long dispc,
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200189 void *data)
190{
191 struct dpi_clk_calc_ctx *ctx = data;
192
Tomi Valkeinen31dca072016-05-18 12:22:32 +0300193 ctx->pll_cinfo.mX[ctx->clkout_idx] = m_dispc;
194 ctx->pll_cinfo.clkout[ctx->clkout_idx] = dispc;
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200195
196 return dispc_div_calc(dispc, ctx->pck_min, ctx->pck_max,
197 dpi_calc_dispc_cb, ctx);
198}
199
200
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300201static bool dpi_calc_pll_cb(int n, int m, unsigned long fint,
202 unsigned long clkdco,
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200203 void *data)
204{
205 struct dpi_clk_calc_ctx *ctx = data;
206
Tomi Valkeinen31dca072016-05-18 12:22:32 +0300207 ctx->pll_cinfo.n = n;
208 ctx->pll_cinfo.m = m;
209 ctx->pll_cinfo.fint = fint;
210 ctx->pll_cinfo.clkdco = clkdco;
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200211
Tomi Valkeinencd0715f2016-05-17 21:23:37 +0300212 return dss_pll_hsdiv_calc_a(ctx->pll, clkdco,
Laurent Pinchart9f0fbae2017-08-05 01:44:17 +0300213 ctx->pck_min, dss_get_max_fck_rate(),
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300214 dpi_calc_hsdiv_cb, ctx);
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200215}
216
Tomi Valkeinend0f58bd2013-10-31 14:44:23 +0200217static bool dpi_calc_dss_cb(unsigned long fck, void *data)
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200218{
219 struct dpi_clk_calc_ctx *ctx = data;
220
Tomi Valkeinend0f58bd2013-10-31 14:44:23 +0200221 ctx->fck = fck;
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200222
223 return dispc_div_calc(fck, ctx->pck_min, ctx->pck_max,
224 dpi_calc_dispc_cb, ctx);
225}
226
Tomi Valkeinen31dca072016-05-18 12:22:32 +0300227static bool dpi_pll_clk_calc(struct dpi_data *dpi, unsigned long pck,
Archit Taneja630d2d02014-05-30 16:26:22 +0530228 struct dpi_clk_calc_ctx *ctx)
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200229{
230 unsigned long clkin;
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200231
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200232 memset(ctx, 0, sizeof(*ctx));
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300233 ctx->pll = dpi->pll;
Tomi Valkeinen13ece4d2016-05-17 16:20:07 +0300234 ctx->clkout_idx = dss_pll_get_clkout_idx_for_src(dpi->clk_src);
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200235
Tomi Valkeinen683cd862016-05-18 12:06:49 +0300236 clkin = clk_get_rate(dpi->pll->clkin);
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200237
Tomi Valkeinen683cd862016-05-18 12:06:49 +0300238 if (dpi->pll->hw->type == DSS_PLL_TYPE_A) {
239 unsigned long pll_min, pll_max;
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300240
Tomi Valkeinen683cd862016-05-18 12:06:49 +0300241 ctx->pck_min = pck - 1000;
242 ctx->pck_max = pck + 1000;
243
244 pll_min = 0;
245 pll_max = 0;
246
247 return dss_pll_calc_a(ctx->pll, clkin,
248 pll_min, pll_max,
249 dpi_calc_pll_cb, ctx);
250 } else { /* DSS_PLL_TYPE_B */
Tomi Valkeinen31dca072016-05-18 12:22:32 +0300251 dss_pll_calc_b(dpi->pll, clkin, pck, &ctx->pll_cinfo);
Tomi Valkeinen683cd862016-05-18 12:06:49 +0300252
253 ctx->dispc_cinfo.lck_div = 1;
254 ctx->dispc_cinfo.pck_div = 1;
Tomi Valkeinen31dca072016-05-18 12:22:32 +0300255 ctx->dispc_cinfo.lck = ctx->pll_cinfo.clkout[0];
Tomi Valkeinen683cd862016-05-18 12:06:49 +0300256 ctx->dispc_cinfo.pck = ctx->dispc_cinfo.lck;
257
258 return true;
259 }
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200260}
261
262static bool dpi_dss_clk_calc(unsigned long pck, struct dpi_clk_calc_ctx *ctx)
263{
264 int i;
265
266 /*
267 * DSS fck gives us very few possibilities, so finding a good pixel
268 * clock may not be possible. We try multiple times to find the clock,
269 * each time widening the pixel clock range we look for, up to
Tomi Valkeinen2c6360f2013-04-10 14:54:54 +0300270 * +/- ~15MHz.
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200271 */
272
Tomi Valkeinen2c6360f2013-04-10 14:54:54 +0300273 for (i = 0; i < 25; ++i) {
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200274 bool ok;
275
276 memset(ctx, 0, sizeof(*ctx));
277 if (pck > 1000 * i * i * i)
278 ctx->pck_min = max(pck - 1000 * i * i * i, 0lu);
279 else
280 ctx->pck_min = 0;
281 ctx->pck_max = pck + 1000 * i * i * i;
282
Tomi Valkeinen688af022013-10-31 16:41:57 +0200283 ok = dss_div_calc(pck, ctx->pck_min, dpi_calc_dss_cb, ctx);
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200284 if (ok)
285 return ok;
286 }
287
288 return false;
289}
290
291
292
Tomi Valkeinen31dca072016-05-18 12:22:32 +0300293static int dpi_set_pll_clk(struct dpi_data *dpi, enum omap_channel channel,
Sumit Semwalff1b2cd2010-12-02 11:27:11 +0000294 unsigned long pck_req, unsigned long *fck, int *lck_div,
295 int *pck_div)
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300296{
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200297 struct dpi_clk_calc_ctx ctx;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300298 int r;
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200299 bool ok;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300300
Tomi Valkeinen31dca072016-05-18 12:22:32 +0300301 ok = dpi_pll_clk_calc(dpi, pck_req, &ctx);
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200302 if (!ok)
303 return -EINVAL;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300304
Tomi Valkeinen31dca072016-05-18 12:22:32 +0300305 r = dss_pll_set_config(dpi->pll, &ctx.pll_cinfo);
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300306 if (r)
307 return r;
308
Tomi Valkeinen331e6072016-05-17 16:08:54 +0300309 dss_select_lcd_clk_source(channel, dpi->clk_src);
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300310
Archit Taneja630d2d02014-05-30 16:26:22 +0530311 dpi->mgr_config.clock_info = ctx.dispc_cinfo;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300312
Tomi Valkeinen31dca072016-05-18 12:22:32 +0300313 *fck = ctx.pll_cinfo.clkout[ctx.clkout_idx];
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200314 *lck_div = ctx.dispc_cinfo.lck_div;
315 *pck_div = ctx.dispc_cinfo.pck_div;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300316
317 return 0;
318}
Archit Taneja7636b3b2011-04-12 13:52:26 +0530319
Archit Taneja630d2d02014-05-30 16:26:22 +0530320static int dpi_set_dispc_clk(struct dpi_data *dpi, unsigned long pck_req,
321 unsigned long *fck, int *lck_div, int *pck_div)
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300322{
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200323 struct dpi_clk_calc_ctx ctx;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300324 int r;
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200325 bool ok;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300326
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200327 ok = dpi_dss_clk_calc(pck_req, &ctx);
328 if (!ok)
329 return -EINVAL;
330
Tomi Valkeinend0f58bd2013-10-31 14:44:23 +0200331 r = dss_set_fck_rate(ctx.fck);
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300332 if (r)
333 return r;
334
Archit Taneja630d2d02014-05-30 16:26:22 +0530335 dpi->mgr_config.clock_info = ctx.dispc_cinfo;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300336
Tomi Valkeinend0f58bd2013-10-31 14:44:23 +0200337 *fck = ctx.fck;
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200338 *lck_div = ctx.dispc_cinfo.lck_div;
339 *pck_div = ctx.dispc_cinfo.pck_div;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300340
341 return 0;
342}
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300343
Archit Taneja630d2d02014-05-30 16:26:22 +0530344static int dpi_set_mode(struct dpi_data *dpi)
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300345{
Archit Taneja630d2d02014-05-30 16:26:22 +0530346 struct omap_dss_device *out = &dpi->output;
Tomi Valkeinena070ba62015-11-05 09:52:00 +0200347 enum omap_channel channel = out->dispc_channel;
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +0300348 struct videomode *vm = &dpi->vm;
Archit Taneja7636b3b2011-04-12 13:52:26 +0530349 int lck_div = 0, pck_div = 0;
350 unsigned long fck = 0;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300351 unsigned long pck;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300352 int r = 0;
353
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300354 if (dpi->pll)
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +0300355 r = dpi_set_pll_clk(dpi, channel, vm->pixelclock, &fck,
Archit Taneja6d523e72012-06-21 09:33:55 +0530356 &lck_div, &pck_div);
Archit Taneja7636b3b2011-04-12 13:52:26 +0530357 else
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +0300358 r = dpi_set_dispc_clk(dpi, vm->pixelclock, &fck,
Archit Taneja6d523e72012-06-21 09:33:55 +0530359 &lck_div, &pck_div);
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300360 if (r)
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300361 return r;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300362
Tomi Valkeinend8d789412013-04-10 14:12:14 +0300363 pck = fck / lck_div / pck_div;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300364
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +0300365 if (pck != vm->pixelclock) {
Peter Ujfalusi7aa91e72016-09-22 14:07:02 +0300366 DSSWARN("Could not find exact pixel clock. Requested %lu Hz, got %lu Hz\n",
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +0300367 vm->pixelclock, pck);
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300368
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +0300369 vm->pixelclock = pck;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300370 }
371
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +0300372 dss_mgr_set_timings(channel, vm);
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300373
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300374 return 0;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300375}
376
Archit Taneja630d2d02014-05-30 16:26:22 +0530377static void dpi_config_lcd_manager(struct dpi_data *dpi)
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300378{
Archit Taneja630d2d02014-05-30 16:26:22 +0530379 struct omap_dss_device *out = &dpi->output;
Tomi Valkeinena070ba62015-11-05 09:52:00 +0200380 enum omap_channel channel = out->dispc_channel;
Archit Taneja569969d2011-08-22 17:41:57 +0530381
Archit Taneja630d2d02014-05-30 16:26:22 +0530382 dpi->mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS;
Archit Taneja5cf9a262012-06-29 14:19:13 +0530383
Archit Taneja630d2d02014-05-30 16:26:22 +0530384 dpi->mgr_config.stallmode = false;
385 dpi->mgr_config.fifohandcheck = false;
Archit Taneja5cf9a262012-06-29 14:19:13 +0530386
Archit Taneja630d2d02014-05-30 16:26:22 +0530387 dpi->mgr_config.video_port_width = dpi->data_lines;
Archit Taneja5cf9a262012-06-29 14:19:13 +0530388
Archit Taneja630d2d02014-05-30 16:26:22 +0530389 dpi->mgr_config.lcden_sig_polarity = 0;
390
Tomi Valkeinena070ba62015-11-05 09:52:00 +0200391 dss_mgr_set_lcd_config(channel, &dpi->mgr_config);
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300392}
393
Tomi Valkeinen86a3efe2013-05-15 10:40:15 +0300394static int dpi_display_enable(struct omap_dss_device *dssdev)
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300395{
Archit Taneja2ac6a1a2014-06-01 12:47:44 +0530396 struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
Archit Taneja630d2d02014-05-30 16:26:22 +0530397 struct omap_dss_device *out = &dpi->output;
Tomi Valkeinena070ba62015-11-05 09:52:00 +0200398 enum omap_channel channel = out->dispc_channel;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300399 int r;
400
Archit Taneja630d2d02014-05-30 16:26:22 +0530401 mutex_lock(&dpi->lock);
Archit Tanejac8a5e4e2012-07-05 12:52:46 +0530402
Tomi Valkeinenf1504ad2015-11-05 09:34:51 +0200403 if (!out->dispc_channel_connected) {
Archit Taneja5d512fc2012-09-07 17:53:38 +0530404 DSSERR("failed to enable display: no output/manager\n");
Archit Tanejac8a5e4e2012-07-05 12:52:46 +0530405 r = -ENODEV;
Archit Taneja5d512fc2012-09-07 17:53:38 +0530406 goto err_no_out_mgr;
Tomi Valkeinen05e1d602011-06-23 16:38:21 +0300407 }
408
Laurent Pincharte65837b2017-08-05 01:43:49 +0300409 if (dpi->vdds_dsi_reg) {
Archit Taneja630d2d02014-05-30 16:26:22 +0530410 r = regulator_enable(dpi->vdds_dsi_reg);
Tomi Valkeinen8a2cfea2010-02-04 17:03:41 +0200411 if (r)
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300412 goto err_reg_enable;
Tomi Valkeinen8a2cfea2010-02-04 17:03:41 +0200413 }
414
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300415 r = dispc_runtime_get();
416 if (r)
417 goto err_get_dispc;
418
Tomi Valkeinena070ba62015-11-05 09:52:00 +0200419 r = dss_dpi_select_source(out->port_num, channel);
Tomi Valkeinende09e452012-09-21 12:09:54 +0300420 if (r)
421 goto err_src_sel;
422
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300423 if (dpi->pll) {
424 r = dss_pll_enable(dpi->pll);
Archit Taneja7636b3b2011-04-12 13:52:26 +0530425 if (r)
Tomi Valkeinen31dca072016-05-18 12:22:32 +0300426 goto err_pll_init;
Archit Taneja7636b3b2011-04-12 13:52:26 +0530427 }
428
Archit Taneja630d2d02014-05-30 16:26:22 +0530429 r = dpi_set_mode(dpi);
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300430 if (r)
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300431 goto err_set_mode;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300432
Archit Taneja630d2d02014-05-30 16:26:22 +0530433 dpi_config_lcd_manager(dpi);
Archit Taneja5cf9a262012-06-29 14:19:13 +0530434
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300435 mdelay(2);
436
Tomi Valkeinena070ba62015-11-05 09:52:00 +0200437 r = dss_mgr_enable(channel);
Tomi Valkeinen33ca2372011-11-21 13:42:58 +0200438 if (r)
439 goto err_mgr_enable;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300440
Archit Taneja630d2d02014-05-30 16:26:22 +0530441 mutex_unlock(&dpi->lock);
Archit Tanejac8a5e4e2012-07-05 12:52:46 +0530442
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300443 return 0;
444
Tomi Valkeinen33ca2372011-11-21 13:42:58 +0200445err_mgr_enable:
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300446err_set_mode:
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300447 if (dpi->pll)
448 dss_pll_disable(dpi->pll);
Tomi Valkeinen31dca072016-05-18 12:22:32 +0300449err_pll_init:
Tomi Valkeinende09e452012-09-21 12:09:54 +0300450err_src_sel:
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300451 dispc_runtime_put();
452err_get_dispc:
Laurent Pincharte65837b2017-08-05 01:43:49 +0300453 if (dpi->vdds_dsi_reg)
Archit Taneja630d2d02014-05-30 16:26:22 +0530454 regulator_disable(dpi->vdds_dsi_reg);
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300455err_reg_enable:
Archit Taneja5d512fc2012-09-07 17:53:38 +0530456err_no_out_mgr:
Archit Taneja630d2d02014-05-30 16:26:22 +0530457 mutex_unlock(&dpi->lock);
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300458 return r;
459}
460
Tomi Valkeinen86a3efe2013-05-15 10:40:15 +0300461static void dpi_display_disable(struct omap_dss_device *dssdev)
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300462{
Archit Taneja2ac6a1a2014-06-01 12:47:44 +0530463 struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
Tomi Valkeinena070ba62015-11-05 09:52:00 +0200464 enum omap_channel channel = dpi->output.dispc_channel;
Archit Taneja5d512fc2012-09-07 17:53:38 +0530465
Archit Taneja630d2d02014-05-30 16:26:22 +0530466 mutex_lock(&dpi->lock);
Archit Tanejac8a5e4e2012-07-05 12:52:46 +0530467
Tomi Valkeinena070ba62015-11-05 09:52:00 +0200468 dss_mgr_disable(channel);
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300469
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300470 if (dpi->pll) {
Tomi Valkeinen3b63ca72016-05-17 14:01:10 +0300471 dss_select_lcd_clk_source(channel, DSS_CLK_SRC_FCK);
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300472 dss_pll_disable(dpi->pll);
Archit Taneja7636b3b2011-04-12 13:52:26 +0530473 }
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300474
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300475 dispc_runtime_put();
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300476
Laurent Pincharte65837b2017-08-05 01:43:49 +0300477 if (dpi->vdds_dsi_reg)
Archit Taneja630d2d02014-05-30 16:26:22 +0530478 regulator_disable(dpi->vdds_dsi_reg);
Tomi Valkeinen8a2cfea2010-02-04 17:03:41 +0200479
Archit Taneja630d2d02014-05-30 16:26:22 +0530480 mutex_unlock(&dpi->lock);
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300481}
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300482
Tomi Valkeinen86a3efe2013-05-15 10:40:15 +0300483static void dpi_set_timings(struct omap_dss_device *dssdev,
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +0300484 struct videomode *vm)
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300485{
Archit Taneja2ac6a1a2014-06-01 12:47:44 +0530486 struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
Archit Taneja630d2d02014-05-30 16:26:22 +0530487
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300488 DSSDBG("dpi_set_timings\n");
Archit Tanejac8a5e4e2012-07-05 12:52:46 +0530489
Archit Taneja630d2d02014-05-30 16:26:22 +0530490 mutex_lock(&dpi->lock);
Archit Tanejac8a5e4e2012-07-05 12:52:46 +0530491
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +0300492 dpi->vm = *vm;
Archit Tanejac4991442012-08-08 14:28:54 +0530493
Archit Taneja630d2d02014-05-30 16:26:22 +0530494 mutex_unlock(&dpi->lock);
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300495}
496
Tomi Valkeinen0b24edb2013-05-24 13:18:52 +0300497static void dpi_get_timings(struct omap_dss_device *dssdev,
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +0300498 struct videomode *vm)
Tomi Valkeinen0b24edb2013-05-24 13:18:52 +0300499{
Archit Taneja2ac6a1a2014-06-01 12:47:44 +0530500 struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
Tomi Valkeinen0b24edb2013-05-24 13:18:52 +0300501
Archit Taneja630d2d02014-05-30 16:26:22 +0530502 mutex_lock(&dpi->lock);
Tomi Valkeinen0b24edb2013-05-24 13:18:52 +0300503
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +0300504 *vm = dpi->vm;
Archit Taneja630d2d02014-05-30 16:26:22 +0530505
506 mutex_unlock(&dpi->lock);
Tomi Valkeinen0b24edb2013-05-24 13:18:52 +0300507}
508
Tomi Valkeinen86a3efe2013-05-15 10:40:15 +0300509static int dpi_check_timings(struct omap_dss_device *dssdev,
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +0300510 struct videomode *vm)
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300511{
Archit Taneja2ac6a1a2014-06-01 12:47:44 +0530512 struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
Tomi Valkeinena070ba62015-11-05 09:52:00 +0200513 enum omap_channel channel = dpi->output.dispc_channel;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300514 int lck_div, pck_div;
515 unsigned long fck;
516 unsigned long pck;
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200517 struct dpi_clk_calc_ctx ctx;
518 bool ok;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300519
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +0300520 if (vm->hactive % 8 != 0)
Tomi Valkeinen2158f2c2016-01-05 11:43:18 +0200521 return -EINVAL;
522
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +0300523 if (!dispc_mgr_timings_ok(channel, vm))
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300524 return -EINVAL;
525
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +0300526 if (vm->pixelclock == 0)
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300527 return -EINVAL;
528
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300529 if (dpi->pll) {
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +0300530 ok = dpi_pll_clk_calc(dpi, vm->pixelclock, &ctx);
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200531 if (!ok)
532 return -EINVAL;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300533
Tomi Valkeinen31dca072016-05-18 12:22:32 +0300534 fck = ctx.pll_cinfo.clkout[ctx.clkout_idx];
Archit Taneja7636b3b2011-04-12 13:52:26 +0530535 } else {
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +0300536 ok = dpi_dss_clk_calc(vm->pixelclock, &ctx);
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200537 if (!ok)
538 return -EINVAL;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300539
Tomi Valkeinend0f58bd2013-10-31 14:44:23 +0200540 fck = ctx.fck;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300541 }
Archit Taneja7636b3b2011-04-12 13:52:26 +0530542
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200543 lck_div = ctx.dispc_cinfo.lck_div;
544 pck_div = ctx.dispc_cinfo.pck_div;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300545
Tomi Valkeinend8d789412013-04-10 14:12:14 +0300546 pck = fck / lck_div / pck_div;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300547
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +0300548 vm->pixelclock = pck;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300549
550 return 0;
551}
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300552
Tomi Valkeinen31dca072016-05-18 12:22:32 +0300553static int dpi_verify_pll(struct dss_pll *pll)
Tomi Valkeinen60616752012-10-30 12:57:43 +0200554{
555 int r;
556
557 /* do initial setup with the PLL to see if it is operational */
558
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300559 r = dss_pll_enable(pll);
Tomi Valkeinen60616752012-10-30 12:57:43 +0200560 if (r)
561 return r;
562
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300563 dss_pll_disable(pll);
Tomi Valkeinen60616752012-10-30 12:57:43 +0200564
565 return 0;
566}
567
Laurent Pinchartd178e032017-08-05 01:44:12 +0300568static const struct soc_device_attribute dpi_soc_devices[] = {
Laurent Pinchartbf25dac2017-11-16 09:50:19 +0100569 { .machine = "OMAP3[456]*" },
570 { .machine = "[AD]M37*" },
Laurent Pinchartd178e032017-08-05 01:44:12 +0300571 { /* sentinel */ }
572};
573
Archit Taneja630d2d02014-05-30 16:26:22 +0530574static int dpi_init_regulator(struct dpi_data *dpi)
Tomi Valkeinen2795f642013-04-19 16:52:27 +0300575{
576 struct regulator *vdds_dsi;
577
Laurent Pinchartd178e032017-08-05 01:44:12 +0300578 /*
579 * The DPI uses the DSI VDDS on OMAP34xx, OMAP35xx, OMAP36xx, AM37xx and
580 * DM37xx only.
581 */
582 if (!soc_device_match(dpi_soc_devices))
Tomi Valkeinen2795f642013-04-19 16:52:27 +0300583 return 0;
584
Archit Taneja630d2d02014-05-30 16:26:22 +0530585 if (dpi->vdds_dsi_reg)
Tomi Valkeinen2795f642013-04-19 16:52:27 +0300586 return 0;
587
Archit Taneja630d2d02014-05-30 16:26:22 +0530588 vdds_dsi = devm_regulator_get(&dpi->pdev->dev, "vdds_dsi");
Tomi Valkeinen2795f642013-04-19 16:52:27 +0300589 if (IS_ERR(vdds_dsi)) {
Tomi Valkeinen40359a92013-12-19 16:15:34 +0200590 if (PTR_ERR(vdds_dsi) != -EPROBE_DEFER)
591 DSSERR("can't get VDDS_DSI regulator\n");
Tomi Valkeinen4123de22013-08-29 10:06:55 +0300592 return PTR_ERR(vdds_dsi);
Tomi Valkeinen2795f642013-04-19 16:52:27 +0300593 }
594
Archit Taneja630d2d02014-05-30 16:26:22 +0530595 dpi->vdds_dsi_reg = vdds_dsi;
Tomi Valkeinen2795f642013-04-19 16:52:27 +0300596
597 return 0;
598}
599
Archit Taneja630d2d02014-05-30 16:26:22 +0530600static void dpi_init_pll(struct dpi_data *dpi)
Tomi Valkeinen2795f642013-04-19 16:52:27 +0300601{
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300602 struct dss_pll *pll;
Tomi Valkeinen2795f642013-04-19 16:52:27 +0300603
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300604 if (dpi->pll)
Tomi Valkeinen2795f642013-04-19 16:52:27 +0300605 return;
606
Laurent Pinchartb8dab2b2017-08-05 01:43:56 +0300607 dpi->clk_src = dpi_get_clk_src(dpi);
Tomi Valkeinen331e6072016-05-17 16:08:54 +0300608
609 pll = dss_pll_find_by_src(dpi->clk_src);
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300610 if (!pll)
Tomi Valkeinen2795f642013-04-19 16:52:27 +0300611 return;
612
Tomi Valkeinen31dca072016-05-18 12:22:32 +0300613 if (dpi_verify_pll(pll)) {
614 DSSWARN("PLL not operational\n");
Tomi Valkeinen2795f642013-04-19 16:52:27 +0300615 return;
616 }
617
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300618 dpi->pll = pll;
Tomi Valkeinen2795f642013-04-19 16:52:27 +0300619}
620
Tomi Valkeinen2eea5ae2013-02-13 11:23:54 +0200621/*
622 * Return a hardcoded channel for the DPI output. This should work for
623 * current use cases, but this can be later expanded to either resolve
624 * the channel in some more dynamic manner, or get the channel as a user
625 * parameter.
626 */
Laurent Pinchartb8dab2b2017-08-05 01:43:56 +0300627static enum omap_channel dpi_get_channel(struct dpi_data *dpi, int port_num)
Tomi Valkeinen2eea5ae2013-02-13 11:23:54 +0200628{
Laurent Pinchartb8dab2b2017-08-05 01:43:56 +0300629 switch (dpi->dss_model) {
630 case DSS_MODEL_OMAP2:
631 case DSS_MODEL_OMAP3:
Tomi Valkeinen2eea5ae2013-02-13 11:23:54 +0200632 return OMAP_DSS_CHANNEL_LCD;
633
Laurent Pinchartb8dab2b2017-08-05 01:43:56 +0300634 case DSS_MODEL_DRA7:
Tomi Valkeinena2408152014-12-31 11:26:06 +0200635 switch (port_num) {
636 case 2:
637 return OMAP_DSS_CHANNEL_LCD3;
638 case 1:
639 return OMAP_DSS_CHANNEL_LCD2;
640 case 0:
641 default:
642 return OMAP_DSS_CHANNEL_LCD;
643 }
644
Laurent Pinchartb8dab2b2017-08-05 01:43:56 +0300645 case DSS_MODEL_OMAP4:
Tomi Valkeinen2eea5ae2013-02-13 11:23:54 +0200646 return OMAP_DSS_CHANNEL_LCD2;
647
Laurent Pinchartb8dab2b2017-08-05 01:43:56 +0300648 case DSS_MODEL_OMAP5:
Tomi Valkeinen2eea5ae2013-02-13 11:23:54 +0200649 return OMAP_DSS_CHANNEL_LCD3;
650
651 default:
652 DSSWARN("unsupported DSS version\n");
653 return OMAP_DSS_CHANNEL_LCD;
654 }
655}
656
Tomi Valkeinen0b24edb2013-05-24 13:18:52 +0300657static int dpi_connect(struct omap_dss_device *dssdev,
658 struct omap_dss_device *dst)
659{
Archit Taneja2ac6a1a2014-06-01 12:47:44 +0530660 struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
Tomi Valkeinena070ba62015-11-05 09:52:00 +0200661 enum omap_channel channel = dpi->output.dispc_channel;
Tomi Valkeinen0b24edb2013-05-24 13:18:52 +0300662 int r;
663
Archit Taneja630d2d02014-05-30 16:26:22 +0530664 r = dpi_init_regulator(dpi);
Tomi Valkeinen0b24edb2013-05-24 13:18:52 +0300665 if (r)
666 return r;
667
Archit Taneja630d2d02014-05-30 16:26:22 +0530668 dpi_init_pll(dpi);
Tomi Valkeinen0b24edb2013-05-24 13:18:52 +0300669
Tomi Valkeinena070ba62015-11-05 09:52:00 +0200670 r = dss_mgr_connect(channel, dssdev);
Tomi Valkeinen0b24edb2013-05-24 13:18:52 +0300671 if (r)
672 return r;
673
674 r = omapdss_output_set_device(dssdev, dst);
675 if (r) {
676 DSSERR("failed to connect output to new device: %s\n",
677 dst->name);
Tomi Valkeinena070ba62015-11-05 09:52:00 +0200678 dss_mgr_disconnect(channel, dssdev);
Tomi Valkeinen0b24edb2013-05-24 13:18:52 +0300679 return r;
680 }
681
682 return 0;
683}
684
685static void dpi_disconnect(struct omap_dss_device *dssdev,
686 struct omap_dss_device *dst)
687{
Tomi Valkeinena070ba62015-11-05 09:52:00 +0200688 struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
689 enum omap_channel channel = dpi->output.dispc_channel;
690
Tomi Valkeinen9560dc102013-07-24 13:06:54 +0300691 WARN_ON(dst != dssdev->dst);
Tomi Valkeinen0b24edb2013-05-24 13:18:52 +0300692
Tomi Valkeinen9560dc102013-07-24 13:06:54 +0300693 if (dst != dssdev->dst)
Tomi Valkeinen0b24edb2013-05-24 13:18:52 +0300694 return;
695
696 omapdss_output_unset_device(dssdev);
697
Tomi Valkeinena070ba62015-11-05 09:52:00 +0200698 dss_mgr_disconnect(channel, dssdev);
Tomi Valkeinen0b24edb2013-05-24 13:18:52 +0300699}
700
701static const struct omapdss_dpi_ops dpi_ops = {
702 .connect = dpi_connect,
703 .disconnect = dpi_disconnect,
704
Tomi Valkeinen86a3efe2013-05-15 10:40:15 +0300705 .enable = dpi_display_enable,
706 .disable = dpi_display_disable,
Tomi Valkeinen0b24edb2013-05-24 13:18:52 +0300707
708 .check_timings = dpi_check_timings,
Tomi Valkeinen86a3efe2013-05-15 10:40:15 +0300709 .set_timings = dpi_set_timings,
Tomi Valkeinen0b24edb2013-05-24 13:18:52 +0300710 .get_timings = dpi_get_timings,
Tomi Valkeinen0b24edb2013-05-24 13:18:52 +0300711};
712
Laurent Pinchartb8dab2b2017-08-05 01:43:56 +0300713static void dpi_init_output_port(struct dpi_data *dpi, struct device_node *port)
Archit Taneja80eb6752014-06-02 14:11:51 +0530714{
Archit Taneja80eb6752014-06-02 14:11:51 +0530715 struct omap_dss_device *out = &dpi->output;
Archit Tanejaf7e38fe2014-05-06 17:07:39 +0530716 int r;
717 u32 port_num;
718
719 r = of_property_read_u32(port, "reg", &port_num);
720 if (r)
721 port_num = 0;
722
723 switch (port_num) {
724 case 2:
725 out->name = "dpi.2";
726 break;
727 case 1:
728 out->name = "dpi.1";
729 break;
730 case 0:
731 default:
732 out->name = "dpi.0";
733 break;
734 }
Archit Taneja80eb6752014-06-02 14:11:51 +0530735
Laurent Pinchartb8dab2b2017-08-05 01:43:56 +0300736 out->dev = &dpi->pdev->dev;
Archit Taneja80eb6752014-06-02 14:11:51 +0530737 out->id = OMAP_DSS_OUTPUT_DPI;
738 out->output_type = OMAP_DISPLAY_TYPE_DPI;
Laurent Pinchartb8dab2b2017-08-05 01:43:56 +0300739 out->dispc_channel = dpi_get_channel(dpi, port_num);
Archit Tanejaf7e38fe2014-05-06 17:07:39 +0530740 out->port_num = port_num;
Archit Taneja80eb6752014-06-02 14:11:51 +0530741 out->ops.dpi = &dpi_ops;
742 out->owner = THIS_MODULE;
743
744 omapdss_register_output(out);
745}
746
Tomi Valkeinenede92692015-06-04 14:12:16 +0300747static void dpi_uninit_output_port(struct device_node *port)
Archit Taneja80eb6752014-06-02 14:11:51 +0530748{
749 struct dpi_data *dpi = port->data;
750 struct omap_dss_device *out = &dpi->output;
751
752 omapdss_unregister_output(out);
753}
754
Laurent Pinchartb8dab2b2017-08-05 01:43:56 +0300755int dpi_init_port(struct platform_device *pdev, struct device_node *port,
756 enum dss_model dss_model)
Tomi Valkeinen2ecef242013-12-16 15:13:24 +0200757{
Archit Taneja2ac6a1a2014-06-01 12:47:44 +0530758 struct dpi_data *dpi;
Tomi Valkeinen2ecef242013-12-16 15:13:24 +0200759 struct device_node *ep;
760 u32 datalines;
761 int r;
762
Archit Taneja2ac6a1a2014-06-01 12:47:44 +0530763 dpi = devm_kzalloc(&pdev->dev, sizeof(*dpi), GFP_KERNEL);
764 if (!dpi)
765 return -ENOMEM;
766
Rob Herring09bffa62017-03-22 08:26:08 -0500767 ep = of_get_next_child(port, NULL);
Tomi Valkeinen2ecef242013-12-16 15:13:24 +0200768 if (!ep)
769 return 0;
770
771 r = of_property_read_u32(ep, "data-lines", &datalines);
772 if (r) {
773 DSSERR("failed to parse datalines\n");
774 goto err_datalines;
775 }
776
Archit Taneja630d2d02014-05-30 16:26:22 +0530777 dpi->data_lines = datalines;
Tomi Valkeinen2ecef242013-12-16 15:13:24 +0200778
779 of_node_put(ep);
780
Archit Taneja630d2d02014-05-30 16:26:22 +0530781 dpi->pdev = pdev;
Laurent Pinchartb8dab2b2017-08-05 01:43:56 +0300782 dpi->dss_model = dss_model;
Archit Taneja80eb6752014-06-02 14:11:51 +0530783 port->data = dpi;
Tomi Valkeinen2ecef242013-12-16 15:13:24 +0200784
Archit Taneja630d2d02014-05-30 16:26:22 +0530785 mutex_init(&dpi->lock);
Tomi Valkeinen2ecef242013-12-16 15:13:24 +0200786
Laurent Pinchartb8dab2b2017-08-05 01:43:56 +0300787 dpi_init_output_port(dpi, port);
Tomi Valkeinen2ecef242013-12-16 15:13:24 +0200788
Archit Taneja630d2d02014-05-30 16:26:22 +0530789 dpi->port_initialized = true;
Tomi Valkeinen2ecef242013-12-16 15:13:24 +0200790
791 return 0;
792
793err_datalines:
794 of_node_put(ep);
795
796 return r;
797}
798
Tomi Valkeinenede92692015-06-04 14:12:16 +0300799void dpi_uninit_port(struct device_node *port)
Tomi Valkeinen2ecef242013-12-16 15:13:24 +0200800{
Archit Taneja80eb6752014-06-02 14:11:51 +0530801 struct dpi_data *dpi = port->data;
Archit Taneja630d2d02014-05-30 16:26:22 +0530802
803 if (!dpi->port_initialized)
Tomi Valkeinen2ecef242013-12-16 15:13:24 +0200804 return;
805
Archit Taneja80eb6752014-06-02 14:11:51 +0530806 dpi_uninit_output_port(port);
Tomi Valkeinen2ecef242013-12-16 15:13:24 +0200807}