1 /* 2 * Copyright (C) 2012 Russell King 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 2 as 6 * published by the Free Software Foundation. 7 * 8 * Armada 510 (aka Dove) variant support 9 */ 10 #include <linux/clk.h> 11 #include <linux/io.h> 12 #include <drm/drm_probe_helper.h> 13 #include "armada_crtc.h" 14 #include "armada_drm.h" 15 #include "armada_hw.h" 16 17 struct armada510_variant_data { 18 struct clk *clks[4]; 19 struct clk *sel_clk; 20 }; 21 22 static int armada510_crtc_init(struct armada_crtc *dcrtc, struct device *dev) 23 { 24 struct armada510_variant_data *v; 25 struct clk *clk; 26 int idx; 27 28 v = devm_kzalloc(dev, sizeof(*v), GFP_KERNEL); 29 if (!v) 30 return -ENOMEM; 31 32 dcrtc->variant_data = v; 33 34 if (dev->of_node) { 35 struct property *prop; 36 const char *s; 37 38 of_property_for_each_string(dev->of_node, "clock-names", prop, 39 s) { 40 if (!strcmp(s, "ext_ref_clk0")) 41 idx = 0; 42 else if (!strcmp(s, "ext_ref_clk1")) 43 idx = 1; 44 else if (!strcmp(s, "plldivider")) 45 idx = 2; 46 else if (!strcmp(s, "axibus")) 47 idx = 3; 48 else 49 continue; 50 51 clk = devm_clk_get(dev, s); 52 if (IS_ERR(clk)) 53 return PTR_ERR(clk) == -ENOENT ? -EPROBE_DEFER : 54 PTR_ERR(clk); 55 v->clks[idx] = clk; 56 } 57 } else { 58 clk = devm_clk_get(dev, "ext_ref_clk1"); 59 if (IS_ERR(clk)) 60 return PTR_ERR(clk) == -ENOENT ? -EPROBE_DEFER : 61 PTR_ERR(clk); 62 63 v->clks[1] = clk; 64 } 65 66 /* 67 * Lower the watermark so to eliminate jitter at higher bandwidths. 68 * Disable SRAM read wait state to avoid system hang with external 69 * clock. 70 */ 71 armada_updatel(CFG_DMA_WM(0x20), CFG_SRAM_WAIT | CFG_DMA_WM_MASK, 72 dcrtc->base + LCD_CFG_RDREG4F); 73 74 /* Initialise SPU register */ 75 writel_relaxed(ADV_HWC32ENABLE | ADV_HWC32ARGB | ADV_HWC32BLEND, 76 dcrtc->base + LCD_SPU_ADV_REG); 77 78 return 0; 79 } 80 81 static const u32 armada510_clk_sels[] = { 82 SCLK_510_EXTCLK0, 83 SCLK_510_EXTCLK1, 84 SCLK_510_PLL, 85 SCLK_510_AXI, 86 }; 87 88 static const struct armada_clocking_params armada510_clocking = { 89 /* HDMI requires -0.6%..+0.5% */ 90 .permillage_min = 994, 91 .permillage_max = 1005, 92 .settable = BIT(0) | BIT(1), 93 .div_max = SCLK_510_INT_DIV_MASK, 94 }; 95 96 /* 97 * Armada510 specific SCLK register selection. 98 * This gets called with sclk = NULL to test whether the mode is 99 * supportable, and again with sclk != NULL to set the clocks up for 100 * that. The former can return an error, but the latter is expected 101 * not to. 102 */ 103 static int armada510_crtc_compute_clock(struct armada_crtc *dcrtc, 104 const struct drm_display_mode *mode, uint32_t *sclk) 105 { 106 struct armada510_variant_data *v = dcrtc->variant_data; 107 unsigned long desired_khz = mode->crtc_clock; 108 struct armada_clk_result res; 109 int ret, idx; 110 111 idx = armada_crtc_select_clock(dcrtc, &res, &armada510_clocking, 112 v->clks, ARRAY_SIZE(v->clks), 113 desired_khz); 114 if (idx < 0) 115 return idx; 116 117 ret = clk_prepare_enable(res.clk); 118 if (ret) 119 return ret; 120 121 if (sclk) { 122 clk_set_rate(res.clk, res.desired_clk_hz); 123 124 *sclk = res.div | armada510_clk_sels[idx]; 125 126 /* We are now using this clock */ 127 v->sel_clk = res.clk; 128 swap(dcrtc->clk, res.clk); 129 } 130 131 clk_disable_unprepare(res.clk); 132 133 return 0; 134 } 135 136 static void armada510_crtc_disable(struct armada_crtc *dcrtc) 137 { 138 if (dcrtc->clk) { 139 clk_disable_unprepare(dcrtc->clk); 140 dcrtc->clk = NULL; 141 } 142 } 143 144 static void armada510_crtc_enable(struct armada_crtc *dcrtc, 145 const struct drm_display_mode *mode) 146 { 147 struct armada510_variant_data *v = dcrtc->variant_data; 148 149 if (!dcrtc->clk && v->sel_clk) { 150 if (!WARN_ON(clk_prepare_enable(v->sel_clk))) 151 dcrtc->clk = v->sel_clk; 152 } 153 } 154 155 const struct armada_variant armada510_ops = { 156 .has_spu_adv_reg = true, 157 .init = armada510_crtc_init, 158 .compute_clock = armada510_crtc_compute_clock, 159 .disable = armada510_crtc_disable, 160 .enable = armada510_crtc_enable, 161 }; 162