162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/drivers/video/omap2/dss/sdi.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2009 Nokia Corporation 662306a36Sopenharmony_ci * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#define DSS_SUBSYS_NAME "SDI" 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/delay.h> 1362306a36Sopenharmony_ci#include <linux/err.h> 1462306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 1562306a36Sopenharmony_ci#include <linux/export.h> 1662306a36Sopenharmony_ci#include <linux/platform_device.h> 1762306a36Sopenharmony_ci#include <linux/string.h> 1862306a36Sopenharmony_ci#include <linux/of.h> 1962306a36Sopenharmony_ci#include <linux/component.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <video/omapfb_dss.h> 2262306a36Sopenharmony_ci#include "dss.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic struct { 2562306a36Sopenharmony_ci struct platform_device *pdev; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci bool update_enabled; 2862306a36Sopenharmony_ci struct regulator *vdds_sdi_reg; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci struct dss_lcd_mgr_config mgr_config; 3162306a36Sopenharmony_ci struct omap_video_timings timings; 3262306a36Sopenharmony_ci int datapairs; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci struct omap_dss_device output; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci bool port_initialized; 3762306a36Sopenharmony_ci} sdi; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistruct sdi_clk_calc_ctx { 4062306a36Sopenharmony_ci unsigned long pck_min, pck_max; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci unsigned long fck; 4362306a36Sopenharmony_ci struct dispc_clock_info dispc_cinfo; 4462306a36Sopenharmony_ci}; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic bool dpi_calc_dispc_cb(int lckd, int pckd, unsigned long lck, 4762306a36Sopenharmony_ci unsigned long pck, void *data) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci struct sdi_clk_calc_ctx *ctx = data; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci ctx->dispc_cinfo.lck_div = lckd; 5262306a36Sopenharmony_ci ctx->dispc_cinfo.pck_div = pckd; 5362306a36Sopenharmony_ci ctx->dispc_cinfo.lck = lck; 5462306a36Sopenharmony_ci ctx->dispc_cinfo.pck = pck; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci return true; 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic bool dpi_calc_dss_cb(unsigned long fck, void *data) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci struct sdi_clk_calc_ctx *ctx = data; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci ctx->fck = fck; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci return dispc_div_calc(fck, ctx->pck_min, ctx->pck_max, 6662306a36Sopenharmony_ci dpi_calc_dispc_cb, ctx); 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic int sdi_calc_clock_div(unsigned long pclk, 7062306a36Sopenharmony_ci unsigned long *fck, 7162306a36Sopenharmony_ci struct dispc_clock_info *dispc_cinfo) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci int i; 7462306a36Sopenharmony_ci struct sdi_clk_calc_ctx ctx; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci /* 7762306a36Sopenharmony_ci * DSS fclk gives us very few possibilities, so finding a good pixel 7862306a36Sopenharmony_ci * clock may not be possible. We try multiple times to find the clock, 7962306a36Sopenharmony_ci * each time widening the pixel clock range we look for, up to 8062306a36Sopenharmony_ci * +/- 1MHz. 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci for (i = 0; i < 10; ++i) { 8462306a36Sopenharmony_ci bool ok; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci memset(&ctx, 0, sizeof(ctx)); 8762306a36Sopenharmony_ci if (pclk > 1000 * i * i * i) 8862306a36Sopenharmony_ci ctx.pck_min = max(pclk - 1000 * i * i * i, 0lu); 8962306a36Sopenharmony_ci else 9062306a36Sopenharmony_ci ctx.pck_min = 0; 9162306a36Sopenharmony_ci ctx.pck_max = pclk + 1000 * i * i * i; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci ok = dss_div_calc(pclk, ctx.pck_min, dpi_calc_dss_cb, &ctx); 9462306a36Sopenharmony_ci if (ok) { 9562306a36Sopenharmony_ci *fck = ctx.fck; 9662306a36Sopenharmony_ci *dispc_cinfo = ctx.dispc_cinfo; 9762306a36Sopenharmony_ci return 0; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci return -EINVAL; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic void sdi_config_lcd_manager(struct omap_dss_device *dssdev) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct omap_overlay_manager *mgr = sdi.output.manager; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci sdi.mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci sdi.mgr_config.stallmode = false; 11162306a36Sopenharmony_ci sdi.mgr_config.fifohandcheck = false; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci sdi.mgr_config.video_port_width = 24; 11462306a36Sopenharmony_ci sdi.mgr_config.lcden_sig_polarity = 1; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci dss_mgr_set_lcd_config(mgr, &sdi.mgr_config); 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic int sdi_display_enable(struct omap_dss_device *dssdev) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci struct omap_dss_device *out = &sdi.output; 12262306a36Sopenharmony_ci struct omap_video_timings *t = &sdi.timings; 12362306a36Sopenharmony_ci unsigned long fck; 12462306a36Sopenharmony_ci struct dispc_clock_info dispc_cinfo; 12562306a36Sopenharmony_ci unsigned long pck; 12662306a36Sopenharmony_ci int r; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci if (out->manager == NULL) { 12962306a36Sopenharmony_ci DSSERR("failed to enable display: no output/manager\n"); 13062306a36Sopenharmony_ci return -ENODEV; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci r = regulator_enable(sdi.vdds_sdi_reg); 13462306a36Sopenharmony_ci if (r) 13562306a36Sopenharmony_ci goto err_reg_enable; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci r = dispc_runtime_get(); 13862306a36Sopenharmony_ci if (r) 13962306a36Sopenharmony_ci goto err_get_dispc; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci /* 15.5.9.1.2 */ 14262306a36Sopenharmony_ci t->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; 14362306a36Sopenharmony_ci t->sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci r = sdi_calc_clock_div(t->pixelclock, &fck, &dispc_cinfo); 14662306a36Sopenharmony_ci if (r) 14762306a36Sopenharmony_ci goto err_calc_clock_div; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci sdi.mgr_config.clock_info = dispc_cinfo; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci pck = fck / dispc_cinfo.lck_div / dispc_cinfo.pck_div; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (pck != t->pixelclock) { 15462306a36Sopenharmony_ci DSSWARN("Could not find exact pixel clock. Requested %d Hz, got %lu Hz\n", 15562306a36Sopenharmony_ci t->pixelclock, pck); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci t->pixelclock = pck; 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci dss_mgr_set_timings(out->manager, t); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci r = dss_set_fck_rate(fck); 16462306a36Sopenharmony_ci if (r) 16562306a36Sopenharmony_ci goto err_set_dss_clock_div; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci sdi_config_lcd_manager(dssdev); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci /* 17062306a36Sopenharmony_ci * LCLK and PCLK divisors are located in shadow registers, and we 17162306a36Sopenharmony_ci * normally write them to DISPC registers when enabling the output. 17262306a36Sopenharmony_ci * However, SDI uses pck-free as source clock for its PLL, and pck-free 17362306a36Sopenharmony_ci * is affected by the divisors. And as we need the PLL before enabling 17462306a36Sopenharmony_ci * the output, we need to write the divisors early. 17562306a36Sopenharmony_ci * 17662306a36Sopenharmony_ci * It seems just writing to the DISPC register is enough, and we don't 17762306a36Sopenharmony_ci * need to care about the shadow register mechanism for pck-free. The 17862306a36Sopenharmony_ci * exact reason for this is unknown. 17962306a36Sopenharmony_ci */ 18062306a36Sopenharmony_ci dispc_mgr_set_clock_div(out->manager->id, &sdi.mgr_config.clock_info); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci dss_sdi_init(sdi.datapairs); 18362306a36Sopenharmony_ci r = dss_sdi_enable(); 18462306a36Sopenharmony_ci if (r) 18562306a36Sopenharmony_ci goto err_sdi_enable; 18662306a36Sopenharmony_ci mdelay(2); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci r = dss_mgr_enable(out->manager); 18962306a36Sopenharmony_ci if (r) 19062306a36Sopenharmony_ci goto err_mgr_enable; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci return 0; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cierr_mgr_enable: 19562306a36Sopenharmony_ci dss_sdi_disable(); 19662306a36Sopenharmony_cierr_sdi_enable: 19762306a36Sopenharmony_cierr_set_dss_clock_div: 19862306a36Sopenharmony_cierr_calc_clock_div: 19962306a36Sopenharmony_ci dispc_runtime_put(); 20062306a36Sopenharmony_cierr_get_dispc: 20162306a36Sopenharmony_ci regulator_disable(sdi.vdds_sdi_reg); 20262306a36Sopenharmony_cierr_reg_enable: 20362306a36Sopenharmony_ci return r; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic void sdi_display_disable(struct omap_dss_device *dssdev) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci struct omap_overlay_manager *mgr = sdi.output.manager; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci dss_mgr_disable(mgr); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci dss_sdi_disable(); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci dispc_runtime_put(); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci regulator_disable(sdi.vdds_sdi_reg); 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic void sdi_set_timings(struct omap_dss_device *dssdev, 22062306a36Sopenharmony_ci struct omap_video_timings *timings) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci sdi.timings = *timings; 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic void sdi_get_timings(struct omap_dss_device *dssdev, 22662306a36Sopenharmony_ci struct omap_video_timings *timings) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci *timings = sdi.timings; 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic int sdi_check_timings(struct omap_dss_device *dssdev, 23262306a36Sopenharmony_ci struct omap_video_timings *timings) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci struct omap_overlay_manager *mgr = sdi.output.manager; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci if (mgr && !dispc_mgr_timings_ok(mgr->id, timings)) 23762306a36Sopenharmony_ci return -EINVAL; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (timings->pixelclock == 0) 24062306a36Sopenharmony_ci return -EINVAL; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci return 0; 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic void sdi_set_datapairs(struct omap_dss_device *dssdev, int datapairs) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci sdi.datapairs = datapairs; 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic int sdi_init_regulator(void) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci struct regulator *vdds_sdi; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci if (sdi.vdds_sdi_reg) 25562306a36Sopenharmony_ci return 0; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci vdds_sdi = devm_regulator_get(&sdi.pdev->dev, "vdds_sdi"); 25862306a36Sopenharmony_ci if (IS_ERR(vdds_sdi)) { 25962306a36Sopenharmony_ci if (PTR_ERR(vdds_sdi) != -EPROBE_DEFER) 26062306a36Sopenharmony_ci DSSERR("can't get VDDS_SDI regulator\n"); 26162306a36Sopenharmony_ci return PTR_ERR(vdds_sdi); 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci sdi.vdds_sdi_reg = vdds_sdi; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci return 0; 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic int sdi_connect(struct omap_dss_device *dssdev, 27062306a36Sopenharmony_ci struct omap_dss_device *dst) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci struct omap_overlay_manager *mgr; 27362306a36Sopenharmony_ci int r; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci r = sdi_init_regulator(); 27662306a36Sopenharmony_ci if (r) 27762306a36Sopenharmony_ci return r; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel); 28062306a36Sopenharmony_ci if (!mgr) 28162306a36Sopenharmony_ci return -ENODEV; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci r = dss_mgr_connect(mgr, dssdev); 28462306a36Sopenharmony_ci if (r) 28562306a36Sopenharmony_ci return r; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci r = omapdss_output_set_device(dssdev, dst); 28862306a36Sopenharmony_ci if (r) { 28962306a36Sopenharmony_ci DSSERR("failed to connect output to new device: %s\n", 29062306a36Sopenharmony_ci dst->name); 29162306a36Sopenharmony_ci dss_mgr_disconnect(mgr, dssdev); 29262306a36Sopenharmony_ci return r; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci return 0; 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic void sdi_disconnect(struct omap_dss_device *dssdev, 29962306a36Sopenharmony_ci struct omap_dss_device *dst) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci WARN_ON(dst != dssdev->dst); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci if (dst != dssdev->dst) 30462306a36Sopenharmony_ci return; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci omapdss_output_unset_device(dssdev); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci if (dssdev->manager) 30962306a36Sopenharmony_ci dss_mgr_disconnect(dssdev->manager, dssdev); 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic const struct omapdss_sdi_ops sdi_ops = { 31362306a36Sopenharmony_ci .connect = sdi_connect, 31462306a36Sopenharmony_ci .disconnect = sdi_disconnect, 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci .enable = sdi_display_enable, 31762306a36Sopenharmony_ci .disable = sdi_display_disable, 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci .check_timings = sdi_check_timings, 32062306a36Sopenharmony_ci .set_timings = sdi_set_timings, 32162306a36Sopenharmony_ci .get_timings = sdi_get_timings, 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci .set_datapairs = sdi_set_datapairs, 32462306a36Sopenharmony_ci}; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic void sdi_init_output(struct platform_device *pdev) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci struct omap_dss_device *out = &sdi.output; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci out->dev = &pdev->dev; 33162306a36Sopenharmony_ci out->id = OMAP_DSS_OUTPUT_SDI; 33262306a36Sopenharmony_ci out->output_type = OMAP_DISPLAY_TYPE_SDI; 33362306a36Sopenharmony_ci out->name = "sdi.0"; 33462306a36Sopenharmony_ci out->dispc_channel = OMAP_DSS_CHANNEL_LCD; 33562306a36Sopenharmony_ci /* We have SDI only on OMAP3, where it's on port 1 */ 33662306a36Sopenharmony_ci out->port_num = 1; 33762306a36Sopenharmony_ci out->ops.sdi = &sdi_ops; 33862306a36Sopenharmony_ci out->owner = THIS_MODULE; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci omapdss_register_output(out); 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_cistatic void sdi_uninit_output(struct platform_device *pdev) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci struct omap_dss_device *out = &sdi.output; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci omapdss_unregister_output(out); 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic int sdi_bind(struct device *dev, struct device *master, void *data) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci sdi.pdev = pdev; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci sdi_init_output(pdev); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci return 0; 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cistatic void sdi_unbind(struct device *dev, struct device *master, void *data) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci sdi_uninit_output(pdev); 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_cistatic const struct component_ops sdi_component_ops = { 36962306a36Sopenharmony_ci .bind = sdi_bind, 37062306a36Sopenharmony_ci .unbind = sdi_unbind, 37162306a36Sopenharmony_ci}; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cistatic int sdi_probe(struct platform_device *pdev) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci return component_add(&pdev->dev, &sdi_component_ops); 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cistatic void sdi_remove(struct platform_device *pdev) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci component_del(&pdev->dev, &sdi_component_ops); 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistatic struct platform_driver omap_sdi_driver = { 38462306a36Sopenharmony_ci .probe = sdi_probe, 38562306a36Sopenharmony_ci .remove_new = sdi_remove, 38662306a36Sopenharmony_ci .driver = { 38762306a36Sopenharmony_ci .name = "omapdss_sdi", 38862306a36Sopenharmony_ci .suppress_bind_attrs = true, 38962306a36Sopenharmony_ci }, 39062306a36Sopenharmony_ci}; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ciint __init sdi_init_platform_driver(void) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci return platform_driver_register(&omap_sdi_driver); 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_civoid sdi_uninit_platform_driver(void) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci platform_driver_unregister(&omap_sdi_driver); 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ciint sdi_init_port(struct platform_device *pdev, struct device_node *port) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci struct device_node *ep; 40562306a36Sopenharmony_ci u32 datapairs; 40662306a36Sopenharmony_ci int r; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci ep = omapdss_of_get_next_endpoint(port, NULL); 40962306a36Sopenharmony_ci if (!ep) 41062306a36Sopenharmony_ci return 0; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci r = of_property_read_u32(ep, "datapairs", &datapairs); 41362306a36Sopenharmony_ci if (r) { 41462306a36Sopenharmony_ci DSSERR("failed to parse datapairs\n"); 41562306a36Sopenharmony_ci goto err_datapairs; 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci sdi.datapairs = datapairs; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci of_node_put(ep); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci sdi.pdev = pdev; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci sdi_init_output(pdev); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci sdi.port_initialized = true; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci return 0; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cierr_datapairs: 43162306a36Sopenharmony_ci of_node_put(ep); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci return r; 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_civoid sdi_uninit_port(struct device_node *port) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci if (!sdi.port_initialized) 43962306a36Sopenharmony_ci return; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci sdi_uninit_output(sdi.pdev); 44262306a36Sopenharmony_ci} 443