dpi.c 6.7 KB
Newer Older
T
Tomi Valkeinen 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
/*
 * linux/drivers/video/omap2/dss/dpi.c
 *
 * Copyright (C) 2009 Nokia Corporation
 * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
 *
 * Some code and ideas taken from drivers/video/omap/ driver
 * by Imre Deak.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published by
 * the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#define DSS_SUBSYS_NAME "DPI"

#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/delay.h>
28
#include <linux/err.h>
T
Tomi Valkeinen 已提交
29
#include <linux/errno.h>
30 31
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
T
Tomi Valkeinen 已提交
32 33 34 35 36 37 38

#include <plat/display.h>
#include <plat/cpu.h>

#include "dss.h"

static struct {
39
	struct regulator *vdds_dsi_reg;
T
Tomi Valkeinen 已提交
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
} dpi;

#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL
static int dpi_set_dsi_clk(bool is_tft, unsigned long pck_req,
		unsigned long *fck, int *lck_div, int *pck_div)
{
	struct dsi_clock_info dsi_cinfo;
	struct dispc_clock_info dispc_cinfo;
	int r;

	r = dsi_pll_calc_clock_div_pck(is_tft, pck_req, &dsi_cinfo,
			&dispc_cinfo);
	if (r)
		return r;

	r = dsi_pll_set_clock_div(&dsi_cinfo);
	if (r)
		return r;

59
	dss_select_dispc_clk_source(DSS_SRC_DSI1_PLL_FCLK);
T
Tomi Valkeinen 已提交
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134

	r = dispc_set_clock_div(&dispc_cinfo);
	if (r)
		return r;

	*fck = dsi_cinfo.dsi1_pll_fclk;
	*lck_div = dispc_cinfo.lck_div;
	*pck_div = dispc_cinfo.pck_div;

	return 0;
}
#else
static int dpi_set_dispc_clk(bool is_tft, unsigned long pck_req,
		unsigned long *fck, int *lck_div, int *pck_div)
{
	struct dss_clock_info dss_cinfo;
	struct dispc_clock_info dispc_cinfo;
	int r;

	r = dss_calc_clock_div(is_tft, pck_req, &dss_cinfo, &dispc_cinfo);
	if (r)
		return r;

	r = dss_set_clock_div(&dss_cinfo);
	if (r)
		return r;

	r = dispc_set_clock_div(&dispc_cinfo);
	if (r)
		return r;

	*fck = dss_cinfo.fck;
	*lck_div = dispc_cinfo.lck_div;
	*pck_div = dispc_cinfo.pck_div;

	return 0;
}
#endif

static int dpi_set_mode(struct omap_dss_device *dssdev)
{
	struct omap_video_timings *t = &dssdev->panel.timings;
	int lck_div, pck_div;
	unsigned long fck;
	unsigned long pck;
	bool is_tft;
	int r = 0;

	dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);

	dispc_set_pol_freq(dssdev->panel.config, dssdev->panel.acbi,
			dssdev->panel.acb);

	is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0;

#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL
	r = dpi_set_dsi_clk(is_tft, t->pixel_clock * 1000,
			&fck, &lck_div, &pck_div);
#else
	r = dpi_set_dispc_clk(is_tft, t->pixel_clock * 1000,
			&fck, &lck_div, &pck_div);
#endif
	if (r)
		goto err0;

	pck = fck / lck_div / pck_div / 1000;

	if (pck != t->pixel_clock) {
		DSSWARN("Could not find exact pixel clock. "
				"Requested %d kHz, got %lu kHz\n",
				t->pixel_clock, pck);

		t->pixel_clock = pck;
	}

135
	dispc_set_lcd_timings(dssdev->manager->id, t);
T
Tomi Valkeinen 已提交
136 137 138 139 140 141 142 143 144 145 146 147

err0:
	dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
	return r;
}

static int dpi_basic_init(struct omap_dss_device *dssdev)
{
	bool is_tft;

	is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0;

148 149 150 151 152 153
	dispc_set_parallel_interface_mode(dssdev->manager->id,
			OMAP_DSS_PARALLELMODE_BYPASS);
	dispc_set_lcd_display_type(dssdev->manager->id, is_tft ?
			OMAP_DSS_LCD_DISPLAY_TFT : OMAP_DSS_LCD_DISPLAY_STN);
	dispc_set_tft_data_lines(dssdev->manager->id,
			dssdev->phy.dpi.data_lines);
T
Tomi Valkeinen 已提交
154 155 156 157

	return 0;
}

158
int omapdss_dpi_display_enable(struct omap_dss_device *dssdev)
T
Tomi Valkeinen 已提交
159 160 161 162 163 164 165 166 167
{
	int r;

	r = omap_dss_start_device(dssdev);
	if (r) {
		DSSERR("failed to start device\n");
		goto err0;
	}

168 169 170
	if (cpu_is_omap34xx()) {
		r = regulator_enable(dpi.vdds_dsi_reg);
		if (r)
171
			goto err1;
172 173
	}

T
Tomi Valkeinen 已提交
174 175 176 177
	dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);

	r = dpi_basic_init(dssdev);
	if (r)
178
		goto err2;
T
Tomi Valkeinen 已提交
179 180 181 182 183

#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL
	dss_clk_enable(DSS_CLK_FCK2);
	r = dsi_pll_init(dssdev, 0, 1);
	if (r)
184
		goto err3;
T
Tomi Valkeinen 已提交
185 186 187
#endif
	r = dpi_set_mode(dssdev);
	if (r)
188
		goto err4;
T
Tomi Valkeinen 已提交
189 190 191

	mdelay(2);

192
	dssdev->manager->enable(dssdev->manager);
T
Tomi Valkeinen 已提交
193 194 195

	return 0;

196
err4:
T
Tomi Valkeinen 已提交
197 198
#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL
	dsi_pll_uninit();
199
err3:
T
Tomi Valkeinen 已提交
200 201
	dss_clk_disable(DSS_CLK_FCK2);
#endif
202
err2:
203
	dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
204 205
	if (cpu_is_omap34xx())
		regulator_disable(dpi.vdds_dsi_reg);
T
Tomi Valkeinen 已提交
206 207 208 209 210
err1:
	omap_dss_stop_device(dssdev);
err0:
	return r;
}
211
EXPORT_SYMBOL(omapdss_dpi_display_enable);
T
Tomi Valkeinen 已提交
212

213
void omapdss_dpi_display_disable(struct omap_dss_device *dssdev)
T
Tomi Valkeinen 已提交
214
{
215
	dssdev->manager->disable(dssdev->manager);
T
Tomi Valkeinen 已提交
216 217

#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL
218
	dss_select_dispc_clk_source(DSS_SRC_DSS1_ALWON_FCLK);
T
Tomi Valkeinen 已提交
219 220 221 222 223 224
	dsi_pll_uninit();
	dss_clk_disable(DSS_CLK_FCK2);
#endif

	dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);

225 226 227
	if (cpu_is_omap34xx())
		regulator_disable(dpi.vdds_dsi_reg);

T
Tomi Valkeinen 已提交
228 229
	omap_dss_stop_device(dssdev);
}
230
EXPORT_SYMBOL(omapdss_dpi_display_disable);
T
Tomi Valkeinen 已提交
231

232
void dpi_set_timings(struct omap_dss_device *dssdev,
T
Tomi Valkeinen 已提交
233 234 235 236 237 238 239 240 241
			struct omap_video_timings *timings)
{
	DSSDBG("dpi_set_timings\n");
	dssdev->panel.timings = *timings;
	if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
		dpi_set_mode(dssdev);
		dispc_go(OMAP_DSS_CHANNEL_LCD);
	}
}
242
EXPORT_SYMBOL(dpi_set_timings);
T
Tomi Valkeinen 已提交
243

244
int dpi_check_timings(struct omap_dss_device *dssdev,
T
Tomi Valkeinen 已提交
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
			struct omap_video_timings *timings)
{
	bool is_tft;
	int r;
	int lck_div, pck_div;
	unsigned long fck;
	unsigned long pck;

	if (!dispc_lcd_timings_ok(timings))
		return -EINVAL;

	if (timings->pixel_clock == 0)
		return -EINVAL;

	is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0;

#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL
	{
		struct dsi_clock_info dsi_cinfo;
		struct dispc_clock_info dispc_cinfo;
		r = dsi_pll_calc_clock_div_pck(is_tft,
				timings->pixel_clock * 1000,
				&dsi_cinfo, &dispc_cinfo);

		if (r)
			return r;

		fck = dsi_cinfo.dsi1_pll_fclk;
		lck_div = dispc_cinfo.lck_div;
		pck_div = dispc_cinfo.pck_div;
	}
#else
	{
		struct dss_clock_info dss_cinfo;
		struct dispc_clock_info dispc_cinfo;
		r = dss_calc_clock_div(is_tft, timings->pixel_clock * 1000,
				&dss_cinfo, &dispc_cinfo);

		if (r)
			return r;

		fck = dss_cinfo.fck;
		lck_div = dispc_cinfo.lck_div;
		pck_div = dispc_cinfo.pck_div;
	}
#endif

	pck = fck / lck_div / pck_div / 1000;

	timings->pixel_clock = pck;

	return 0;
}
298
EXPORT_SYMBOL(dpi_check_timings);
T
Tomi Valkeinen 已提交
299 300 301 302 303 304 305 306

int dpi_init_display(struct omap_dss_device *dssdev)
{
	DSSDBG("init_display\n");

	return 0;
}

307
int dpi_init(struct platform_device *pdev)
T
Tomi Valkeinen 已提交
308
{
309 310 311 312 313 314 315 316
	if (cpu_is_omap34xx()) {
		dpi.vdds_dsi_reg = dss_get_vdds_dsi();
		if (IS_ERR(dpi.vdds_dsi_reg)) {
			DSSERR("can't get VDDS_DSI regulator\n");
			return PTR_ERR(dpi.vdds_dsi_reg);
		}
	}

T
Tomi Valkeinen 已提交
317 318 319 320 321 322 323
	return 0;
}

void dpi_exit(void)
{
}