18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/drivers/video/omap2/dss/sdi.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2009 Nokia Corporation 68c2ecf20Sopenharmony_ci * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#define DSS_SUBSYS_NAME "SDI" 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/delay.h> 138c2ecf20Sopenharmony_ci#include <linux/err.h> 148c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 158c2ecf20Sopenharmony_ci#include <linux/export.h> 168c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 178c2ecf20Sopenharmony_ci#include <linux/string.h> 188c2ecf20Sopenharmony_ci#include <linux/of.h> 198c2ecf20Sopenharmony_ci#include <linux/component.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <video/omapfb_dss.h> 228c2ecf20Sopenharmony_ci#include "dss.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic struct { 258c2ecf20Sopenharmony_ci struct platform_device *pdev; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci bool update_enabled; 288c2ecf20Sopenharmony_ci struct regulator *vdds_sdi_reg; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci struct dss_lcd_mgr_config mgr_config; 318c2ecf20Sopenharmony_ci struct omap_video_timings timings; 328c2ecf20Sopenharmony_ci int datapairs; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci struct omap_dss_device output; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci bool port_initialized; 378c2ecf20Sopenharmony_ci} sdi; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistruct sdi_clk_calc_ctx { 408c2ecf20Sopenharmony_ci unsigned long pck_min, pck_max; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci unsigned long fck; 438c2ecf20Sopenharmony_ci struct dispc_clock_info dispc_cinfo; 448c2ecf20Sopenharmony_ci}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic bool dpi_calc_dispc_cb(int lckd, int pckd, unsigned long lck, 478c2ecf20Sopenharmony_ci unsigned long pck, void *data) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci struct sdi_clk_calc_ctx *ctx = data; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci ctx->dispc_cinfo.lck_div = lckd; 528c2ecf20Sopenharmony_ci ctx->dispc_cinfo.pck_div = pckd; 538c2ecf20Sopenharmony_ci ctx->dispc_cinfo.lck = lck; 548c2ecf20Sopenharmony_ci ctx->dispc_cinfo.pck = pck; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci return true; 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic bool dpi_calc_dss_cb(unsigned long fck, void *data) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci struct sdi_clk_calc_ctx *ctx = data; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci ctx->fck = fck; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci return dispc_div_calc(fck, ctx->pck_min, ctx->pck_max, 668c2ecf20Sopenharmony_ci dpi_calc_dispc_cb, ctx); 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic int sdi_calc_clock_div(unsigned long pclk, 708c2ecf20Sopenharmony_ci unsigned long *fck, 718c2ecf20Sopenharmony_ci struct dispc_clock_info *dispc_cinfo) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci int i; 748c2ecf20Sopenharmony_ci struct sdi_clk_calc_ctx ctx; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci /* 778c2ecf20Sopenharmony_ci * DSS fclk gives us very few possibilities, so finding a good pixel 788c2ecf20Sopenharmony_ci * clock may not be possible. We try multiple times to find the clock, 798c2ecf20Sopenharmony_ci * each time widening the pixel clock range we look for, up to 808c2ecf20Sopenharmony_ci * +/- 1MHz. 818c2ecf20Sopenharmony_ci */ 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci for (i = 0; i < 10; ++i) { 848c2ecf20Sopenharmony_ci bool ok; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci memset(&ctx, 0, sizeof(ctx)); 878c2ecf20Sopenharmony_ci if (pclk > 1000 * i * i * i) 888c2ecf20Sopenharmony_ci ctx.pck_min = max(pclk - 1000 * i * i * i, 0lu); 898c2ecf20Sopenharmony_ci else 908c2ecf20Sopenharmony_ci ctx.pck_min = 0; 918c2ecf20Sopenharmony_ci ctx.pck_max = pclk + 1000 * i * i * i; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci ok = dss_div_calc(pclk, ctx.pck_min, dpi_calc_dss_cb, &ctx); 948c2ecf20Sopenharmony_ci if (ok) { 958c2ecf20Sopenharmony_ci *fck = ctx.fck; 968c2ecf20Sopenharmony_ci *dispc_cinfo = ctx.dispc_cinfo; 978c2ecf20Sopenharmony_ci return 0; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci return -EINVAL; 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic void sdi_config_lcd_manager(struct omap_dss_device *dssdev) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci struct omap_overlay_manager *mgr = sdi.output.manager; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci sdi.mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci sdi.mgr_config.stallmode = false; 1118c2ecf20Sopenharmony_ci sdi.mgr_config.fifohandcheck = false; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci sdi.mgr_config.video_port_width = 24; 1148c2ecf20Sopenharmony_ci sdi.mgr_config.lcden_sig_polarity = 1; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci dss_mgr_set_lcd_config(mgr, &sdi.mgr_config); 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic int sdi_display_enable(struct omap_dss_device *dssdev) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci struct omap_dss_device *out = &sdi.output; 1228c2ecf20Sopenharmony_ci struct omap_video_timings *t = &sdi.timings; 1238c2ecf20Sopenharmony_ci unsigned long fck; 1248c2ecf20Sopenharmony_ci struct dispc_clock_info dispc_cinfo; 1258c2ecf20Sopenharmony_ci unsigned long pck; 1268c2ecf20Sopenharmony_ci int r; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci if (out->manager == NULL) { 1298c2ecf20Sopenharmony_ci DSSERR("failed to enable display: no output/manager\n"); 1308c2ecf20Sopenharmony_ci return -ENODEV; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci r = regulator_enable(sdi.vdds_sdi_reg); 1348c2ecf20Sopenharmony_ci if (r) 1358c2ecf20Sopenharmony_ci goto err_reg_enable; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci r = dispc_runtime_get(); 1388c2ecf20Sopenharmony_ci if (r) 1398c2ecf20Sopenharmony_ci goto err_get_dispc; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci /* 15.5.9.1.2 */ 1428c2ecf20Sopenharmony_ci t->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; 1438c2ecf20Sopenharmony_ci t->sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci r = sdi_calc_clock_div(t->pixelclock, &fck, &dispc_cinfo); 1468c2ecf20Sopenharmony_ci if (r) 1478c2ecf20Sopenharmony_ci goto err_calc_clock_div; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci sdi.mgr_config.clock_info = dispc_cinfo; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci pck = fck / dispc_cinfo.lck_div / dispc_cinfo.pck_div; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (pck != t->pixelclock) { 1548c2ecf20Sopenharmony_ci DSSWARN("Could not find exact pixel clock. Requested %d Hz, got %lu Hz\n", 1558c2ecf20Sopenharmony_ci t->pixelclock, pck); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci t->pixelclock = pck; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci dss_mgr_set_timings(out->manager, t); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci r = dss_set_fck_rate(fck); 1648c2ecf20Sopenharmony_ci if (r) 1658c2ecf20Sopenharmony_ci goto err_set_dss_clock_div; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci sdi_config_lcd_manager(dssdev); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci /* 1708c2ecf20Sopenharmony_ci * LCLK and PCLK divisors are located in shadow registers, and we 1718c2ecf20Sopenharmony_ci * normally write them to DISPC registers when enabling the output. 1728c2ecf20Sopenharmony_ci * However, SDI uses pck-free as source clock for its PLL, and pck-free 1738c2ecf20Sopenharmony_ci * is affected by the divisors. And as we need the PLL before enabling 1748c2ecf20Sopenharmony_ci * the output, we need to write the divisors early. 1758c2ecf20Sopenharmony_ci * 1768c2ecf20Sopenharmony_ci * It seems just writing to the DISPC register is enough, and we don't 1778c2ecf20Sopenharmony_ci * need to care about the shadow register mechanism for pck-free. The 1788c2ecf20Sopenharmony_ci * exact reason for this is unknown. 1798c2ecf20Sopenharmony_ci */ 1808c2ecf20Sopenharmony_ci dispc_mgr_set_clock_div(out->manager->id, &sdi.mgr_config.clock_info); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci dss_sdi_init(sdi.datapairs); 1838c2ecf20Sopenharmony_ci r = dss_sdi_enable(); 1848c2ecf20Sopenharmony_ci if (r) 1858c2ecf20Sopenharmony_ci goto err_sdi_enable; 1868c2ecf20Sopenharmony_ci mdelay(2); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci r = dss_mgr_enable(out->manager); 1898c2ecf20Sopenharmony_ci if (r) 1908c2ecf20Sopenharmony_ci goto err_mgr_enable; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci return 0; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cierr_mgr_enable: 1958c2ecf20Sopenharmony_ci dss_sdi_disable(); 1968c2ecf20Sopenharmony_cierr_sdi_enable: 1978c2ecf20Sopenharmony_cierr_set_dss_clock_div: 1988c2ecf20Sopenharmony_cierr_calc_clock_div: 1998c2ecf20Sopenharmony_ci dispc_runtime_put(); 2008c2ecf20Sopenharmony_cierr_get_dispc: 2018c2ecf20Sopenharmony_ci regulator_disable(sdi.vdds_sdi_reg); 2028c2ecf20Sopenharmony_cierr_reg_enable: 2038c2ecf20Sopenharmony_ci return r; 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic void sdi_display_disable(struct omap_dss_device *dssdev) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci struct omap_overlay_manager *mgr = sdi.output.manager; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci dss_mgr_disable(mgr); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci dss_sdi_disable(); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci dispc_runtime_put(); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci regulator_disable(sdi.vdds_sdi_reg); 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic void sdi_set_timings(struct omap_dss_device *dssdev, 2208c2ecf20Sopenharmony_ci struct omap_video_timings *timings) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci sdi.timings = *timings; 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic void sdi_get_timings(struct omap_dss_device *dssdev, 2268c2ecf20Sopenharmony_ci struct omap_video_timings *timings) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci *timings = sdi.timings; 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic int sdi_check_timings(struct omap_dss_device *dssdev, 2328c2ecf20Sopenharmony_ci struct omap_video_timings *timings) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci struct omap_overlay_manager *mgr = sdi.output.manager; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (mgr && !dispc_mgr_timings_ok(mgr->id, timings)) 2378c2ecf20Sopenharmony_ci return -EINVAL; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci if (timings->pixelclock == 0) 2408c2ecf20Sopenharmony_ci return -EINVAL; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci return 0; 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic void sdi_set_datapairs(struct omap_dss_device *dssdev, int datapairs) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci sdi.datapairs = datapairs; 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic int sdi_init_regulator(void) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci struct regulator *vdds_sdi; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci if (sdi.vdds_sdi_reg) 2558c2ecf20Sopenharmony_ci return 0; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci vdds_sdi = devm_regulator_get(&sdi.pdev->dev, "vdds_sdi"); 2588c2ecf20Sopenharmony_ci if (IS_ERR(vdds_sdi)) { 2598c2ecf20Sopenharmony_ci if (PTR_ERR(vdds_sdi) != -EPROBE_DEFER) 2608c2ecf20Sopenharmony_ci DSSERR("can't get VDDS_SDI regulator\n"); 2618c2ecf20Sopenharmony_ci return PTR_ERR(vdds_sdi); 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci sdi.vdds_sdi_reg = vdds_sdi; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci return 0; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic int sdi_connect(struct omap_dss_device *dssdev, 2708c2ecf20Sopenharmony_ci struct omap_dss_device *dst) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci struct omap_overlay_manager *mgr; 2738c2ecf20Sopenharmony_ci int r; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci r = sdi_init_regulator(); 2768c2ecf20Sopenharmony_ci if (r) 2778c2ecf20Sopenharmony_ci return r; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel); 2808c2ecf20Sopenharmony_ci if (!mgr) 2818c2ecf20Sopenharmony_ci return -ENODEV; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci r = dss_mgr_connect(mgr, dssdev); 2848c2ecf20Sopenharmony_ci if (r) 2858c2ecf20Sopenharmony_ci return r; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci r = omapdss_output_set_device(dssdev, dst); 2888c2ecf20Sopenharmony_ci if (r) { 2898c2ecf20Sopenharmony_ci DSSERR("failed to connect output to new device: %s\n", 2908c2ecf20Sopenharmony_ci dst->name); 2918c2ecf20Sopenharmony_ci dss_mgr_disconnect(mgr, dssdev); 2928c2ecf20Sopenharmony_ci return r; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci return 0; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic void sdi_disconnect(struct omap_dss_device *dssdev, 2998c2ecf20Sopenharmony_ci struct omap_dss_device *dst) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci WARN_ON(dst != dssdev->dst); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci if (dst != dssdev->dst) 3048c2ecf20Sopenharmony_ci return; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci omapdss_output_unset_device(dssdev); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci if (dssdev->manager) 3098c2ecf20Sopenharmony_ci dss_mgr_disconnect(dssdev->manager, dssdev); 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic const struct omapdss_sdi_ops sdi_ops = { 3138c2ecf20Sopenharmony_ci .connect = sdi_connect, 3148c2ecf20Sopenharmony_ci .disconnect = sdi_disconnect, 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci .enable = sdi_display_enable, 3178c2ecf20Sopenharmony_ci .disable = sdi_display_disable, 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci .check_timings = sdi_check_timings, 3208c2ecf20Sopenharmony_ci .set_timings = sdi_set_timings, 3218c2ecf20Sopenharmony_ci .get_timings = sdi_get_timings, 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci .set_datapairs = sdi_set_datapairs, 3248c2ecf20Sopenharmony_ci}; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_cistatic void sdi_init_output(struct platform_device *pdev) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci struct omap_dss_device *out = &sdi.output; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci out->dev = &pdev->dev; 3318c2ecf20Sopenharmony_ci out->id = OMAP_DSS_OUTPUT_SDI; 3328c2ecf20Sopenharmony_ci out->output_type = OMAP_DISPLAY_TYPE_SDI; 3338c2ecf20Sopenharmony_ci out->name = "sdi.0"; 3348c2ecf20Sopenharmony_ci out->dispc_channel = OMAP_DSS_CHANNEL_LCD; 3358c2ecf20Sopenharmony_ci /* We have SDI only on OMAP3, where it's on port 1 */ 3368c2ecf20Sopenharmony_ci out->port_num = 1; 3378c2ecf20Sopenharmony_ci out->ops.sdi = &sdi_ops; 3388c2ecf20Sopenharmony_ci out->owner = THIS_MODULE; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci omapdss_register_output(out); 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic void sdi_uninit_output(struct platform_device *pdev) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci struct omap_dss_device *out = &sdi.output; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci omapdss_unregister_output(out); 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_cistatic int sdi_bind(struct device *dev, struct device *master, void *data) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci sdi.pdev = pdev; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci sdi_init_output(pdev); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci return 0; 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic void sdi_unbind(struct device *dev, struct device *master, void *data) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci sdi_uninit_output(pdev); 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic const struct component_ops sdi_component_ops = { 3698c2ecf20Sopenharmony_ci .bind = sdi_bind, 3708c2ecf20Sopenharmony_ci .unbind = sdi_unbind, 3718c2ecf20Sopenharmony_ci}; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_cistatic int sdi_probe(struct platform_device *pdev) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci return component_add(&pdev->dev, &sdi_component_ops); 3768c2ecf20Sopenharmony_ci} 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_cistatic int sdi_remove(struct platform_device *pdev) 3798c2ecf20Sopenharmony_ci{ 3808c2ecf20Sopenharmony_ci component_del(&pdev->dev, &sdi_component_ops); 3818c2ecf20Sopenharmony_ci return 0; 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_cistatic struct platform_driver omap_sdi_driver = { 3858c2ecf20Sopenharmony_ci .probe = sdi_probe, 3868c2ecf20Sopenharmony_ci .remove = sdi_remove, 3878c2ecf20Sopenharmony_ci .driver = { 3888c2ecf20Sopenharmony_ci .name = "omapdss_sdi", 3898c2ecf20Sopenharmony_ci .suppress_bind_attrs = true, 3908c2ecf20Sopenharmony_ci }, 3918c2ecf20Sopenharmony_ci}; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ciint __init sdi_init_platform_driver(void) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci return platform_driver_register(&omap_sdi_driver); 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_civoid sdi_uninit_platform_driver(void) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci platform_driver_unregister(&omap_sdi_driver); 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ciint sdi_init_port(struct platform_device *pdev, struct device_node *port) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci struct device_node *ep; 4068c2ecf20Sopenharmony_ci u32 datapairs; 4078c2ecf20Sopenharmony_ci int r; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci ep = omapdss_of_get_next_endpoint(port, NULL); 4108c2ecf20Sopenharmony_ci if (!ep) 4118c2ecf20Sopenharmony_ci return 0; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci r = of_property_read_u32(ep, "datapairs", &datapairs); 4148c2ecf20Sopenharmony_ci if (r) { 4158c2ecf20Sopenharmony_ci DSSERR("failed to parse datapairs\n"); 4168c2ecf20Sopenharmony_ci goto err_datapairs; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci sdi.datapairs = datapairs; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci of_node_put(ep); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci sdi.pdev = pdev; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci sdi_init_output(pdev); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci sdi.port_initialized = true; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci return 0; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_cierr_datapairs: 4328c2ecf20Sopenharmony_ci of_node_put(ep); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci return r; 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_civoid sdi_uninit_port(struct device_node *port) 4388c2ecf20Sopenharmony_ci{ 4398c2ecf20Sopenharmony_ci if (!sdi.port_initialized) 4408c2ecf20Sopenharmony_ci return; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci sdi_uninit_output(sdi.pdev); 4438c2ecf20Sopenharmony_ci} 444