linux/drivers/video/fbdev/omap2/omapfb/dss/hdmi_pll.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * HDMI PLL
   4 *
   5 * Copyright (C) 2013 Texas Instruments Incorporated
   6 */
   7
   8#define DSS_SUBSYS_NAME "HDMIPLL"
   9
  10#include <linux/kernel.h>
  11#include <linux/module.h>
  12#include <linux/err.h>
  13#include <linux/io.h>
  14#include <linux/platform_device.h>
  15#include <linux/clk.h>
  16#include <linux/seq_file.h>
  17
  18#include <video/omapfb_dss.h>
  19
  20#include "dss.h"
  21#include "hdmi.h"
  22
  23void hdmi_pll_dump(struct hdmi_pll_data *pll, struct seq_file *s)
  24{
  25#define DUMPPLL(r) seq_printf(s, "%-35s %08x\n", #r,\
  26                hdmi_read_reg(pll->base, r))
  27
  28        DUMPPLL(PLLCTRL_PLL_CONTROL);
  29        DUMPPLL(PLLCTRL_PLL_STATUS);
  30        DUMPPLL(PLLCTRL_PLL_GO);
  31        DUMPPLL(PLLCTRL_CFG1);
  32        DUMPPLL(PLLCTRL_CFG2);
  33        DUMPPLL(PLLCTRL_CFG3);
  34        DUMPPLL(PLLCTRL_SSC_CFG1);
  35        DUMPPLL(PLLCTRL_SSC_CFG2);
  36        DUMPPLL(PLLCTRL_CFG4);
  37}
  38
  39void hdmi_pll_compute(struct hdmi_pll_data *pll,
  40        unsigned long target_tmds, struct dss_pll_clock_info *pi)
  41{
  42        unsigned long fint, clkdco, clkout;
  43        unsigned long target_bitclk, target_clkdco;
  44        unsigned long min_dco;
  45        unsigned n, m, mf, m2, sd;
  46        unsigned long clkin;
  47        const struct dss_pll_hw *hw = pll->pll.hw;
  48
  49        clkin = clk_get_rate(pll->pll.clkin);
  50
  51        DSSDBG("clkin %lu, target tmds %lu\n", clkin, target_tmds);
  52
  53        target_bitclk = target_tmds * 10;
  54
  55        /* Fint */
  56        n = DIV_ROUND_UP(clkin, hw->fint_max);
  57        fint = clkin / n;
  58
  59        /* adjust m2 so that the clkdco will be high enough */
  60        min_dco = roundup(hw->clkdco_min, fint);
  61        m2 = DIV_ROUND_UP(min_dco, target_bitclk);
  62        if (m2 == 0)
  63                m2 = 1;
  64
  65        target_clkdco = target_bitclk * m2;
  66        m = target_clkdco / fint;
  67
  68        clkdco = fint * m;
  69
  70        /* adjust clkdco with fractional mf */
  71        if (WARN_ON(target_clkdco - clkdco > fint))
  72                mf = 0;
  73        else
  74                mf = (u32)div_u64(262144ull * (target_clkdco - clkdco), fint);
  75
  76        if (mf > 0)
  77                clkdco += (u32)div_u64((u64)mf * fint, 262144);
  78
  79        clkout = clkdco / m2;
  80
  81        /* sigma-delta */
  82        sd = DIV_ROUND_UP(fint * m, 250000000);
  83
  84        DSSDBG("N = %u, M = %u, M.f = %u, M2 = %u, SD = %u\n",
  85                n, m, mf, m2, sd);
  86        DSSDBG("Fint %lu, clkdco %lu, clkout %lu\n", fint, clkdco, clkout);
  87
  88        pi->n = n;
  89        pi->m = m;
  90        pi->mf = mf;
  91        pi->mX[0] = m2;
  92        pi->sd = sd;
  93
  94        pi->fint = fint;
  95        pi->clkdco = clkdco;
  96        pi->clkout[0] = clkout;
  97}
  98
  99static int hdmi_pll_enable(struct dss_pll *dsspll)
 100{
 101        struct hdmi_pll_data *pll = container_of(dsspll, struct hdmi_pll_data, pll);
 102        struct hdmi_wp_data *wp = pll->wp;
 103
 104        dss_ctrl_pll_enable(DSS_PLL_HDMI, true);
 105
 106        return hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_BOTHON_ALLCLKS);
 107}
 108
 109static void hdmi_pll_disable(struct dss_pll *dsspll)
 110{
 111        struct hdmi_pll_data *pll = container_of(dsspll, struct hdmi_pll_data, pll);
 112        struct hdmi_wp_data *wp = pll->wp;
 113
 114        hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_ALLOFF);
 115
 116        dss_ctrl_pll_enable(DSS_PLL_HDMI, false);
 117}
 118
 119static const struct dss_pll_ops dsi_pll_ops = {
 120        .enable = hdmi_pll_enable,
 121        .disable = hdmi_pll_disable,
 122        .set_config = dss_pll_write_config_type_b,
 123};
 124
 125static const struct dss_pll_hw dss_omap4_hdmi_pll_hw = {
 126        .n_max = 255,
 127        .m_min = 20,
 128        .m_max = 4095,
 129        .mX_max = 127,
 130        .fint_min = 500000,
 131        .fint_max = 2500000,
 132
 133        .clkdco_min = 500000000,
 134        .clkdco_low = 1000000000,
 135        .clkdco_max = 2000000000,
 136
 137        .n_msb = 8,
 138        .n_lsb = 1,
 139        .m_msb = 20,
 140        .m_lsb = 9,
 141
 142        .mX_msb[0] = 24,
 143        .mX_lsb[0] = 18,
 144
 145        .has_selfreqdco = true,
 146};
 147
 148static const struct dss_pll_hw dss_omap5_hdmi_pll_hw = {
 149        .n_max = 255,
 150        .m_min = 20,
 151        .m_max = 2045,
 152        .mX_max = 127,
 153        .fint_min = 620000,
 154        .fint_max = 2500000,
 155
 156        .clkdco_min = 750000000,
 157        .clkdco_low = 1500000000,
 158        .clkdco_max = 2500000000UL,
 159
 160        .n_msb = 8,
 161        .n_lsb = 1,
 162        .m_msb = 20,
 163        .m_lsb = 9,
 164
 165        .mX_msb[0] = 24,
 166        .mX_lsb[0] = 18,
 167
 168        .has_selfreqdco = true,
 169        .has_refsel = true,
 170};
 171
 172static int dsi_init_pll_data(struct platform_device *pdev, struct hdmi_pll_data *hpll)
 173{
 174        struct dss_pll *pll = &hpll->pll;
 175        struct clk *clk;
 176        int r;
 177
 178        clk = devm_clk_get(&pdev->dev, "sys_clk");
 179        if (IS_ERR(clk)) {
 180                DSSERR("can't get sys_clk\n");
 181                return PTR_ERR(clk);
 182        }
 183
 184        pll->name = "hdmi";
 185        pll->id = DSS_PLL_HDMI;
 186        pll->base = hpll->base;
 187        pll->clkin = clk;
 188
 189        switch (omapdss_get_version()) {
 190        case OMAPDSS_VER_OMAP4430_ES1:
 191        case OMAPDSS_VER_OMAP4430_ES2:
 192        case OMAPDSS_VER_OMAP4:
 193                pll->hw = &dss_omap4_hdmi_pll_hw;
 194                break;
 195
 196        case OMAPDSS_VER_OMAP5:
 197        case OMAPDSS_VER_DRA7xx:
 198                pll->hw = &dss_omap5_hdmi_pll_hw;
 199                break;
 200
 201        default:
 202                return -ENODEV;
 203        }
 204
 205        pll->ops = &dsi_pll_ops;
 206
 207        r = dss_pll_register(pll);
 208        if (r)
 209                return r;
 210
 211        return 0;
 212}
 213
 214int hdmi_pll_init(struct platform_device *pdev, struct hdmi_pll_data *pll,
 215        struct hdmi_wp_data *wp)
 216{
 217        int r;
 218
 219        pll->wp = wp;
 220
 221        pll->base = devm_platform_ioremap_resource_byname(pdev, "pll");
 222        if (IS_ERR(pll->base)) {
 223                DSSERR("can't ioremap PLLCTRL\n");
 224                return PTR_ERR(pll->base);
 225        }
 226
 227        r = dsi_init_pll_data(pdev, pll);
 228        if (r) {
 229                DSSERR("failed to init HDMI PLL\n");
 230                return r;
 231        }
 232
 233        return 0;
 234}
 235
 236void hdmi_pll_uninit(struct hdmi_pll_data *hpll)
 237{
 238        struct dss_pll *pll = &hpll->pll;
 239
 240        dss_pll_unregister(pll);
 241}
 242