Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

OMAP: DSS2: SDI driver

SDI (Serial Display Interface) implements TI Flatlink 3G display
interface.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@nokia.com>

+277
+277
drivers/video/omap2/dss/sdi.c
··· 1 + /* 2 + * linux/drivers/video/omap2/dss/sdi.c 3 + * 4 + * Copyright (C) 2009 Nokia Corporation 5 + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> 6 + * 7 + * This program is free software; you can redistribute it and/or modify it 8 + * under the terms of the GNU General Public License version 2 as published by 9 + * the Free Software Foundation. 10 + * 11 + * This program is distributed in the hope that it will be useful, but WITHOUT 12 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 14 + * more details. 15 + * 16 + * You should have received a copy of the GNU General Public License along with 17 + * this program. If not, see <http://www.gnu.org/licenses/>. 18 + */ 19 + 20 + #define DSS_SUBSYS_NAME "SDI" 21 + 22 + #include <linux/kernel.h> 23 + #include <linux/clk.h> 24 + #include <linux/delay.h> 25 + #include <linux/err.h> 26 + 27 + #include <plat/display.h> 28 + #include "dss.h" 29 + 30 + static struct { 31 + bool skip_init; 32 + bool update_enabled; 33 + } sdi; 34 + 35 + static void sdi_basic_init(void) 36 + { 37 + dispc_set_parallel_interface_mode(OMAP_DSS_PARALLELMODE_BYPASS); 38 + 39 + dispc_set_lcd_display_type(OMAP_DSS_LCD_DISPLAY_TFT); 40 + dispc_set_tft_data_lines(24); 41 + dispc_lcd_enable_signal_polarity(1); 42 + } 43 + 44 + static int sdi_display_enable(struct omap_dss_device *dssdev) 45 + { 46 + struct omap_video_timings *t = &dssdev->panel.timings; 47 + struct dss_clock_info dss_cinfo; 48 + struct dispc_clock_info dispc_cinfo; 49 + u16 lck_div, pck_div; 50 + unsigned long fck; 51 + unsigned long pck; 52 + int r; 53 + 54 + r = omap_dss_start_device(dssdev); 55 + if (r) { 56 + DSSERR("failed to start device\n"); 57 + goto err0; 58 + } 59 + 60 + if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) { 61 + DSSERR("dssdev already enabled\n"); 62 + r = -EINVAL; 63 + goto err1; 64 + } 65 + 66 + /* In case of skip_init sdi_init has already enabled the clocks */ 67 + if (!sdi.skip_init) 68 + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); 69 + 70 + sdi_basic_init(); 71 + 72 + /* 15.5.9.1.2 */ 73 + dssdev->panel.config |= OMAP_DSS_LCD_RF | OMAP_DSS_LCD_ONOFF; 74 + 75 + dispc_set_pol_freq(dssdev->panel.config, dssdev->panel.acbi, 76 + dssdev->panel.acb); 77 + 78 + if (!sdi.skip_init) { 79 + r = dss_calc_clock_div(1, t->pixel_clock * 1000, 80 + &dss_cinfo, &dispc_cinfo); 81 + } else { 82 + r = dss_get_clock_div(&dss_cinfo); 83 + r = dispc_get_clock_div(&dispc_cinfo); 84 + } 85 + 86 + if (r) 87 + goto err2; 88 + 89 + fck = dss_cinfo.fck; 90 + lck_div = dispc_cinfo.lck_div; 91 + pck_div = dispc_cinfo.pck_div; 92 + 93 + pck = fck / lck_div / pck_div / 1000; 94 + 95 + if (pck != t->pixel_clock) { 96 + DSSWARN("Could not find exact pixel clock. Requested %d kHz, " 97 + "got %lu kHz\n", 98 + t->pixel_clock, pck); 99 + 100 + t->pixel_clock = pck; 101 + } 102 + 103 + 104 + dispc_set_lcd_timings(t); 105 + 106 + r = dss_set_clock_div(&dss_cinfo); 107 + if (r) 108 + goto err2; 109 + 110 + r = dispc_set_clock_div(&dispc_cinfo); 111 + if (r) 112 + goto err2; 113 + 114 + if (!sdi.skip_init) { 115 + dss_sdi_init(dssdev->phy.sdi.datapairs); 116 + r = dss_sdi_enable(); 117 + if (r) 118 + goto err1; 119 + mdelay(2); 120 + } 121 + 122 + dispc_enable_lcd_out(1); 123 + 124 + if (dssdev->driver->enable) { 125 + r = dssdev->driver->enable(dssdev); 126 + if (r) 127 + goto err3; 128 + } 129 + 130 + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; 131 + 132 + sdi.skip_init = 0; 133 + 134 + return 0; 135 + err3: 136 + dispc_enable_lcd_out(0); 137 + err2: 138 + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); 139 + err1: 140 + omap_dss_stop_device(dssdev); 141 + err0: 142 + return r; 143 + } 144 + 145 + static int sdi_display_resume(struct omap_dss_device *dssdev); 146 + 147 + static void sdi_display_disable(struct omap_dss_device *dssdev) 148 + { 149 + if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED) 150 + return; 151 + 152 + if (dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) 153 + if (sdi_display_resume(dssdev)) 154 + return; 155 + 156 + if (dssdev->driver->disable) 157 + dssdev->driver->disable(dssdev); 158 + 159 + dispc_enable_lcd_out(0); 160 + 161 + dss_sdi_disable(); 162 + 163 + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); 164 + 165 + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; 166 + 167 + omap_dss_stop_device(dssdev); 168 + } 169 + 170 + static int sdi_display_suspend(struct omap_dss_device *dssdev) 171 + { 172 + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) 173 + return -EINVAL; 174 + 175 + if (dssdev->driver->suspend) 176 + dssdev->driver->suspend(dssdev); 177 + 178 + dispc_enable_lcd_out(0); 179 + 180 + dss_sdi_disable(); 181 + 182 + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); 183 + 184 + dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; 185 + 186 + return 0; 187 + } 188 + 189 + static int sdi_display_resume(struct omap_dss_device *dssdev) 190 + { 191 + int r; 192 + 193 + if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) 194 + return -EINVAL; 195 + 196 + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); 197 + 198 + r = dss_sdi_enable(); 199 + if (r) 200 + goto err; 201 + mdelay(2); 202 + 203 + dispc_enable_lcd_out(1); 204 + 205 + if (dssdev->driver->resume) 206 + dssdev->driver->resume(dssdev); 207 + 208 + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; 209 + 210 + return 0; 211 + err: 212 + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); 213 + return r; 214 + } 215 + 216 + static int sdi_display_set_update_mode(struct omap_dss_device *dssdev, 217 + enum omap_dss_update_mode mode) 218 + { 219 + if (mode == OMAP_DSS_UPDATE_MANUAL) 220 + return -EINVAL; 221 + 222 + if (mode == OMAP_DSS_UPDATE_DISABLED) { 223 + dispc_enable_lcd_out(0); 224 + sdi.update_enabled = 0; 225 + } else { 226 + dispc_enable_lcd_out(1); 227 + sdi.update_enabled = 1; 228 + } 229 + 230 + return 0; 231 + } 232 + 233 + static enum omap_dss_update_mode sdi_display_get_update_mode( 234 + struct omap_dss_device *dssdev) 235 + { 236 + return sdi.update_enabled ? OMAP_DSS_UPDATE_AUTO : 237 + OMAP_DSS_UPDATE_DISABLED; 238 + } 239 + 240 + static void sdi_get_timings(struct omap_dss_device *dssdev, 241 + struct omap_video_timings *timings) 242 + { 243 + *timings = dssdev->panel.timings; 244 + } 245 + 246 + int sdi_init_display(struct omap_dss_device *dssdev) 247 + { 248 + DSSDBG("SDI init\n"); 249 + 250 + dssdev->enable = sdi_display_enable; 251 + dssdev->disable = sdi_display_disable; 252 + dssdev->suspend = sdi_display_suspend; 253 + dssdev->resume = sdi_display_resume; 254 + dssdev->set_update_mode = sdi_display_set_update_mode; 255 + dssdev->get_update_mode = sdi_display_get_update_mode; 256 + dssdev->get_timings = sdi_get_timings; 257 + 258 + return 0; 259 + } 260 + 261 + int sdi_init(bool skip_init) 262 + { 263 + /* we store this for first display enable, then clear it */ 264 + sdi.skip_init = skip_init; 265 + 266 + /* 267 + * Enable clocks already here, otherwise there would be a toggle 268 + * of them until sdi_display_enable is called. 269 + */ 270 + if (skip_init) 271 + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); 272 + return 0; 273 + } 274 + 275 + void sdi_exit(void) 276 + { 277 + }