blob: 7d70bfcf89c97597f3f19273aa6ddb6060fb4830 [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>
Tomi Valkeinen736e60d2015-06-04 15:22:23 +030035#include <linux/component.h>
Tomi Valkeinen553c48c2009-08-07 13:15:50 +030036
Tomi Valkeinena0b38cc2011-05-11 14:05:07 +030037#include <video/omapdss.h>
Tomi Valkeinen553c48c2009-08-07 13:15:50 +030038
39#include "dss.h"
Chandrabhanu Mahapatra195e6722012-08-22 11:44:06 +053040#include "dss_features.h"
Tomi Valkeinen553c48c2009-08-07 13:15:50 +030041
Archit Taneja630d2d02014-05-30 16:26:22 +053042struct dpi_data {
Tomi Valkeinen00df43b2013-03-19 11:33:52 +020043 struct platform_device *pdev;
44
Tomi Valkeinen8a2cfea2010-02-04 17:03:41 +020045 struct regulator *vdds_dsi_reg;
Tomi Valkeinen331e6072016-05-17 16:08:54 +030046 enum dss_clk_source clk_src;
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +030047 struct dss_pll *pll;
Archit Taneja5cf9a262012-06-29 14:19:13 +053048
Archit Tanejac8a5e4e2012-07-05 12:52:46 +053049 struct mutex lock;
50
Archit Tanejac4991442012-08-08 14:28:54 +053051 struct omap_video_timings timings;
Archit Taneja5cf9a262012-06-29 14:19:13 +053052 struct dss_lcd_mgr_config mgr_config;
Archit Tanejac6b393d2012-07-06 15:30:52 +053053 int data_lines;
Archit Taneja81b87f52012-09-26 16:30:49 +053054
Tomi Valkeinen1f68d9c2013-04-19 15:09:34 +030055 struct omap_dss_device output;
Tomi Valkeinen2ecef242013-12-16 15:13:24 +020056
57 bool port_initialized;
Archit Taneja630d2d02014-05-30 16:26:22 +053058};
59
Archit Taneja2ac6a1a2014-06-01 12:47:44 +053060static struct dpi_data *dpi_get_data_from_dssdev(struct omap_dss_device *dssdev)
61{
62 return container_of(dssdev, struct dpi_data, output);
63}
64
Archit Taneja80eb6752014-06-02 14:11:51 +053065/* only used in non-DT mode */
Archit Taneja2ac6a1a2014-06-01 12:47:44 +053066static struct dpi_data *dpi_get_data_from_pdev(struct platform_device *pdev)
67{
68 return dev_get_drvdata(&pdev->dev);
69}
Tomi Valkeinen553c48c2009-08-07 13:15:50 +030070
Tomi Valkeinen331e6072016-05-17 16:08:54 +030071static enum dss_clk_source dpi_get_clk_src(enum omap_channel channel)
Archit Tanejaa72b64b2011-05-12 17:26:26 +053072{
Tomi Valkeinenbd0f5cc2012-12-13 14:21:30 +020073 /*
74 * XXX we can't currently use DSI PLL for DPI with OMAP3, as the DSI PLL
75 * would also be used for DISPC fclk. Meaning, when the DPI output is
76 * disabled, DISPC clock will be disabled, and TV out will stop.
77 */
78 switch (omapdss_get_version()) {
79 case OMAPDSS_VER_OMAP24xx:
80 case OMAPDSS_VER_OMAP34xx_ES1:
81 case OMAPDSS_VER_OMAP34xx_ES3:
82 case OMAPDSS_VER_OMAP3630:
83 case OMAPDSS_VER_AM35xx:
Sathya Prakash M Rd6279d42014-03-24 16:31:51 +053084 case OMAPDSS_VER_AM43xx:
Tomi Valkeinen331e6072016-05-17 16:08:54 +030085 return DSS_CLK_SRC_FCK;
Tomi Valkeinenbd0f5cc2012-12-13 14:21:30 +020086
Tomi Valkeinenf8ad9842013-03-11 13:57:38 +020087 case OMAPDSS_VER_OMAP4430_ES1:
88 case OMAPDSS_VER_OMAP4430_ES2:
89 case OMAPDSS_VER_OMAP4:
90 switch (channel) {
91 case OMAP_DSS_CHANNEL_LCD:
Tomi Valkeinen331e6072016-05-17 16:08:54 +030092 return DSS_CLK_SRC_PLL1_1;
Tomi Valkeinenf8ad9842013-03-11 13:57:38 +020093 case OMAP_DSS_CHANNEL_LCD2:
Tomi Valkeinen331e6072016-05-17 16:08:54 +030094 return DSS_CLK_SRC_PLL2_1;
Tomi Valkeinenf8ad9842013-03-11 13:57:38 +020095 default:
Tomi Valkeinen331e6072016-05-17 16:08:54 +030096 return DSS_CLK_SRC_FCK;
Tomi Valkeinenf8ad9842013-03-11 13:57:38 +020097 }
98
99 case OMAPDSS_VER_OMAP5:
100 switch (channel) {
101 case OMAP_DSS_CHANNEL_LCD:
Tomi Valkeinen331e6072016-05-17 16:08:54 +0300102 return DSS_CLK_SRC_PLL1_1;
Tomi Valkeinenf8ad9842013-03-11 13:57:38 +0200103 case OMAP_DSS_CHANNEL_LCD3:
Tomi Valkeinen331e6072016-05-17 16:08:54 +0300104 return DSS_CLK_SRC_PLL2_1;
105 case OMAP_DSS_CHANNEL_LCD2:
Tomi Valkeinenf8ad9842013-03-11 13:57:38 +0200106 default:
Tomi Valkeinen331e6072016-05-17 16:08:54 +0300107 return DSS_CLK_SRC_FCK;
Tomi Valkeinenf8ad9842013-03-11 13:57:38 +0200108 }
109
Tomi Valkeinena2408152014-12-31 11:26:06 +0200110 case OMAPDSS_VER_DRA7xx:
111 switch (channel) {
112 case OMAP_DSS_CHANNEL_LCD:
Tomi Valkeinen331e6072016-05-17 16:08:54 +0300113 return DSS_CLK_SRC_PLL1_1;
Tomi Valkeinena2408152014-12-31 11:26:06 +0200114 case OMAP_DSS_CHANNEL_LCD2:
Tomi Valkeinen331e6072016-05-17 16:08:54 +0300115 return DSS_CLK_SRC_PLL1_3;
Tomi Valkeinena2408152014-12-31 11:26:06 +0200116 case OMAP_DSS_CHANNEL_LCD3:
Tomi Valkeinen331e6072016-05-17 16:08:54 +0300117 return DSS_CLK_SRC_PLL2_1;
Tomi Valkeinena2408152014-12-31 11:26:06 +0200118 default:
Tomi Valkeinen331e6072016-05-17 16:08:54 +0300119 return DSS_CLK_SRC_FCK;
Tomi Valkeinena2408152014-12-31 11:26:06 +0200120 }
121
Tomi Valkeinen0e8276e2012-10-22 16:12:58 +0300122 default:
Tomi Valkeinen3b63ca72016-05-17 14:01:10 +0300123 return DSS_CLK_SRC_FCK;
Tomi Valkeinen0e8276e2012-10-22 16:12:58 +0300124 }
Archit Taneja7636b3b2011-04-12 13:52:26 +0530125}
126
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200127struct dpi_clk_calc_ctx {
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300128 struct dss_pll *pll;
Tomi Valkeinen13ece4d2016-05-17 16:20:07 +0300129 unsigned clkout_idx;
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200130
131 /* inputs */
132
133 unsigned long pck_min, pck_max;
134
135 /* outputs */
136
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300137 struct dss_pll_clock_info dsi_cinfo;
Tomi Valkeinenc56812f2014-01-28 08:50:47 +0200138 unsigned long fck;
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200139 struct dispc_clock_info dispc_cinfo;
140};
141
142static bool dpi_calc_dispc_cb(int lckd, int pckd, unsigned long lck,
143 unsigned long pck, void *data)
144{
145 struct dpi_clk_calc_ctx *ctx = data;
146
147 /*
148 * Odd dividers give us uneven duty cycle, causing problem when level
149 * shifted. So skip all odd dividers when the pixel clock is on the
150 * higher side.
151 */
Tomi Valkeinen72e55122013-06-12 09:44:52 +0300152 if (ctx->pck_min >= 100000000) {
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200153 if (lckd > 1 && lckd % 2 != 0)
154 return false;
155
156 if (pckd > 1 && pckd % 2 != 0)
157 return false;
158 }
159
160 ctx->dispc_cinfo.lck_div = lckd;
161 ctx->dispc_cinfo.pck_div = pckd;
162 ctx->dispc_cinfo.lck = lck;
163 ctx->dispc_cinfo.pck = pck;
164
165 return true;
166}
167
168
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300169static bool dpi_calc_hsdiv_cb(int m_dispc, unsigned long dispc,
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200170 void *data)
171{
172 struct dpi_clk_calc_ctx *ctx = data;
173
174 /*
175 * Odd dividers give us uneven duty cycle, causing problem when level
176 * shifted. So skip all odd dividers when the pixel clock is on the
177 * higher side.
178 */
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300179 if (m_dispc > 1 && m_dispc % 2 != 0 && ctx->pck_min >= 100000000)
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200180 return false;
181
Tomi Valkeinen13ece4d2016-05-17 16:20:07 +0300182 ctx->dsi_cinfo.mX[ctx->clkout_idx] = m_dispc;
183 ctx->dsi_cinfo.clkout[ctx->clkout_idx] = dispc;
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200184
185 return dispc_div_calc(dispc, ctx->pck_min, ctx->pck_max,
186 dpi_calc_dispc_cb, ctx);
187}
188
189
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300190static bool dpi_calc_pll_cb(int n, int m, unsigned long fint,
191 unsigned long clkdco,
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200192 void *data)
193{
194 struct dpi_clk_calc_ctx *ctx = data;
195
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300196 ctx->dsi_cinfo.n = n;
197 ctx->dsi_cinfo.m = m;
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200198 ctx->dsi_cinfo.fint = fint;
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300199 ctx->dsi_cinfo.clkdco = clkdco;
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200200
Tomi Valkeinencd0715f2016-05-17 21:23:37 +0300201 return dss_pll_hsdiv_calc_a(ctx->pll, clkdco,
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300202 ctx->pck_min, dss_feat_get_param_max(FEAT_PARAM_DSS_FCK),
203 dpi_calc_hsdiv_cb, ctx);
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200204}
205
Tomi Valkeinend0f58bd2013-10-31 14:44:23 +0200206static bool dpi_calc_dss_cb(unsigned long fck, void *data)
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200207{
208 struct dpi_clk_calc_ctx *ctx = data;
209
Tomi Valkeinend0f58bd2013-10-31 14:44:23 +0200210 ctx->fck = fck;
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200211
212 return dispc_div_calc(fck, ctx->pck_min, ctx->pck_max,
213 dpi_calc_dispc_cb, ctx);
214}
215
Archit Taneja630d2d02014-05-30 16:26:22 +0530216static bool dpi_dsi_clk_calc(struct dpi_data *dpi, unsigned long pck,
217 struct dpi_clk_calc_ctx *ctx)
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200218{
219 unsigned long clkin;
220 unsigned long pll_min, pll_max;
221
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200222 memset(ctx, 0, sizeof(*ctx));
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300223 ctx->pll = dpi->pll;
Tomi Valkeinen13ece4d2016-05-17 16:20:07 +0300224 ctx->clkout_idx = dss_pll_get_clkout_idx_for_src(dpi->clk_src);
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200225 ctx->pck_min = pck - 1000;
226 ctx->pck_max = pck + 1000;
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200227
228 pll_min = 0;
229 pll_max = 0;
230
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300231 clkin = clk_get_rate(ctx->pll->clkin);
232
Tomi Valkeinencd0715f2016-05-17 21:23:37 +0300233 return dss_pll_calc_a(ctx->pll, clkin,
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200234 pll_min, pll_max,
235 dpi_calc_pll_cb, ctx);
236}
237
238static bool dpi_dss_clk_calc(unsigned long pck, struct dpi_clk_calc_ctx *ctx)
239{
240 int i;
241
242 /*
243 * DSS fck gives us very few possibilities, so finding a good pixel
244 * clock may not be possible. We try multiple times to find the clock,
245 * each time widening the pixel clock range we look for, up to
Tomi Valkeinen2c6360f2013-04-10 14:54:54 +0300246 * +/- ~15MHz.
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200247 */
248
Tomi Valkeinen2c6360f2013-04-10 14:54:54 +0300249 for (i = 0; i < 25; ++i) {
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200250 bool ok;
251
252 memset(ctx, 0, sizeof(*ctx));
253 if (pck > 1000 * i * i * i)
254 ctx->pck_min = max(pck - 1000 * i * i * i, 0lu);
255 else
256 ctx->pck_min = 0;
257 ctx->pck_max = pck + 1000 * i * i * i;
258
Tomi Valkeinen688af022013-10-31 16:41:57 +0200259 ok = dss_div_calc(pck, ctx->pck_min, dpi_calc_dss_cb, ctx);
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200260 if (ok)
261 return ok;
262 }
263
264 return false;
265}
266
267
268
Archit Taneja630d2d02014-05-30 16:26:22 +0530269static int dpi_set_dsi_clk(struct dpi_data *dpi, enum omap_channel channel,
Sumit Semwalff1b2cd2010-12-02 11:27:11 +0000270 unsigned long pck_req, unsigned long *fck, int *lck_div,
271 int *pck_div)
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300272{
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200273 struct dpi_clk_calc_ctx ctx;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300274 int r;
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200275 bool ok;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300276
Archit Taneja630d2d02014-05-30 16:26:22 +0530277 ok = dpi_dsi_clk_calc(dpi, pck_req, &ctx);
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200278 if (!ok)
279 return -EINVAL;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300280
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300281 r = dss_pll_set_config(dpi->pll, &ctx.dsi_cinfo);
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300282 if (r)
283 return r;
284
Tomi Valkeinen331e6072016-05-17 16:08:54 +0300285 dss_select_lcd_clk_source(channel, dpi->clk_src);
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300286
Archit Taneja630d2d02014-05-30 16:26:22 +0530287 dpi->mgr_config.clock_info = ctx.dispc_cinfo;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300288
Tomi Valkeinen13ece4d2016-05-17 16:20:07 +0300289 *fck = ctx.dsi_cinfo.clkout[ctx.clkout_idx];
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200290 *lck_div = ctx.dispc_cinfo.lck_div;
291 *pck_div = ctx.dispc_cinfo.pck_div;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300292
293 return 0;
294}
Archit Taneja7636b3b2011-04-12 13:52:26 +0530295
Archit Taneja630d2d02014-05-30 16:26:22 +0530296static int dpi_set_dispc_clk(struct dpi_data *dpi, unsigned long pck_req,
297 unsigned long *fck, int *lck_div, int *pck_div)
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300298{
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200299 struct dpi_clk_calc_ctx ctx;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300300 int r;
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200301 bool ok;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300302
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200303 ok = dpi_dss_clk_calc(pck_req, &ctx);
304 if (!ok)
305 return -EINVAL;
306
Tomi Valkeinend0f58bd2013-10-31 14:44:23 +0200307 r = dss_set_fck_rate(ctx.fck);
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300308 if (r)
309 return r;
310
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 Valkeinend0f58bd2013-10-31 14:44:23 +0200313 *fck = ctx.fck;
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}
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300319
Archit Taneja630d2d02014-05-30 16:26:22 +0530320static int dpi_set_mode(struct dpi_data *dpi)
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300321{
Archit Taneja630d2d02014-05-30 16:26:22 +0530322 struct omap_dss_device *out = &dpi->output;
Tomi Valkeinena070ba62015-11-05 09:52:00 +0200323 enum omap_channel channel = out->dispc_channel;
Archit Taneja630d2d02014-05-30 16:26:22 +0530324 struct omap_video_timings *t = &dpi->timings;
Archit Taneja7636b3b2011-04-12 13:52:26 +0530325 int lck_div = 0, pck_div = 0;
326 unsigned long fck = 0;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300327 unsigned long pck;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300328 int r = 0;
329
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300330 if (dpi->pll)
Tomi Valkeinena070ba62015-11-05 09:52:00 +0200331 r = dpi_set_dsi_clk(dpi, channel, t->pixelclock, &fck,
Archit Taneja6d523e72012-06-21 09:33:55 +0530332 &lck_div, &pck_div);
Archit Taneja7636b3b2011-04-12 13:52:26 +0530333 else
Archit Taneja630d2d02014-05-30 16:26:22 +0530334 r = dpi_set_dispc_clk(dpi, t->pixelclock, &fck,
Archit Taneja6d523e72012-06-21 09:33:55 +0530335 &lck_div, &pck_div);
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300336 if (r)
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300337 return r;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300338
Tomi Valkeinend8d789412013-04-10 14:12:14 +0300339 pck = fck / lck_div / pck_div;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300340
Tomi Valkeinend8d789412013-04-10 14:12:14 +0300341 if (pck != t->pixelclock) {
342 DSSWARN("Could not find exact pixel clock. Requested %d Hz, got %lu Hz\n",
343 t->pixelclock, pck);
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300344
Tomi Valkeinend8d789412013-04-10 14:12:14 +0300345 t->pixelclock = pck;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300346 }
347
Tomi Valkeinena070ba62015-11-05 09:52:00 +0200348 dss_mgr_set_timings(channel, t);
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300349
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300350 return 0;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300351}
352
Archit Taneja630d2d02014-05-30 16:26:22 +0530353static void dpi_config_lcd_manager(struct dpi_data *dpi)
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300354{
Archit Taneja630d2d02014-05-30 16:26:22 +0530355 struct omap_dss_device *out = &dpi->output;
Tomi Valkeinena070ba62015-11-05 09:52:00 +0200356 enum omap_channel channel = out->dispc_channel;
Archit Taneja569969d2011-08-22 17:41:57 +0530357
Archit Taneja630d2d02014-05-30 16:26:22 +0530358 dpi->mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS;
Archit Taneja5cf9a262012-06-29 14:19:13 +0530359
Archit Taneja630d2d02014-05-30 16:26:22 +0530360 dpi->mgr_config.stallmode = false;
361 dpi->mgr_config.fifohandcheck = false;
Archit Taneja5cf9a262012-06-29 14:19:13 +0530362
Archit Taneja630d2d02014-05-30 16:26:22 +0530363 dpi->mgr_config.video_port_width = dpi->data_lines;
Archit Taneja5cf9a262012-06-29 14:19:13 +0530364
Archit Taneja630d2d02014-05-30 16:26:22 +0530365 dpi->mgr_config.lcden_sig_polarity = 0;
366
Tomi Valkeinena070ba62015-11-05 09:52:00 +0200367 dss_mgr_set_lcd_config(channel, &dpi->mgr_config);
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300368}
369
Tomi Valkeinen86a3efe2013-05-15 10:40:15 +0300370static int dpi_display_enable(struct omap_dss_device *dssdev)
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300371{
Archit Taneja2ac6a1a2014-06-01 12:47:44 +0530372 struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
Archit Taneja630d2d02014-05-30 16:26:22 +0530373 struct omap_dss_device *out = &dpi->output;
Tomi Valkeinena070ba62015-11-05 09:52:00 +0200374 enum omap_channel channel = out->dispc_channel;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300375 int r;
376
Archit Taneja630d2d02014-05-30 16:26:22 +0530377 mutex_lock(&dpi->lock);
Archit Tanejac8a5e4e2012-07-05 12:52:46 +0530378
Archit Taneja630d2d02014-05-30 16:26:22 +0530379 if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI) && !dpi->vdds_dsi_reg) {
Russell King40410712012-02-07 09:44:55 +0000380 DSSERR("no VDSS_DSI regulator\n");
Archit Tanejac8a5e4e2012-07-05 12:52:46 +0530381 r = -ENODEV;
382 goto err_no_reg;
Russell King40410712012-02-07 09:44:55 +0000383 }
384
Tomi Valkeinenf1504ad2015-11-05 09:34:51 +0200385 if (!out->dispc_channel_connected) {
Archit Taneja5d512fc2012-09-07 17:53:38 +0530386 DSSERR("failed to enable display: no output/manager\n");
Archit Tanejac8a5e4e2012-07-05 12:52:46 +0530387 r = -ENODEV;
Archit Taneja5d512fc2012-09-07 17:53:38 +0530388 goto err_no_out_mgr;
Tomi Valkeinen05e1d602011-06-23 16:38:21 +0300389 }
390
Chandrabhanu Mahapatra195e6722012-08-22 11:44:06 +0530391 if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI)) {
Archit Taneja630d2d02014-05-30 16:26:22 +0530392 r = regulator_enable(dpi->vdds_dsi_reg);
Tomi Valkeinen8a2cfea2010-02-04 17:03:41 +0200393 if (r)
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300394 goto err_reg_enable;
Tomi Valkeinen8a2cfea2010-02-04 17:03:41 +0200395 }
396
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300397 r = dispc_runtime_get();
398 if (r)
399 goto err_get_dispc;
400
Tomi Valkeinena070ba62015-11-05 09:52:00 +0200401 r = dss_dpi_select_source(out->port_num, channel);
Tomi Valkeinende09e452012-09-21 12:09:54 +0300402 if (r)
403 goto err_src_sel;
404
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300405 if (dpi->pll) {
406 r = dss_pll_enable(dpi->pll);
Archit Taneja7636b3b2011-04-12 13:52:26 +0530407 if (r)
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300408 goto err_dsi_pll_init;
Archit Taneja7636b3b2011-04-12 13:52:26 +0530409 }
410
Archit Taneja630d2d02014-05-30 16:26:22 +0530411 r = dpi_set_mode(dpi);
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300412 if (r)
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300413 goto err_set_mode;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300414
Archit Taneja630d2d02014-05-30 16:26:22 +0530415 dpi_config_lcd_manager(dpi);
Archit Taneja5cf9a262012-06-29 14:19:13 +0530416
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300417 mdelay(2);
418
Tomi Valkeinena070ba62015-11-05 09:52:00 +0200419 r = dss_mgr_enable(channel);
Tomi Valkeinen33ca2372011-11-21 13:42:58 +0200420 if (r)
421 goto err_mgr_enable;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300422
Archit Taneja630d2d02014-05-30 16:26:22 +0530423 mutex_unlock(&dpi->lock);
Archit Tanejac8a5e4e2012-07-05 12:52:46 +0530424
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300425 return 0;
426
Tomi Valkeinen33ca2372011-11-21 13:42:58 +0200427err_mgr_enable:
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300428err_set_mode:
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300429 if (dpi->pll)
430 dss_pll_disable(dpi->pll);
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300431err_dsi_pll_init:
Tomi Valkeinende09e452012-09-21 12:09:54 +0300432err_src_sel:
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300433 dispc_runtime_put();
434err_get_dispc:
Chandrabhanu Mahapatra195e6722012-08-22 11:44:06 +0530435 if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI))
Archit Taneja630d2d02014-05-30 16:26:22 +0530436 regulator_disable(dpi->vdds_dsi_reg);
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300437err_reg_enable:
Archit Taneja5d512fc2012-09-07 17:53:38 +0530438err_no_out_mgr:
Archit Tanejac8a5e4e2012-07-05 12:52:46 +0530439err_no_reg:
Archit Taneja630d2d02014-05-30 16:26:22 +0530440 mutex_unlock(&dpi->lock);
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300441 return r;
442}
443
Tomi Valkeinen86a3efe2013-05-15 10:40:15 +0300444static void dpi_display_disable(struct omap_dss_device *dssdev)
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300445{
Archit Taneja2ac6a1a2014-06-01 12:47:44 +0530446 struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
Tomi Valkeinena070ba62015-11-05 09:52:00 +0200447 enum omap_channel channel = dpi->output.dispc_channel;
Archit Taneja5d512fc2012-09-07 17:53:38 +0530448
Archit Taneja630d2d02014-05-30 16:26:22 +0530449 mutex_lock(&dpi->lock);
Archit Tanejac8a5e4e2012-07-05 12:52:46 +0530450
Tomi Valkeinena070ba62015-11-05 09:52:00 +0200451 dss_mgr_disable(channel);
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300452
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300453 if (dpi->pll) {
Tomi Valkeinen3b63ca72016-05-17 14:01:10 +0300454 dss_select_lcd_clk_source(channel, DSS_CLK_SRC_FCK);
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300455 dss_pll_disable(dpi->pll);
Archit Taneja7636b3b2011-04-12 13:52:26 +0530456 }
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300457
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300458 dispc_runtime_put();
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300459
Chandrabhanu Mahapatra195e6722012-08-22 11:44:06 +0530460 if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI))
Archit Taneja630d2d02014-05-30 16:26:22 +0530461 regulator_disable(dpi->vdds_dsi_reg);
Tomi Valkeinen8a2cfea2010-02-04 17:03:41 +0200462
Archit Taneja630d2d02014-05-30 16:26:22 +0530463 mutex_unlock(&dpi->lock);
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300464}
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300465
Tomi Valkeinen86a3efe2013-05-15 10:40:15 +0300466static void dpi_set_timings(struct omap_dss_device *dssdev,
Archit Tanejac4991442012-08-08 14:28:54 +0530467 struct omap_video_timings *timings)
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300468{
Archit Taneja2ac6a1a2014-06-01 12:47:44 +0530469 struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
Archit Taneja630d2d02014-05-30 16:26:22 +0530470
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300471 DSSDBG("dpi_set_timings\n");
Archit Tanejac8a5e4e2012-07-05 12:52:46 +0530472
Archit Taneja630d2d02014-05-30 16:26:22 +0530473 mutex_lock(&dpi->lock);
Archit Tanejac8a5e4e2012-07-05 12:52:46 +0530474
Archit Taneja630d2d02014-05-30 16:26:22 +0530475 dpi->timings = *timings;
Archit Tanejac4991442012-08-08 14:28:54 +0530476
Archit Taneja630d2d02014-05-30 16:26:22 +0530477 mutex_unlock(&dpi->lock);
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300478}
479
Tomi Valkeinen0b24edb2013-05-24 13:18:52 +0300480static void dpi_get_timings(struct omap_dss_device *dssdev,
481 struct omap_video_timings *timings)
482{
Archit Taneja2ac6a1a2014-06-01 12:47:44 +0530483 struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
Tomi Valkeinen0b24edb2013-05-24 13:18:52 +0300484
Archit Taneja630d2d02014-05-30 16:26:22 +0530485 mutex_lock(&dpi->lock);
Tomi Valkeinen0b24edb2013-05-24 13:18:52 +0300486
Archit Taneja630d2d02014-05-30 16:26:22 +0530487 *timings = dpi->timings;
488
489 mutex_unlock(&dpi->lock);
Tomi Valkeinen0b24edb2013-05-24 13:18:52 +0300490}
491
Tomi Valkeinen86a3efe2013-05-15 10:40:15 +0300492static int dpi_check_timings(struct omap_dss_device *dssdev,
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300493 struct omap_video_timings *timings)
494{
Archit Taneja2ac6a1a2014-06-01 12:47:44 +0530495 struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
Tomi Valkeinena070ba62015-11-05 09:52:00 +0200496 enum omap_channel channel = dpi->output.dispc_channel;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300497 int lck_div, pck_div;
498 unsigned long fck;
499 unsigned long pck;
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200500 struct dpi_clk_calc_ctx ctx;
501 bool ok;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300502
Tomi Valkeinen2158f2c2016-01-05 11:43:18 +0200503 if (timings->x_res % 8 != 0)
504 return -EINVAL;
505
Tomi Valkeinena070ba62015-11-05 09:52:00 +0200506 if (!dispc_mgr_timings_ok(channel, timings))
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300507 return -EINVAL;
508
Tomi Valkeinend8d789412013-04-10 14:12:14 +0300509 if (timings->pixelclock == 0)
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300510 return -EINVAL;
511
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300512 if (dpi->pll) {
Archit Taneja630d2d02014-05-30 16:26:22 +0530513 ok = dpi_dsi_clk_calc(dpi, timings->pixelclock, &ctx);
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200514 if (!ok)
515 return -EINVAL;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300516
Tomi Valkeinen13ece4d2016-05-17 16:20:07 +0300517 fck = ctx.dsi_cinfo.clkout[ctx.clkout_idx];
Archit Taneja7636b3b2011-04-12 13:52:26 +0530518 } else {
Tomi Valkeinend8d789412013-04-10 14:12:14 +0300519 ok = dpi_dss_clk_calc(timings->pixelclock, &ctx);
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200520 if (!ok)
521 return -EINVAL;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300522
Tomi Valkeinend0f58bd2013-10-31 14:44:23 +0200523 fck = ctx.fck;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300524 }
Archit Taneja7636b3b2011-04-12 13:52:26 +0530525
Tomi Valkeinen100c8262013-03-05 17:07:16 +0200526 lck_div = ctx.dispc_cinfo.lck_div;
527 pck_div = ctx.dispc_cinfo.pck_div;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300528
Tomi Valkeinend8d789412013-04-10 14:12:14 +0300529 pck = fck / lck_div / pck_div;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300530
Tomi Valkeinend8d789412013-04-10 14:12:14 +0300531 timings->pixelclock = pck;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300532
533 return 0;
534}
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300535
Tomi Valkeinen86a3efe2013-05-15 10:40:15 +0300536static void dpi_set_data_lines(struct omap_dss_device *dssdev, int data_lines)
Archit Tanejac6b393d2012-07-06 15:30:52 +0530537{
Archit Taneja2ac6a1a2014-06-01 12:47:44 +0530538 struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
Archit Tanejac6b393d2012-07-06 15:30:52 +0530539
Archit Taneja630d2d02014-05-30 16:26:22 +0530540 mutex_lock(&dpi->lock);
Archit Tanejac6b393d2012-07-06 15:30:52 +0530541
Archit Taneja630d2d02014-05-30 16:26:22 +0530542 dpi->data_lines = data_lines;
543
544 mutex_unlock(&dpi->lock);
Archit Tanejac6b393d2012-07-06 15:30:52 +0530545}
Archit Tanejac6b393d2012-07-06 15:30:52 +0530546
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300547static int dpi_verify_dsi_pll(struct dss_pll *pll)
Tomi Valkeinen60616752012-10-30 12:57:43 +0200548{
549 int r;
550
551 /* do initial setup with the PLL to see if it is operational */
552
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300553 r = dss_pll_enable(pll);
Tomi Valkeinen60616752012-10-30 12:57:43 +0200554 if (r)
555 return r;
556
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300557 dss_pll_disable(pll);
Tomi Valkeinen60616752012-10-30 12:57:43 +0200558
559 return 0;
560}
561
Archit Taneja630d2d02014-05-30 16:26:22 +0530562static int dpi_init_regulator(struct dpi_data *dpi)
Tomi Valkeinen2795f642013-04-19 16:52:27 +0300563{
564 struct regulator *vdds_dsi;
565
566 if (!dss_has_feature(FEAT_DPI_USES_VDDS_DSI))
567 return 0;
568
Archit Taneja630d2d02014-05-30 16:26:22 +0530569 if (dpi->vdds_dsi_reg)
Tomi Valkeinen2795f642013-04-19 16:52:27 +0300570 return 0;
571
Archit Taneja630d2d02014-05-30 16:26:22 +0530572 vdds_dsi = devm_regulator_get(&dpi->pdev->dev, "vdds_dsi");
Tomi Valkeinen2795f642013-04-19 16:52:27 +0300573 if (IS_ERR(vdds_dsi)) {
Tomi Valkeinen40359a92013-12-19 16:15:34 +0200574 if (PTR_ERR(vdds_dsi) != -EPROBE_DEFER)
575 DSSERR("can't get VDDS_DSI regulator\n");
Tomi Valkeinen4123de22013-08-29 10:06:55 +0300576 return PTR_ERR(vdds_dsi);
Tomi Valkeinen2795f642013-04-19 16:52:27 +0300577 }
578
Archit Taneja630d2d02014-05-30 16:26:22 +0530579 dpi->vdds_dsi_reg = vdds_dsi;
Tomi Valkeinen2795f642013-04-19 16:52:27 +0300580
581 return 0;
582}
583
Archit Taneja630d2d02014-05-30 16:26:22 +0530584static void dpi_init_pll(struct dpi_data *dpi)
Tomi Valkeinen2795f642013-04-19 16:52:27 +0300585{
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300586 struct dss_pll *pll;
Tomi Valkeinen2795f642013-04-19 16:52:27 +0300587
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300588 if (dpi->pll)
Tomi Valkeinen2795f642013-04-19 16:52:27 +0300589 return;
590
Tomi Valkeinen331e6072016-05-17 16:08:54 +0300591 dpi->clk_src = dpi_get_clk_src(dpi->output.dispc_channel);
592
593 pll = dss_pll_find_by_src(dpi->clk_src);
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300594 if (!pll)
Tomi Valkeinen2795f642013-04-19 16:52:27 +0300595 return;
596
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300597 if (dpi_verify_dsi_pll(pll)) {
Tomi Valkeinen2795f642013-04-19 16:52:27 +0300598 DSSWARN("DSI PLL not operational\n");
599 return;
600 }
601
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300602 dpi->pll = pll;
Tomi Valkeinen2795f642013-04-19 16:52:27 +0300603}
604
Tomi Valkeinen2eea5ae2013-02-13 11:23:54 +0200605/*
606 * Return a hardcoded channel for the DPI output. This should work for
607 * current use cases, but this can be later expanded to either resolve
608 * the channel in some more dynamic manner, or get the channel as a user
609 * parameter.
610 */
Archit Tanejaf7e38fe2014-05-06 17:07:39 +0530611static enum omap_channel dpi_get_channel(int port_num)
Tomi Valkeinen2eea5ae2013-02-13 11:23:54 +0200612{
613 switch (omapdss_get_version()) {
614 case OMAPDSS_VER_OMAP24xx:
615 case OMAPDSS_VER_OMAP34xx_ES1:
616 case OMAPDSS_VER_OMAP34xx_ES3:
617 case OMAPDSS_VER_OMAP3630:
618 case OMAPDSS_VER_AM35xx:
Sathya Prakash M Rd6279d42014-03-24 16:31:51 +0530619 case OMAPDSS_VER_AM43xx:
Tomi Valkeinen2eea5ae2013-02-13 11:23:54 +0200620 return OMAP_DSS_CHANNEL_LCD;
621
Tomi Valkeinena2408152014-12-31 11:26:06 +0200622 case OMAPDSS_VER_DRA7xx:
623 switch (port_num) {
624 case 2:
625 return OMAP_DSS_CHANNEL_LCD3;
626 case 1:
627 return OMAP_DSS_CHANNEL_LCD2;
628 case 0:
629 default:
630 return OMAP_DSS_CHANNEL_LCD;
631 }
632
Tomi Valkeinen2eea5ae2013-02-13 11:23:54 +0200633 case OMAPDSS_VER_OMAP4430_ES1:
634 case OMAPDSS_VER_OMAP4430_ES2:
635 case OMAPDSS_VER_OMAP4:
636 return OMAP_DSS_CHANNEL_LCD2;
637
638 case OMAPDSS_VER_OMAP5:
639 return OMAP_DSS_CHANNEL_LCD3;
640
641 default:
642 DSSWARN("unsupported DSS version\n");
643 return OMAP_DSS_CHANNEL_LCD;
644 }
645}
646
Tomi Valkeinen0b24edb2013-05-24 13:18:52 +0300647static int dpi_connect(struct omap_dss_device *dssdev,
648 struct omap_dss_device *dst)
649{
Archit Taneja2ac6a1a2014-06-01 12:47:44 +0530650 struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
Tomi Valkeinena070ba62015-11-05 09:52:00 +0200651 enum omap_channel channel = dpi->output.dispc_channel;
Tomi Valkeinen0b24edb2013-05-24 13:18:52 +0300652 int r;
653
Archit Taneja630d2d02014-05-30 16:26:22 +0530654 r = dpi_init_regulator(dpi);
Tomi Valkeinen0b24edb2013-05-24 13:18:52 +0300655 if (r)
656 return r;
657
Archit Taneja630d2d02014-05-30 16:26:22 +0530658 dpi_init_pll(dpi);
Tomi Valkeinen0b24edb2013-05-24 13:18:52 +0300659
Tomi Valkeinena070ba62015-11-05 09:52:00 +0200660 r = dss_mgr_connect(channel, dssdev);
Tomi Valkeinen0b24edb2013-05-24 13:18:52 +0300661 if (r)
662 return r;
663
664 r = omapdss_output_set_device(dssdev, dst);
665 if (r) {
666 DSSERR("failed to connect output to new device: %s\n",
667 dst->name);
Tomi Valkeinena070ba62015-11-05 09:52:00 +0200668 dss_mgr_disconnect(channel, dssdev);
Tomi Valkeinen0b24edb2013-05-24 13:18:52 +0300669 return r;
670 }
671
672 return 0;
673}
674
675static void dpi_disconnect(struct omap_dss_device *dssdev,
676 struct omap_dss_device *dst)
677{
Tomi Valkeinena070ba62015-11-05 09:52:00 +0200678 struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
679 enum omap_channel channel = dpi->output.dispc_channel;
680
Tomi Valkeinen9560dc102013-07-24 13:06:54 +0300681 WARN_ON(dst != dssdev->dst);
Tomi Valkeinen0b24edb2013-05-24 13:18:52 +0300682
Tomi Valkeinen9560dc102013-07-24 13:06:54 +0300683 if (dst != dssdev->dst)
Tomi Valkeinen0b24edb2013-05-24 13:18:52 +0300684 return;
685
686 omapdss_output_unset_device(dssdev);
687
Tomi Valkeinena070ba62015-11-05 09:52:00 +0200688 dss_mgr_disconnect(channel, dssdev);
Tomi Valkeinen0b24edb2013-05-24 13:18:52 +0300689}
690
691static const struct omapdss_dpi_ops dpi_ops = {
692 .connect = dpi_connect,
693 .disconnect = dpi_disconnect,
694
Tomi Valkeinen86a3efe2013-05-15 10:40:15 +0300695 .enable = dpi_display_enable,
696 .disable = dpi_display_disable,
Tomi Valkeinen0b24edb2013-05-24 13:18:52 +0300697
698 .check_timings = dpi_check_timings,
Tomi Valkeinen86a3efe2013-05-15 10:40:15 +0300699 .set_timings = dpi_set_timings,
Tomi Valkeinen0b24edb2013-05-24 13:18:52 +0300700 .get_timings = dpi_get_timings,
701
Tomi Valkeinen86a3efe2013-05-15 10:40:15 +0300702 .set_data_lines = dpi_set_data_lines,
Tomi Valkeinen0b24edb2013-05-24 13:18:52 +0300703};
704
Tomi Valkeinen94cf3942013-04-26 14:27:44 +0300705static void dpi_init_output(struct platform_device *pdev)
Archit Taneja81b87f52012-09-26 16:30:49 +0530706{
Archit Taneja2ac6a1a2014-06-01 12:47:44 +0530707 struct dpi_data *dpi = dpi_get_data_from_pdev(pdev);
Archit Taneja630d2d02014-05-30 16:26:22 +0530708 struct omap_dss_device *out = &dpi->output;
Archit Taneja81b87f52012-09-26 16:30:49 +0530709
Tomi Valkeinen1f68d9c2013-04-19 15:09:34 +0300710 out->dev = &pdev->dev;
Archit Taneja81b87f52012-09-26 16:30:49 +0530711 out->id = OMAP_DSS_OUTPUT_DPI;
Tomi Valkeinen1f68d9c2013-04-19 15:09:34 +0300712 out->output_type = OMAP_DISPLAY_TYPE_DPI;
Tomi Valkeinen7286a082013-02-18 13:06:01 +0200713 out->name = "dpi.0";
Archit Tanejaf7e38fe2014-05-06 17:07:39 +0530714 out->dispc_channel = dpi_get_channel(0);
Tomi Valkeinen0b24edb2013-05-24 13:18:52 +0300715 out->ops.dpi = &dpi_ops;
Tomi Valkeinenb7328e12013-05-03 11:42:18 +0300716 out->owner = THIS_MODULE;
Archit Taneja81b87f52012-09-26 16:30:49 +0530717
Tomi Valkeinen5d47dbc2013-04-24 13:32:51 +0300718 omapdss_register_output(out);
Archit Taneja81b87f52012-09-26 16:30:49 +0530719}
720
Tomi Valkeinenede92692015-06-04 14:12:16 +0300721static void dpi_uninit_output(struct platform_device *pdev)
Archit Taneja81b87f52012-09-26 16:30:49 +0530722{
Archit Taneja2ac6a1a2014-06-01 12:47:44 +0530723 struct dpi_data *dpi = dpi_get_data_from_pdev(pdev);
Archit Taneja630d2d02014-05-30 16:26:22 +0530724 struct omap_dss_device *out = &dpi->output;
Archit Taneja81b87f52012-09-26 16:30:49 +0530725
Tomi Valkeinen5d47dbc2013-04-24 13:32:51 +0300726 omapdss_unregister_output(out);
Archit Taneja81b87f52012-09-26 16:30:49 +0530727}
728
Archit Taneja80eb6752014-06-02 14:11:51 +0530729static void dpi_init_output_port(struct platform_device *pdev,
730 struct device_node *port)
731{
732 struct dpi_data *dpi = port->data;
733 struct omap_dss_device *out = &dpi->output;
Archit Tanejaf7e38fe2014-05-06 17:07:39 +0530734 int r;
735 u32 port_num;
736
737 r = of_property_read_u32(port, "reg", &port_num);
738 if (r)
739 port_num = 0;
740
741 switch (port_num) {
742 case 2:
743 out->name = "dpi.2";
744 break;
745 case 1:
746 out->name = "dpi.1";
747 break;
748 case 0:
749 default:
750 out->name = "dpi.0";
751 break;
752 }
Archit Taneja80eb6752014-06-02 14:11:51 +0530753
754 out->dev = &pdev->dev;
755 out->id = OMAP_DSS_OUTPUT_DPI;
756 out->output_type = OMAP_DISPLAY_TYPE_DPI;
Archit Tanejaf7e38fe2014-05-06 17:07:39 +0530757 out->dispc_channel = dpi_get_channel(port_num);
758 out->port_num = port_num;
Archit Taneja80eb6752014-06-02 14:11:51 +0530759 out->ops.dpi = &dpi_ops;
760 out->owner = THIS_MODULE;
761
762 omapdss_register_output(out);
763}
764
Tomi Valkeinenede92692015-06-04 14:12:16 +0300765static void dpi_uninit_output_port(struct device_node *port)
Archit Taneja80eb6752014-06-02 14:11:51 +0530766{
767 struct dpi_data *dpi = port->data;
768 struct omap_dss_device *out = &dpi->output;
769
770 omapdss_unregister_output(out);
771}
772
Tomi Valkeinen736e60d2015-06-04 15:22:23 +0300773static int dpi_bind(struct device *dev, struct device *master, void *data)
Tomi Valkeinen38f3daf2012-05-02 14:55:12 +0300774{
Tomi Valkeinen736e60d2015-06-04 15:22:23 +0300775 struct platform_device *pdev = to_platform_device(dev);
Archit Taneja2ac6a1a2014-06-01 12:47:44 +0530776 struct dpi_data *dpi;
777
778 dpi = devm_kzalloc(&pdev->dev, sizeof(*dpi), GFP_KERNEL);
779 if (!dpi)
780 return -ENOMEM;
Tomi Valkeinen00df43b2013-03-19 11:33:52 +0200781
Archit Taneja630d2d02014-05-30 16:26:22 +0530782 dpi->pdev = pdev;
783
784 dev_set_drvdata(&pdev->dev, dpi);
785
786 mutex_init(&dpi->lock);
Archit Tanejac8a5e4e2012-07-05 12:52:46 +0530787
Archit Taneja81b87f52012-09-26 16:30:49 +0530788 dpi_init_output(pdev);
789
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300790 return 0;
791}
792
Tomi Valkeinen736e60d2015-06-04 15:22:23 +0300793static void dpi_unbind(struct device *dev, struct device *master, void *data)
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300794{
Tomi Valkeinen736e60d2015-06-04 15:22:23 +0300795 struct platform_device *pdev = to_platform_device(dev);
Archit Taneja81b87f52012-09-26 16:30:49 +0530796
Tomi Valkeinen736e60d2015-06-04 15:22:23 +0300797 dpi_uninit_output(pdev);
798}
799
800static const struct component_ops dpi_component_ops = {
801 .bind = dpi_bind,
802 .unbind = dpi_unbind,
803};
804
805static int dpi_probe(struct platform_device *pdev)
806{
807 return component_add(&pdev->dev, &dpi_component_ops);
808}
809
810static int dpi_remove(struct platform_device *pdev)
811{
812 component_del(&pdev->dev, &dpi_component_ops);
Tomi Valkeinena57dd4f2012-02-20 16:57:37 +0200813 return 0;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300814}
815
Tomi Valkeinena57dd4f2012-02-20 16:57:37 +0200816static struct platform_driver omap_dpi_driver = {
Tomi Valkeinen736e60d2015-06-04 15:22:23 +0300817 .probe = dpi_probe,
818 .remove = dpi_remove,
Tomi Valkeinena57dd4f2012-02-20 16:57:37 +0200819 .driver = {
820 .name = "omapdss_dpi",
Tomi Valkeinen422ccbd2014-10-16 09:54:25 +0300821 .suppress_bind_attrs = true,
Tomi Valkeinena57dd4f2012-02-20 16:57:37 +0200822 },
823};
824
Tomi Valkeinen6e7e8f02012-02-17 17:41:13 +0200825int __init dpi_init_platform_driver(void)
Tomi Valkeinena57dd4f2012-02-20 16:57:37 +0200826{
Tomi Valkeinen94cf3942013-04-26 14:27:44 +0300827 return platform_driver_register(&omap_dpi_driver);
Tomi Valkeinena57dd4f2012-02-20 16:57:37 +0200828}
829
Tomi Valkeinenede92692015-06-04 14:12:16 +0300830void dpi_uninit_platform_driver(void)
Tomi Valkeinena57dd4f2012-02-20 16:57:37 +0200831{
832 platform_driver_unregister(&omap_dpi_driver);
833}
Tomi Valkeinen2ecef242013-12-16 15:13:24 +0200834
Tomi Valkeinenede92692015-06-04 14:12:16 +0300835int dpi_init_port(struct platform_device *pdev, struct device_node *port)
Tomi Valkeinen2ecef242013-12-16 15:13:24 +0200836{
Archit Taneja2ac6a1a2014-06-01 12:47:44 +0530837 struct dpi_data *dpi;
Tomi Valkeinen2ecef242013-12-16 15:13:24 +0200838 struct device_node *ep;
839 u32 datalines;
840 int r;
841
Archit Taneja2ac6a1a2014-06-01 12:47:44 +0530842 dpi = devm_kzalloc(&pdev->dev, sizeof(*dpi), GFP_KERNEL);
843 if (!dpi)
844 return -ENOMEM;
845
Tomi Valkeinen2ecef242013-12-16 15:13:24 +0200846 ep = omapdss_of_get_next_endpoint(port, NULL);
847 if (!ep)
848 return 0;
849
850 r = of_property_read_u32(ep, "data-lines", &datalines);
851 if (r) {
852 DSSERR("failed to parse datalines\n");
853 goto err_datalines;
854 }
855
Archit Taneja630d2d02014-05-30 16:26:22 +0530856 dpi->data_lines = datalines;
Tomi Valkeinen2ecef242013-12-16 15:13:24 +0200857
858 of_node_put(ep);
859
Archit Taneja630d2d02014-05-30 16:26:22 +0530860 dpi->pdev = pdev;
Archit Taneja80eb6752014-06-02 14:11:51 +0530861 port->data = dpi;
Tomi Valkeinen2ecef242013-12-16 15:13:24 +0200862
Archit Taneja630d2d02014-05-30 16:26:22 +0530863 mutex_init(&dpi->lock);
Tomi Valkeinen2ecef242013-12-16 15:13:24 +0200864
Archit Taneja80eb6752014-06-02 14:11:51 +0530865 dpi_init_output_port(pdev, port);
Tomi Valkeinen2ecef242013-12-16 15:13:24 +0200866
Archit Taneja630d2d02014-05-30 16:26:22 +0530867 dpi->port_initialized = true;
Tomi Valkeinen2ecef242013-12-16 15:13:24 +0200868
869 return 0;
870
871err_datalines:
872 of_node_put(ep);
873
874 return r;
875}
876
Tomi Valkeinenede92692015-06-04 14:12:16 +0300877void dpi_uninit_port(struct device_node *port)
Tomi Valkeinen2ecef242013-12-16 15:13:24 +0200878{
Archit Taneja80eb6752014-06-02 14:11:51 +0530879 struct dpi_data *dpi = port->data;
Archit Taneja630d2d02014-05-30 16:26:22 +0530880
881 if (!dpi->port_initialized)
Tomi Valkeinen2ecef242013-12-16 15:13:24 +0200882 return;
883
Archit Taneja80eb6752014-06-02 14:11:51 +0530884 dpi_uninit_output_port(port);
Tomi Valkeinen2ecef242013-12-16 15:13:24 +0200885}