Commit 0446e8d8 authored by Guido Gunther's avatar Guido Gunther

Merge branch 'f/4.18/drm' into imx8-4.18-wip

parents b33a1f0b c961f048
NXP specific extensions to the Northwest Logic MIPI-DSI
================================
Platform specific extentions for the NWL MIPI-DSI host controller found in
MX8 platforms. This is an encoder/bridge that manages the platform specific
initializations required for the NWL MIPI-DSI host.
Required properties:
- compatible: "fsl,<chip>-mipi-dsi"
The following strings are expected:
"fsl,imx8qm-mipi-dsi"
"fsl,imx8qxp-mipi-dsi"
"fsl,imx8mq-mipi-dsi_drm"
- reg: the register range of the MIPI-DSI controller
- interrupts: the interrupt number for this module
- clock, clock-names: phandles to the MIPI-DSI clocks
The following clocks are expected on all platforms:
"phy_ref" - PHY_REF clock
"tx_esc" - TX_ESC clock (used in escape mode)
"rx_esc" - RX_ESC clock (used in escape mode)
The following clocks are expected on i.MX8qm and i.MX8qxp:
"bypass" - bypass clock
"pixel" - pixel clock (for the pixel link)
- assigned-clocks: phandles to clocks that requires initial configuration
- assigned-clock-rates: rates of the clocks that requires initial configuration
The following clocks needs to have an initial configuration:
"tx_esc" and "rx_esc"
- port: input and output port nodes with endpoint definitions as
defined in Documentation/devicetree/bindings/graph.txt;
the input port should be connected to a display
interface and the output port should be connected to a
NWL MIPI-DSI host
- phys: phandle to the phy module representing the DPHY
inside MIPI-DSI IP block
- phy-names: should be "dphy"
Optional properties:
- power-domains phandle to the power domain
- interrupt-parent phandle to the interrupt parent, if there is one;
usually, on i.MX8qm and i.MX8qxp there is an irq
steer handling the MIPI DSI interrupts
- csr phandle to the CSR register set (required on i.MX8qm
and i.MX8qxp for the reset functions)
- assigned-clock-parents phandles to parent clocks that needs to be assigned as
parents to clocks defined in assigned-clocks
- sync-pol horizontal and vertical sync polarity of the input
signal; can be <0> for LOW (negative) or <1> for HIGH
(positive) polarity; default value is <0>, when this
property is ommited
- pwr-delay delay used in enable, before enabling the clocks; this is
useful when the PLL needs some time to become stable;
this value represents milliseconds
Example:
mipi_dsi1: mipi_dsi {
compatible = "fsl,imx8qxp-mipi-dsi";
clocks =
<&clk IMX8QXP_MIPI0_PIXEL_CLK>,
<&clk IMX8QXP_MIPI0_BYPASS_CLK>,
<&clk IMX8QXP_CLK_DUMMY>;
clock-names = "pixel", "bypass", "phy_ref";
power-domains = <&pd_mipi_dsi0>;
csr = <&mipi_dsi_csr1>;
phys = <&mipi_dsi_phy1>;
phy-names = "dphy";
status = "disabled";
port@0 {
mipi_dsi1_in: endpoint {
remote-endpoint = <&dpu_disp0_mipi_dsi>;
};
};
port@1 {
mipi_dsi1_out: endpoint {
remote-endpoint = <&mipi_dsi_bridge1_in>;
};
};
};
Mixel DSI DPHY for MX8
The MIPI-DSI DPHY IP block found on MX8 platforms comes along the MIPI-DSI
IP from Northwest Logic and represents the physical layer for the electrical
signals for DSI.
Required properties:
- compatible: "mixel,<soc>-mipi-dsi-phy"
The following strings are expected:
"mixel,imx8qm-mipi-dsi-phy"
"mixel,imx8qxp-mipi-dsi-phy"
"mixel,imx8mq-mipi-dsi-phy"
- reg: the register range of the PHY controller
- #phy-cells: number of cells in PHY, as defined in
Documentation/devicetree/bindings/phy/phy-bindings.txt
this must be <0>
Optional properties:
- power-domains: phandle to power domain
Example:
mipi0_phy: mipi0_phy@56228300 {
compatible = "mixel,imx8qm-mipi-dsi-phy";
reg = <0x0 0x56228300 0x0 0x100>;
power-domains = <&pd_mipi0>;
#phy-cells = <0>;
};
......@@ -731,7 +731,7 @@ static void nwl_dsi_begin_transmission(struct nwl_mipi_dsi *dsi)
const u8 *payload;
size_t length;
u16 word_count;
u8 lp_mode;
u8 hs_mode;
u32 val;
/* Send the payload, if any */
......@@ -769,11 +769,11 @@ static void nwl_dsi_begin_transmission(struct nwl_mipi_dsi *dsi)
* header[2] = Word Count MSB
*/
word_count = pkt->header[1] | (pkt->header[2] << 8);
lp_mode = (xfer->msg->flags & MIPI_DSI_MSG_USE_LPM)?0:1;
hs_mode = (xfer->msg->flags & MIPI_DSI_MSG_USE_LPM)?0:1;
val = WC(word_count) |
TX_VC(xfer->msg->channel) |
TX_DT(xfer->msg->type) |
HS_SEL(lp_mode) |
HS_SEL(hs_mode) |
BTA_TX(xfer->need_bta);
nwl_dsi_write(dsi, PKT_CONTROL, val);
......
......@@ -142,6 +142,23 @@ static void dcss_crtc_atomic_flush(struct drm_crtc *crtc,
dcss_ctxld_enable(dcss);
}
/* Print Four-character-code (FOURCC) */
static char *print_fourcc(u32 fmt)
{
static char code[5];
code[0] = (unsigned char)(fmt & 0xff);
code[1] = (unsigned char)((fmt >> 8) & 0xff);
code[2] = (unsigned char)((fmt >> 16) & 0xff);
code[3] = (unsigned char)((fmt >> 24) & 0xff);
code[4] = '\0';
return code;
}
void dcss_crtc_setup_opipe(struct drm_crtc *crtc, struct drm_connector *conn,
u32 colorimetry, u32 eotf,
enum hdmi_quantization_range qr)
......@@ -183,9 +200,9 @@ void dcss_crtc_setup_opipe(struct drm_crtc *crtc, struct drm_connector *conn,
else
dcss_crtc->opipe_pix_format = DRM_FORMAT_ARGB8888;
DRM_INFO("OPIPE_CFG: gamut = %d, nl = %d, pr = %d, pix_format = %d\n",
DRM_INFO("OPIPE_CFG: gamut = %d, nl = %d, pr = %d, pix_format = %s",
dcss_crtc->opipe_g, dcss_crtc->opipe_nl,
dcss_crtc->opipe_pr, dcss_crtc->opipe_pix_format);
dcss_crtc->opipe_pr, print_fourcc(dcss_crtc->opipe_pix_format));
}
int dcss_crtc_get_opipe_cfg(struct drm_crtc *crtc,
......
......@@ -592,7 +592,7 @@ static int imx_nwl_try_phy_speed(struct imx_mipi_dsi *dsi,
ret = mixel_phy_mipi_set_phy_speed(dsi->phy,
bit_clk,
dsi->phyref_rate,
true);
false);
/* Pick the first non-failing rate */
if (!ret)
break;
......@@ -785,8 +785,8 @@ static int imx_nwl_dsi_parse_of(struct device *dev, bool as_bridge)
clk_id, ret);
return ret;
}
dev_dbg(dev, "Setup clk %s (rate: %lu)\n",
clk_id, clk_get_rate(clk));
DRM_DEV_DEBUG_DRIVER(dev, "Setup clk %s (rate: %lu)\n",
clk_id, clk_get_rate(clk));
dsi->clk_config[i].clk = clk;
}
......@@ -800,19 +800,19 @@ static int imx_nwl_dsi_parse_of(struct device *dev, bool as_bridge)
dsi->csr = syscon_regmap_lookup_by_phandle(np, "csr");
if (IS_ERR(dsi->csr) && (devtype->ext_regs & IMX_REG_CSR)) {
ret = PTR_ERR(dsi->csr);
dev_err(dev, "Failed to get CSR regmap (%d)\n", ret);
DRM_DEV_ERROR(dev, "Failed to get CSR regmap (%d)\n", ret);
return ret;
}
dsi->reset = syscon_regmap_lookup_by_phandle(np, "src");
if (IS_ERR(dsi->reset) && (devtype->ext_regs & IMX_REG_SRC)) {
ret = PTR_ERR(dsi->reset);
dev_err(dev, "Failed to get SRC regmap (%d)\n", ret);
DRM_DEV_ERROR(dev, "Failed to get SRC regmap (%d)\n", ret);
return ret;
}
dsi->mux_sel = syscon_regmap_lookup_by_phandle(np, "mux-sel");
if (IS_ERR(dsi->mux_sel) && (devtype->ext_regs & IMX_REG_GPR)) {
ret = PTR_ERR(dsi->mux_sel);
dev_err(dev, "Failed to get GPR regmap (%d)\n", ret);
DRM_DEV_ERROR(dev, "Failed to get GPR regmap (%d)\n", ret);
return ret;
}
if (IS_ERR(dsi->mux_sel))
......@@ -821,8 +821,8 @@ static int imx_nwl_dsi_parse_of(struct device *dev, bool as_bridge)
mux_val = IMX8MQ_GPR13_MIPI_MUX_SEL;
if (as_bridge)
mux_val = 0;
dev_info(dev, "Using %s as input source\n",
(mux_val)?"DCSS":"LCDIF");
DRM_DEV_INFO(dev, "Using %s as input source\n",
(mux_val)?"DCSS":"LCDIF");
regmap_update_bits(dsi->mux_sel,
IOMUXC_GPR13,
IMX8MQ_GPR13_MIPI_MUX_SEL,
......
......@@ -18,23 +18,23 @@
/* The Rockteck jh057n00900 uses a Sitronix ST7703 */
/* Manufacturer Command Set */
#define ST_SETDISP 0xB2 /* display resolution */
#define ST_SETRGBIF 0xB3 /* porch adjustment */
#define ST_SETCYC 0xB4 /* display inversion type */
#define ST_SETBGP 0xB5 /* Reference Voltage */
#define ST_SETVCOM 0xB6 /* VCom */
#define ST_SETOTP 0xB7
#define ST_SETPOWER_EXT 0xB8
#define ST_SETEXTC 0xB9
#define ST_SETMIPI 0xBA
#define ST_SETVDC 0xBC
#define ST_SETSCR 0xC0
#define ST_SETPOWER 0xC1
#define ST_SETPANEL 0xCC
#define ST_SETGAMMA 0xE0
#define ST_SETEQ 0xE3
#define ST_SETGIP1 0xE9
#define ST_SETGIP2 0xEA
#define ST7703_CMD_SETDISP 0xB2 /* display resolution */
#define ST7703_CMD_SETRGBIF 0xB3 /* porch adjustment */
#define ST7703_CMD_SETCYC 0xB4 /* display inversion type */
#define ST7703_CMD_SETBGP 0xB5 /* Reference Voltage */
#define ST7703_CMD_SETVCOM 0xB6 /* VCom */
#define ST7703_CMD_SETOTP 0xB7
#define ST7703_CMD_SETPOWER_EXT 0xB8
#define ST7703_CMD_SETEXTC 0xB9
#define ST7703_CMD_SETMIPI 0xBA
#define ST7703_CMD_SETVDC 0xBC
#define ST7703_CMD_SETSCR 0xC0
#define ST7703_CMD_SETPOWER 0xC1
#define ST7703_CMD_SETPANEL 0xCC
#define ST7703_CMD_SETGAMMA 0xE0
#define ST7703_CMD_SETEQ 0xE3
#define ST7703_CMD_SETGIP1 0xE9
#define ST7703_CMD_SETGIP2 0xEA
struct jh057n {
struct device *dev;
......@@ -45,21 +45,22 @@ struct jh057n {
};
static const struct drm_display_mode default_mode = {
.hdisplay = 720,
.vdisplay = 1440,
/* From panel vendor */
.hsync_start = 720 + 50 /* front porch */,
.hsync_end = 720 + 50 + 10 /* sync_len */,
.htotal = 720 + 50 + 10 + 50 /* back porch */,
.vsync_start = 1440 + 17 /* front porch */,
.vsync_end = 1440 + 17 + 4 /* sync_len */,
.vtotal = 1440 + 17 + 4 + 21 /* back porch */,
.vrefresh = 60, /* guessed */
/* (720 + 50 + 10 + 50)*(1440 + 17 + 4 + 21)* 60/1000 */
.clock = 73803, /* kHz */
.flags = 0 /* DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC */,
.width_mm = 65,
.height_mm = 130,
.hdisplay = 720,
.hsync_start = 720 + 40 /* front porch */,
.hsync_end = 720 + 40 + 10 /* sync_len */,
.htotal = 720 + 40 + 10 + 45 /* back porch */,
.vdisplay = 1440,
.vsync_start = 1440 + 10 /* front porch */,
.vsync_end = 1440 + 10 + 4 /* sync_len */,
.vtotal = 1440 + 10 + 4 + 11 /* back porch */,
.vrefresh = 60, /* confirmed from qualcom XML */
/* htotal * vtotal * vrefresh / 1000 */
/* actually 71638 but then we can't use best_match=false in mixel_phy_mipi_set_phy_speed
so let's use this for now to be on the safe side */
.clock = 72000, /* kHz */
.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
.width_mm = 65,
.height_mm = 130,
};
......@@ -73,8 +74,6 @@ static void jh057n_dcs_write_buf(struct jh057n *ctx, const void *data,
{
struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
/* HACK, needed?? */
msleep(5);
if (mipi_dsi_generic_write(dsi, data, len) < 0)
DRM_DEV_ERROR(ctx->dev, "mipi dsi dcs write buffer failed\n");
}
......@@ -115,47 +114,48 @@ static int jh057n_init_sequence(struct jh057n *ctx)
u8 out[1] = { 0 };
msleep(200);
/* Enable user command */
dcs_write_seq(ctx, ST_SETEXTC, 0xF1, 0x12, 0x83);
dcs_write_seq(ctx, ST7703_CMD_SETEXTC, /* 3 */
0xF1, 0x12, 0x83);
/* 6 params in ST7703 docs */
dcs_write_seq(ctx, ST_SETMIPI, /* 27 */
0x33, 0x81, 0x05, 0xF9, 0x0e, 0x0e, 0x20, 0x00,
dcs_write_seq(ctx, ST7703_CMD_SETMIPI, /* 27 */
0x33, 0x81, 0x05, 0xF9, 0x0E, 0x0E, 0x20, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x25,
0x00, 0x91, 0x0a, 0x00, 0x00, 0x02, 0x4F, 0x11,
0x00, 0x91, 0x0A, 0x00, 0x00, 0x02, 0x4F, 0x11,
0x00, 0x00, 0x37);
/* 6 params in ST7703 docs */
dcs_write_seq(ctx, ST_SETPOWER_EXT, /* 4 */
dcs_write_seq(ctx, ST7703_CMD_SETPOWER_EXT, /* 4 */
0x76, 0x22, 0x20, 0x03);
/* 4 params in ST7703 docs */
dcs_write_seq(ctx, ST_SETRGBIF, /* 10 */
dcs_write_seq(ctx, ST7703_CMD_SETRGBIF, /* 10 */
0x10, 0x10, 0x05, 0x05, 0x03, 0xFF, 0x00, 0x00,
0x00, 0x00);
/* 8 params in ST7703 docs */
dcs_write_seq(ctx, ST_SETSCR, /* 9 */
dcs_write_seq(ctx, ST7703_CMD_SETSCR, /* 9 */
0x73, 0x73, 0x50, 0x50, 0x00, 0x00, 0x08, 0x70,
0x00);
/* -1.6V & + 1.9V */
dcs_write_seq(ctx, ST_SETVDC, 0x4E);
dcs_write_seq(ctx, ST7703_CMD_SETVDC, 0x4E);
/* SS_PANEL, REV_PANEL, BGR_PANEL */
dcs_write_seq(ctx, ST_SETPANEL, 0x0B);
dcs_write_seq(ctx, ST7703_CMD_SETPANEL, 0x0B);
/* 2 params in ST7703 docs */
dcs_write_seq(ctx, ST_SETCYC, 0x80);
dcs_write_seq(ctx, ST7703_CMD_SETCYC, 0x80);
/* weird values, e.g. a 720 panel has BIT(1) 3rd param */
dcs_write_seq(ctx, ST_SETDISP, 0xF0, 0x12, 0x30);
dcs_write_seq(ctx, ST_SETEQ, /* 14 */
dcs_write_seq(ctx, ST7703_CMD_SETDISP, 0xF0, 0x12, 0x30);
dcs_write_seq(ctx, ST7703_CMD_SETEQ, /* 14 */
0x07, 0x07, 0x0B, 0x0B, 0x03, 0x0B, 0x00, 0x00,
0x00, 0x00, 0xFF, 0x00, 0xC0, 0x10);
/* 16 params in ST7703 docs */
dcs_write_seq(ctx, ST_SETPOWER, /* 12 */
dcs_write_seq(ctx, ST7703_CMD_SETPOWER, /* 12 */
0x54, 0x00, 0x1E, 0x1E, 0x77, 0xF1, 0xFF, 0xFF,
0xCC, 0xCC, 0x77, 0x77);
/* ± 4.2 V */
dcs_write_seq(ctx, ST_SETBGP, 0x07, 0x07);
/* ± 0.6 V */
dcs_write_seq(ctx, ST_SETVCOM, 0x25, 0x25);
/* setbgp is different from our first data set*/
dcs_write_seq(ctx, ST7703_CMD_SETBGP, 0x08, 0x08);
/* setvcom is different from our first data set*/
dcs_write_seq(ctx, ST7703_CMD_SETVCOM, 0x28, 0x28);
/* undocumented */
dcs_write_seq(ctx, 0xBF, 0x02, 0x11, 0x00);
dcs_write_seq(ctx, ST_SETGIP1, /* 63 */
dcs_write_seq(ctx, ST7703_CMD_SETGIP1, /* 63 */
0x82, 0x10, 0x06, 0x05, 0x9E, 0x0A, 0xA5, 0x12,
0x31, 0x23, 0x37, 0x83, 0x04, 0xBC, 0x27, 0x38,
0x0C, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00,
......@@ -165,7 +165,7 @@ static int jh057n_init_sequence(struct jh057n *ctx)
0x02, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
/* 39 parameters accordin to ST7703 docs */
dcs_write_seq(ctx, ST_SETGIP2, /* 61 */
dcs_write_seq(ctx, ST7703_CMD_SETGIP2, /* 61 */
0x02, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x46, 0x02, 0x88,
0x88, 0x88, 0x88, 0x88, 0x88, 0x64, 0x88, 0x13,
......@@ -174,20 +174,21 @@ static int jh057n_init_sequence(struct jh057n *ctx)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0A,
0xA5, 0x00, 0x00, 0x00, 0x00);
dcs_write_seq(ctx, ST_SETGAMMA, /* 34 */
dcs_write_seq(ctx, ST7703_CMD_SETGAMMA, /* 34 */
0x00, 0x09, 0x0E, 0x29, 0x2D, 0x3C, 0x41, 0x37,
0x07, 0x0B, 0x0D, 0x10, 0x11, 0x0F, 0x10, 0x11,
0x18, 0x00, 0x09, 0x0E, 0x29, 0x2D, 0x3C, 0x41,
0x37, 0x07, 0x0B, 0x0D, 0x10, 0x11, 0x0F, 0x10,
0x11, 0x18);
msleep(200); /* docs say nothing here */
msleep(78); /* docs say nothing here */
ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
if (ret < 0) {
DRM_DEV_ERROR(dev, "Failed to exit sleep mode");
return ret;
}
msleep(200); /* docs say 120ms */
msleep(150); /* docs say 120ms */
#if 0
/* This fails if we do more than the first "enter user mode" command and if fails
......@@ -204,7 +205,7 @@ static int jh057n_init_sequence(struct jh057n *ctx)
if (ret)
return ret;
msleep(200); /* docs say 5ms, vendor uses 120 ms */
msleep(120); /* docs say 5ms, vendor uses 120 ms */
DRM_DEV_DEBUG_DRIVER (dev, "Panel init sequence done");
return 0;
......@@ -242,8 +243,14 @@ static int jh057n_unprepare(struct drm_panel *panel)
return 0;
if (ctx->reset_gpio) {
gpiod_set_value_cansleep(ctx->reset_gpio, 0);
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
msleep(20);
usleep_range(20, 40); /* docs say 20 usec */
gpiod_set_value_cansleep(ctx->reset_gpio, 0);
msleep(140); /* docs say 120 msec */
} else {
DRM_DEV_DEBUG_DRIVER(ctx->dev, "No reset gpio found");
return -EINVAL;
}
ctx->prepared = false;
......@@ -260,11 +267,14 @@ static int jh057n_prepare(struct drm_panel *panel)
return 0;
if (ctx->reset_gpio) {
gpiod_set_value_cansleep(ctx->reset_gpio, 0);
DRM_DEV_DEBUG_DRIVER(ctx->dev, "Resetting the panel.");
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
msleep(200); /* docs say 120 msec */
usleep_range(20, 40); /* docs say 20 usec */
gpiod_set_value_cansleep(ctx->reset_gpio, 0);
msleep(100);
msleep(140); /* docs say 120 msec */
} else {
DRM_DEV_DEBUG_DRIVER(ctx->dev, "No reset gpio found");
return -EINVAL;
}
ret = jh057n_init_sequence(ctx);
......@@ -336,7 +346,7 @@ static int jh057n_probe(struct mipi_dsi_device *dsi)
if (!ctx)
return -ENOMEM;
ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(ctx->reset_gpio)) {
DRM_DEV_ERROR(dev, "cannot get reset gpio");
return PTR_ERR(ctx->reset_gpio);
......@@ -348,10 +358,9 @@ static int jh057n_probe(struct mipi_dsi_device *dsi)
dsi->lanes = 4;
dsi->format = MIPI_DSI_FMT_RGB888;
dsi->mode_flags = MIPI_DSI_MODE_VIDEO
/* p.184 of ST7703 */
| MIPI_DSI_MODE_VIDEO_BURST
/* the st7703 support LPM and HSM */
dsi->mode_flags = MIPI_DSI_MODE_VIDEO /* mdss-dsi-panel-type */
| MIPI_DSI_MODE_VIDEO_BURST /* mdss-dsi-traffic-mode */
/* the st7703 supports LPM and HSM */
| MIPI_DSI_MODE_LPM
/* transition into LP between transmissions */
| MIPI_DSI_CLOCK_NON_CONTINUOUS
......
......@@ -12,6 +12,8 @@
* for more details.
*/
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/kernel.h>
......@@ -75,6 +77,8 @@ struct mixel_mipi_phy_priv {
struct pll_divider divider;
struct mutex lock;
unsigned long data_rate;
struct clk_hw dsi_clk;
unsigned long frequency;
};
struct devtype {
......@@ -95,6 +99,114 @@ static inline void phy_write(struct phy *phy, u32 value, unsigned int reg)
writel(value, priv->base + reg);
}
#ifdef CONFIG_COMMON_CLK
#define dsi_clk_to_data(_hw) container_of(_hw, struct mixel_mipi_phy_priv, dsi_clk)
static unsigned long mixel_dsi_clk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
return dsi_clk_to_data(hw)->frequency;
}
static long mixel_dsi_clk_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
{
return dsi_clk_to_data(hw)->frequency;
}
static int mixel_dsi_clk_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
return 0;
}
static int mixel_dsi_clk_prepare(struct clk_hw *hw)
{
return 0;
}
static void mixel_dsi_clk_unprepare(struct clk_hw *hw)
{
return;
}
static int mixel_dsi_clk_is_prepared(struct clk_hw *hw)
{
struct mixel_mipi_phy_priv *priv = dsi_clk_to_data(hw);
if (priv->divider.cm < 16 || priv->divider.cm > 255 ||
priv->divider.cn < 1 || priv->divider.cn > 32 ||
priv->divider.co < 1 || priv->divider.co > 8)
return 0;
return 1;
}
static const struct clk_ops mixel_dsi_clk_ops = {
.prepare = mixel_dsi_clk_prepare,
.unprepare = mixel_dsi_clk_unprepare,
.is_prepared = mixel_dsi_clk_is_prepared,
.recalc_rate = mixel_dsi_clk_recalc_rate,
.round_rate = mixel_dsi_clk_round_rate,
.set_rate = mixel_dsi_clk_set_rate,
};
static struct clk *mixel_dsi_clk_register_clk(struct mixel_mipi_phy_priv *priv)
{
struct clk *clk;
struct clk_init_data init;
init.name = "mixel-dsi-clk";
init.ops = &mixel_dsi_clk_ops;
init.flags = 0;
init.parent_names = NULL;
init.num_parents = 0;
priv->dsi_clk.init = &init;
/* optional override of the clockname */
of_property_read_string(priv->dev->of_node, "clock-output-names", &init.name);
/* register the clock */
clk = clk_register(priv->dev, &priv->dsi_clk);
if (!IS_ERR(clk))
of_clk_add_provider(priv->dev->of_node, of_clk_src_simple_get, clk);
return clk;
}
#endif
/*
* continued fraction
* 2 1 2 1 2
* 0 1 2 3 8 11 30
* 1 0 1 1 3 4 11
*/
static void get_best_ratio(unsigned long *pnum, unsigned long *pdenom, unsigned max_n, unsigned max_d)
{
unsigned long a = *pnum;
unsigned long b = *pdenom;
unsigned long c;
unsigned n[] = {0, 1};
unsigned d[] = {1, 0};
unsigned whole;
unsigned i = 1;
while (b) {
i ^= 1;
whole = a / b;
n[i] += (n[i ^ 1] * whole);
d[i] += (d[i ^ 1] * whole);
// printf("cf=%i n=%i d=%i\n", whole, n[i], d[i]);
if ((n[i] > max_n) || (d[i] > max_d)) {
i ^= 1;
break;
}
c = a - (b * whole);
a = b;
b = c;
}
*pnum = n[i];
*pdenom = d[i];
}
/*
* mixel_phy_mipi_set_phy_speed:
* Input params:
......@@ -111,54 +223,50 @@ int mixel_phy_mipi_set_phy_speed(struct phy *phy,
bool best_match)
{
struct mixel_mipi_phy_priv *priv = dev_get_drvdata(phy->dev.parent);
u32 div_rate;
u32 numerator = 0;
u32 denominator = 1;
int i;
unsigned long numerator;
unsigned long denominator;
unsigned max_d = 256;
if (bit_clk > DATA_RATE_MAX_SPEED || bit_clk < DATA_RATE_MIN_SPEED)
return -EINVAL;
/* simulated fixed point with 3 decimals */
div_rate = (bit_clk * 1000) / ref_clk;
while (denominator <= 256) {
if (div_rate % 1000 == 0)
numerator = div_rate / 1000;
if (numerator > 15)
break;
denominator = denominator << 1;
div_rate = div_rate << 1;
}
/* CM ranges between 16 and 255 */
/* CN ranges between 1 and 32 */
/* CO is power of 2: 1, 2, 4, 8 */
if (best_match && numerator < 16)
numerator = div_rate / 1000;
if (best_match && numerator > 255) {
while (numerator > 255 && denominator > 1) {
numerator = DIV_ROUND_UP(numerator, 2);
denominator = denominator >> 1;
}
do {
numerator = bit_clk;
denominator = ref_clk;
get_best_ratio(&numerator, &denominator, 255, max_d);
max_d >>= 1;
} while ((denominator >> __ffs(denominator)) > 32);
while ((numerator < 16) && (denominator <= 128)) {
numerator = numerator << 1;
denominator = denominator << 1;
}
pr_debug("%s: %ld/%ld = %ld/%ld\n", __func__, numerator, denominator, bit_clk, ref_clk);
if (numerator < 16 || numerator > 255)
if (numerator < 16 || numerator > 255 || !denominator) {
pr_info("%s: bit_clk=%ld ref_clk=%ld, numerator=%ld, denominator=%ld\n",
__func__, bit_clk, ref_clk, numerator, denominator);
return -EINVAL;
if (best_match)
numerator = DIV_ROUND_UP(numerator, denominator) * denominator;
priv->divider.cn = 1;
if (denominator > 8) {
priv->divider.cn = denominator >> 3;
denominator = 8;
}
priv->divider.co = denominator;
i = __ffs(denominator);
if (i > 3)
i = 3;
priv->divider.cn = denominator >> i;
priv->divider.co = 1 << i;
priv->divider.cm = numerator;
priv->data_rate = bit_clk;
/* Divided by 2 because mipi output clock is DDR */
priv->frequency = ref_clk * numerator / (2 * denominator);
if (priv->dsi_clk.clk)
clk_set_rate(priv->dsi_clk.clk, priv->frequency);
pr_info("%s:%ld,%ld, ref_clk=%ld numerator=%ld, denominator=%ld\n",
__func__, priv->frequency, bit_clk, ref_clk, numerator, denominator);
return 0;
}
EXPORT_SYMBOL_GPL(mixel_phy_mipi_set_phy_speed);
......@@ -245,7 +353,7 @@ static void mixel_phy_set_prg_regs(struct phy *phy)
}
}
int mixel_mipi_phy_init(struct phy *phy)
static int mixel_mipi_phy_init(struct phy *phy)
{
struct mixel_mipi_phy_priv *priv = dev_get_drvdata(phy->dev.parent);
......@@ -407,6 +515,9 @@ static int mixel_mipi_phy_probe(struct platform_device *pdev)
phy_set_drvdata(phy, priv);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
#ifdef CONFIG_COMMON_CLK
mixel_dsi_clk_register_clk(priv);
#endif
return PTR_ERR_OR_ZERO(phy_provider);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment