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