linux/drivers/gpu/drm/sti/sti_dvo.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) STMicroelectronics SA 2014
   3 * Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics.
   4 * License terms:  GNU General Public License (GPL), version 2
   5 */
   6
   7#include <linux/clk.h>
   8#include <linux/component.h>
   9#include <linux/module.h>
  10#include <linux/of_gpio.h>
  11#include <linux/platform_device.h>
  12
  13#include <drm/drmP.h>
  14#include <drm/drm_atomic_helper.h>
  15#include <drm/drm_crtc_helper.h>
  16#include <drm/drm_panel.h>
  17
  18#include "sti_awg_utils.h"
  19#include "sti_mixer.h"
  20
  21/* DVO registers */
  22#define DVO_AWG_DIGSYNC_CTRL      0x0000
  23#define DVO_DOF_CFG               0x0004
  24#define DVO_LUT_PROG_LOW          0x0008
  25#define DVO_LUT_PROG_MID          0x000C
  26#define DVO_LUT_PROG_HIGH         0x0010
  27#define DVO_DIGSYNC_INSTR_I       0x0100
  28
  29#define DVO_AWG_CTRL_EN           BIT(0)
  30#define DVO_AWG_FRAME_BASED_SYNC  BIT(2)
  31
  32#define DVO_DOF_EN_LOWBYTE        BIT(0)
  33#define DVO_DOF_EN_MIDBYTE        BIT(1)
  34#define DVO_DOF_EN_HIGHBYTE       BIT(2)
  35#define DVO_DOF_EN                BIT(6)
  36#define DVO_DOF_MOD_COUNT_SHIFT   8
  37
  38#define DVO_LUT_ZERO              0
  39#define DVO_LUT_Y_G               1
  40#define DVO_LUT_Y_G_DEL           2
  41#define DVO_LUT_CB_B              3
  42#define DVO_LUT_CB_B_DEL          4
  43#define DVO_LUT_CR_R              5
  44#define DVO_LUT_CR_R_DEL          6
  45#define DVO_LUT_HOLD              7
  46
  47struct dvo_config {
  48        u32 flags;
  49        u32 lowbyte;
  50        u32 midbyte;
  51        u32 highbyte;
  52        int (*awg_fwgen_fct)(
  53                        struct awg_code_generation_params *fw_gen_params,
  54                        struct awg_timing *timing);
  55};
  56
  57static struct dvo_config rgb_24bit_de_cfg = {
  58        .flags         = (0L << DVO_DOF_MOD_COUNT_SHIFT),
  59        .lowbyte       = DVO_LUT_CR_R,
  60        .midbyte       = DVO_LUT_Y_G,
  61        .highbyte      = DVO_LUT_CB_B,
  62        .awg_fwgen_fct = sti_awg_generate_code_data_enable_mode,
  63};
  64
  65/**
  66 * STI digital video output structure
  67 *
  68 * @dev: driver device
  69 * @drm_dev: pointer to drm device
  70 * @mode: current display mode selected
  71 * @regs: dvo registers
  72 * @clk_pix: pixel clock for dvo
  73 * @clk: clock for dvo
  74 * @clk_main_parent: dvo parent clock if main path used
  75 * @clk_aux_parent: dvo parent clock if aux path used
  76 * @panel_node: panel node reference from device tree
  77 * @panel: reference to the panel connected to the dvo
  78 * @enabled: true if dvo is enabled else false
  79 * @encoder: drm_encoder it is bound
  80 */
  81struct sti_dvo {
  82        struct device dev;
  83        struct drm_device *drm_dev;
  84        struct drm_display_mode mode;
  85        void __iomem *regs;
  86        struct clk *clk_pix;
  87        struct clk *clk;
  88        struct clk *clk_main_parent;
  89        struct clk *clk_aux_parent;
  90        struct device_node *panel_node;
  91        struct drm_panel *panel;
  92        struct dvo_config *config;
  93        bool enabled;
  94        struct drm_encoder *encoder;
  95        struct drm_bridge *bridge;
  96};
  97
  98struct sti_dvo_connector {
  99        struct drm_connector drm_connector;
 100        struct drm_encoder *encoder;
 101        struct sti_dvo *dvo;
 102};
 103
 104#define to_sti_dvo_connector(x) \
 105        container_of(x, struct sti_dvo_connector, drm_connector)
 106
 107#define BLANKING_LEVEL 16
 108int dvo_awg_generate_code(struct sti_dvo *dvo, u8 *ram_size, u32 *ram_code)
 109{
 110        struct drm_display_mode *mode = &dvo->mode;
 111        struct dvo_config *config = dvo->config;
 112        struct awg_code_generation_params fw_gen_params;
 113        struct awg_timing timing;
 114
 115        fw_gen_params.ram_code = ram_code;
 116        fw_gen_params.instruction_offset = 0;
 117
 118        timing.total_lines = mode->vtotal;
 119        timing.active_lines = mode->vdisplay;
 120        timing.blanking_lines = mode->vsync_start - mode->vdisplay;
 121        timing.trailing_lines = mode->vtotal - mode->vsync_start;
 122        timing.total_pixels = mode->htotal;
 123        timing.active_pixels = mode->hdisplay;
 124        timing.blanking_pixels = mode->hsync_start - mode->hdisplay;
 125        timing.trailing_pixels = mode->htotal - mode->hsync_start;
 126        timing.blanking_level = BLANKING_LEVEL;
 127
 128        if (config->awg_fwgen_fct(&fw_gen_params, &timing)) {
 129                DRM_ERROR("AWG firmware not properly generated\n");
 130                return -EINVAL;
 131        }
 132
 133        *ram_size = fw_gen_params.instruction_offset;
 134
 135        return 0;
 136}
 137
 138/* Configure AWG, writing instructions
 139 *
 140 * @dvo: pointer to DVO structure
 141 * @awg_ram_code: pointer to AWG instructions table
 142 * @nb: nb of AWG instructions
 143 */
 144static void dvo_awg_configure(struct sti_dvo *dvo, u32 *awg_ram_code, int nb)
 145{
 146        int i;
 147
 148        DRM_DEBUG_DRIVER("\n");
 149
 150        for (i = 0; i < nb; i++)
 151                writel(awg_ram_code[i],
 152                       dvo->regs + DVO_DIGSYNC_INSTR_I + i * 4);
 153        for (i = nb; i < AWG_MAX_INST; i++)
 154                writel(0, dvo->regs + DVO_DIGSYNC_INSTR_I + i * 4);
 155
 156        writel(DVO_AWG_CTRL_EN, dvo->regs + DVO_AWG_DIGSYNC_CTRL);
 157}
 158
 159static void sti_dvo_disable(struct drm_bridge *bridge)
 160{
 161        struct sti_dvo *dvo = bridge->driver_private;
 162
 163        if (!dvo->enabled)
 164                return;
 165
 166        DRM_DEBUG_DRIVER("\n");
 167
 168        if (dvo->config->awg_fwgen_fct)
 169                writel(0x00000000, dvo->regs + DVO_AWG_DIGSYNC_CTRL);
 170
 171        writel(0x00000000, dvo->regs + DVO_DOF_CFG);
 172
 173        if (dvo->panel)
 174                dvo->panel->funcs->disable(dvo->panel);
 175
 176        /* Disable/unprepare dvo clock */
 177        clk_disable_unprepare(dvo->clk_pix);
 178        clk_disable_unprepare(dvo->clk);
 179
 180        dvo->enabled = false;
 181}
 182
 183static void sti_dvo_pre_enable(struct drm_bridge *bridge)
 184{
 185        struct sti_dvo *dvo = bridge->driver_private;
 186        struct dvo_config *config = dvo->config;
 187        u32 val;
 188
 189        DRM_DEBUG_DRIVER("\n");
 190
 191        if (dvo->enabled)
 192                return;
 193
 194        /* Make sure DVO is disabled */
 195        writel(0x00000000, dvo->regs + DVO_DOF_CFG);
 196        writel(0x00000000, dvo->regs + DVO_AWG_DIGSYNC_CTRL);
 197
 198        if (config->awg_fwgen_fct) {
 199                u8 nb_instr;
 200                u32 awg_ram_code[AWG_MAX_INST];
 201                /* Configure AWG */
 202                if (!dvo_awg_generate_code(dvo, &nb_instr, awg_ram_code))
 203                        dvo_awg_configure(dvo, awg_ram_code, nb_instr);
 204                else
 205                        return;
 206        }
 207
 208        /* Prepare/enable clocks */
 209        if (clk_prepare_enable(dvo->clk_pix))
 210                DRM_ERROR("Failed to prepare/enable dvo_pix clk\n");
 211        if (clk_prepare_enable(dvo->clk))
 212                DRM_ERROR("Failed to prepare/enable dvo clk\n");
 213
 214        if (dvo->panel)
 215                dvo->panel->funcs->enable(dvo->panel);
 216
 217        /* Set LUT */
 218        writel(config->lowbyte,  dvo->regs + DVO_LUT_PROG_LOW);
 219        writel(config->midbyte,  dvo->regs + DVO_LUT_PROG_MID);
 220        writel(config->highbyte, dvo->regs + DVO_LUT_PROG_HIGH);
 221
 222        /* Digital output formatter config */
 223        val = (config->flags | DVO_DOF_EN);
 224        writel(val, dvo->regs + DVO_DOF_CFG);
 225
 226        dvo->enabled = true;
 227}
 228
 229static void sti_dvo_set_mode(struct drm_bridge *bridge,
 230                             struct drm_display_mode *mode,
 231                             struct drm_display_mode *adjusted_mode)
 232{
 233        struct sti_dvo *dvo = bridge->driver_private;
 234        struct sti_mixer *mixer = to_sti_mixer(dvo->encoder->crtc);
 235        int rate = mode->clock * 1000;
 236        struct clk *clkp;
 237        int ret;
 238
 239        DRM_DEBUG_DRIVER("\n");
 240
 241        memcpy(&dvo->mode, mode, sizeof(struct drm_display_mode));
 242
 243        /* According to the path used (main or aux), the dvo clocks should
 244         * have a different parent clock. */
 245        if (mixer->id == STI_MIXER_MAIN)
 246                clkp = dvo->clk_main_parent;
 247        else
 248                clkp = dvo->clk_aux_parent;
 249
 250        if (clkp) {
 251                clk_set_parent(dvo->clk_pix, clkp);
 252                clk_set_parent(dvo->clk, clkp);
 253        }
 254
 255        /* DVO clocks = compositor clock */
 256        ret = clk_set_rate(dvo->clk_pix, rate);
 257        if (ret < 0) {
 258                DRM_ERROR("Cannot set rate (%dHz) for dvo_pix clk\n", rate);
 259                return;
 260        }
 261
 262        ret = clk_set_rate(dvo->clk, rate);
 263        if (ret < 0) {
 264                DRM_ERROR("Cannot set rate (%dHz) for dvo clk\n", rate);
 265                return;
 266        }
 267
 268        /* For now, we only support 24bit data enable (DE) synchro format */
 269        dvo->config = &rgb_24bit_de_cfg;
 270}
 271
 272static void sti_dvo_bridge_nope(struct drm_bridge *bridge)
 273{
 274        /* do nothing */
 275}
 276
 277static const struct drm_bridge_funcs sti_dvo_bridge_funcs = {
 278        .pre_enable = sti_dvo_pre_enable,
 279        .enable = sti_dvo_bridge_nope,
 280        .disable = sti_dvo_disable,
 281        .post_disable = sti_dvo_bridge_nope,
 282        .mode_set = sti_dvo_set_mode,
 283};
 284
 285static int sti_dvo_connector_get_modes(struct drm_connector *connector)
 286{
 287        struct sti_dvo_connector *dvo_connector
 288                = to_sti_dvo_connector(connector);
 289        struct sti_dvo *dvo = dvo_connector->dvo;
 290
 291        if (dvo->panel)
 292                return dvo->panel->funcs->get_modes(dvo->panel);
 293
 294        return 0;
 295}
 296
 297#define CLK_TOLERANCE_HZ 50
 298
 299static int sti_dvo_connector_mode_valid(struct drm_connector *connector,
 300                                        struct drm_display_mode *mode)
 301{
 302        int target = mode->clock * 1000;
 303        int target_min = target - CLK_TOLERANCE_HZ;
 304        int target_max = target + CLK_TOLERANCE_HZ;
 305        int result;
 306        struct sti_dvo_connector *dvo_connector
 307                = to_sti_dvo_connector(connector);
 308        struct sti_dvo *dvo = dvo_connector->dvo;
 309
 310        result = clk_round_rate(dvo->clk_pix, target);
 311
 312        DRM_DEBUG_DRIVER("target rate = %d => available rate = %d\n",
 313                         target, result);
 314
 315        if ((result < target_min) || (result > target_max)) {
 316                DRM_DEBUG_DRIVER("dvo pixclk=%d not supported\n", target);
 317                return MODE_BAD;
 318        }
 319
 320        return MODE_OK;
 321}
 322
 323struct drm_encoder *sti_dvo_best_encoder(struct drm_connector *connector)
 324{
 325        struct sti_dvo_connector *dvo_connector
 326                = to_sti_dvo_connector(connector);
 327
 328        /* Best encoder is the one associated during connector creation */
 329        return dvo_connector->encoder;
 330}
 331
 332static const
 333struct drm_connector_helper_funcs sti_dvo_connector_helper_funcs = {
 334        .get_modes = sti_dvo_connector_get_modes,
 335        .mode_valid = sti_dvo_connector_mode_valid,
 336        .best_encoder = sti_dvo_best_encoder,
 337};
 338
 339static enum drm_connector_status
 340sti_dvo_connector_detect(struct drm_connector *connector, bool force)
 341{
 342        struct sti_dvo_connector *dvo_connector
 343                = to_sti_dvo_connector(connector);
 344        struct sti_dvo *dvo = dvo_connector->dvo;
 345
 346        DRM_DEBUG_DRIVER("\n");
 347
 348        if (!dvo->panel)
 349                dvo->panel = of_drm_find_panel(dvo->panel_node);
 350
 351        if (dvo->panel)
 352                if (!drm_panel_attach(dvo->panel, connector))
 353                        return connector_status_connected;
 354
 355        return connector_status_disconnected;
 356}
 357
 358static void sti_dvo_connector_destroy(struct drm_connector *connector)
 359{
 360        struct sti_dvo_connector *dvo_connector
 361                = to_sti_dvo_connector(connector);
 362
 363        drm_connector_unregister(connector);
 364        drm_connector_cleanup(connector);
 365        kfree(dvo_connector);
 366}
 367
 368static const struct drm_connector_funcs sti_dvo_connector_funcs = {
 369        .dpms = drm_atomic_helper_connector_dpms,
 370        .fill_modes = drm_helper_probe_single_connector_modes,
 371        .detect = sti_dvo_connector_detect,
 372        .destroy = sti_dvo_connector_destroy,
 373        .reset = drm_atomic_helper_connector_reset,
 374        .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
 375        .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 376};
 377
 378static struct drm_encoder *sti_dvo_find_encoder(struct drm_device *dev)
 379{
 380        struct drm_encoder *encoder;
 381
 382        list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
 383                if (encoder->encoder_type == DRM_MODE_ENCODER_LVDS)
 384                        return encoder;
 385        }
 386
 387        return NULL;
 388}
 389
 390static int sti_dvo_bind(struct device *dev, struct device *master, void *data)
 391{
 392        struct sti_dvo *dvo = dev_get_drvdata(dev);
 393        struct drm_device *drm_dev = data;
 394        struct drm_encoder *encoder;
 395        struct sti_dvo_connector *connector;
 396        struct drm_connector *drm_connector;
 397        struct drm_bridge *bridge;
 398        int err;
 399
 400        /* Set the drm device handle */
 401        dvo->drm_dev = drm_dev;
 402
 403        encoder = sti_dvo_find_encoder(drm_dev);
 404        if (!encoder)
 405                return -ENOMEM;
 406
 407        connector = devm_kzalloc(dev, sizeof(*connector), GFP_KERNEL);
 408        if (!connector)
 409                return -ENOMEM;
 410
 411        connector->dvo = dvo;
 412
 413        bridge = devm_kzalloc(dev, sizeof(*bridge), GFP_KERNEL);
 414        if (!bridge)
 415                return -ENOMEM;
 416
 417        bridge->driver_private = dvo;
 418        bridge->funcs = &sti_dvo_bridge_funcs;
 419        bridge->of_node = dvo->dev.of_node;
 420        err = drm_bridge_add(bridge);
 421        if (err) {
 422                DRM_ERROR("Failed to add bridge\n");
 423                return err;
 424        }
 425
 426        err = drm_bridge_attach(drm_dev, bridge);
 427        if (err) {
 428                DRM_ERROR("Failed to attach bridge\n");
 429                return err;
 430        }
 431
 432        dvo->bridge = bridge;
 433        encoder->bridge = bridge;
 434        connector->encoder = encoder;
 435        dvo->encoder = encoder;
 436
 437        drm_connector = (struct drm_connector *)connector;
 438
 439        drm_connector->polled = DRM_CONNECTOR_POLL_HPD;
 440
 441        drm_connector_init(drm_dev, drm_connector,
 442                           &sti_dvo_connector_funcs, DRM_MODE_CONNECTOR_LVDS);
 443        drm_connector_helper_add(drm_connector,
 444                                 &sti_dvo_connector_helper_funcs);
 445
 446        err = drm_connector_register(drm_connector);
 447        if (err)
 448                goto err_connector;
 449
 450        err = drm_mode_connector_attach_encoder(drm_connector, encoder);
 451        if (err) {
 452                DRM_ERROR("Failed to attach a connector to a encoder\n");
 453                goto err_sysfs;
 454        }
 455
 456        return 0;
 457
 458err_sysfs:
 459        drm_connector_unregister(drm_connector);
 460err_connector:
 461        drm_bridge_remove(bridge);
 462        drm_connector_cleanup(drm_connector);
 463        return -EINVAL;
 464}
 465
 466static void sti_dvo_unbind(struct device *dev,
 467                           struct device *master, void *data)
 468{
 469        struct sti_dvo *dvo = dev_get_drvdata(dev);
 470
 471        drm_bridge_remove(dvo->bridge);
 472}
 473
 474static const struct component_ops sti_dvo_ops = {
 475        .bind = sti_dvo_bind,
 476        .unbind = sti_dvo_unbind,
 477};
 478
 479static int sti_dvo_probe(struct platform_device *pdev)
 480{
 481        struct device *dev = &pdev->dev;
 482        struct sti_dvo *dvo;
 483        struct resource *res;
 484        struct device_node *np = dev->of_node;
 485
 486        DRM_INFO("%s\n", __func__);
 487
 488        dvo = devm_kzalloc(dev, sizeof(*dvo), GFP_KERNEL);
 489        if (!dvo) {
 490                DRM_ERROR("Failed to allocate memory for DVO\n");
 491                return -ENOMEM;
 492        }
 493
 494        dvo->dev = pdev->dev;
 495
 496        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dvo-reg");
 497        if (!res) {
 498                DRM_ERROR("Invalid dvo resource\n");
 499                return -ENOMEM;
 500        }
 501        dvo->regs = devm_ioremap_nocache(dev, res->start,
 502                        resource_size(res));
 503        if (!dvo->regs)
 504                return -ENOMEM;
 505
 506        dvo->clk_pix = devm_clk_get(dev, "dvo_pix");
 507        if (IS_ERR(dvo->clk_pix)) {
 508                DRM_ERROR("Cannot get dvo_pix clock\n");
 509                return PTR_ERR(dvo->clk_pix);
 510        }
 511
 512        dvo->clk = devm_clk_get(dev, "dvo");
 513        if (IS_ERR(dvo->clk)) {
 514                DRM_ERROR("Cannot get dvo clock\n");
 515                return PTR_ERR(dvo->clk);
 516        }
 517
 518        dvo->clk_main_parent = devm_clk_get(dev, "main_parent");
 519        if (IS_ERR(dvo->clk_main_parent)) {
 520                DRM_DEBUG_DRIVER("Cannot get main_parent clock\n");
 521                dvo->clk_main_parent = NULL;
 522        }
 523
 524        dvo->clk_aux_parent = devm_clk_get(dev, "aux_parent");
 525        if (IS_ERR(dvo->clk_aux_parent)) {
 526                DRM_DEBUG_DRIVER("Cannot get aux_parent clock\n");
 527                dvo->clk_aux_parent = NULL;
 528        }
 529
 530        dvo->panel_node = of_parse_phandle(np, "sti,panel", 0);
 531        if (!dvo->panel_node)
 532                DRM_ERROR("No panel associated to the dvo output\n");
 533
 534        platform_set_drvdata(pdev, dvo);
 535
 536        return component_add(&pdev->dev, &sti_dvo_ops);
 537}
 538
 539static int sti_dvo_remove(struct platform_device *pdev)
 540{
 541        component_del(&pdev->dev, &sti_dvo_ops);
 542        return 0;
 543}
 544
 545static struct of_device_id dvo_of_match[] = {
 546        { .compatible = "st,stih407-dvo", },
 547        { /* end node */ }
 548};
 549MODULE_DEVICE_TABLE(of, dvo_of_match);
 550
 551struct platform_driver sti_dvo_driver = {
 552        .driver = {
 553                .name = "sti-dvo",
 554                .owner = THIS_MODULE,
 555                .of_match_table = dvo_of_match,
 556        },
 557        .probe = sti_dvo_probe,
 558        .remove = sti_dvo_remove,
 559};
 560
 561MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>");
 562MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver");
 563MODULE_LICENSE("GPL");
 564