linux/drivers/gpu/drm/tegra/rgb.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2012 Avionic Design GmbH
   3 * Copyright (C) 2012 NVIDIA CORPORATION.  All rights reserved.
   4 *
   5 * This program is free software; you can redistribute it and/or modify
   6 * it under the terms of the GNU General Public License version 2 as
   7 * published by the Free Software Foundation.
   8 */
   9
  10#include <linux/clk.h>
  11
  12#include "drm.h"
  13#include "dc.h"
  14
  15struct tegra_rgb {
  16        struct tegra_output output;
  17        struct tegra_dc *dc;
  18        bool enabled;
  19
  20        struct clk *clk_parent;
  21        struct clk *clk;
  22};
  23
  24static inline struct tegra_rgb *to_rgb(struct tegra_output *output)
  25{
  26        return container_of(output, struct tegra_rgb, output);
  27}
  28
  29struct reg_entry {
  30        unsigned long offset;
  31        unsigned long value;
  32};
  33
  34static const struct reg_entry rgb_enable[] = {
  35        { DC_COM_PIN_OUTPUT_ENABLE(0),   0x00000000 },
  36        { DC_COM_PIN_OUTPUT_ENABLE(1),   0x00000000 },
  37        { DC_COM_PIN_OUTPUT_ENABLE(2),   0x00000000 },
  38        { DC_COM_PIN_OUTPUT_ENABLE(3),   0x00000000 },
  39        { DC_COM_PIN_OUTPUT_POLARITY(0), 0x00000000 },
  40        { DC_COM_PIN_OUTPUT_POLARITY(1), 0x01000000 },
  41        { DC_COM_PIN_OUTPUT_POLARITY(2), 0x00000000 },
  42        { DC_COM_PIN_OUTPUT_POLARITY(3), 0x00000000 },
  43        { DC_COM_PIN_OUTPUT_DATA(0),     0x00000000 },
  44        { DC_COM_PIN_OUTPUT_DATA(1),     0x00000000 },
  45        { DC_COM_PIN_OUTPUT_DATA(2),     0x00000000 },
  46        { DC_COM_PIN_OUTPUT_DATA(3),     0x00000000 },
  47        { DC_COM_PIN_OUTPUT_SELECT(0),   0x00000000 },
  48        { DC_COM_PIN_OUTPUT_SELECT(1),   0x00000000 },
  49        { DC_COM_PIN_OUTPUT_SELECT(2),   0x00000000 },
  50        { DC_COM_PIN_OUTPUT_SELECT(3),   0x00000000 },
  51        { DC_COM_PIN_OUTPUT_SELECT(4),   0x00210222 },
  52        { DC_COM_PIN_OUTPUT_SELECT(5),   0x00002200 },
  53        { DC_COM_PIN_OUTPUT_SELECT(6),   0x00020000 },
  54};
  55
  56static const struct reg_entry rgb_disable[] = {
  57        { DC_COM_PIN_OUTPUT_SELECT(6),   0x00000000 },
  58        { DC_COM_PIN_OUTPUT_SELECT(5),   0x00000000 },
  59        { DC_COM_PIN_OUTPUT_SELECT(4),   0x00000000 },
  60        { DC_COM_PIN_OUTPUT_SELECT(3),   0x00000000 },
  61        { DC_COM_PIN_OUTPUT_SELECT(2),   0x00000000 },
  62        { DC_COM_PIN_OUTPUT_SELECT(1),   0x00000000 },
  63        { DC_COM_PIN_OUTPUT_SELECT(0),   0x00000000 },
  64        { DC_COM_PIN_OUTPUT_DATA(3),     0xaaaaaaaa },
  65        { DC_COM_PIN_OUTPUT_DATA(2),     0xaaaaaaaa },
  66        { DC_COM_PIN_OUTPUT_DATA(1),     0xaaaaaaaa },
  67        { DC_COM_PIN_OUTPUT_DATA(0),     0xaaaaaaaa },
  68        { DC_COM_PIN_OUTPUT_POLARITY(3), 0x00000000 },
  69        { DC_COM_PIN_OUTPUT_POLARITY(2), 0x00000000 },
  70        { DC_COM_PIN_OUTPUT_POLARITY(1), 0x00000000 },
  71        { DC_COM_PIN_OUTPUT_POLARITY(0), 0x00000000 },
  72        { DC_COM_PIN_OUTPUT_ENABLE(3),   0x55555555 },
  73        { DC_COM_PIN_OUTPUT_ENABLE(2),   0x55555555 },
  74        { DC_COM_PIN_OUTPUT_ENABLE(1),   0x55150005 },
  75        { DC_COM_PIN_OUTPUT_ENABLE(0),   0x55555555 },
  76};
  77
  78static void tegra_dc_write_regs(struct tegra_dc *dc,
  79                                const struct reg_entry *table,
  80                                unsigned int num)
  81{
  82        unsigned int i;
  83
  84        for (i = 0; i < num; i++)
  85                tegra_dc_writel(dc, table[i].value, table[i].offset);
  86}
  87
  88static int tegra_output_rgb_enable(struct tegra_output *output)
  89{
  90        struct tegra_rgb *rgb = to_rgb(output);
  91        unsigned long value;
  92
  93        if (rgb->enabled)
  94                return 0;
  95
  96        tegra_dc_write_regs(rgb->dc, rgb_enable, ARRAY_SIZE(rgb_enable));
  97
  98        value = DE_SELECT_ACTIVE | DE_CONTROL_NORMAL;
  99        tegra_dc_writel(rgb->dc, value, DC_DISP_DATA_ENABLE_OPTIONS);
 100
 101        /* XXX: parameterize? */
 102        value = tegra_dc_readl(rgb->dc, DC_COM_PIN_OUTPUT_POLARITY(1));
 103        value &= ~LVS_OUTPUT_POLARITY_LOW;
 104        value &= ~LHS_OUTPUT_POLARITY_LOW;
 105        tegra_dc_writel(rgb->dc, value, DC_COM_PIN_OUTPUT_POLARITY(1));
 106
 107        /* XXX: parameterize? */
 108        value = DISP_DATA_FORMAT_DF1P1C | DISP_ALIGNMENT_MSB |
 109                DISP_ORDER_RED_BLUE;
 110        tegra_dc_writel(rgb->dc, value, DC_DISP_DISP_INTERFACE_CONTROL);
 111
 112        /* XXX: parameterize? */
 113        value = SC0_H_QUALIFIER_NONE | SC1_H_QUALIFIER_NONE;
 114        tegra_dc_writel(rgb->dc, value, DC_DISP_SHIFT_CLOCK_OPTIONS);
 115
 116        value = tegra_dc_readl(rgb->dc, DC_CMD_DISPLAY_COMMAND);
 117        value &= ~DISP_CTRL_MODE_MASK;
 118        value |= DISP_CTRL_MODE_C_DISPLAY;
 119        tegra_dc_writel(rgb->dc, value, DC_CMD_DISPLAY_COMMAND);
 120
 121        value = tegra_dc_readl(rgb->dc, DC_CMD_DISPLAY_POWER_CONTROL);
 122        value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
 123                 PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
 124        tegra_dc_writel(rgb->dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
 125
 126        tegra_dc_writel(rgb->dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
 127        tegra_dc_writel(rgb->dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
 128
 129        rgb->enabled = true;
 130
 131        return 0;
 132}
 133
 134static int tegra_output_rgb_disable(struct tegra_output *output)
 135{
 136        struct tegra_rgb *rgb = to_rgb(output);
 137        unsigned long value;
 138
 139        if (!rgb->enabled)
 140                return 0;
 141
 142        value = tegra_dc_readl(rgb->dc, DC_CMD_DISPLAY_POWER_CONTROL);
 143        value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
 144                   PW4_ENABLE | PM0_ENABLE | PM1_ENABLE);
 145        tegra_dc_writel(rgb->dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
 146
 147        value = tegra_dc_readl(rgb->dc, DC_CMD_DISPLAY_COMMAND);
 148        value &= ~DISP_CTRL_MODE_MASK;
 149        tegra_dc_writel(rgb->dc, value, DC_CMD_DISPLAY_COMMAND);
 150
 151        tegra_dc_writel(rgb->dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
 152        tegra_dc_writel(rgb->dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
 153
 154        tegra_dc_write_regs(rgb->dc, rgb_disable, ARRAY_SIZE(rgb_disable));
 155
 156        rgb->enabled = false;
 157
 158        return 0;
 159}
 160
 161static int tegra_output_rgb_setup_clock(struct tegra_output *output,
 162                                        struct clk *clk, unsigned long pclk,
 163                                        unsigned int *div)
 164{
 165        struct tegra_rgb *rgb = to_rgb(output);
 166        int err;
 167
 168        err = clk_set_parent(clk, rgb->clk_parent);
 169        if (err < 0) {
 170                dev_err(output->dev, "failed to set parent: %d\n", err);
 171                return err;
 172        }
 173
 174        /*
 175         * We may not want to change the frequency of the parent clock, since
 176         * it may be a parent for other peripherals. This is due to the fact
 177         * that on Tegra20 there's only a single clock dedicated to display
 178         * (pll_d_out0), whereas later generations have a second one that can
 179         * be used to independently drive a second output (pll_d2_out0).
 180         *
 181         * As a way to support multiple outputs on Tegra20 as well, pll_p is
 182         * typically used as the parent clock for the display controllers.
 183         * But this comes at a cost: pll_p is the parent of several other
 184         * peripherals, so its frequency shouldn't change out of the blue.
 185         *
 186         * The best we can do at this point is to use the shift clock divider
 187         * and hope that the desired frequency can be matched (or at least
 188         * matched sufficiently close that the panel will still work).
 189         */
 190
 191        *div = ((clk_get_rate(clk) * 2) / pclk) - 2;
 192
 193        return 0;
 194}
 195
 196static int tegra_output_rgb_check_mode(struct tegra_output *output,
 197                                       struct drm_display_mode *mode,
 198                                       enum drm_mode_status *status)
 199{
 200        /*
 201         * FIXME: For now, always assume that the mode is okay. There are
 202         * unresolved issues with clk_round_rate(), which doesn't always
 203         * reliably report whether a frequency can be set or not.
 204         */
 205
 206        *status = MODE_OK;
 207
 208        return 0;
 209}
 210
 211static const struct tegra_output_ops rgb_ops = {
 212        .enable = tegra_output_rgb_enable,
 213        .disable = tegra_output_rgb_disable,
 214        .setup_clock = tegra_output_rgb_setup_clock,
 215        .check_mode = tegra_output_rgb_check_mode,
 216};
 217
 218int tegra_dc_rgb_probe(struct tegra_dc *dc)
 219{
 220        struct device_node *np;
 221        struct tegra_rgb *rgb;
 222        int err;
 223
 224        np = of_get_child_by_name(dc->dev->of_node, "rgb");
 225        if (!np || !of_device_is_available(np))
 226                return -ENODEV;
 227
 228        rgb = devm_kzalloc(dc->dev, sizeof(*rgb), GFP_KERNEL);
 229        if (!rgb)
 230                return -ENOMEM;
 231
 232        rgb->output.dev = dc->dev;
 233        rgb->output.of_node = np;
 234        rgb->dc = dc;
 235
 236        err = tegra_output_probe(&rgb->output);
 237        if (err < 0)
 238                return err;
 239
 240        rgb->clk = devm_clk_get(dc->dev, NULL);
 241        if (IS_ERR(rgb->clk)) {
 242                dev_err(dc->dev, "failed to get clock\n");
 243                return PTR_ERR(rgb->clk);
 244        }
 245
 246        rgb->clk_parent = devm_clk_get(dc->dev, "parent");
 247        if (IS_ERR(rgb->clk_parent)) {
 248                dev_err(dc->dev, "failed to get parent clock\n");
 249                return PTR_ERR(rgb->clk_parent);
 250        }
 251
 252        err = clk_set_parent(rgb->clk, rgb->clk_parent);
 253        if (err < 0) {
 254                dev_err(dc->dev, "failed to set parent clock: %d\n", err);
 255                return err;
 256        }
 257
 258        dc->rgb = &rgb->output;
 259
 260        return 0;
 261}
 262
 263int tegra_dc_rgb_remove(struct tegra_dc *dc)
 264{
 265        int err;
 266
 267        if (!dc->rgb)
 268                return 0;
 269
 270        err = tegra_output_remove(dc->rgb);
 271        if (err < 0)
 272                return err;
 273
 274        return 0;
 275}
 276
 277int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc)
 278{
 279        struct tegra_rgb *rgb = to_rgb(dc->rgb);
 280        int err;
 281
 282        if (!dc->rgb)
 283                return -ENODEV;
 284
 285        rgb->output.type = TEGRA_OUTPUT_RGB;
 286        rgb->output.ops = &rgb_ops;
 287
 288        err = tegra_output_init(dc->base.dev, &rgb->output);
 289        if (err < 0) {
 290                dev_err(dc->dev, "output setup failed: %d\n", err);
 291                return err;
 292        }
 293
 294        /*
 295         * By default, outputs can be associated with each display controller.
 296         * RGB outputs are an exception, so we make sure they can be attached
 297         * to only their parent display controller.
 298         */
 299        rgb->output.encoder.possible_crtcs = drm_crtc_mask(&dc->base);
 300
 301        return 0;
 302}
 303
 304int tegra_dc_rgb_exit(struct tegra_dc *dc)
 305{
 306        if (dc->rgb) {
 307                int err;
 308
 309                err = tegra_output_disable(dc->rgb);
 310                if (err < 0) {
 311                        dev_err(dc->dev, "output failed to disable: %d\n", err);
 312                        return err;
 313                }
 314
 315                err = tegra_output_exit(dc->rgb);
 316                if (err < 0) {
 317                        dev_err(dc->dev, "output cleanup failed: %d\n", err);
 318                        return err;
 319                }
 320
 321                dc->rgb = NULL;
 322        }
 323
 324        return 0;
 325}
 326