1*9960aa7cSTomi Valkeinen /* 2*9960aa7cSTomi Valkeinen * linux/drivers/video/omap2/dss/sdi.c 3*9960aa7cSTomi Valkeinen * 4*9960aa7cSTomi Valkeinen * Copyright (C) 2009 Nokia Corporation 5*9960aa7cSTomi Valkeinen * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> 6*9960aa7cSTomi Valkeinen * 7*9960aa7cSTomi Valkeinen * This program is free software; you can redistribute it and/or modify it 8*9960aa7cSTomi Valkeinen * under the terms of the GNU General Public License version 2 as published by 9*9960aa7cSTomi Valkeinen * the Free Software Foundation. 10*9960aa7cSTomi Valkeinen * 11*9960aa7cSTomi Valkeinen * This program is distributed in the hope that it will be useful, but WITHOUT 12*9960aa7cSTomi Valkeinen * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13*9960aa7cSTomi Valkeinen * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 14*9960aa7cSTomi Valkeinen * more details. 15*9960aa7cSTomi Valkeinen * 16*9960aa7cSTomi Valkeinen * You should have received a copy of the GNU General Public License along with 17*9960aa7cSTomi Valkeinen * this program. If not, see <http://www.gnu.org/licenses/>. 18*9960aa7cSTomi Valkeinen */ 19*9960aa7cSTomi Valkeinen 20*9960aa7cSTomi Valkeinen #define DSS_SUBSYS_NAME "SDI" 21*9960aa7cSTomi Valkeinen 22*9960aa7cSTomi Valkeinen #include <linux/kernel.h> 23*9960aa7cSTomi Valkeinen #include <linux/delay.h> 24*9960aa7cSTomi Valkeinen #include <linux/err.h> 25*9960aa7cSTomi Valkeinen #include <linux/regulator/consumer.h> 26*9960aa7cSTomi Valkeinen #include <linux/export.h> 27*9960aa7cSTomi Valkeinen #include <linux/platform_device.h> 28*9960aa7cSTomi Valkeinen #include <linux/string.h> 29*9960aa7cSTomi Valkeinen #include <linux/of.h> 30*9960aa7cSTomi Valkeinen #include <linux/component.h> 31*9960aa7cSTomi Valkeinen 32*9960aa7cSTomi Valkeinen #include <video/omapdss.h> 33*9960aa7cSTomi Valkeinen #include "dss.h" 34*9960aa7cSTomi Valkeinen 35*9960aa7cSTomi Valkeinen static struct { 36*9960aa7cSTomi Valkeinen struct platform_device *pdev; 37*9960aa7cSTomi Valkeinen 38*9960aa7cSTomi Valkeinen bool update_enabled; 39*9960aa7cSTomi Valkeinen struct regulator *vdds_sdi_reg; 40*9960aa7cSTomi Valkeinen 41*9960aa7cSTomi Valkeinen struct dss_lcd_mgr_config mgr_config; 42*9960aa7cSTomi Valkeinen struct omap_video_timings timings; 43*9960aa7cSTomi Valkeinen int datapairs; 44*9960aa7cSTomi Valkeinen 45*9960aa7cSTomi Valkeinen struct omap_dss_device output; 46*9960aa7cSTomi Valkeinen 47*9960aa7cSTomi Valkeinen bool port_initialized; 48*9960aa7cSTomi Valkeinen } sdi; 49*9960aa7cSTomi Valkeinen 50*9960aa7cSTomi Valkeinen struct sdi_clk_calc_ctx { 51*9960aa7cSTomi Valkeinen unsigned long pck_min, pck_max; 52*9960aa7cSTomi Valkeinen 53*9960aa7cSTomi Valkeinen unsigned long fck; 54*9960aa7cSTomi Valkeinen struct dispc_clock_info dispc_cinfo; 55*9960aa7cSTomi Valkeinen }; 56*9960aa7cSTomi Valkeinen 57*9960aa7cSTomi Valkeinen static bool dpi_calc_dispc_cb(int lckd, int pckd, unsigned long lck, 58*9960aa7cSTomi Valkeinen unsigned long pck, void *data) 59*9960aa7cSTomi Valkeinen { 60*9960aa7cSTomi Valkeinen struct sdi_clk_calc_ctx *ctx = data; 61*9960aa7cSTomi Valkeinen 62*9960aa7cSTomi Valkeinen ctx->dispc_cinfo.lck_div = lckd; 63*9960aa7cSTomi Valkeinen ctx->dispc_cinfo.pck_div = pckd; 64*9960aa7cSTomi Valkeinen ctx->dispc_cinfo.lck = lck; 65*9960aa7cSTomi Valkeinen ctx->dispc_cinfo.pck = pck; 66*9960aa7cSTomi Valkeinen 67*9960aa7cSTomi Valkeinen return true; 68*9960aa7cSTomi Valkeinen } 69*9960aa7cSTomi Valkeinen 70*9960aa7cSTomi Valkeinen static bool dpi_calc_dss_cb(unsigned long fck, void *data) 71*9960aa7cSTomi Valkeinen { 72*9960aa7cSTomi Valkeinen struct sdi_clk_calc_ctx *ctx = data; 73*9960aa7cSTomi Valkeinen 74*9960aa7cSTomi Valkeinen ctx->fck = fck; 75*9960aa7cSTomi Valkeinen 76*9960aa7cSTomi Valkeinen return dispc_div_calc(fck, ctx->pck_min, ctx->pck_max, 77*9960aa7cSTomi Valkeinen dpi_calc_dispc_cb, ctx); 78*9960aa7cSTomi Valkeinen } 79*9960aa7cSTomi Valkeinen 80*9960aa7cSTomi Valkeinen static int sdi_calc_clock_div(unsigned long pclk, 81*9960aa7cSTomi Valkeinen unsigned long *fck, 82*9960aa7cSTomi Valkeinen struct dispc_clock_info *dispc_cinfo) 83*9960aa7cSTomi Valkeinen { 84*9960aa7cSTomi Valkeinen int i; 85*9960aa7cSTomi Valkeinen struct sdi_clk_calc_ctx ctx; 86*9960aa7cSTomi Valkeinen 87*9960aa7cSTomi Valkeinen /* 88*9960aa7cSTomi Valkeinen * DSS fclk gives us very few possibilities, so finding a good pixel 89*9960aa7cSTomi Valkeinen * clock may not be possible. We try multiple times to find the clock, 90*9960aa7cSTomi Valkeinen * each time widening the pixel clock range we look for, up to 91*9960aa7cSTomi Valkeinen * +/- 1MHz. 92*9960aa7cSTomi Valkeinen */ 93*9960aa7cSTomi Valkeinen 94*9960aa7cSTomi Valkeinen for (i = 0; i < 10; ++i) { 95*9960aa7cSTomi Valkeinen bool ok; 96*9960aa7cSTomi Valkeinen 97*9960aa7cSTomi Valkeinen memset(&ctx, 0, sizeof(ctx)); 98*9960aa7cSTomi Valkeinen if (pclk > 1000 * i * i * i) 99*9960aa7cSTomi Valkeinen ctx.pck_min = max(pclk - 1000 * i * i * i, 0lu); 100*9960aa7cSTomi Valkeinen else 101*9960aa7cSTomi Valkeinen ctx.pck_min = 0; 102*9960aa7cSTomi Valkeinen ctx.pck_max = pclk + 1000 * i * i * i; 103*9960aa7cSTomi Valkeinen 104*9960aa7cSTomi Valkeinen ok = dss_div_calc(pclk, ctx.pck_min, dpi_calc_dss_cb, &ctx); 105*9960aa7cSTomi Valkeinen if (ok) { 106*9960aa7cSTomi Valkeinen *fck = ctx.fck; 107*9960aa7cSTomi Valkeinen *dispc_cinfo = ctx.dispc_cinfo; 108*9960aa7cSTomi Valkeinen return 0; 109*9960aa7cSTomi Valkeinen } 110*9960aa7cSTomi Valkeinen } 111*9960aa7cSTomi Valkeinen 112*9960aa7cSTomi Valkeinen return -EINVAL; 113*9960aa7cSTomi Valkeinen } 114*9960aa7cSTomi Valkeinen 115*9960aa7cSTomi Valkeinen static void sdi_config_lcd_manager(struct omap_dss_device *dssdev) 116*9960aa7cSTomi Valkeinen { 117*9960aa7cSTomi Valkeinen struct omap_overlay_manager *mgr = sdi.output.manager; 118*9960aa7cSTomi Valkeinen 119*9960aa7cSTomi Valkeinen sdi.mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS; 120*9960aa7cSTomi Valkeinen 121*9960aa7cSTomi Valkeinen sdi.mgr_config.stallmode = false; 122*9960aa7cSTomi Valkeinen sdi.mgr_config.fifohandcheck = false; 123*9960aa7cSTomi Valkeinen 124*9960aa7cSTomi Valkeinen sdi.mgr_config.video_port_width = 24; 125*9960aa7cSTomi Valkeinen sdi.mgr_config.lcden_sig_polarity = 1; 126*9960aa7cSTomi Valkeinen 127*9960aa7cSTomi Valkeinen dss_mgr_set_lcd_config(mgr, &sdi.mgr_config); 128*9960aa7cSTomi Valkeinen } 129*9960aa7cSTomi Valkeinen 130*9960aa7cSTomi Valkeinen static int sdi_display_enable(struct omap_dss_device *dssdev) 131*9960aa7cSTomi Valkeinen { 132*9960aa7cSTomi Valkeinen struct omap_dss_device *out = &sdi.output; 133*9960aa7cSTomi Valkeinen struct omap_video_timings *t = &sdi.timings; 134*9960aa7cSTomi Valkeinen unsigned long fck; 135*9960aa7cSTomi Valkeinen struct dispc_clock_info dispc_cinfo; 136*9960aa7cSTomi Valkeinen unsigned long pck; 137*9960aa7cSTomi Valkeinen int r; 138*9960aa7cSTomi Valkeinen 139*9960aa7cSTomi Valkeinen if (out->manager == NULL) { 140*9960aa7cSTomi Valkeinen DSSERR("failed to enable display: no output/manager\n"); 141*9960aa7cSTomi Valkeinen return -ENODEV; 142*9960aa7cSTomi Valkeinen } 143*9960aa7cSTomi Valkeinen 144*9960aa7cSTomi Valkeinen r = regulator_enable(sdi.vdds_sdi_reg); 145*9960aa7cSTomi Valkeinen if (r) 146*9960aa7cSTomi Valkeinen goto err_reg_enable; 147*9960aa7cSTomi Valkeinen 148*9960aa7cSTomi Valkeinen r = dispc_runtime_get(); 149*9960aa7cSTomi Valkeinen if (r) 150*9960aa7cSTomi Valkeinen goto err_get_dispc; 151*9960aa7cSTomi Valkeinen 152*9960aa7cSTomi Valkeinen /* 15.5.9.1.2 */ 153*9960aa7cSTomi Valkeinen t->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; 154*9960aa7cSTomi Valkeinen t->sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; 155*9960aa7cSTomi Valkeinen 156*9960aa7cSTomi Valkeinen r = sdi_calc_clock_div(t->pixelclock, &fck, &dispc_cinfo); 157*9960aa7cSTomi Valkeinen if (r) 158*9960aa7cSTomi Valkeinen goto err_calc_clock_div; 159*9960aa7cSTomi Valkeinen 160*9960aa7cSTomi Valkeinen sdi.mgr_config.clock_info = dispc_cinfo; 161*9960aa7cSTomi Valkeinen 162*9960aa7cSTomi Valkeinen pck = fck / dispc_cinfo.lck_div / dispc_cinfo.pck_div; 163*9960aa7cSTomi Valkeinen 164*9960aa7cSTomi Valkeinen if (pck != t->pixelclock) { 165*9960aa7cSTomi Valkeinen DSSWARN("Could not find exact pixel clock. Requested %d Hz, got %lu Hz\n", 166*9960aa7cSTomi Valkeinen t->pixelclock, pck); 167*9960aa7cSTomi Valkeinen 168*9960aa7cSTomi Valkeinen t->pixelclock = pck; 169*9960aa7cSTomi Valkeinen } 170*9960aa7cSTomi Valkeinen 171*9960aa7cSTomi Valkeinen 172*9960aa7cSTomi Valkeinen dss_mgr_set_timings(out->manager, t); 173*9960aa7cSTomi Valkeinen 174*9960aa7cSTomi Valkeinen r = dss_set_fck_rate(fck); 175*9960aa7cSTomi Valkeinen if (r) 176*9960aa7cSTomi Valkeinen goto err_set_dss_clock_div; 177*9960aa7cSTomi Valkeinen 178*9960aa7cSTomi Valkeinen sdi_config_lcd_manager(dssdev); 179*9960aa7cSTomi Valkeinen 180*9960aa7cSTomi Valkeinen /* 181*9960aa7cSTomi Valkeinen * LCLK and PCLK divisors are located in shadow registers, and we 182*9960aa7cSTomi Valkeinen * normally write them to DISPC registers when enabling the output. 183*9960aa7cSTomi Valkeinen * However, SDI uses pck-free as source clock for its PLL, and pck-free 184*9960aa7cSTomi Valkeinen * is affected by the divisors. And as we need the PLL before enabling 185*9960aa7cSTomi Valkeinen * the output, we need to write the divisors early. 186*9960aa7cSTomi Valkeinen * 187*9960aa7cSTomi Valkeinen * It seems just writing to the DISPC register is enough, and we don't 188*9960aa7cSTomi Valkeinen * need to care about the shadow register mechanism for pck-free. The 189*9960aa7cSTomi Valkeinen * exact reason for this is unknown. 190*9960aa7cSTomi Valkeinen */ 191*9960aa7cSTomi Valkeinen dispc_mgr_set_clock_div(out->manager->id, &sdi.mgr_config.clock_info); 192*9960aa7cSTomi Valkeinen 193*9960aa7cSTomi Valkeinen dss_sdi_init(sdi.datapairs); 194*9960aa7cSTomi Valkeinen r = dss_sdi_enable(); 195*9960aa7cSTomi Valkeinen if (r) 196*9960aa7cSTomi Valkeinen goto err_sdi_enable; 197*9960aa7cSTomi Valkeinen mdelay(2); 198*9960aa7cSTomi Valkeinen 199*9960aa7cSTomi Valkeinen r = dss_mgr_enable(out->manager); 200*9960aa7cSTomi Valkeinen if (r) 201*9960aa7cSTomi Valkeinen goto err_mgr_enable; 202*9960aa7cSTomi Valkeinen 203*9960aa7cSTomi Valkeinen return 0; 204*9960aa7cSTomi Valkeinen 205*9960aa7cSTomi Valkeinen err_mgr_enable: 206*9960aa7cSTomi Valkeinen dss_sdi_disable(); 207*9960aa7cSTomi Valkeinen err_sdi_enable: 208*9960aa7cSTomi Valkeinen err_set_dss_clock_div: 209*9960aa7cSTomi Valkeinen err_calc_clock_div: 210*9960aa7cSTomi Valkeinen dispc_runtime_put(); 211*9960aa7cSTomi Valkeinen err_get_dispc: 212*9960aa7cSTomi Valkeinen regulator_disable(sdi.vdds_sdi_reg); 213*9960aa7cSTomi Valkeinen err_reg_enable: 214*9960aa7cSTomi Valkeinen return r; 215*9960aa7cSTomi Valkeinen } 216*9960aa7cSTomi Valkeinen 217*9960aa7cSTomi Valkeinen static void sdi_display_disable(struct omap_dss_device *dssdev) 218*9960aa7cSTomi Valkeinen { 219*9960aa7cSTomi Valkeinen struct omap_overlay_manager *mgr = sdi.output.manager; 220*9960aa7cSTomi Valkeinen 221*9960aa7cSTomi Valkeinen dss_mgr_disable(mgr); 222*9960aa7cSTomi Valkeinen 223*9960aa7cSTomi Valkeinen dss_sdi_disable(); 224*9960aa7cSTomi Valkeinen 225*9960aa7cSTomi Valkeinen dispc_runtime_put(); 226*9960aa7cSTomi Valkeinen 227*9960aa7cSTomi Valkeinen regulator_disable(sdi.vdds_sdi_reg); 228*9960aa7cSTomi Valkeinen } 229*9960aa7cSTomi Valkeinen 230*9960aa7cSTomi Valkeinen static void sdi_set_timings(struct omap_dss_device *dssdev, 231*9960aa7cSTomi Valkeinen struct omap_video_timings *timings) 232*9960aa7cSTomi Valkeinen { 233*9960aa7cSTomi Valkeinen sdi.timings = *timings; 234*9960aa7cSTomi Valkeinen } 235*9960aa7cSTomi Valkeinen 236*9960aa7cSTomi Valkeinen static void sdi_get_timings(struct omap_dss_device *dssdev, 237*9960aa7cSTomi Valkeinen struct omap_video_timings *timings) 238*9960aa7cSTomi Valkeinen { 239*9960aa7cSTomi Valkeinen *timings = sdi.timings; 240*9960aa7cSTomi Valkeinen } 241*9960aa7cSTomi Valkeinen 242*9960aa7cSTomi Valkeinen static int sdi_check_timings(struct omap_dss_device *dssdev, 243*9960aa7cSTomi Valkeinen struct omap_video_timings *timings) 244*9960aa7cSTomi Valkeinen { 245*9960aa7cSTomi Valkeinen struct omap_overlay_manager *mgr = sdi.output.manager; 246*9960aa7cSTomi Valkeinen 247*9960aa7cSTomi Valkeinen if (mgr && !dispc_mgr_timings_ok(mgr->id, timings)) 248*9960aa7cSTomi Valkeinen return -EINVAL; 249*9960aa7cSTomi Valkeinen 250*9960aa7cSTomi Valkeinen if (timings->pixelclock == 0) 251*9960aa7cSTomi Valkeinen return -EINVAL; 252*9960aa7cSTomi Valkeinen 253*9960aa7cSTomi Valkeinen return 0; 254*9960aa7cSTomi Valkeinen } 255*9960aa7cSTomi Valkeinen 256*9960aa7cSTomi Valkeinen static void sdi_set_datapairs(struct omap_dss_device *dssdev, int datapairs) 257*9960aa7cSTomi Valkeinen { 258*9960aa7cSTomi Valkeinen sdi.datapairs = datapairs; 259*9960aa7cSTomi Valkeinen } 260*9960aa7cSTomi Valkeinen 261*9960aa7cSTomi Valkeinen static int sdi_init_regulator(void) 262*9960aa7cSTomi Valkeinen { 263*9960aa7cSTomi Valkeinen struct regulator *vdds_sdi; 264*9960aa7cSTomi Valkeinen 265*9960aa7cSTomi Valkeinen if (sdi.vdds_sdi_reg) 266*9960aa7cSTomi Valkeinen return 0; 267*9960aa7cSTomi Valkeinen 268*9960aa7cSTomi Valkeinen vdds_sdi = devm_regulator_get(&sdi.pdev->dev, "vdds_sdi"); 269*9960aa7cSTomi Valkeinen if (IS_ERR(vdds_sdi)) { 270*9960aa7cSTomi Valkeinen if (PTR_ERR(vdds_sdi) != -EPROBE_DEFER) 271*9960aa7cSTomi Valkeinen DSSERR("can't get VDDS_SDI regulator\n"); 272*9960aa7cSTomi Valkeinen return PTR_ERR(vdds_sdi); 273*9960aa7cSTomi Valkeinen } 274*9960aa7cSTomi Valkeinen 275*9960aa7cSTomi Valkeinen sdi.vdds_sdi_reg = vdds_sdi; 276*9960aa7cSTomi Valkeinen 277*9960aa7cSTomi Valkeinen return 0; 278*9960aa7cSTomi Valkeinen } 279*9960aa7cSTomi Valkeinen 280*9960aa7cSTomi Valkeinen static int sdi_connect(struct omap_dss_device *dssdev, 281*9960aa7cSTomi Valkeinen struct omap_dss_device *dst) 282*9960aa7cSTomi Valkeinen { 283*9960aa7cSTomi Valkeinen struct omap_overlay_manager *mgr; 284*9960aa7cSTomi Valkeinen int r; 285*9960aa7cSTomi Valkeinen 286*9960aa7cSTomi Valkeinen r = sdi_init_regulator(); 287*9960aa7cSTomi Valkeinen if (r) 288*9960aa7cSTomi Valkeinen return r; 289*9960aa7cSTomi Valkeinen 290*9960aa7cSTomi Valkeinen mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel); 291*9960aa7cSTomi Valkeinen if (!mgr) 292*9960aa7cSTomi Valkeinen return -ENODEV; 293*9960aa7cSTomi Valkeinen 294*9960aa7cSTomi Valkeinen r = dss_mgr_connect(mgr, dssdev); 295*9960aa7cSTomi Valkeinen if (r) 296*9960aa7cSTomi Valkeinen return r; 297*9960aa7cSTomi Valkeinen 298*9960aa7cSTomi Valkeinen r = omapdss_output_set_device(dssdev, dst); 299*9960aa7cSTomi Valkeinen if (r) { 300*9960aa7cSTomi Valkeinen DSSERR("failed to connect output to new device: %s\n", 301*9960aa7cSTomi Valkeinen dst->name); 302*9960aa7cSTomi Valkeinen dss_mgr_disconnect(mgr, dssdev); 303*9960aa7cSTomi Valkeinen return r; 304*9960aa7cSTomi Valkeinen } 305*9960aa7cSTomi Valkeinen 306*9960aa7cSTomi Valkeinen return 0; 307*9960aa7cSTomi Valkeinen } 308*9960aa7cSTomi Valkeinen 309*9960aa7cSTomi Valkeinen static void sdi_disconnect(struct omap_dss_device *dssdev, 310*9960aa7cSTomi Valkeinen struct omap_dss_device *dst) 311*9960aa7cSTomi Valkeinen { 312*9960aa7cSTomi Valkeinen WARN_ON(dst != dssdev->dst); 313*9960aa7cSTomi Valkeinen 314*9960aa7cSTomi Valkeinen if (dst != dssdev->dst) 315*9960aa7cSTomi Valkeinen return; 316*9960aa7cSTomi Valkeinen 317*9960aa7cSTomi Valkeinen omapdss_output_unset_device(dssdev); 318*9960aa7cSTomi Valkeinen 319*9960aa7cSTomi Valkeinen if (dssdev->manager) 320*9960aa7cSTomi Valkeinen dss_mgr_disconnect(dssdev->manager, dssdev); 321*9960aa7cSTomi Valkeinen } 322*9960aa7cSTomi Valkeinen 323*9960aa7cSTomi Valkeinen static const struct omapdss_sdi_ops sdi_ops = { 324*9960aa7cSTomi Valkeinen .connect = sdi_connect, 325*9960aa7cSTomi Valkeinen .disconnect = sdi_disconnect, 326*9960aa7cSTomi Valkeinen 327*9960aa7cSTomi Valkeinen .enable = sdi_display_enable, 328*9960aa7cSTomi Valkeinen .disable = sdi_display_disable, 329*9960aa7cSTomi Valkeinen 330*9960aa7cSTomi Valkeinen .check_timings = sdi_check_timings, 331*9960aa7cSTomi Valkeinen .set_timings = sdi_set_timings, 332*9960aa7cSTomi Valkeinen .get_timings = sdi_get_timings, 333*9960aa7cSTomi Valkeinen 334*9960aa7cSTomi Valkeinen .set_datapairs = sdi_set_datapairs, 335*9960aa7cSTomi Valkeinen }; 336*9960aa7cSTomi Valkeinen 337*9960aa7cSTomi Valkeinen static void sdi_init_output(struct platform_device *pdev) 338*9960aa7cSTomi Valkeinen { 339*9960aa7cSTomi Valkeinen struct omap_dss_device *out = &sdi.output; 340*9960aa7cSTomi Valkeinen 341*9960aa7cSTomi Valkeinen out->dev = &pdev->dev; 342*9960aa7cSTomi Valkeinen out->id = OMAP_DSS_OUTPUT_SDI; 343*9960aa7cSTomi Valkeinen out->output_type = OMAP_DISPLAY_TYPE_SDI; 344*9960aa7cSTomi Valkeinen out->name = "sdi.0"; 345*9960aa7cSTomi Valkeinen out->dispc_channel = OMAP_DSS_CHANNEL_LCD; 346*9960aa7cSTomi Valkeinen /* We have SDI only on OMAP3, where it's on port 1 */ 347*9960aa7cSTomi Valkeinen out->port_num = 1; 348*9960aa7cSTomi Valkeinen out->ops.sdi = &sdi_ops; 349*9960aa7cSTomi Valkeinen out->owner = THIS_MODULE; 350*9960aa7cSTomi Valkeinen 351*9960aa7cSTomi Valkeinen omapdss_register_output(out); 352*9960aa7cSTomi Valkeinen } 353*9960aa7cSTomi Valkeinen 354*9960aa7cSTomi Valkeinen static void sdi_uninit_output(struct platform_device *pdev) 355*9960aa7cSTomi Valkeinen { 356*9960aa7cSTomi Valkeinen struct omap_dss_device *out = &sdi.output; 357*9960aa7cSTomi Valkeinen 358*9960aa7cSTomi Valkeinen omapdss_unregister_output(out); 359*9960aa7cSTomi Valkeinen } 360*9960aa7cSTomi Valkeinen 361*9960aa7cSTomi Valkeinen static int sdi_bind(struct device *dev, struct device *master, void *data) 362*9960aa7cSTomi Valkeinen { 363*9960aa7cSTomi Valkeinen struct platform_device *pdev = to_platform_device(dev); 364*9960aa7cSTomi Valkeinen 365*9960aa7cSTomi Valkeinen sdi.pdev = pdev; 366*9960aa7cSTomi Valkeinen 367*9960aa7cSTomi Valkeinen sdi_init_output(pdev); 368*9960aa7cSTomi Valkeinen 369*9960aa7cSTomi Valkeinen return 0; 370*9960aa7cSTomi Valkeinen } 371*9960aa7cSTomi Valkeinen 372*9960aa7cSTomi Valkeinen static void sdi_unbind(struct device *dev, struct device *master, void *data) 373*9960aa7cSTomi Valkeinen { 374*9960aa7cSTomi Valkeinen struct platform_device *pdev = to_platform_device(dev); 375*9960aa7cSTomi Valkeinen 376*9960aa7cSTomi Valkeinen sdi_uninit_output(pdev); 377*9960aa7cSTomi Valkeinen } 378*9960aa7cSTomi Valkeinen 379*9960aa7cSTomi Valkeinen static const struct component_ops sdi_component_ops = { 380*9960aa7cSTomi Valkeinen .bind = sdi_bind, 381*9960aa7cSTomi Valkeinen .unbind = sdi_unbind, 382*9960aa7cSTomi Valkeinen }; 383*9960aa7cSTomi Valkeinen 384*9960aa7cSTomi Valkeinen static int sdi_probe(struct platform_device *pdev) 385*9960aa7cSTomi Valkeinen { 386*9960aa7cSTomi Valkeinen return component_add(&pdev->dev, &sdi_component_ops); 387*9960aa7cSTomi Valkeinen } 388*9960aa7cSTomi Valkeinen 389*9960aa7cSTomi Valkeinen static int sdi_remove(struct platform_device *pdev) 390*9960aa7cSTomi Valkeinen { 391*9960aa7cSTomi Valkeinen component_del(&pdev->dev, &sdi_component_ops); 392*9960aa7cSTomi Valkeinen return 0; 393*9960aa7cSTomi Valkeinen } 394*9960aa7cSTomi Valkeinen 395*9960aa7cSTomi Valkeinen static struct platform_driver omap_sdi_driver = { 396*9960aa7cSTomi Valkeinen .probe = sdi_probe, 397*9960aa7cSTomi Valkeinen .remove = sdi_remove, 398*9960aa7cSTomi Valkeinen .driver = { 399*9960aa7cSTomi Valkeinen .name = "omapdss_sdi", 400*9960aa7cSTomi Valkeinen .suppress_bind_attrs = true, 401*9960aa7cSTomi Valkeinen }, 402*9960aa7cSTomi Valkeinen }; 403*9960aa7cSTomi Valkeinen 404*9960aa7cSTomi Valkeinen int __init sdi_init_platform_driver(void) 405*9960aa7cSTomi Valkeinen { 406*9960aa7cSTomi Valkeinen return platform_driver_register(&omap_sdi_driver); 407*9960aa7cSTomi Valkeinen } 408*9960aa7cSTomi Valkeinen 409*9960aa7cSTomi Valkeinen void sdi_uninit_platform_driver(void) 410*9960aa7cSTomi Valkeinen { 411*9960aa7cSTomi Valkeinen platform_driver_unregister(&omap_sdi_driver); 412*9960aa7cSTomi Valkeinen } 413*9960aa7cSTomi Valkeinen 414*9960aa7cSTomi Valkeinen int sdi_init_port(struct platform_device *pdev, struct device_node *port) 415*9960aa7cSTomi Valkeinen { 416*9960aa7cSTomi Valkeinen struct device_node *ep; 417*9960aa7cSTomi Valkeinen u32 datapairs; 418*9960aa7cSTomi Valkeinen int r; 419*9960aa7cSTomi Valkeinen 420*9960aa7cSTomi Valkeinen ep = omapdss_of_get_next_endpoint(port, NULL); 421*9960aa7cSTomi Valkeinen if (!ep) 422*9960aa7cSTomi Valkeinen return 0; 423*9960aa7cSTomi Valkeinen 424*9960aa7cSTomi Valkeinen r = of_property_read_u32(ep, "datapairs", &datapairs); 425*9960aa7cSTomi Valkeinen if (r) { 426*9960aa7cSTomi Valkeinen DSSERR("failed to parse datapairs\n"); 427*9960aa7cSTomi Valkeinen goto err_datapairs; 428*9960aa7cSTomi Valkeinen } 429*9960aa7cSTomi Valkeinen 430*9960aa7cSTomi Valkeinen sdi.datapairs = datapairs; 431*9960aa7cSTomi Valkeinen 432*9960aa7cSTomi Valkeinen of_node_put(ep); 433*9960aa7cSTomi Valkeinen 434*9960aa7cSTomi Valkeinen sdi.pdev = pdev; 435*9960aa7cSTomi Valkeinen 436*9960aa7cSTomi Valkeinen sdi_init_output(pdev); 437*9960aa7cSTomi Valkeinen 438*9960aa7cSTomi Valkeinen sdi.port_initialized = true; 439*9960aa7cSTomi Valkeinen 440*9960aa7cSTomi Valkeinen return 0; 441*9960aa7cSTomi Valkeinen 442*9960aa7cSTomi Valkeinen err_datapairs: 443*9960aa7cSTomi Valkeinen of_node_put(ep); 444*9960aa7cSTomi Valkeinen 445*9960aa7cSTomi Valkeinen return r; 446*9960aa7cSTomi Valkeinen } 447*9960aa7cSTomi Valkeinen 448*9960aa7cSTomi Valkeinen void sdi_uninit_port(struct device_node *port) 449*9960aa7cSTomi Valkeinen { 450*9960aa7cSTomi Valkeinen if (!sdi.port_initialized) 451*9960aa7cSTomi Valkeinen return; 452*9960aa7cSTomi Valkeinen 453*9960aa7cSTomi Valkeinen sdi_uninit_output(sdi.pdev); 454*9960aa7cSTomi Valkeinen } 455