blob: 1d3bd9475c098ebd33bdf47e813bb51d1b06fd40 [file] [log] [blame]
/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/clk/msm-clk-provider.h>
#include <linux/clk/msm-clk.h>
#include <linux/workqueue.h>
#include <linux/clk/msm-clock-generic.h>
#include <dt-bindings/clock/msm-clocks-8952.h>
#include "mdss-pll.h"
#include "mdss-dsi-pll.h"
#include "mdss-dsi-pll-12nm.h"
/*
* Clock tree model for generating DSI byte clock and pclk for 12nm DSI PLL
*
*
* +---------------+
* +----------| vco_clk |----------+
* | +---------------+ |
* | |
* | |
* | |
* +---------+---------+----+----+---------+---------+ |
* | | | | | | |
* | | | | | | |
* | | | | | | |
* +---v---+ +---v---+ +---v---+ +---v---+ +---v---+ +---v---+ |
* | DIV(1)| | DIV(2)| | DIV(4)| | DIV(8)| |DIV(16)| |DIV(32)| |
* +---+---+ +---+---+ +---+---+ +---+---+ +---+---+ +---+---+ |
* | | | | | | |
* | | +---+ +---+ | | |
* | +-----------+ | | +-----------+ | |
* +-------------------+ | | | | +-------------------+ |
* | | | | | | |
* +--v-v-v-v-v-v---+ |
* \ post_div_mux / |
* \ / |
* +-----+----+ +---------------------+
* | |
* +------------------------+ |
* | |
* +----v----+ +---------+---------+----+----+---------+---------+
* | DIV-4 | | | | | | |
* +----+----+ | | | | | |
* | +---v---+ +---v---+ +---v---+ +---v---+ +---v---+ +---v---+
* | | DIV(1)| | DIV(2)| | DIV(4)| | DIV(8)| |DIV(16)| |DIV(32)|
* | +---+---+ +---+---+ +---+---+ +---+---+ +---+---+ +---+---+
* | | | | | | |
* v | | +---+ +---+ | |
* byte_clk_src | +-----------+ | | +-----------+ |
* +-------------------+ | | | | +-------------------+
* | | | | | |
* +--v-v-v-v-v-v---+
* \ gp_cntrl_mux /
* \ /
* +-----+----+
* |
* |
* +-------v-------+
* | (DIV + 1) |
* | DIV = 0...127 |
* +-------+-------+
* |
* |
* v
* dsi_pclk input to Clock Controller MND
*/
static struct dsi_pll_db pll_db[DSI_PLL_NUM];
static struct clk_ops pixel_div_clk_src_ops;
/* Op structures */
static const struct clk_ops clk_ops_dsi_vco = {
.set_rate = pll_vco_set_rate_12nm,
.round_rate = pll_vco_round_rate_12nm,
.handoff = pll_vco_handoff_12nm,
.prepare = pll_vco_prepare_12nm,
.unprepare = pll_vco_unprepare_12nm,
.enable = pll_vco_enable_12nm,
};
static struct clk_div_ops pixel_div_ops = {
.set_div = pixel_div_set_div,
.get_div = pixel_div_get_div,
};
static struct clk_mux_ops post_div_mux_ops = {
.set_mux_sel = set_post_div_mux_sel,
.get_mux_sel = get_post_div_mux_sel,
};
static struct clk_mux_ops gp_div_mux_ops = {
.set_mux_sel = set_gp_mux_sel,
.get_mux_sel = get_gp_mux_sel,
};
static struct dsi_pll_vco_clk dsi0pll_vco_clk = {
.ref_clk_rate = 19200000UL,
.min_rate = 1000000000UL,
.max_rate = 2000000000UL,
.pll_en_seq_cnt = 1,
.pll_enable_seqs[0] = dsi_pll_enable_seq_12nm,
.c = {
.dbg_name = "dsi0pll_vco_clk_12nm",
.ops = &clk_ops_dsi_vco,
CLK_INIT(dsi0pll_vco_clk.c),
},
};
static struct dsi_pll_vco_clk dsi1pll_vco_clk = {
.ref_clk_rate = 19200000UL,
.min_rate = 1000000000UL,
.max_rate = 2000000000UL,
.pll_en_seq_cnt = 1,
.pll_enable_seqs[0] = dsi_pll_enable_seq_12nm,
.c = {
.dbg_name = "dsi1pll_vco_clk_12nm",
.ops = &clk_ops_dsi_vco,
CLK_INIT(dsi1pll_vco_clk.c),
},
};
static struct div_clk dsi0pll_post_div1 = {
.data = {
.div = 1,
.min_div = 1,
.max_div = 1,
},
.c = {
.parent = &dsi0pll_vco_clk.c,
.dbg_name = "dsi0pll_post_div1",
.ops = &clk_ops_div,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi0pll_post_div1.c),
},
};
static struct div_clk dsi0pll_post_div2 = {
.data = {
.div = 2,
.min_div = 2,
.max_div = 2,
},
.c = {
.parent = &dsi0pll_vco_clk.c,
.dbg_name = "dsi0pll_post_div2",
.ops = &clk_ops_div,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi0pll_post_div2.c),
},
};
static struct div_clk dsi0pll_post_div4 = {
.data = {
.div = 4,
.min_div = 4,
.max_div = 4,
},
.c = {
.parent = &dsi0pll_vco_clk.c,
.dbg_name = "dsi0pll_post_div4",
.ops = &clk_ops_div,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi0pll_post_div4.c),
},
};
static struct div_clk dsi0pll_post_div8 = {
.data = {
.div = 8,
.min_div = 8,
.max_div = 8,
},
.c = {
.parent = &dsi0pll_vco_clk.c,
.dbg_name = "dsi0pll_post_div8",
.ops = &clk_ops_div,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi0pll_post_div8.c),
},
};
static struct div_clk dsi0pll_post_div16 = {
.data = {
.div = 16,
.min_div = 16,
.max_div = 16,
},
.c = {
.parent = &dsi0pll_vco_clk.c,
.dbg_name = "dsi0pll_post_div16",
.ops = &clk_ops_div,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi0pll_post_div16.c),
},
};
static struct div_clk dsi0pll_post_div32 = {
.data = {
.div = 32,
.min_div = 32,
.max_div = 32,
},
.c = {
.parent = &dsi0pll_vco_clk.c,
.dbg_name = "dsi0pll_post_div32",
.ops = &clk_ops_div,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi0pll_post_div32.c),
},
};
static struct mux_clk dsi0pll_post_div_mux = {
.num_parents = 6,
.parents = (struct clk_src[]) {
{&dsi0pll_post_div1.c, 0},
{&dsi0pll_post_div2.c, 1},
{&dsi0pll_post_div4.c, 2},
{&dsi0pll_post_div8.c, 3},
{&dsi0pll_post_div16.c, 4},
{&dsi0pll_post_div32.c, 5},
},
.ops = &post_div_mux_ops,
.c = {
.parent = &dsi0pll_post_div1.c,
.dbg_name = "dsi0pll_post_div_mux",
.ops = &clk_ops_gen_mux,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi0pll_post_div_mux.c),
}
};
static struct div_clk dsi1pll_post_div1 = {
.data = {
.div = 1,
.min_div = 1,
.max_div = 1,
},
.c = {
.parent = &dsi1pll_vco_clk.c,
.dbg_name = "dsi1pll_post_div1",
.ops = &clk_ops_div,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi1pll_post_div1.c),
},
};
static struct div_clk dsi1pll_post_div2 = {
.data = {
.div = 2,
.min_div = 2,
.max_div = 2,
},
.c = {
.parent = &dsi1pll_vco_clk.c,
.dbg_name = "dsi1pll_post_div2",
.ops = &clk_ops_div,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi1pll_post_div2.c),
},
};
static struct div_clk dsi1pll_post_div4 = {
.data = {
.div = 4,
.min_div = 4,
.max_div = 4,
},
.c = {
.parent = &dsi1pll_vco_clk.c,
.dbg_name = "dsi1pll_post_div4",
.ops = &clk_ops_div,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi1pll_post_div4.c),
},
};
static struct div_clk dsi1pll_post_div8 = {
.data = {
.div = 8,
.min_div = 8,
.max_div = 8,
},
.c = {
.parent = &dsi1pll_vco_clk.c,
.dbg_name = "dsi1pll_post_div8",
.ops = &clk_ops_div,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi1pll_post_div8.c),
},
};
static struct div_clk dsi1pll_post_div16 = {
.data = {
.div = 16,
.min_div = 16,
.max_div = 16,
},
.c = {
.parent = &dsi1pll_vco_clk.c,
.dbg_name = "dsi1pll_post_div16",
.ops = &clk_ops_div,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi1pll_post_div16.c),
},
};
static struct div_clk dsi1pll_post_div32 = {
.data = {
.div = 32,
.min_div = 32,
.max_div = 32,
},
.c = {
.parent = &dsi1pll_vco_clk.c,
.dbg_name = "dsi1pll_post_div32",
.ops = &clk_ops_div,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi1pll_post_div32.c),
},
};
static struct mux_clk dsi1pll_post_div_mux = {
.num_parents = 6,
.parents = (struct clk_src[]) {
{&dsi1pll_post_div1.c, 0},
{&dsi1pll_post_div2.c, 1},
{&dsi1pll_post_div4.c, 2},
{&dsi1pll_post_div8.c, 3},
{&dsi1pll_post_div16.c, 4},
{&dsi1pll_post_div32.c, 5},
},
.ops = &post_div_mux_ops,
.c = {
.parent = &dsi1pll_post_div1.c,
.dbg_name = "dsi1pll_post_div_mux",
.ops = &clk_ops_gen_mux,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi1pll_post_div_mux.c),
}
};
static struct div_clk dsi0pll_gp_div1 = {
.data = {
.div = 1,
.min_div = 1,
.max_div = 1,
},
.c = {
.parent = &dsi0pll_vco_clk.c,
.dbg_name = "dsi0pll_gp_div1",
.ops = &clk_ops_slave_div,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi0pll_gp_div1.c),
},
};
static struct div_clk dsi0pll_gp_div2 = {
.data = {
.div = 2,
.min_div = 2,
.max_div = 2,
},
.c = {
.parent = &dsi0pll_vco_clk.c,
.dbg_name = "dsi0pll_gp_div2",
.ops = &clk_ops_slave_div,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi0pll_gp_div2.c),
},
};
static struct div_clk dsi0pll_gp_div4 = {
.data = {
.div = 4,
.min_div = 4,
.max_div = 4,
},
.c = {
.parent = &dsi0pll_vco_clk.c,
.dbg_name = "dsi0pll_gp_div4",
.ops = &clk_ops_slave_div,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi0pll_gp_div4.c),
},
};
static struct div_clk dsi0pll_gp_div8 = {
.data = {
.div = 8,
.min_div = 8,
.max_div = 8,
},
.c = {
.parent = &dsi0pll_vco_clk.c,
.dbg_name = "dsi0pll_gp_div8",
.ops = &clk_ops_slave_div,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi0pll_gp_div8.c),
},
};
static struct div_clk dsi0pll_gp_div16 = {
.data = {
.div = 16,
.min_div = 16,
.max_div = 16,
},
.c = {
.parent = &dsi0pll_vco_clk.c,
.dbg_name = "dsi0pll_gp_div16",
.ops = &clk_ops_slave_div,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi0pll_gp_div16.c),
},
};
static struct div_clk dsi0pll_gp_div32 = {
.data = {
.div = 32,
.min_div = 32,
.max_div = 32,
},
.c = {
.parent = &dsi0pll_vco_clk.c,
.dbg_name = "dsi0pll_gp_div32",
.ops = &clk_ops_slave_div,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi0pll_gp_div32.c),
},
};
static struct mux_clk dsi0pll_gp_div_mux = {
.num_parents = 6,
.parents = (struct clk_src[]) {
{&dsi0pll_gp_div1.c, 0},
{&dsi0pll_gp_div2.c, 1},
{&dsi0pll_gp_div4.c, 2},
{&dsi0pll_gp_div8.c, 3},
{&dsi0pll_gp_div16.c, 4},
{&dsi0pll_gp_div32.c, 5},
},
.ops = &gp_div_mux_ops,
.c = {
.parent = &dsi0pll_gp_div1.c,
.dbg_name = "dsi0pll_gp_div_mux",
.ops = &clk_ops_gen_mux,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi0pll_gp_div_mux.c),
}
};
static struct div_clk dsi1pll_gp_div1 = {
.data = {
.div = 1,
.min_div = 1,
.max_div = 1,
},
.c = {
.parent = &dsi1pll_vco_clk.c,
.dbg_name = "dsi1pll_gp_div1",
.ops = &clk_ops_slave_div,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi1pll_gp_div1.c),
},
};
static struct div_clk dsi1pll_gp_div2 = {
.data = {
.div = 2,
.min_div = 2,
.max_div = 2,
},
.c = {
.parent = &dsi1pll_vco_clk.c,
.dbg_name = "dsi1pll_gp_div2",
.ops = &clk_ops_slave_div,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi1pll_gp_div2.c),
},
};
static struct div_clk dsi1pll_gp_div4 = {
.data = {
.div = 4,
.min_div = 4,
.max_div = 4,
},
.c = {
.parent = &dsi1pll_vco_clk.c,
.dbg_name = "dsi1pll_gp_div4",
.ops = &clk_ops_slave_div,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi1pll_gp_div4.c),
},
};
static struct div_clk dsi1pll_gp_div8 = {
.data = {
.div = 8,
.min_div = 8,
.max_div = 8,
},
.c = {
.parent = &dsi1pll_vco_clk.c,
.dbg_name = "dsi1pll_gp_div8",
.ops = &clk_ops_slave_div,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi1pll_gp_div8.c),
},
};
static struct div_clk dsi1pll_gp_div16 = {
.data = {
.div = 16,
.min_div = 16,
.max_div = 16,
},
.c = {
.parent = &dsi1pll_vco_clk.c,
.dbg_name = "dsi1pll_gp_div16",
.ops = &clk_ops_slave_div,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi1pll_gp_div16.c),
},
};
static struct div_clk dsi1pll_gp_div32 = {
.data = {
.div = 32,
.min_div = 32,
.max_div = 32,
},
.c = {
.parent = &dsi1pll_vco_clk.c,
.dbg_name = "dsi1pll_gp_div32",
.ops = &clk_ops_slave_div,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi1pll_gp_div32.c),
},
};
static struct mux_clk dsi1pll_gp_div_mux = {
.num_parents = 6,
.parents = (struct clk_src[]) {
{&dsi1pll_gp_div1.c, 0},
{&dsi1pll_gp_div2.c, 1},
{&dsi1pll_gp_div4.c, 2},
{&dsi1pll_gp_div8.c, 3},
{&dsi1pll_gp_div16.c, 4},
{&dsi1pll_gp_div32.c, 5},
},
.ops = &gp_div_mux_ops,
.c = {
.parent = &dsi1pll_gp_div1.c,
.dbg_name = "dsi1pll_gp_div_mux",
.ops = &clk_ops_gen_mux,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi1pll_gp_div_mux.c),
}
};
static struct div_clk dsi0pll_pixel_clk_src = {
.data = {
.max_div = 128,
.min_div = 1,
},
.ops = &pixel_div_ops,
.c = {
.parent = &dsi0pll_gp_div_mux.c,
.dbg_name = "dsi0pll_pixel_clk_src",
.ops = &pixel_div_clk_src_ops,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi0pll_pixel_clk_src.c),
},
};
static struct div_clk dsi1pll_pixel_clk_src = {
.data = {
.max_div = 128,
.min_div = 1,
},
.ops = &pixel_div_ops,
.c = {
.parent = &dsi1pll_gp_div_mux.c,
.dbg_name = "dsi1pll_pixel_clk_src",
.ops = &pixel_div_clk_src_ops,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi1pll_pixel_clk_src.c),
},
};
static struct div_clk dsi0pll_byte_clk_src = {
.data = {
.div = 4,
.min_div = 4,
.max_div = 4,
},
.c = {
.parent = &dsi0pll_post_div_mux.c,
.dbg_name = "dsi0pll_byte_clk_src",
.ops = &clk_ops_div,
CLK_INIT(dsi0pll_byte_clk_src.c),
},
};
static struct div_clk dsi1pll_byte_clk_src = {
.data = {
.div = 4,
.min_div = 4,
.max_div = 4,
},
.c = {
.parent = &dsi1pll_post_div_mux.c,
.dbg_name = "dsi1pll_byte_clk_src",
.ops = &clk_ops_div,
CLK_INIT(dsi1pll_byte_clk_src.c),
},
};
static struct clk_lookup mdss_dsi_pllcc_12nm[] = {
CLK_LIST(dsi0pll_vco_clk),
CLK_LIST(dsi0pll_byte_clk_src),
CLK_LIST(dsi0pll_pixel_clk_src),
};
static struct clk_lookup mdss_dsi_pllcc_12nm_1[] = {
CLK_LIST(dsi1pll_vco_clk),
CLK_LIST(dsi1pll_byte_clk_src),
CLK_LIST(dsi1pll_pixel_clk_src),
};
int dsi_pll_clock_register_12nm(struct platform_device *pdev,
struct mdss_pll_resources *pll_res)
{
int rc = 0, ndx;
struct dsi_pll_db *pdb;
if (!pdev || !pdev->dev.of_node) {
pr_err("Invalid input parameters\n");
return -EINVAL;
}
if (!pll_res || !pll_res->pll_base) {
pr_err("Invalid PLL resources\n");
return -EPROBE_DEFER;
}
if (pll_res->index >= DSI_PLL_NUM) {
pr_err("pll ndx=%d is NOT supported\n", pll_res->index);
return -EINVAL;
}
ndx = pll_res->index;
pdb = &pll_db[ndx];
pll_res->priv = pdb;
pdb->pll = pll_res;
ndx++;
ndx %= DSI_PLL_NUM;
pdb->next = &pll_db[ndx];
/* Set clock source operations */
/* pixel_clk */
pixel_div_clk_src_ops = clk_ops_div;
pixel_div_clk_src_ops.prepare = dsi_pll_div_prepare;
/* Set client data to mux, div and vco clocks. */
if (pll_res->index == DSI_PLL_1) {
dsi1pll_byte_clk_src.priv = pll_res;
dsi1pll_post_div_mux.priv = pll_res;
dsi1pll_post_div1.priv = pll_res;
dsi1pll_post_div2.priv = pll_res;
dsi1pll_post_div4.priv = pll_res;
dsi1pll_post_div8.priv = pll_res;
dsi1pll_post_div16.priv = pll_res;
dsi1pll_post_div32.priv = pll_res;
dsi1pll_pixel_clk_src.priv = pll_res;
dsi1pll_gp_div_mux.priv = pll_res;
dsi1pll_gp_div1.priv = pll_res;
dsi1pll_gp_div2.priv = pll_res;
dsi1pll_gp_div4.priv = pll_res;
dsi1pll_gp_div8.priv = pll_res;
dsi1pll_gp_div16.priv = pll_res;
dsi1pll_gp_div32.priv = pll_res;
dsi1pll_vco_clk.priv = pll_res;
if (pll_res->target_id == MDSS_PLL_TARGET_SDM439)
rc = of_msm_clock_register(pdev->dev.of_node,
mdss_dsi_pllcc_12nm_1,
ARRAY_SIZE(mdss_dsi_pllcc_12nm_1));
} else {
dsi0pll_byte_clk_src.priv = pll_res;
dsi0pll_post_div_mux.priv = pll_res;
dsi0pll_post_div1.priv = pll_res;
dsi0pll_post_div2.priv = pll_res;
dsi0pll_post_div4.priv = pll_res;
dsi0pll_post_div8.priv = pll_res;
dsi0pll_post_div16.priv = pll_res;
dsi0pll_post_div32.priv = pll_res;
dsi0pll_pixel_clk_src.priv = pll_res;
dsi0pll_gp_div_mux.priv = pll_res;
dsi0pll_gp_div1.priv = pll_res;
dsi0pll_gp_div2.priv = pll_res;
dsi0pll_gp_div4.priv = pll_res;
dsi0pll_gp_div8.priv = pll_res;
dsi0pll_gp_div16.priv = pll_res;
dsi0pll_gp_div32.priv = pll_res;
dsi0pll_vco_clk.priv = pll_res;
if (pll_res->target_id == MDSS_PLL_TARGET_SDM439)
rc = of_msm_clock_register(pdev->dev.of_node,
mdss_dsi_pllcc_12nm,
ARRAY_SIZE(mdss_dsi_pllcc_12nm));
}
if (!rc) {
pr_info("Registered DSI PLL ndx=%d clocks successfully\n",
pll_res->index);
}
return rc;
}