From patchwork Fri Aug 7 12:27:48 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 39911 X-Patchwork-Delegate: tomi.valkeinen@nokia.com Received: from vger.kernel.org (vger.kernel.org [209.132.176.167]) by demeter.kernel.org (8.14.2/8.14.2) with ESMTP id n77CUiXo024935 for ; Fri, 7 Aug 2009 12:30:45 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1757833AbZHGM3s (ORCPT ); Fri, 7 Aug 2009 08:29:48 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1757816AbZHGM3r (ORCPT ); Fri, 7 Aug 2009 08:29:47 -0400 Received: from smtp.nokia.com ([192.100.122.233]:64460 "EHLO mgw-mx06.nokia.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1757799AbZHGM3R (ORCPT ); Fri, 7 Aug 2009 08:29:17 -0400 Received: from vaebh105.NOE.Nokia.com (vaebh105.europe.nokia.com [10.160.244.31]) by mgw-mx06.nokia.com (Switch-3.3.3/Switch-3.3.3) with ESMTP id n77CSbXK019456; Fri, 7 Aug 2009 15:28:44 +0300 Received: from vaebh104.NOE.Nokia.com ([10.160.244.30]) by vaebh105.NOE.Nokia.com with Microsoft SMTPSVC(6.0.3790.3959); Fri, 7 Aug 2009 15:28:47 +0300 Received: from mgw-da01.ext.nokia.com ([147.243.128.24]) by vaebh104.NOE.Nokia.com over TLS secured channel with Microsoft SMTPSVC(6.0.3790.3959); Fri, 7 Aug 2009 15:28:46 +0300 Received: from localhost.localdomain (esdhcp04190.research.nokia.com [172.21.41.90]) by mgw-da01.ext.nokia.com (Switch-3.3.3/Switch-3.3.3) with ESMTP id n77CS9D8010447; Fri, 7 Aug 2009 15:28:40 +0300 From: Tomi Valkeinen To: linux-fbdev-devel@lists.sourceforge.net Cc: linux-omap@vger.kernel.org, Tomi Valkeinen Subject: [PATCH 10/20] OMAP: DSS2: DPI driver Date: Fri, 7 Aug 2009 15:27:48 +0300 Message-Id: <1249648078-7780-11-git-send-email-tomi.valkeinen@nokia.com> X-Mailer: git-send-email 1.6.4 In-Reply-To: <1249648078-7780-10-git-send-email-tomi.valkeinen@nokia.com> References: <1249648078-7780-1-git-send-email-tomi.valkeinen@nokia.com> <1249648078-7780-2-git-send-email-tomi.valkeinen@nokia.com> <1249648078-7780-3-git-send-email-tomi.valkeinen@nokia.com> <1249648078-7780-4-git-send-email-tomi.valkeinen@nokia.com> <1249648078-7780-5-git-send-email-tomi.valkeinen@nokia.com> <1249648078-7780-6-git-send-email-tomi.valkeinen@nokia.com> <1249648078-7780-7-git-send-email-tomi.valkeinen@nokia.com> <1249648078-7780-8-git-send-email-tomi.valkeinen@nokia.com> <1249648078-7780-9-git-send-email-tomi.valkeinen@nokia.com> <1249648078-7780-10-git-send-email-tomi.valkeinen@nokia.com> X-OriginalArrivalTime: 07 Aug 2009 12:28:47.0295 (UTC) FILETIME=[9C75ECF0:01CA175A] X-Nokia-AV: Clean Sender: linux-omap-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org This implements MIPI DPI interface. Signed-off-by: Tomi Valkeinen --- drivers/video/omap2/dss/dpi.c | 388 +++++++++++++++++++++++++++++++++++++++++ 1 files changed, 388 insertions(+), 0 deletions(-) create mode 100644 drivers/video/omap2/dss/dpi.c diff --git a/drivers/video/omap2/dss/dpi.c b/drivers/video/omap2/dss/dpi.c new file mode 100644 index 0000000..b6543c8 --- /dev/null +++ b/drivers/video/omap2/dss/dpi.c @@ -0,0 +1,388 @@ +/* + * linux/drivers/video/omap2/dss/dpi.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen + * + * 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 . + */ + +#define DSS_SUBSYS_NAME "DPI" + +#include +#include +#include +#include + +#include +#include +#include + +#include "dss.h" + +static struct { + int update_enabled; +} 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 cinfo; + int r; + + r = dsi_pll_calc_pck(is_tft, pck_req, &cinfo); + if (r) + return r; + + r = dsi_pll_program(&cinfo); + if (r) + return r; + + dss_select_clk_source(0, 1); + + dispc_set_lcd_divisor(cinfo.lck_div, cinfo.pck_div); + + *fck = cinfo.dsi1_pll_fclk; + *lck_div = cinfo.lck_div; + *pck_div = 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 dispc_clock_info cinfo; + int r; + + r = dispc_calc_clock_div(is_tft, pck_req, &cinfo); + if (r) + return r; + + r = dispc_set_clock_div(&cinfo); + if (r) + return r; + + *fck = cinfo.fck; + *lck_div = cinfo.lck_div; + *pck_div = 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; + } + + dispc_set_lcd_timings(t); + +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; + + dispc_set_parallel_interface_mode(OMAP_DSS_PARALLELMODE_BYPASS); + dispc_set_lcd_display_type(is_tft ? OMAP_DSS_LCD_DISPLAY_TFT : + OMAP_DSS_LCD_DISPLAY_STN); + dispc_set_tft_data_lines(dssdev->phy.dpi.data_lines); + + return 0; +} + +static int dpi_display_enable(struct omap_dss_device *dssdev) +{ + int r; + + r = omap_dss_start_device(dssdev); + if (r) { + DSSERR("failed to start device\n"); + goto err0; + } + + if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) { + DSSERR("display already enabled\n"); + r = -EINVAL; + goto err1; + } + + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + + r = dpi_basic_init(dssdev); + if (r) + goto err2; + +#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL + dss_clk_enable(DSS_CLK_FCK2); + r = dsi_pll_init(0, 1); + if (r) + goto err3; +#endif + r = dpi_set_mode(dssdev); + if (r) + goto err4; + + mdelay(2); + + dispc_enable_lcd_out(1); + + r = dssdev->driver->enable(dssdev); + if (r) + goto err5; + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + return 0; + +err5: + dispc_enable_lcd_out(0); +err4: +#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL + dsi_pll_uninit(); +err3: + dss_clk_disable(DSS_CLK_FCK2); +#endif +err2: + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); +err1: + omap_dss_stop_device(dssdev); +err0: + return r; +} + +static int dpi_display_resume(struct omap_dss_device *dssdev); + +static void dpi_display_disable(struct omap_dss_device *dssdev) +{ + if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED) + return; + + if (dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) + dpi_display_resume(dssdev); + + dssdev->driver->disable(dssdev); + + dispc_enable_lcd_out(0); + +#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL + dss_select_clk_source(0, 0); + dsi_pll_uninit(); + dss_clk_disable(DSS_CLK_FCK2); +#endif + + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; + + omap_dss_stop_device(dssdev); +} + +static int dpi_display_suspend(struct omap_dss_device *dssdev) +{ + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) + return -EINVAL; + + DSSDBG("dpi_display_suspend\n"); + + if (dssdev->driver->suspend) + dssdev->driver->suspend(dssdev); + + dispc_enable_lcd_out(0); + + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + + dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; + + return 0; +} + +static int dpi_display_resume(struct omap_dss_device *dssdev) +{ + if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) + return -EINVAL; + + DSSDBG("dpi_display_resume\n"); + + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + + dispc_enable_lcd_out(1); + + if (dssdev->driver->resume) + dssdev->driver->resume(dssdev); + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + return 0; +} + +static void dpi_set_timings(struct omap_dss_device *dssdev, + 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); + } +} + +static int dpi_check_timings(struct omap_dss_device *dssdev, + 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 cinfo; + r = dsi_pll_calc_pck(is_tft, timings->pixel_clock * 1000, + &cinfo); + + if (r) + return r; + + fck = cinfo.dsi1_pll_fclk; + lck_div = cinfo.lck_div; + pck_div = cinfo.pck_div; + } +#else + { + struct dispc_clock_info cinfo; + r = dispc_calc_clock_div(is_tft, timings->pixel_clock * 1000, + &cinfo); + + if (r) + return r; + + fck = cinfo.fck; + lck_div = cinfo.lck_div; + pck_div = cinfo.pck_div; + } +#endif + + pck = fck / lck_div / pck_div / 1000; + + timings->pixel_clock = pck; + + return 0; +} + +static void dpi_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + *timings = dssdev->panel.timings; +} + +static int dpi_display_set_update_mode(struct omap_dss_device *dssdev, + enum omap_dss_update_mode mode) +{ + if (mode == OMAP_DSS_UPDATE_MANUAL) + return -EINVAL; + + if (mode == OMAP_DSS_UPDATE_DISABLED) { + dispc_enable_lcd_out(0); + dpi.update_enabled = 0; + } else { + dispc_enable_lcd_out(1); + dpi.update_enabled = 1; + } + + return 0; +} + +static enum omap_dss_update_mode dpi_display_get_update_mode( + struct omap_dss_device *dssdev) +{ + return dpi.update_enabled ? OMAP_DSS_UPDATE_AUTO : + OMAP_DSS_UPDATE_DISABLED; +} + +int dpi_init_display(struct omap_dss_device *dssdev) +{ + DSSDBG("init_display\n"); + + dssdev->enable = dpi_display_enable; + dssdev->disable = dpi_display_disable; + dssdev->suspend = dpi_display_suspend; + dssdev->resume = dpi_display_resume; + dssdev->set_timings = dpi_set_timings; + dssdev->check_timings = dpi_check_timings; + dssdev->get_timings = dpi_get_timings; + dssdev->set_update_mode = dpi_display_set_update_mode; + dssdev->get_update_mode = dpi_display_get_update_mode; + + return 0; +} + +int dpi_init(void) +{ + return 0; +} + +void dpi_exit(void) +{ +} +