162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2012 Russell King 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Armada 510 (aka Dove) variant support 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include <linux/clk.h> 862306a36Sopenharmony_ci#include <linux/io.h> 962306a36Sopenharmony_ci#include <linux/of.h> 1062306a36Sopenharmony_ci#include <drm/drm_probe_helper.h> 1162306a36Sopenharmony_ci#include "armada_crtc.h" 1262306a36Sopenharmony_ci#include "armada_drm.h" 1362306a36Sopenharmony_ci#include "armada_hw.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistruct armada510_variant_data { 1662306a36Sopenharmony_ci struct clk *clks[4]; 1762306a36Sopenharmony_ci struct clk *sel_clk; 1862306a36Sopenharmony_ci}; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic int armada510_crtc_init(struct armada_crtc *dcrtc, struct device *dev) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci struct armada510_variant_data *v; 2362306a36Sopenharmony_ci struct clk *clk; 2462306a36Sopenharmony_ci int idx; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci v = devm_kzalloc(dev, sizeof(*v), GFP_KERNEL); 2762306a36Sopenharmony_ci if (!v) 2862306a36Sopenharmony_ci return -ENOMEM; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci dcrtc->variant_data = v; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci if (dev->of_node) { 3362306a36Sopenharmony_ci struct property *prop; 3462306a36Sopenharmony_ci const char *s; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci of_property_for_each_string(dev->of_node, "clock-names", prop, 3762306a36Sopenharmony_ci s) { 3862306a36Sopenharmony_ci if (!strcmp(s, "ext_ref_clk0")) 3962306a36Sopenharmony_ci idx = 0; 4062306a36Sopenharmony_ci else if (!strcmp(s, "ext_ref_clk1")) 4162306a36Sopenharmony_ci idx = 1; 4262306a36Sopenharmony_ci else if (!strcmp(s, "plldivider")) 4362306a36Sopenharmony_ci idx = 2; 4462306a36Sopenharmony_ci else if (!strcmp(s, "axibus")) 4562306a36Sopenharmony_ci idx = 3; 4662306a36Sopenharmony_ci else 4762306a36Sopenharmony_ci continue; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci clk = devm_clk_get(dev, s); 5062306a36Sopenharmony_ci if (IS_ERR(clk)) 5162306a36Sopenharmony_ci return PTR_ERR(clk) == -ENOENT ? -EPROBE_DEFER : 5262306a36Sopenharmony_ci PTR_ERR(clk); 5362306a36Sopenharmony_ci v->clks[idx] = clk; 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci } else { 5662306a36Sopenharmony_ci clk = devm_clk_get(dev, "ext_ref_clk1"); 5762306a36Sopenharmony_ci if (IS_ERR(clk)) 5862306a36Sopenharmony_ci return PTR_ERR(clk) == -ENOENT ? -EPROBE_DEFER : 5962306a36Sopenharmony_ci PTR_ERR(clk); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci v->clks[1] = clk; 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci /* 6562306a36Sopenharmony_ci * Lower the watermark so to eliminate jitter at higher bandwidths. 6662306a36Sopenharmony_ci * Disable SRAM read wait state to avoid system hang with external 6762306a36Sopenharmony_ci * clock. 6862306a36Sopenharmony_ci */ 6962306a36Sopenharmony_ci armada_updatel(CFG_DMA_WM(0x20), CFG_SRAM_WAIT | CFG_DMA_WM_MASK, 7062306a36Sopenharmony_ci dcrtc->base + LCD_CFG_RDREG4F); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci /* Initialise SPU register */ 7362306a36Sopenharmony_ci writel_relaxed(ADV_HWC32ENABLE | ADV_HWC32ARGB | ADV_HWC32BLEND, 7462306a36Sopenharmony_ci dcrtc->base + LCD_SPU_ADV_REG); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci return 0; 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic const u32 armada510_clk_sels[] = { 8062306a36Sopenharmony_ci SCLK_510_EXTCLK0, 8162306a36Sopenharmony_ci SCLK_510_EXTCLK1, 8262306a36Sopenharmony_ci SCLK_510_PLL, 8362306a36Sopenharmony_ci SCLK_510_AXI, 8462306a36Sopenharmony_ci}; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic const struct armada_clocking_params armada510_clocking = { 8762306a36Sopenharmony_ci /* HDMI requires -0.6%..+0.5% */ 8862306a36Sopenharmony_ci .permillage_min = 994, 8962306a36Sopenharmony_ci .permillage_max = 1005, 9062306a36Sopenharmony_ci .settable = BIT(0) | BIT(1), 9162306a36Sopenharmony_ci .div_max = SCLK_510_INT_DIV_MASK, 9262306a36Sopenharmony_ci}; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/* 9562306a36Sopenharmony_ci * Armada510 specific SCLK register selection. 9662306a36Sopenharmony_ci * This gets called with sclk = NULL to test whether the mode is 9762306a36Sopenharmony_ci * supportable, and again with sclk != NULL to set the clocks up for 9862306a36Sopenharmony_ci * that. The former can return an error, but the latter is expected 9962306a36Sopenharmony_ci * not to. 10062306a36Sopenharmony_ci */ 10162306a36Sopenharmony_cistatic int armada510_crtc_compute_clock(struct armada_crtc *dcrtc, 10262306a36Sopenharmony_ci const struct drm_display_mode *mode, uint32_t *sclk) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct armada510_variant_data *v = dcrtc->variant_data; 10562306a36Sopenharmony_ci unsigned long desired_khz = mode->crtc_clock; 10662306a36Sopenharmony_ci struct armada_clk_result res; 10762306a36Sopenharmony_ci int ret, idx; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci idx = armada_crtc_select_clock(dcrtc, &res, &armada510_clocking, 11062306a36Sopenharmony_ci v->clks, ARRAY_SIZE(v->clks), 11162306a36Sopenharmony_ci desired_khz); 11262306a36Sopenharmony_ci if (idx < 0) 11362306a36Sopenharmony_ci return idx; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci ret = clk_prepare_enable(res.clk); 11662306a36Sopenharmony_ci if (ret) 11762306a36Sopenharmony_ci return ret; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (sclk) { 12062306a36Sopenharmony_ci clk_set_rate(res.clk, res.desired_clk_hz); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci *sclk = res.div | armada510_clk_sels[idx]; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci /* We are now using this clock */ 12562306a36Sopenharmony_ci v->sel_clk = res.clk; 12662306a36Sopenharmony_ci swap(dcrtc->clk, res.clk); 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci clk_disable_unprepare(res.clk); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci return 0; 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic void armada510_crtc_disable(struct armada_crtc *dcrtc) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci if (dcrtc->clk) { 13762306a36Sopenharmony_ci clk_disable_unprepare(dcrtc->clk); 13862306a36Sopenharmony_ci dcrtc->clk = NULL; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic void armada510_crtc_enable(struct armada_crtc *dcrtc, 14362306a36Sopenharmony_ci const struct drm_display_mode *mode) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci struct armada510_variant_data *v = dcrtc->variant_data; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (!dcrtc->clk && v->sel_clk) { 14862306a36Sopenharmony_ci if (!WARN_ON(clk_prepare_enable(v->sel_clk))) 14962306a36Sopenharmony_ci dcrtc->clk = v->sel_clk; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ciconst struct armada_variant armada510_ops = { 15462306a36Sopenharmony_ci .has_spu_adv_reg = true, 15562306a36Sopenharmony_ci .init = armada510_crtc_init, 15662306a36Sopenharmony_ci .compute_clock = armada510_crtc_compute_clock, 15762306a36Sopenharmony_ci .disable = armada510_crtc_disable, 15862306a36Sopenharmony_ci .enable = armada510_crtc_enable, 15962306a36Sopenharmony_ci}; 160