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{
 164        struct tegra_rgb *rgb = to_rgb(output);
 165
 166        return clk_set_parent(clk, rgb->clk_parent);
 167}
 168
 169static int tegra_output_rgb_check_mode(struct tegra_output *output,
 170                                       struct drm_display_mode *mode,
 171                                       enum drm_mode_status *status)
 172{
 173        /*
 174         * FIXME: For now, always assume that the mode is okay. There are
 175         * unresolved issues with clk_round_rate(), which doesn't always
 176         * reliably report whether a frequency can be set or not.
 177         */
 178
 179        *status = MODE_OK;
 180
 181        return 0;
 182}
 183
 184static const struct tegra_output_ops rgb_ops = {
 185        .enable = tegra_output_rgb_enable,
 186        .disable = tegra_output_rgb_disable,
 187        .setup_clock = tegra_output_rgb_setup_clock,
 188        .check_mode = tegra_output_rgb_check_mode,
 189};
 190
 191int tegra_dc_rgb_probe(struct tegra_dc *dc)
 192{
 193        struct device_node *np;
 194        struct tegra_rgb *rgb;
 195        int err;
 196
 197        np = of_get_child_by_name(dc->dev->of_node, "rgb");
 198        if (!np || !of_device_is_available(np))
 199                return -ENODEV;
 200
 201        rgb = devm_kzalloc(dc->dev, sizeof(*rgb), GFP_KERNEL);
 202        if (!rgb)
 203                return -ENOMEM;
 204
 205        rgb->output.dev = dc->dev;
 206        rgb->output.of_node = np;
 207        rgb->dc = dc;
 208
 209        err = tegra_output_probe(&rgb->output);
 210        if (err < 0)
 211                return err;
 212
 213        rgb->clk = devm_clk_get(dc->dev, NULL);
 214        if (IS_ERR(rgb->clk)) {
 215                dev_err(dc->dev, "failed to get clock\n");
 216                return PTR_ERR(rgb->clk);
 217        }
 218
 219        rgb->clk_parent = devm_clk_get(dc->dev, "parent");
 220        if (IS_ERR(rgb->clk_parent)) {
 221                dev_err(dc->dev, "failed to get parent clock\n");
 222                return PTR_ERR(rgb->clk_parent);
 223        }
 224
 225        err = clk_set_parent(rgb->clk, rgb->clk_parent);
 226        if (err < 0) {
 227                dev_err(dc->dev, "failed to set parent clock: %d\n", err);
 228                return err;
 229        }
 230
 231        dc->rgb = &rgb->output;
 232
 233        return 0;
 234}
 235
 236int tegra_dc_rgb_remove(struct tegra_dc *dc)
 237{
 238        int err;
 239
 240        if (!dc->rgb)
 241                return 0;
 242
 243        err = tegra_output_remove(dc->rgb);
 244        if (err < 0)
 245                return err;
 246
 247        return 0;
 248}
 249
 250int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc)
 251{
 252        struct tegra_rgb *rgb = to_rgb(dc->rgb);
 253        int err;
 254
 255        if (!dc->rgb)
 256                return -ENODEV;
 257
 258        rgb->output.type = TEGRA_OUTPUT_RGB;
 259        rgb->output.ops = &rgb_ops;
 260
 261        err = tegra_output_init(dc->base.dev, &rgb->output);
 262        if (err < 0) {
 263                dev_err(dc->dev, "output setup failed: %d\n", err);
 264                return err;
 265        }
 266
 267        /*
 268         * By default, outputs can be associated with each display controller.
 269         * RGB outputs are an exception, so we make sure they can be attached
 270         * to only their parent display controller.
 271         */
 272        rgb->output.encoder.possible_crtcs = drm_crtc_mask(&dc->base);
 273
 274        return 0;
 275}
 276
 277int tegra_dc_rgb_exit(struct tegra_dc *dc)
 278{
 279        if (dc->rgb) {
 280                int err;
 281
 282                err = tegra_output_disable(dc->rgb);
 283                if (err < 0) {
 284                        dev_err(dc->dev, "output failed to disable: %d\n", err);
 285                        return err;
 286                }
 287
 288                err = tegra_output_exit(dc->rgb);
 289                if (err < 0) {
 290                        dev_err(dc->dev, "output cleanup failed: %d\n", err);
 291                        return err;
 292                }
 293
 294                dc->rgb = NULL;
 295        }
 296
 297        return 0;
 298}
 299