1*dee8268fSThierry Reding /* 2*dee8268fSThierry Reding * Copyright (C) 2012 Avionic Design GmbH 3*dee8268fSThierry Reding * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved. 4*dee8268fSThierry Reding * 5*dee8268fSThierry Reding * This program is free software; you can redistribute it and/or modify 6*dee8268fSThierry Reding * it under the terms of the GNU General Public License version 2 as 7*dee8268fSThierry Reding * published by the Free Software Foundation. 8*dee8268fSThierry Reding */ 9*dee8268fSThierry Reding 10*dee8268fSThierry Reding #include <linux/clk.h> 11*dee8268fSThierry Reding 12*dee8268fSThierry Reding #include "drm.h" 13*dee8268fSThierry Reding #include "dc.h" 14*dee8268fSThierry Reding 15*dee8268fSThierry Reding struct tegra_rgb { 16*dee8268fSThierry Reding struct tegra_output output; 17*dee8268fSThierry Reding struct clk *clk_parent; 18*dee8268fSThierry Reding struct clk *clk; 19*dee8268fSThierry Reding }; 20*dee8268fSThierry Reding 21*dee8268fSThierry Reding static inline struct tegra_rgb *to_rgb(struct tegra_output *output) 22*dee8268fSThierry Reding { 23*dee8268fSThierry Reding return container_of(output, struct tegra_rgb, output); 24*dee8268fSThierry Reding } 25*dee8268fSThierry Reding 26*dee8268fSThierry Reding struct reg_entry { 27*dee8268fSThierry Reding unsigned long offset; 28*dee8268fSThierry Reding unsigned long value; 29*dee8268fSThierry Reding }; 30*dee8268fSThierry Reding 31*dee8268fSThierry Reding static const struct reg_entry rgb_enable[] = { 32*dee8268fSThierry Reding { DC_COM_PIN_OUTPUT_ENABLE(0), 0x00000000 }, 33*dee8268fSThierry Reding { DC_COM_PIN_OUTPUT_ENABLE(1), 0x00000000 }, 34*dee8268fSThierry Reding { DC_COM_PIN_OUTPUT_ENABLE(2), 0x00000000 }, 35*dee8268fSThierry Reding { DC_COM_PIN_OUTPUT_ENABLE(3), 0x00000000 }, 36*dee8268fSThierry Reding { DC_COM_PIN_OUTPUT_POLARITY(0), 0x00000000 }, 37*dee8268fSThierry Reding { DC_COM_PIN_OUTPUT_POLARITY(1), 0x01000000 }, 38*dee8268fSThierry Reding { DC_COM_PIN_OUTPUT_POLARITY(2), 0x00000000 }, 39*dee8268fSThierry Reding { DC_COM_PIN_OUTPUT_POLARITY(3), 0x00000000 }, 40*dee8268fSThierry Reding { DC_COM_PIN_OUTPUT_DATA(0), 0x00000000 }, 41*dee8268fSThierry Reding { DC_COM_PIN_OUTPUT_DATA(1), 0x00000000 }, 42*dee8268fSThierry Reding { DC_COM_PIN_OUTPUT_DATA(2), 0x00000000 }, 43*dee8268fSThierry Reding { DC_COM_PIN_OUTPUT_DATA(3), 0x00000000 }, 44*dee8268fSThierry Reding { DC_COM_PIN_OUTPUT_SELECT(0), 0x00000000 }, 45*dee8268fSThierry Reding { DC_COM_PIN_OUTPUT_SELECT(1), 0x00000000 }, 46*dee8268fSThierry Reding { DC_COM_PIN_OUTPUT_SELECT(2), 0x00000000 }, 47*dee8268fSThierry Reding { DC_COM_PIN_OUTPUT_SELECT(3), 0x00000000 }, 48*dee8268fSThierry Reding { DC_COM_PIN_OUTPUT_SELECT(4), 0x00210222 }, 49*dee8268fSThierry Reding { DC_COM_PIN_OUTPUT_SELECT(5), 0x00002200 }, 50*dee8268fSThierry Reding { DC_COM_PIN_OUTPUT_SELECT(6), 0x00020000 }, 51*dee8268fSThierry Reding }; 52*dee8268fSThierry Reding 53*dee8268fSThierry Reding static const struct reg_entry rgb_disable[] = { 54*dee8268fSThierry Reding { DC_COM_PIN_OUTPUT_SELECT(6), 0x00000000 }, 55*dee8268fSThierry Reding { DC_COM_PIN_OUTPUT_SELECT(5), 0x00000000 }, 56*dee8268fSThierry Reding { DC_COM_PIN_OUTPUT_SELECT(4), 0x00000000 }, 57*dee8268fSThierry Reding { DC_COM_PIN_OUTPUT_SELECT(3), 0x00000000 }, 58*dee8268fSThierry Reding { DC_COM_PIN_OUTPUT_SELECT(2), 0x00000000 }, 59*dee8268fSThierry Reding { DC_COM_PIN_OUTPUT_SELECT(1), 0x00000000 }, 60*dee8268fSThierry Reding { DC_COM_PIN_OUTPUT_SELECT(0), 0x00000000 }, 61*dee8268fSThierry Reding { DC_COM_PIN_OUTPUT_DATA(3), 0xaaaaaaaa }, 62*dee8268fSThierry Reding { DC_COM_PIN_OUTPUT_DATA(2), 0xaaaaaaaa }, 63*dee8268fSThierry Reding { DC_COM_PIN_OUTPUT_DATA(1), 0xaaaaaaaa }, 64*dee8268fSThierry Reding { DC_COM_PIN_OUTPUT_DATA(0), 0xaaaaaaaa }, 65*dee8268fSThierry Reding { DC_COM_PIN_OUTPUT_POLARITY(3), 0x00000000 }, 66*dee8268fSThierry Reding { DC_COM_PIN_OUTPUT_POLARITY(2), 0x00000000 }, 67*dee8268fSThierry Reding { DC_COM_PIN_OUTPUT_POLARITY(1), 0x00000000 }, 68*dee8268fSThierry Reding { DC_COM_PIN_OUTPUT_POLARITY(0), 0x00000000 }, 69*dee8268fSThierry Reding { DC_COM_PIN_OUTPUT_ENABLE(3), 0x55555555 }, 70*dee8268fSThierry Reding { DC_COM_PIN_OUTPUT_ENABLE(2), 0x55555555 }, 71*dee8268fSThierry Reding { DC_COM_PIN_OUTPUT_ENABLE(1), 0x55150005 }, 72*dee8268fSThierry Reding { DC_COM_PIN_OUTPUT_ENABLE(0), 0x55555555 }, 73*dee8268fSThierry Reding }; 74*dee8268fSThierry Reding 75*dee8268fSThierry Reding static void tegra_dc_write_regs(struct tegra_dc *dc, 76*dee8268fSThierry Reding const struct reg_entry *table, 77*dee8268fSThierry Reding unsigned int num) 78*dee8268fSThierry Reding { 79*dee8268fSThierry Reding unsigned int i; 80*dee8268fSThierry Reding 81*dee8268fSThierry Reding for (i = 0; i < num; i++) 82*dee8268fSThierry Reding tegra_dc_writel(dc, table[i].value, table[i].offset); 83*dee8268fSThierry Reding } 84*dee8268fSThierry Reding 85*dee8268fSThierry Reding static int tegra_output_rgb_enable(struct tegra_output *output) 86*dee8268fSThierry Reding { 87*dee8268fSThierry Reding struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); 88*dee8268fSThierry Reding 89*dee8268fSThierry Reding tegra_dc_write_regs(dc, rgb_enable, ARRAY_SIZE(rgb_enable)); 90*dee8268fSThierry Reding 91*dee8268fSThierry Reding return 0; 92*dee8268fSThierry Reding } 93*dee8268fSThierry Reding 94*dee8268fSThierry Reding static int tegra_output_rgb_disable(struct tegra_output *output) 95*dee8268fSThierry Reding { 96*dee8268fSThierry Reding struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); 97*dee8268fSThierry Reding 98*dee8268fSThierry Reding tegra_dc_write_regs(dc, rgb_disable, ARRAY_SIZE(rgb_disable)); 99*dee8268fSThierry Reding 100*dee8268fSThierry Reding return 0; 101*dee8268fSThierry Reding } 102*dee8268fSThierry Reding 103*dee8268fSThierry Reding static int tegra_output_rgb_setup_clock(struct tegra_output *output, 104*dee8268fSThierry Reding struct clk *clk, unsigned long pclk) 105*dee8268fSThierry Reding { 106*dee8268fSThierry Reding struct tegra_rgb *rgb = to_rgb(output); 107*dee8268fSThierry Reding 108*dee8268fSThierry Reding return clk_set_parent(clk, rgb->clk_parent); 109*dee8268fSThierry Reding } 110*dee8268fSThierry Reding 111*dee8268fSThierry Reding static int tegra_output_rgb_check_mode(struct tegra_output *output, 112*dee8268fSThierry Reding struct drm_display_mode *mode, 113*dee8268fSThierry Reding enum drm_mode_status *status) 114*dee8268fSThierry Reding { 115*dee8268fSThierry Reding /* 116*dee8268fSThierry Reding * FIXME: For now, always assume that the mode is okay. There are 117*dee8268fSThierry Reding * unresolved issues with clk_round_rate(), which doesn't always 118*dee8268fSThierry Reding * reliably report whether a frequency can be set or not. 119*dee8268fSThierry Reding */ 120*dee8268fSThierry Reding 121*dee8268fSThierry Reding *status = MODE_OK; 122*dee8268fSThierry Reding 123*dee8268fSThierry Reding return 0; 124*dee8268fSThierry Reding } 125*dee8268fSThierry Reding 126*dee8268fSThierry Reding static const struct tegra_output_ops rgb_ops = { 127*dee8268fSThierry Reding .enable = tegra_output_rgb_enable, 128*dee8268fSThierry Reding .disable = tegra_output_rgb_disable, 129*dee8268fSThierry Reding .setup_clock = tegra_output_rgb_setup_clock, 130*dee8268fSThierry Reding .check_mode = tegra_output_rgb_check_mode, 131*dee8268fSThierry Reding }; 132*dee8268fSThierry Reding 133*dee8268fSThierry Reding int tegra_dc_rgb_probe(struct tegra_dc *dc) 134*dee8268fSThierry Reding { 135*dee8268fSThierry Reding struct device_node *np; 136*dee8268fSThierry Reding struct tegra_rgb *rgb; 137*dee8268fSThierry Reding int err; 138*dee8268fSThierry Reding 139*dee8268fSThierry Reding np = of_get_child_by_name(dc->dev->of_node, "rgb"); 140*dee8268fSThierry Reding if (!np || !of_device_is_available(np)) 141*dee8268fSThierry Reding return -ENODEV; 142*dee8268fSThierry Reding 143*dee8268fSThierry Reding rgb = devm_kzalloc(dc->dev, sizeof(*rgb), GFP_KERNEL); 144*dee8268fSThierry Reding if (!rgb) 145*dee8268fSThierry Reding return -ENOMEM; 146*dee8268fSThierry Reding 147*dee8268fSThierry Reding rgb->output.dev = dc->dev; 148*dee8268fSThierry Reding rgb->output.of_node = np; 149*dee8268fSThierry Reding 150*dee8268fSThierry Reding err = tegra_output_parse_dt(&rgb->output); 151*dee8268fSThierry Reding if (err < 0) 152*dee8268fSThierry Reding return err; 153*dee8268fSThierry Reding 154*dee8268fSThierry Reding rgb->clk = devm_clk_get(dc->dev, NULL); 155*dee8268fSThierry Reding if (IS_ERR(rgb->clk)) { 156*dee8268fSThierry Reding dev_err(dc->dev, "failed to get clock\n"); 157*dee8268fSThierry Reding return PTR_ERR(rgb->clk); 158*dee8268fSThierry Reding } 159*dee8268fSThierry Reding 160*dee8268fSThierry Reding rgb->clk_parent = devm_clk_get(dc->dev, "parent"); 161*dee8268fSThierry Reding if (IS_ERR(rgb->clk_parent)) { 162*dee8268fSThierry Reding dev_err(dc->dev, "failed to get parent clock\n"); 163*dee8268fSThierry Reding return PTR_ERR(rgb->clk_parent); 164*dee8268fSThierry Reding } 165*dee8268fSThierry Reding 166*dee8268fSThierry Reding err = clk_set_parent(rgb->clk, rgb->clk_parent); 167*dee8268fSThierry Reding if (err < 0) { 168*dee8268fSThierry Reding dev_err(dc->dev, "failed to set parent clock: %d\n", err); 169*dee8268fSThierry Reding return err; 170*dee8268fSThierry Reding } 171*dee8268fSThierry Reding 172*dee8268fSThierry Reding dc->rgb = &rgb->output; 173*dee8268fSThierry Reding 174*dee8268fSThierry Reding return 0; 175*dee8268fSThierry Reding } 176*dee8268fSThierry Reding 177*dee8268fSThierry Reding int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc) 178*dee8268fSThierry Reding { 179*dee8268fSThierry Reding struct tegra_rgb *rgb = to_rgb(dc->rgb); 180*dee8268fSThierry Reding int err; 181*dee8268fSThierry Reding 182*dee8268fSThierry Reding if (!dc->rgb) 183*dee8268fSThierry Reding return -ENODEV; 184*dee8268fSThierry Reding 185*dee8268fSThierry Reding rgb->output.type = TEGRA_OUTPUT_RGB; 186*dee8268fSThierry Reding rgb->output.ops = &rgb_ops; 187*dee8268fSThierry Reding 188*dee8268fSThierry Reding err = tegra_output_init(dc->base.dev, &rgb->output); 189*dee8268fSThierry Reding if (err < 0) { 190*dee8268fSThierry Reding dev_err(dc->dev, "output setup failed: %d\n", err); 191*dee8268fSThierry Reding return err; 192*dee8268fSThierry Reding } 193*dee8268fSThierry Reding 194*dee8268fSThierry Reding /* 195*dee8268fSThierry Reding * By default, outputs can be associated with each display controller. 196*dee8268fSThierry Reding * RGB outputs are an exception, so we make sure they can be attached 197*dee8268fSThierry Reding * to only their parent display controller. 198*dee8268fSThierry Reding */ 199*dee8268fSThierry Reding rgb->output.encoder.possible_crtcs = 1 << dc->pipe; 200*dee8268fSThierry Reding 201*dee8268fSThierry Reding return 0; 202*dee8268fSThierry Reding } 203*dee8268fSThierry Reding 204*dee8268fSThierry Reding int tegra_dc_rgb_exit(struct tegra_dc *dc) 205*dee8268fSThierry Reding { 206*dee8268fSThierry Reding if (dc->rgb) { 207*dee8268fSThierry Reding int err; 208*dee8268fSThierry Reding 209*dee8268fSThierry Reding err = tegra_output_disable(dc->rgb); 210*dee8268fSThierry Reding if (err < 0) { 211*dee8268fSThierry Reding dev_err(dc->dev, "output failed to disable: %d\n", err); 212*dee8268fSThierry Reding return err; 213*dee8268fSThierry Reding } 214*dee8268fSThierry Reding 215*dee8268fSThierry Reding err = tegra_output_exit(dc->rgb); 216*dee8268fSThierry Reding if (err < 0) { 217*dee8268fSThierry Reding dev_err(dc->dev, "output cleanup failed: %d\n", err); 218*dee8268fSThierry Reding return err; 219*dee8268fSThierry Reding } 220*dee8268fSThierry Reding 221*dee8268fSThierry Reding dc->rgb = NULL; 222*dee8268fSThierry Reding } 223*dee8268fSThierry Reding 224*dee8268fSThierry Reding return 0; 225*dee8268fSThierry Reding } 226