···11+/*22+ * linux/drivers/video/omap2/dss/sdi.c33+ *44+ * Copyright (C) 2009 Nokia Corporation55+ * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>66+ *77+ * This program is free software; you can redistribute it and/or modify it88+ * under the terms of the GNU General Public License version 2 as published by99+ * the Free Software Foundation.1010+ *1111+ * This program is distributed in the hope that it will be useful, but WITHOUT1212+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or1313+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for1414+ * more details.1515+ *1616+ * You should have received a copy of the GNU General Public License along with1717+ * this program. If not, see <http://www.gnu.org/licenses/>.1818+ */1919+2020+#define DSS_SUBSYS_NAME "SDI"2121+2222+#include <linux/kernel.h>2323+#include <linux/clk.h>2424+#include <linux/delay.h>2525+#include <linux/err.h>2626+2727+#include <plat/display.h>2828+#include "dss.h"2929+3030+static struct {3131+ bool skip_init;3232+ bool update_enabled;3333+} sdi;3434+3535+static void sdi_basic_init(void)3636+{3737+ dispc_set_parallel_interface_mode(OMAP_DSS_PARALLELMODE_BYPASS);3838+3939+ dispc_set_lcd_display_type(OMAP_DSS_LCD_DISPLAY_TFT);4040+ dispc_set_tft_data_lines(24);4141+ dispc_lcd_enable_signal_polarity(1);4242+}4343+4444+static int sdi_display_enable(struct omap_dss_device *dssdev)4545+{4646+ struct omap_video_timings *t = &dssdev->panel.timings;4747+ struct dss_clock_info dss_cinfo;4848+ struct dispc_clock_info dispc_cinfo;4949+ u16 lck_div, pck_div;5050+ unsigned long fck;5151+ unsigned long pck;5252+ int r;5353+5454+ r = omap_dss_start_device(dssdev);5555+ if (r) {5656+ DSSERR("failed to start device\n");5757+ goto err0;5858+ }5959+6060+ if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) {6161+ DSSERR("dssdev already enabled\n");6262+ r = -EINVAL;6363+ goto err1;6464+ }6565+6666+ /* In case of skip_init sdi_init has already enabled the clocks */6767+ if (!sdi.skip_init)6868+ dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);6969+7070+ sdi_basic_init();7171+7272+ /* 15.5.9.1.2 */7373+ dssdev->panel.config |= OMAP_DSS_LCD_RF | OMAP_DSS_LCD_ONOFF;7474+7575+ dispc_set_pol_freq(dssdev->panel.config, dssdev->panel.acbi,7676+ dssdev->panel.acb);7777+7878+ if (!sdi.skip_init) {7979+ r = dss_calc_clock_div(1, t->pixel_clock * 1000,8080+ &dss_cinfo, &dispc_cinfo);8181+ } else {8282+ r = dss_get_clock_div(&dss_cinfo);8383+ r = dispc_get_clock_div(&dispc_cinfo);8484+ }8585+8686+ if (r)8787+ goto err2;8888+8989+ fck = dss_cinfo.fck;9090+ lck_div = dispc_cinfo.lck_div;9191+ pck_div = dispc_cinfo.pck_div;9292+9393+ pck = fck / lck_div / pck_div / 1000;9494+9595+ if (pck != t->pixel_clock) {9696+ DSSWARN("Could not find exact pixel clock. Requested %d kHz, "9797+ "got %lu kHz\n",9898+ t->pixel_clock, pck);9999+100100+ t->pixel_clock = pck;101101+ }102102+103103+104104+ dispc_set_lcd_timings(t);105105+106106+ r = dss_set_clock_div(&dss_cinfo);107107+ if (r)108108+ goto err2;109109+110110+ r = dispc_set_clock_div(&dispc_cinfo);111111+ if (r)112112+ goto err2;113113+114114+ if (!sdi.skip_init) {115115+ dss_sdi_init(dssdev->phy.sdi.datapairs);116116+ r = dss_sdi_enable();117117+ if (r)118118+ goto err1;119119+ mdelay(2);120120+ }121121+122122+ dispc_enable_lcd_out(1);123123+124124+ if (dssdev->driver->enable) {125125+ r = dssdev->driver->enable(dssdev);126126+ if (r)127127+ goto err3;128128+ }129129+130130+ dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;131131+132132+ sdi.skip_init = 0;133133+134134+ return 0;135135+err3:136136+ dispc_enable_lcd_out(0);137137+err2:138138+ dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);139139+err1:140140+ omap_dss_stop_device(dssdev);141141+err0:142142+ return r;143143+}144144+145145+static int sdi_display_resume(struct omap_dss_device *dssdev);146146+147147+static void sdi_display_disable(struct omap_dss_device *dssdev)148148+{149149+ if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED)150150+ return;151151+152152+ if (dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED)153153+ if (sdi_display_resume(dssdev))154154+ return;155155+156156+ if (dssdev->driver->disable)157157+ dssdev->driver->disable(dssdev);158158+159159+ dispc_enable_lcd_out(0);160160+161161+ dss_sdi_disable();162162+163163+ dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);164164+165165+ dssdev->state = OMAP_DSS_DISPLAY_DISABLED;166166+167167+ omap_dss_stop_device(dssdev);168168+}169169+170170+static int sdi_display_suspend(struct omap_dss_device *dssdev)171171+{172172+ if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)173173+ return -EINVAL;174174+175175+ if (dssdev->driver->suspend)176176+ dssdev->driver->suspend(dssdev);177177+178178+ dispc_enable_lcd_out(0);179179+180180+ dss_sdi_disable();181181+182182+ dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);183183+184184+ dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;185185+186186+ return 0;187187+}188188+189189+static int sdi_display_resume(struct omap_dss_device *dssdev)190190+{191191+ int r;192192+193193+ if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED)194194+ return -EINVAL;195195+196196+ dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);197197+198198+ r = dss_sdi_enable();199199+ if (r)200200+ goto err;201201+ mdelay(2);202202+203203+ dispc_enable_lcd_out(1);204204+205205+ if (dssdev->driver->resume)206206+ dssdev->driver->resume(dssdev);207207+208208+ dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;209209+210210+ return 0;211211+err:212212+ dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);213213+ return r;214214+}215215+216216+static int sdi_display_set_update_mode(struct omap_dss_device *dssdev,217217+ enum omap_dss_update_mode mode)218218+{219219+ if (mode == OMAP_DSS_UPDATE_MANUAL)220220+ return -EINVAL;221221+222222+ if (mode == OMAP_DSS_UPDATE_DISABLED) {223223+ dispc_enable_lcd_out(0);224224+ sdi.update_enabled = 0;225225+ } else {226226+ dispc_enable_lcd_out(1);227227+ sdi.update_enabled = 1;228228+ }229229+230230+ return 0;231231+}232232+233233+static enum omap_dss_update_mode sdi_display_get_update_mode(234234+ struct omap_dss_device *dssdev)235235+{236236+ return sdi.update_enabled ? OMAP_DSS_UPDATE_AUTO :237237+ OMAP_DSS_UPDATE_DISABLED;238238+}239239+240240+static void sdi_get_timings(struct omap_dss_device *dssdev,241241+ struct omap_video_timings *timings)242242+{243243+ *timings = dssdev->panel.timings;244244+}245245+246246+int sdi_init_display(struct omap_dss_device *dssdev)247247+{248248+ DSSDBG("SDI init\n");249249+250250+ dssdev->enable = sdi_display_enable;251251+ dssdev->disable = sdi_display_disable;252252+ dssdev->suspend = sdi_display_suspend;253253+ dssdev->resume = sdi_display_resume;254254+ dssdev->set_update_mode = sdi_display_set_update_mode;255255+ dssdev->get_update_mode = sdi_display_get_update_mode;256256+ dssdev->get_timings = sdi_get_timings;257257+258258+ return 0;259259+}260260+261261+int sdi_init(bool skip_init)262262+{263263+ /* we store this for first display enable, then clear it */264264+ sdi.skip_init = skip_init;265265+266266+ /*267267+ * Enable clocks already here, otherwise there would be a toggle268268+ * of them until sdi_display_enable is called.269269+ */270270+ if (skip_init)271271+ dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);272272+ return 0;273273+}274274+275275+void sdi_exit(void)276276+{277277+}