[PATCH v8 3/4] drm/kmb: Mipi DSI part of the display driver

Daniel Vetter daniel at ffwll.ch
Sat Oct 10 08:25:07 UTC 2020


On Fri, Oct 02, 2020 at 07:17:01PM -0700, Anitha Chrisanthus wrote:
> Initializes Mipi DSI and sets up connects to ADV bridge
> 
> v2: removed license text
>     upclassed dev_private, removed HAVE_IRQ. (Sam)
> 
> v3: Squashed all 59 commits to one
> 
> v4: review changes from Sam Ravnborg
> 	renamed dev_p to kmb
> 
> v5: corrected spellings
> v6: corrected checkpatch warnings
> v7: review changes Sam Ravnborg and Thomas Zimmerman
> 	removed unnecessary logs and defines and ifdef codes (Sam)
> 	split dphy_init_sequence smaller (Sam)
> 	removed redundant checks in kmb_dsi (Sam)
> 	changed kmb_dsi_init to drm_bridge_connector_init and
> 	drm_connector_attach_encoder to bridge's connector (Sam)
> v8: call drm_bridge_attach with DRM_BRIDGE_ATTACH_NO_CONNECTOR
> 
> Cc: Sam Ravnborg <sam at ravnborg.org>
> Signed-off-by: Anitha Chrisanthus <anitha.chrisanthus at intel.com>
> Reviewed-by: Bob Paauwe <bob.j.paauwe at intel.com>
> ---
>  drivers/gpu/drm/kmb/kmb_dsi.c | 1524 +++++++++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/kmb/kmb_dsi.h |  350 ++++++++++
>  2 files changed, 1874 insertions(+)
>  create mode 100644 drivers/gpu/drm/kmb/kmb_dsi.c
>  create mode 100644 drivers/gpu/drm/kmb/kmb_dsi.h
> 
> diff --git a/drivers/gpu/drm/kmb/kmb_dsi.c b/drivers/gpu/drm/kmb/kmb_dsi.c
> new file mode 100644
> index 0000000..849f1cc
> --- /dev/null
> +++ b/drivers/gpu/drm/kmb/kmb_dsi.c
> @@ -0,0 +1,1524 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright © 2019-2020 Intel Corporation
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_bridge.h>
> +#include <drm/drm_bridge_connector.h>
> +#include <drm/drm_connector.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_edid.h>
> +#include <drm/drm_mipi_dsi.h>
> +#include <drm/drm_print.h>
> +#include <drm/drm_probe_helper.h>
> +
> +#include "kmb_drv.h"
> +#include "kmb_dsi.h"
> +#include "kmb_regs.h"
> +
> +static struct mipi_dsi_host *dsi_host;
> +static struct mipi_dsi_device *dsi_device;
> +
> +/* Default setting is 1080p, 4 lanes */
> +#define IMG_HEIGHT_LINES  1080
> +#define IMG_WIDTH_PX      1920
> +#define MIPI_TX_ACTIVE_LANES 4
> +
> +struct mipi_tx_frame_section_cfg mipi_tx_frame0_sect_cfg = {
> +	.width_pixels = IMG_WIDTH_PX,
> +	.height_lines = IMG_HEIGHT_LINES,
> +	.data_type = DSI_LP_DT_PPS_RGB888_24B,
> +	.data_mode = MIPI_DATA_MODE1,
> +	.dma_packed = 0
> +};
> +
> +struct mipi_tx_frame_cfg mipitx_frame0_cfg = {
> +	.sections[0] = &mipi_tx_frame0_sect_cfg,
> +	.sections[1] = NULL,
> +	.sections[2] = NULL,
> +	.sections[3] = NULL,
> +	.vsync_width = 5,
> +	.v_backporch = 36,
> +	.v_frontporch = 4,
> +	.hsync_width = 44,
> +	.h_backporch = 148,
> +	.h_frontporch = 88
> +};
> +
> +struct mipi_tx_dsi_cfg mipitx_dsi_cfg = {
> +	.hfp_blank_en = 0,
> +	.eotp_en = 0,
> +	.lpm_last_vfp_line = 0,
> +	.lpm_first_vsa_line = 0,
> +	.sync_pulse_eventn = DSI_VIDEO_MODE_NO_BURST_EVENT,
> +	.hfp_blanking = SEND_BLANK_PACKET,
> +	.hbp_blanking = SEND_BLANK_PACKET,
> +	.hsa_blanking = SEND_BLANK_PACKET,
> +	.v_blanking = SEND_BLANK_PACKET,
> +};
> +
> +struct mipi_ctrl_cfg mipi_tx_init_cfg = {
> +	.active_lanes = MIPI_TX_ACTIVE_LANES,
> +	.lane_rate_mbps = MIPI_TX_LANE_DATA_RATE_MBPS,
> +	.ref_clk_khz = MIPI_TX_REF_CLK_KHZ,
> +	.cfg_clk_khz = MIPI_TX_CFG_CLK_KHZ,
> +	.tx_ctrl_cfg = {
> +			.frames[0] = &mipitx_frame0_cfg,
> +			.frames[1] = NULL,
> +			.frames[2] = NULL,
> +			.frames[3] = NULL,
> +			.tx_dsi_cfg = &mipitx_dsi_cfg,
> +			.line_sync_pkt_en = 0,
> +			.line_counter_active = 0,
> +			.frame_counter_active = 0,
> +			.tx_always_use_hact = 1,
> +			.tx_hact_wait_stop = 1,
> +			}
> +};
> +
> +struct  mipi_hs_freq_range_cfg {
> +	u16 default_bit_rate_mbps;
> +	u8 hsfreqrange_code;
> +};
> +
> +struct vco_params {
> +	u32 freq;
> +	u32 range;
> +	u32 divider;
> +};
> +
> +static struct vco_params vco_table[] = {
> +	{52, 0x3f, 8},
> +	{80, 0x39, 8},
> +	{105, 0x2f, 4},
> +	{160, 0x29, 4},
> +	{210, 0x1f, 2},
> +	{320, 0x19, 2},
> +	{420, 0x0f, 1},
> +	{630, 0x09, 1},
> +	{1100, 0x03, 1},
> +	{0xffff, 0x01, 1},
> +};
> +
> +static struct mipi_hs_freq_range_cfg
> +mipi_hs_freq_range[MIPI_DPHY_DEFAULT_BIT_RATES] = {
> +	{.default_bit_rate_mbps = 80, .hsfreqrange_code = 0x00},
> +	{.default_bit_rate_mbps = 90, .hsfreqrange_code = 0x10},
> +	{.default_bit_rate_mbps = 100, .hsfreqrange_code = 0x20},
> +	{.default_bit_rate_mbps = 110, .hsfreqrange_code = 0x30},
> +	{.default_bit_rate_mbps = 120, .hsfreqrange_code = 0x01},
> +	{.default_bit_rate_mbps = 130, .hsfreqrange_code = 0x11},
> +	{.default_bit_rate_mbps = 140, .hsfreqrange_code = 0x21},
> +	{.default_bit_rate_mbps = 150, .hsfreqrange_code = 0x31},
> +	{.default_bit_rate_mbps = 160, .hsfreqrange_code = 0x02},
> +	{.default_bit_rate_mbps = 170, .hsfreqrange_code = 0x12},
> +	{.default_bit_rate_mbps = 180, .hsfreqrange_code = 0x22},
> +	{.default_bit_rate_mbps = 190, .hsfreqrange_code = 0x32},
> +	{.default_bit_rate_mbps = 205, .hsfreqrange_code = 0x03},
> +	{.default_bit_rate_mbps = 220, .hsfreqrange_code = 0x13},
> +	{.default_bit_rate_mbps = 235, .hsfreqrange_code = 0x23},
> +	{.default_bit_rate_mbps = 250, .hsfreqrange_code = 0x33},
> +	{.default_bit_rate_mbps = 275, .hsfreqrange_code = 0x04},
> +	{.default_bit_rate_mbps = 300, .hsfreqrange_code = 0x14},
> +	{.default_bit_rate_mbps = 325, .hsfreqrange_code = 0x25},
> +	{.default_bit_rate_mbps = 350, .hsfreqrange_code = 0x35},
> +	{.default_bit_rate_mbps = 400, .hsfreqrange_code = 0x05},
> +	{.default_bit_rate_mbps = 450, .hsfreqrange_code = 0x16},
> +	{.default_bit_rate_mbps = 500, .hsfreqrange_code = 0x26},
> +	{.default_bit_rate_mbps = 550, .hsfreqrange_code = 0x37},
> +	{.default_bit_rate_mbps = 600, .hsfreqrange_code = 0x07},
> +	{.default_bit_rate_mbps = 650, .hsfreqrange_code = 0x18},
> +	{.default_bit_rate_mbps = 700, .hsfreqrange_code = 0x28},
> +	{.default_bit_rate_mbps = 750, .hsfreqrange_code = 0x39},
> +	{.default_bit_rate_mbps = 800, .hsfreqrange_code = 0x09},
> +	{.default_bit_rate_mbps = 850, .hsfreqrange_code = 0x19},
> +	{.default_bit_rate_mbps = 900, .hsfreqrange_code = 0x29},
> +	{.default_bit_rate_mbps = 1000, .hsfreqrange_code = 0x0A},
> +	{.default_bit_rate_mbps = 1050, .hsfreqrange_code = 0x1A},
> +	{.default_bit_rate_mbps = 1100, .hsfreqrange_code = 0x2A},
> +	{.default_bit_rate_mbps = 1150, .hsfreqrange_code = 0x3B},
> +	{.default_bit_rate_mbps = 1200, .hsfreqrange_code = 0x0B},
> +	{.default_bit_rate_mbps = 1250, .hsfreqrange_code = 0x1B},
> +	{.default_bit_rate_mbps = 1300, .hsfreqrange_code = 0x2B},
> +	{.default_bit_rate_mbps = 1350, .hsfreqrange_code = 0x3C},
> +	{.default_bit_rate_mbps = 1400, .hsfreqrange_code = 0x0C},
> +	{.default_bit_rate_mbps = 1450, .hsfreqrange_code = 0x1C},
> +	{.default_bit_rate_mbps = 1500, .hsfreqrange_code = 0x2C},
> +	{.default_bit_rate_mbps = 1550, .hsfreqrange_code = 0x3D},
> +	{.default_bit_rate_mbps = 1600, .hsfreqrange_code = 0x0D},
> +	{.default_bit_rate_mbps = 1650, .hsfreqrange_code = 0x1D},
> +	{.default_bit_rate_mbps = 1700, .hsfreqrange_code = 0x2E},
> +	{.default_bit_rate_mbps = 1750, .hsfreqrange_code = 0x3E},
> +	{.default_bit_rate_mbps = 1800, .hsfreqrange_code = 0x0E},
> +	{.default_bit_rate_mbps = 1850, .hsfreqrange_code = 0x1E},
> +	{.default_bit_rate_mbps = 1900, .hsfreqrange_code = 0x2F},
> +	{.default_bit_rate_mbps = 1950, .hsfreqrange_code = 0x3F},
> +	{.default_bit_rate_mbps = 2000, .hsfreqrange_code = 0x0F},
> +	{.default_bit_rate_mbps = 2050, .hsfreqrange_code = 0x40},
> +	{.default_bit_rate_mbps = 2100, .hsfreqrange_code = 0x41},
> +	{.default_bit_rate_mbps = 2150, .hsfreqrange_code = 0x42},
> +	{.default_bit_rate_mbps = 2200, .hsfreqrange_code = 0x43},
> +	{.default_bit_rate_mbps = 2250, .hsfreqrange_code = 0x44},
> +	{.default_bit_rate_mbps = 2300, .hsfreqrange_code = 0x45},
> +	{.default_bit_rate_mbps = 2350, .hsfreqrange_code = 0x46},
> +	{.default_bit_rate_mbps = 2400, .hsfreqrange_code = 0x47},
> +	{.default_bit_rate_mbps = 2450, .hsfreqrange_code = 0x48},
> +	{.default_bit_rate_mbps = 2500, .hsfreqrange_code = 0x49}
> +};
> +
> +static enum drm_mode_status
> +kmb_dsi_mode_valid(struct drm_connector *connector,
> +		   struct drm_display_mode *mode)
> +{
> +	struct drm_device *dev = connector->dev;
> +	struct drm_mode_config *mode_config = &dev->mode_config;
> +
> +	if (mode->hdisplay < mode_config->min_width ||
> +	    mode->hdisplay > mode_config->max_width)
> +		return MODE_BAD_HVALUE;
> +
> +	if (mode->vdisplay < mode_config->min_height ||
> +	    mode->vdisplay > mode_config->max_height)
> +		return MODE_BAD_VVALUE;
> +
> +	return MODE_OK;
> +}
> +
> +static int kmb_dsi_get_modes(struct drm_connector *connector)
> +{
> +	int num_modes = 0;
> +
> +	num_modes = drm_add_modes_noedid(connector,
> +					 connector->dev->mode_config.max_width,
> +			 connector->dev->mode_config.max_height);
> +
> +	DRM_INFO("width=%d height=%d\n",
> +		 connector->dev->mode_config.max_width,
> +		 connector->dev->mode_config.max_height);
> +	DRM_INFO("num modes=%d\n", num_modes);
> +
> +	return num_modes;
> +}
> +
> +void kmb_dsi_host_unregister(void)
> +{
> +	mipi_dsi_host_unregister(dsi_host);
> +	kfree(dsi_host);
> +}
> +
> +static void kmb_dsi_connector_destroy(struct drm_connector *connector)
> +{
> +	struct kmb_connector *kmb_connector = to_kmb_connector(connector);
> +
> +	drm_connector_cleanup(connector);
> +	kfree(kmb_connector);
> +}
> +
> +static void kmb_dsi_encoder_destroy(struct drm_encoder *encoder)
> +{
> +	struct kmb_dsi *kmb_dsi = to_kmb_dsi(encoder);
> +
> +	if (!kmb_dsi)
> +		return;
> +
> +	kfree(kmb_dsi->dsi_host);
> +
> +	drm_encoder_cleanup(encoder);
> +
> +	kfree(kmb_dsi);
> +	if (!dsi_device)
> +		kfree(dsi_device);
> +}
> +
> +static const struct drm_encoder_funcs kmb_dsi_funcs = {
> +	.destroy = kmb_dsi_encoder_destroy,
> +};
> +
> +static const struct
> +drm_connector_helper_funcs kmb_dsi_connector_helper_funcs = {
> +	.get_modes = kmb_dsi_get_modes,
> +	.mode_valid = kmb_dsi_mode_valid,
> +};
> +
> +static const struct drm_connector_funcs kmb_dsi_connector_funcs = {
> +	.destroy = kmb_dsi_connector_destroy,
> +	.fill_modes = drm_helper_probe_single_connector_modes,
> +	.reset = drm_atomic_helper_connector_reset,
> +	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> +	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> +};
> +
> +static ssize_t kmb_dsi_host_transfer(struct mipi_dsi_host *host,
> +				     const struct mipi_dsi_msg *msg)
> +{
> +	return 0;
> +}
> +
> +static int kmb_dsi_host_attach(struct mipi_dsi_host *host,
> +			       struct mipi_dsi_device *dev)
> +{
> +	return 0;
> +}
> +
> +static int kmb_dsi_host_detach(struct mipi_dsi_host *host,
> +			       struct mipi_dsi_device *dev)
> +{
> +	return 0;
> +}
> +
> +static const struct mipi_dsi_host_ops kmb_dsi_host_ops = {
> +	.attach = kmb_dsi_host_attach,
> +	.detach = kmb_dsi_host_detach,
> +	.transfer = kmb_dsi_host_transfer,
> +};

I looked through all mpi host drivers we have, and none have empty
implementations like this. Some don't have them all set (I guess when some
features aren't support), but none with implementations that do nothing.

I have no idea how this should be done, but I think this entire dsi host
driver needs to be carefully looked at by someone who knows how to
structure and implement a dsi driver for an armsoc/dt world.
-Daniel

> +
> +static struct kmb_dsi_host *kmb_dsi_host_init(struct drm_device *drm,
> +					      struct kmb_dsi *kmb_dsi)
> +{
> +	struct kmb_dsi_host *host;
> +
> +	host = kzalloc(sizeof(*host), GFP_KERNEL);
> +	if (!host)
> +		return NULL;
> +
> +	host->base = dsi_host;
> +	host->base->ops = &kmb_dsi_host_ops;
> +	host->kmb_dsi = kmb_dsi;
> +
> +	host->base->dev = drm->dev;
> +
> +	dsi_device->host = host->base;
> +	host->device = dsi_device;
> +	return host;
> +}
> +
> +struct drm_bridge *kmb_dsi_host_bridge_init(struct device *dev)
> +{
> +	struct drm_bridge *bridge = NULL;
> +	struct device_node *encoder_node;
> +
> +	/* Create and register MIPI DSI host */
> +	if (!dsi_host) {
> +		dsi_host = kzalloc(sizeof(*dsi_host), GFP_KERNEL);
> +		if (!dsi_host)
> +			return ERR_PTR(-ENOMEM);
> +
> +		dsi_host->ops = &kmb_dsi_host_ops;
> +
> +		if (!dsi_device) {
> +			dsi_device = kzalloc(sizeof(*dsi_device), GFP_KERNEL);
> +			if (!dsi_device) {
> +				kfree(dsi_host);
> +				return ERR_PTR(-ENOMEM);
> +			}
> +		}
> +
> +		dsi_host->dev = dev;
> +		mipi_dsi_host_register(dsi_host);
> +	}
> +	/* Find ADV7535 node and initialize it */
> +	encoder_node = of_parse_phandle(dev->of_node, "encoder-slave", 0);
> +
> +	if (!encoder_node) {
> +		DRM_ERROR("Failed to get bridge info from DT\n");
> +		return ERR_PTR(-EINVAL);
> +	}
> +
> +	/* Locate drm bridge from the hdmi encoder DT node */
> +	bridge = of_drm_find_bridge(encoder_node);
> +	of_node_put(encoder_node);
> +	if (!bridge) {
> +		DRM_INFO("Wait for external bridge driver DT\n");
> +		return ERR_PTR(-EPROBE_DEFER);
> +	}
> +	return bridge;
> +}
> +
> +static u32 mipi_get_datatype_params(u32 data_type, u32 data_mode,
> +				    struct mipi_data_type_params *params)
> +{
> +	struct mipi_data_type_params data_type_param;
> +
> +	switch (data_type) {
> +	case DSI_LP_DT_PPS_YCBCR420_12B:
> +		data_type_param.size_constraint_pixels = 2;
> +		data_type_param.size_constraint_bytes = 3;
> +		switch (data_mode) {
> +			/* Case 0 not supported according to MDK */
> +		case 1:
> +		case 2:
> +		case 3:
> +			data_type_param.pixels_per_pclk = 2;
> +			data_type_param.bits_per_pclk = 24;
> +			break;
> +		default:
> +			DRM_ERROR("DSI: Invalid data_mode %d\n", data_mode);
> +			return -EINVAL;
> +		};
> +		break;
> +	case DSI_LP_DT_PPS_YCBCR422_16B:
> +		data_type_param.size_constraint_pixels = 2;
> +		data_type_param.size_constraint_bytes = 4;
> +		switch (data_mode) {
> +			/* Case 0 and 1 not supported according
> +			 * to MDK
> +			 */
> +		case 2:
> +			data_type_param.pixels_per_pclk = 1;
> +			data_type_param.bits_per_pclk = 16;
> +			break;
> +		case 3:
> +			data_type_param.pixels_per_pclk = 2;
> +			data_type_param.bits_per_pclk = 32;
> +			break;
> +		default:
> +			DRM_ERROR("DSI: Invalid data_mode %d\n", data_mode);
> +			return -EINVAL;
> +		};
> +		break;
> +	case DSI_LP_DT_LPPS_YCBCR422_20B:
> +	case DSI_LP_DT_PPS_YCBCR422_24B:
> +		data_type_param.size_constraint_pixels = 2;
> +		data_type_param.size_constraint_bytes = 6;
> +		switch (data_mode) {
> +			/* Case 0 not supported according to MDK */
> +		case 1:
> +		case 2:
> +		case 3:
> +			data_type_param.pixels_per_pclk = 1;
> +			data_type_param.bits_per_pclk = 24;
> +			break;
> +		default:
> +			DRM_ERROR("DSI: Invalid data_mode %d\n", data_mode);
> +			return -EINVAL;
> +		};
> +		break;
> +	case DSI_LP_DT_PPS_RGB565_16B:
> +		data_type_param.size_constraint_pixels = 1;
> +		data_type_param.size_constraint_bytes = 2;
> +		switch (data_mode) {
> +		case 0:
> +		case 1:
> +			data_type_param.pixels_per_pclk = 1;
> +			data_type_param.bits_per_pclk = 16;
> +			break;
> +		case 2:
> +		case 3:
> +			data_type_param.pixels_per_pclk = 2;
> +			data_type_param.bits_per_pclk = 32;
> +			break;
> +		default:
> +			DRM_ERROR("DSI: Invalid data_mode %d\n", data_mode);
> +			return -EINVAL;
> +		};
> +		break;
> +	case DSI_LP_DT_PPS_RGB666_18B:
> +		data_type_param.size_constraint_pixels = 4;
> +		data_type_param.size_constraint_bytes = 9;
> +		data_type_param.bits_per_pclk = 18;
> +		data_type_param.pixels_per_pclk = 1;
> +		break;
> +	case DSI_LP_DT_LPPS_RGB666_18B:
> +	case DSI_LP_DT_PPS_RGB888_24B:
> +		data_type_param.size_constraint_pixels = 1;
> +		data_type_param.size_constraint_bytes = 3;
> +		data_type_param.bits_per_pclk = 24;
> +		data_type_param.pixels_per_pclk = 1;
> +		break;
> +	case DSI_LP_DT_PPS_RGB101010_30B:
> +		data_type_param.size_constraint_pixels = 4;
> +		data_type_param.size_constraint_bytes = 15;
> +		data_type_param.bits_per_pclk = 30;
> +		data_type_param.pixels_per_pclk = 1;
> +		break;
> +	default:
> +		DRM_ERROR("DSI: Invalid data_type %d\n", data_type);
> +		return -EINVAL;
> +	};
> +
> +	*params = data_type_param;
> +	return 0;
> +}
> +
> +static u32 compute_wc(u32 width_px, u8 size_constr_p, u8 size_constr_b)
> +{
> +	/* Calculate the word count for each long packet */
> +	return (((width_px / size_constr_p) * size_constr_b) & 0xffff);
> +}
> +
> +static u32 compute_unpacked_bytes(u32 wc, u8 bits_per_pclk)
> +{
> +	/* Number of PCLK cycles needed to transfer a line
> +	 * with each PCLK cycle, 4 Bytes are sent through the PPL module
> +	 */
> +	return ((wc * 8) / bits_per_pclk) * 4;
> +}
> +
> +static u32 mipi_tx_fg_section_cfg_regs(struct kmb_drm_private *kmb,
> +				       u8 frame_id, u8 section,
> +				       u32 height_lines, u32 unpacked_bytes,
> +				       struct mipi_tx_frame_sect_phcfg *ph_cfg)
> +{
> +	u32 cfg = 0;
> +	u32 ctrl_no = MIPI_CTRL6;
> +	u32 reg_adr;
> +
> +	/* Frame section packet header */
> +	/* Word count bits [15:0] */
> +	cfg = (ph_cfg->wc & MIPI_TX_SECT_WC_MASK) << 0;
> +
> +	/* Data type (bits [21:16]) */
> +	cfg |= ((ph_cfg->data_type & MIPI_TX_SECT_DT_MASK)
> +		<< MIPI_TX_SECT_DT_SHIFT);
> +
> +	/* Virtual channel (bits [23:22]) */
> +	cfg |= ((ph_cfg->vchannel & MIPI_TX_SECT_VC_MASK)
> +		<< MIPI_TX_SECT_VC_SHIFT);
> +
> +	/* Data mode (bits [24:25]) */
> +	cfg |= ((ph_cfg->data_mode & MIPI_TX_SECT_DM_MASK)
> +		<< MIPI_TX_SECT_DM_SHIFT);
> +	if (ph_cfg->dma_packed)
> +		cfg |= MIPI_TX_SECT_DMA_PACKED;
> +
> +	drm_dbg(&kmb->drm,
> +		"ctrl=%d frame_id=%d section=%d cfg=%x packed=%d\n",
> +		  ctrl_no, frame_id, section, cfg, ph_cfg->dma_packed);
> +	kmb_write_mipi(kmb,
> +		       (MIPI_TXm_HS_FGn_SECTo_PH(ctrl_no, frame_id, section)),
> +		       cfg);
> +
> +	/* Unpacked bytes */
> +
> +	/* There are 4 frame generators and each fg has 4 sections
> +	 * There are 2 registers for unpacked bytes (# bytes each
> +	 * section occupies in memory)
> +	 * REG_UNPACKED_BYTES0: [15:0]-BYTES0, [31:16]-BYTES1
> +	 * REG_UNPACKED_BYTES1: [15:0]-BYTES2, [31:16]-BYTES3
> +	 */
> +	reg_adr =
> +	    MIPI_TXm_HS_FGn_SECT_UNPACKED_BYTES0(ctrl_no,
> +						 frame_id) + (section / 2) * 4;
> +	kmb_write_bits_mipi(kmb, reg_adr, (section % 2) * 16, 16,
> +			    unpacked_bytes);
> +	drm_dbg(&kmb->drm,
> +		"unpacked_bytes = %d, wordcount = %d\n", unpacked_bytes,
> +		  ph_cfg->wc);
> +
> +	/* Line config */
> +	reg_adr = MIPI_TXm_HS_FGn_SECTo_LINE_CFG(ctrl_no, frame_id, section);
> +	kmb_write_mipi(kmb, reg_adr, height_lines);
> +	return 0;
> +}
> +
> +static u32 mipi_tx_fg_section_cfg(struct kmb_drm_private *kmb,
> +				  u8 frame_id, u8 section,
> +				  struct mipi_tx_frame_section_cfg *frame_scfg,
> +				  u32 *bits_per_pclk, u32 *wc)
> +{
> +	u32 ret = 0;
> +	u32 unpacked_bytes;
> +	struct mipi_data_type_params data_type_parameters;
> +	struct mipi_tx_frame_sect_phcfg ph_cfg;
> +
> +	ret = mipi_get_datatype_params(frame_scfg->data_type,
> +				       frame_scfg->data_mode,
> +				       &data_type_parameters);
> +	if (ret)
> +		return ret;
> +
> +	/* Packet width has to be a multiple of the minimum packet width
> +	 * (in pixels) set for each data type
> +	 */
> +	if (frame_scfg->width_pixels %
> +	    data_type_parameters.size_constraint_pixels != 0)
> +		return -EINVAL;
> +
> +	*wc = compute_wc(frame_scfg->width_pixels,
> +			 data_type_parameters.size_constraint_pixels,
> +			 data_type_parameters.size_constraint_bytes);
> +	unpacked_bytes = compute_unpacked_bytes(*wc,
> +						data_type_parameters.bits_per_pclk);
> +	ph_cfg.wc = *wc;
> +	ph_cfg.data_mode = frame_scfg->data_mode;
> +	ph_cfg.data_type = frame_scfg->data_type;
> +	ph_cfg.dma_packed = frame_scfg->dma_packed;
> +	ph_cfg.vchannel = frame_id;
> +
> +	mipi_tx_fg_section_cfg_regs(kmb, frame_id, section,
> +				    frame_scfg->height_lines,
> +				    unpacked_bytes, &ph_cfg);
> +
> +	/* Caller needs bits_per_clk for additional caluclations */
> +	*bits_per_pclk = data_type_parameters.bits_per_pclk;
> +
> +	return 0;
> +}
> +
> +static void mipi_tx_fg_cfg_regs(struct kmb_drm_private *kmb, u8 frame_gen,
> +				struct mipi_tx_frame_timing_cfg *fg_cfg)
> +{
> +	u32 sysclk;
> +	u32 ppl_llp_ratio;
> +	u32 ctrl_no = MIPI_CTRL6, reg_adr, val, offset;
> +
> +	/* 500 Mhz system clock minus 50 to account for the difference in
> +	 * MIPI clock speed in RTL tests
> +	 */
> +	sysclk = kmb->sys_clk_mhz - 50;
> +
> +	/* PPL-Pixel Packing Layer, LLP-Low Level Protocol
> +	 * Frame genartor timing parameters are clocked on the system clock,
> +	 * whereas as the equivalent parameters in the LLP blocks are clocked
> +	 * on LLP Tx clock from the D-PHY - BYTE clock
> +	 */
> +
> +	/* Multiply by 1000 to maintain precision */
> +	ppl_llp_ratio = ((fg_cfg->bpp / 8) * sysclk * 1000) /
> +	    ((fg_cfg->lane_rate_mbps / 8) * fg_cfg->active_lanes);
> +
> +	drm_dbg(&kmb->drm, "ppl_llp_ratio=%d\n", ppl_llp_ratio);
> +	drm_dbg(&kmb->drm, "bpp=%d sysclk=%d lane-rate=%d active-lanes=%d\n",
> +		fg_cfg->bpp, sysclk, fg_cfg->lane_rate_mbps,
> +		 fg_cfg->active_lanes);
> +
> +	/* Frame generator number of lines */
> +	reg_adr = MIPI_TXm_HS_FGn_NUM_LINES(ctrl_no, frame_gen);
> +	kmb_write_mipi(kmb, reg_adr, fg_cfg->v_active);
> +
> +	/* vsync width
> +	 * There are 2 registers for vsync width (VSA in lines for
> +	 * channels 0-3)
> +	 * REG_VSYNC_WIDTH0: [15:0]-VSA for channel0, [31:16]-VSA for channel1
> +	 * REG_VSYNC_WIDTH1: [15:0]-VSA for channel2, [31:16]-VSA for channel3
> +	 */
> +	offset = (frame_gen % 2) * 16;
> +	reg_adr = MIPI_TXm_HS_VSYNC_WIDTHn(ctrl_no, frame_gen / 2);
> +	kmb_write_bits_mipi(kmb, reg_adr, offset, 16, fg_cfg->vsync_width);
> +
> +	/* vertical backporch (vbp) */
> +	reg_adr = MIPI_TXm_HS_V_BACKPORCHESn(ctrl_no, frame_gen / 2);
> +	kmb_write_bits_mipi(kmb, reg_adr, offset, 16, fg_cfg->v_backporch);
> +
> +	/* vertical frontporch (vfp) */
> +	reg_adr = MIPI_TXm_HS_V_FRONTPORCHESn(ctrl_no, frame_gen / 2);
> +	kmb_write_bits_mipi(kmb, reg_adr, offset, 16, fg_cfg->v_frontporch);
> +
> +	/* vertical active (vactive) */
> +	reg_adr = MIPI_TXm_HS_V_ACTIVEn(ctrl_no, frame_gen / 2);
> +	kmb_write_bits_mipi(kmb, reg_adr, offset, 16, fg_cfg->v_active);
> +
> +	/* hsync width */
> +	reg_adr = MIPI_TXm_HS_HSYNC_WIDTHn(ctrl_no, frame_gen);
> +	kmb_write_mipi(kmb, reg_adr,
> +		       (fg_cfg->hsync_width * ppl_llp_ratio) / 1000);
> +
> +	/* horizontal backporch (hbp) */
> +	reg_adr = MIPI_TXm_HS_H_BACKPORCHn(ctrl_no, frame_gen);
> +	kmb_write_mipi(kmb, reg_adr,
> +		       (fg_cfg->h_backporch * ppl_llp_ratio) / 1000);
> +
> +	/* horizontal frontporch (hfp) */
> +	reg_adr = MIPI_TXm_HS_H_FRONTPORCHn(ctrl_no, frame_gen);
> +	kmb_write_mipi(kmb, reg_adr,
> +		       (fg_cfg->h_frontporch * ppl_llp_ratio) / 1000);
> +
> +	/* horizontal active (ha) */
> +	reg_adr = MIPI_TXm_HS_H_ACTIVEn(ctrl_no, frame_gen);
> +
> +	/* convert h_active which is wc in bytes to cycles */
> +	val = (fg_cfg->h_active * sysclk * 1000) /
> +	    ((fg_cfg->lane_rate_mbps / 8) * fg_cfg->active_lanes);
> +	val /= 1000;
> +	kmb_write_mipi(kmb, reg_adr, val);
> +
> +	/* llp hsync width */
> +	reg_adr = MIPI_TXm_HS_LLP_HSYNC_WIDTHn(ctrl_no, frame_gen);
> +	kmb_write_mipi(kmb, reg_adr, fg_cfg->hsync_width * (fg_cfg->bpp / 8));
> +
> +	/* llp h backporch */
> +	reg_adr = MIPI_TXm_HS_LLP_H_BACKPORCHn(ctrl_no, frame_gen);
> +	kmb_write_mipi(kmb, reg_adr, fg_cfg->h_backporch * (fg_cfg->bpp / 8));
> +
> +	/* llp h frontporch */
> +	reg_adr = MIPI_TXm_HS_LLP_H_FRONTPORCHn(ctrl_no, frame_gen);
> +	kmb_write_mipi(kmb, reg_adr,
> +		       fg_cfg->h_frontporch * (fg_cfg->bpp / 8));
> +}
> +
> +static void mipi_tx_fg_cfg(struct kmb_drm_private *kmb, u8 frame_gen,
> +			   u8 active_lanes, u32 bpp, u32 wc,
> +			   u32 lane_rate_mbps, struct mipi_tx_frame_cfg *fg_cfg)
> +{
> +	u32 i, fg_num_lines = 0;
> +	struct mipi_tx_frame_timing_cfg fg_t_cfg;
> +
> +	/* Calculate the total frame generator number of
> +	 * lines based on it's active sections
> +	 */
> +	for (i = 0; i < MIPI_TX_FRAME_GEN_SECTIONS; i++) {
> +		if (fg_cfg->sections[i])
> +			fg_num_lines += fg_cfg->sections[i]->height_lines;
> +	}
> +
> +	fg_t_cfg.bpp = bpp;
> +	fg_t_cfg.lane_rate_mbps = lane_rate_mbps;
> +	fg_t_cfg.hsync_width = fg_cfg->hsync_width;
> +	fg_t_cfg.h_backporch = fg_cfg->h_backporch;
> +	fg_t_cfg.h_frontporch = fg_cfg->h_frontporch;
> +	fg_t_cfg.h_active = wc;
> +	fg_t_cfg.vsync_width = fg_cfg->vsync_width;
> +	fg_t_cfg.v_backporch = fg_cfg->v_backporch;
> +	fg_t_cfg.v_frontporch = fg_cfg->v_frontporch;
> +	fg_t_cfg.v_active = fg_num_lines;
> +	fg_t_cfg.active_lanes = active_lanes;
> +
> +	/* Apply frame generator timing setting */
> +	mipi_tx_fg_cfg_regs(kmb, frame_gen, &fg_t_cfg);
> +}
> +
> +static void mipi_tx_multichannel_fifo_cfg(struct kmb_drm_private *kmb,
> +					  u8 active_lanes, u8 vchannel_id)
> +{
> +	u32 fifo_size, fifo_rthreshold;
> +	u32 ctrl_no = MIPI_CTRL6;
> +
> +	/* Clear all mc fifo channel sizes and thresholds */
> +	kmb_write_mipi(kmb, MIPI_TX_HS_MC_FIFO_CTRL_EN, 0);
> +	kmb_write_mipi(kmb, MIPI_TX_HS_MC_FIFO_CHAN_ALLOC0, 0);
> +	kmb_write_mipi(kmb, MIPI_TX_HS_MC_FIFO_CHAN_ALLOC1, 0);
> +	kmb_write_mipi(kmb, MIPI_TX_HS_MC_FIFO_RTHRESHOLD0, 0);
> +	kmb_write_mipi(kmb, MIPI_TX_HS_MC_FIFO_RTHRESHOLD1, 0);
> +
> +	fifo_size = ((active_lanes > MIPI_D_LANES_PER_DPHY) ?
> +		     MIPI_CTRL_4LANE_MAX_MC_FIFO_LOC :
> +		     MIPI_CTRL_2LANE_MAX_MC_FIFO_LOC) - 1;
> +
> +	/* MC fifo size for virtual channels 0-3
> +	 * REG_MC_FIFO_CHAN_ALLOC0: [8:0]-channel0, [24:16]-channel1
> +	 * REG_MC_FIFO_CHAN_ALLOC1: [8:0]-2, [24:16]-channel3
> +	 */
> +	SET_MC_FIFO_CHAN_ALLOC(kmb, ctrl_no, vchannel_id, fifo_size);
> +
> +	/* Set threshold to half the fifo size, actual size=size*16 */
> +	fifo_rthreshold = ((fifo_size) * 8) & BIT_MASK_16;
> +	SET_MC_FIFO_RTHRESHOLD(kmb, ctrl_no, vchannel_id, fifo_rthreshold);
> +
> +	/* Enable the MC FIFO channel corresponding to the Virtual Channel */
> +	kmb_set_bit_mipi(kmb, MIPI_TXm_HS_MC_FIFO_CTRL_EN(ctrl_no),
> +			 vchannel_id);
> +}
> +
> +static void mipi_tx_ctrl_cfg(struct kmb_drm_private *kmb, u8 fg_id,
> +			     struct mipi_ctrl_cfg *ctrl_cfg)
> +{
> +	u32 sync_cfg = 0, ctrl = 0, fg_en;
> +	u32 ctrl_no = MIPI_CTRL6;
> +
> +	/* MIPI_TX_HS_SYNC_CFG */
> +	if (ctrl_cfg->tx_ctrl_cfg.line_sync_pkt_en)
> +		sync_cfg |= LINE_SYNC_PKT_ENABLE;
> +	if (ctrl_cfg->tx_ctrl_cfg.frame_counter_active)
> +		sync_cfg |= FRAME_COUNTER_ACTIVE;
> +	if (ctrl_cfg->tx_ctrl_cfg.line_counter_active)
> +		sync_cfg |= LINE_COUNTER_ACTIVE;
> +	if (ctrl_cfg->tx_ctrl_cfg.tx_dsi_cfg->v_blanking)
> +		sync_cfg |= DSI_V_BLANKING;
> +	if (ctrl_cfg->tx_ctrl_cfg.tx_dsi_cfg->hsa_blanking)
> +		sync_cfg |= DSI_HSA_BLANKING;
> +	if (ctrl_cfg->tx_ctrl_cfg.tx_dsi_cfg->hbp_blanking)
> +		sync_cfg |= DSI_HBP_BLANKING;
> +	if (ctrl_cfg->tx_ctrl_cfg.tx_dsi_cfg->hfp_blanking)
> +		sync_cfg |= DSI_HFP_BLANKING;
> +	if (ctrl_cfg->tx_ctrl_cfg.tx_dsi_cfg->sync_pulse_eventn)
> +		sync_cfg |= DSI_SYNC_PULSE_EVENTN;
> +	if (ctrl_cfg->tx_ctrl_cfg.tx_dsi_cfg->lpm_first_vsa_line)
> +		sync_cfg |= DSI_LPM_FIRST_VSA_LINE;
> +	if (ctrl_cfg->tx_ctrl_cfg.tx_dsi_cfg->lpm_last_vfp_line)
> +		sync_cfg |= DSI_LPM_LAST_VFP_LINE;
> +
> +	/* Enable frame generator */
> +	fg_en = 1 << fg_id;
> +	sync_cfg |= FRAME_GEN_EN(fg_en);
> +
> +	if (ctrl_cfg->tx_ctrl_cfg.tx_always_use_hact)
> +		sync_cfg |= ALWAYS_USE_HACT(fg_en);
> +	if (ctrl_cfg->tx_ctrl_cfg.tx_hact_wait_stop)
> +		sync_cfg |= HACT_WAIT_STOP(fg_en);
> +
> +	drm_dbg(&kmb->drm, "sync_cfg=%d fg_en=%d\n", sync_cfg, fg_en);
> +
> +	/* MIPI_TX_HS_CTRL */
> +
> +	/* type:DSI, source:LCD */
> +	ctrl = HS_CTRL_EN | TX_SOURCE;
> +	ctrl |= LCD_VC(fg_id);
> +	ctrl |= ACTIVE_LANES(ctrl_cfg->active_lanes - 1);
> +	if (ctrl_cfg->tx_ctrl_cfg.tx_dsi_cfg->eotp_en)
> +		ctrl |= DSI_EOTP_EN;
> +	if (ctrl_cfg->tx_ctrl_cfg.tx_dsi_cfg->hfp_blank_en)
> +		ctrl |= DSI_CMD_HFP_EN;
> +
> +	/*67 ns stop time */
> +	ctrl |= HSEXIT_CNT(0x43);
> +
> +	kmb_write_mipi(kmb, MIPI_TXm_HS_SYNC_CFG(ctrl_no), sync_cfg);
> +	kmb_write_mipi(kmb, MIPI_TXm_HS_CTRL(ctrl_no), ctrl);
> +}
> +
> +static u32 mipi_tx_init_cntrl(struct kmb_drm_private *kmb,
> +			      struct mipi_ctrl_cfg *ctrl_cfg)
> +{
> +	u32 ret = 0;
> +	u8 active_vchannels = 0;
> +	u8 frame_id, sect;
> +	u32 bits_per_pclk = 0;
> +	u32 word_count = 0;
> +	struct mipi_tx_frame_cfg *frame;
> +
> +	/* This is the order to initialize MIPI TX:
> +	 * 1. set frame section parameters
> +	 * 2. set frame specific parameters
> +	 * 3. connect lcd to mipi
> +	 * 4. multi channel fifo cfg
> +	 * 5. set mipitxcctrlcfg
> +	 */
> +
> +	for (frame_id = 0; frame_id < 4; frame_id++) {
> +		frame = ctrl_cfg->tx_ctrl_cfg.frames[frame_id];
> +
> +		/* Find valid frame, assume only one valid frame */
> +		if (!frame)
> +			continue;
> +
> +		/* Frame Section configuration */
> +		/* TODO - assume there is only one valid section in a frame,
> +		 * so bits_per_pclk and word_count are only set once
> +		 */
> +		for (sect = 0; sect < MIPI_CTRL_VIRTUAL_CHANNELS; sect++) {
> +			if (!frame->sections[sect])
> +				continue;
> +
> +			ret = mipi_tx_fg_section_cfg(kmb, frame_id, sect,
> +						     frame->sections[sect],
> +						     &bits_per_pclk,
> +						     &word_count);
> +			if (ret)
> +				return ret;
> +		}
> +
> +		/* Set frame specific parameters */
> +		mipi_tx_fg_cfg(kmb, frame_id, ctrl_cfg->active_lanes,
> +			       bits_per_pclk, word_count,
> +			       ctrl_cfg->lane_rate_mbps, frame);
> +
> +		active_vchannels++;
> +
> +		/* Stop iterating as only one virtual channel
> +		 * shall be used for LCD connection
> +		 */
> +		break;
> +	}
> +
> +	if (active_vchannels == 0)
> +		return -EINVAL;
> +	/* Multi-Channel FIFO Configuration */
> +	mipi_tx_multichannel_fifo_cfg(kmb, ctrl_cfg->active_lanes, frame_id);
> +
> +	/* Frame Generator Enable */
> +	mipi_tx_ctrl_cfg(kmb, frame_id, ctrl_cfg);
> +
> +	return ret;
> +}
> +
> +static void test_mode_send(struct kmb_drm_private *kmb, u32 dphy_no,
> +			   u32 test_code, u32 test_data)
> +{
> +	/* Steps to send test code:
> +	 * - set testclk HIGH
> +	 * - set testdin with test code
> +	 * - set testen HIGH
> +	 * - set testclk LOW
> +	 * - set testen LOW
> +	 */
> +
> +	/* Set testclk high */
> +	SET_DPHY_TEST_CTRL1_CLK(kmb, dphy_no);
> +
> +	/* Set testdin */
> +	SET_TEST_DIN0_3(kmb, dphy_no, test_code);
> +
> +	/* Set testen high */
> +	SET_DPHY_TEST_CTRL1_EN(kmb, dphy_no);
> +
> +	/* Set testclk low */
> +	CLR_DPHY_TEST_CTRL1_CLK(kmb, dphy_no);
> +
> +	/* Set testen low */
> +	CLR_DPHY_TEST_CTRL1_EN(kmb, dphy_no);
> +
> +	if (test_code) {
> +		/*  Steps to send test data:
> +		 * - set testen LOW
> +		 * - set testclk LOW
> +		 * - set testdin with data
> +		 * - set testclk HIGH
> +		 */
> +
> +		/* Set testen low */
> +		CLR_DPHY_TEST_CTRL1_EN(kmb, dphy_no);
> +
> +		/* Set testclk low */
> +		CLR_DPHY_TEST_CTRL1_CLK(kmb, dphy_no);
> +
> +		/* Set data in testdin */
> +		kmb_write_mipi(kmb,
> +			       DPHY_TEST_DIN0_3 + ((dphy_no / 0x4) * 0x4),
> +			       test_data << ((dphy_no % 4) * 8));
> +
> +		/* Set testclk high */
> +		SET_DPHY_TEST_CTRL1_CLK(kmb, dphy_no);
> +	}
> +}
> +
> +static inline void
> +	set_test_mode_src_osc_freq_target_low_bits(struct kmb_drm_private *kmb,
> +						   u32 dphy_no,
> +						   u32 freq)
> +{
> +	/* Typical rise/fall time=166, refer Table 1207 databook,
> +	 * sr_osc_freq_target[7:0]
> +	 */
> +	test_mode_send(kmb, dphy_no, TEST_CODE_SLEW_RATE_DDL_CYCLES,
> +		       (freq & 0x7f));
> +}
> +
> +static inline void
> +	set_test_mode_src_osc_freq_target_hi_bits(struct kmb_drm_private *kmb,
> +						  u32 dphy_no,
> +						  u32 freq)
> +{
> +	u32 data;
> +
> +	/* Flag this as high nibble */
> +	data = ((freq >> 6) & 0x1f) | (1 << 7);
> +
> +	/* Typical rise/fall time=166, refer Table 1207 databook,
> +	 * sr_osc_freq_target[11:7]
> +	 */
> +	test_mode_send(kmb, dphy_no, TEST_CODE_SLEW_RATE_DDL_CYCLES, data);
> +}
> +
> +static void mipi_tx_get_vco_params(struct vco_params *vco)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(vco_table); i++) {
> +		if (vco->freq < vco_table[i].freq) {
> +			*vco = vco_table[i];
> +			return;
> +		}
> +	}
> +
> +	WARN_ONCE(1, "Invalid vco freq = %u for PLL setup\n", vco->freq);
> +}
> +
> +static void mipi_tx_pll_setup(struct kmb_drm_private *kmb, u32 dphy_no,
> +			      u32 ref_clk_mhz, u32 target_freq_mhz)
> +{
> +	u32 best_n = 0, best_m = 0;
> +	u32 n = 0, m = 0, div = 0, delta, freq = 0, t_freq;
> +	u32 best_freq_delta = 3000;
> +
> +	/* pll_ref_clk: - valid range: 2~64 MHz; Typically 24 MHz
> +	 * Fvco: - valid range: 320~1250 MHz (Gen3 D-PHY)
> +	 * Fout: - valid range: 40~1250 MHz (Gen3 D-PHY)
> +	 * n: - valid range [0 15]
> +	 * N: - N = n + 1
> +	 *      -valid range: [1 16]
> +	 *      -conditions: - (pll_ref_clk / N) >= 2 MHz
> +	 *              -(pll_ref_clk / N) <= 8 MHz
> +	 * m: valid range [62 623]
> +	 * M: - M = m + 2
> +	 *      -valid range [64 625]
> +	 *      -Fvco = (M/N) * pll_ref_clk
> +	 */
> +	struct vco_params vco_p = {
> +		.range = 0,
> +		.divider = 1,
> +	};
> +
> +	vco_p.freq = target_freq_mhz;
> +	mipi_tx_get_vco_params(&vco_p);
> +
> +	/* Search pll n parameter */
> +	for (n = PLL_N_MIN; n <= PLL_N_MAX; n++) {
> +		/* Calculate the pll input frequency division ratio
> +		 * multiply by 1000 for precision -
> +		 * no floating point, add n for rounding
> +		 */
> +		div = ((ref_clk_mhz * 1000) + n) / (n + 1);
> +
> +		/* Found a valid n parameter */
> +		if ((div < 2000 || div > 8000))
> +			continue;
> +
> +		/* Search pll m parameter */
> +		for (m = PLL_M_MIN; m <= PLL_M_MAX; m++) {
> +			/* Calculate the Fvco(DPHY PLL output frequency)
> +			 * using the current n,m params
> +			 */
> +			freq = div * (m + 2);
> +			freq /= 1000;
> +
> +			/* Trim the potential pll freq to max supported */
> +			if (freq > PLL_FVCO_MAX)
> +				continue;
> +
> +			delta = abs(freq - target_freq_mhz);
> +
> +			/* Select the best (closest to target pll freq)
> +			 * n,m parameters so far
> +			 */
> +			if (delta < best_freq_delta) {
> +				best_n = n;
> +				best_m = m;
> +				best_freq_delta = delta;
> +			}
> +		}
> +	}
> +
> +	/* Program vco_cntrl parameter
> +	 * PLL_VCO_Control[5:0] = pll_vco_cntrl_ovr,
> +	 * PLL_VCO_Control[6]   = pll_vco_cntrl_ovr_en
> +	 */
> +	test_mode_send(kmb, dphy_no, TEST_CODE_PLL_VCO_CTRL, (vco_p.range
> +								| (1 << 6)));
> +
> +	/* Program m, n pll parameters */
> +	drm_dbg(&kmb->drm, "m = %d n = %d\n", best_m, best_n);
> +
> +	/* PLL_Input_Divider_Ratio[3:0] = pll_n_ovr */
> +	test_mode_send(kmb, dphy_no, TEST_CODE_PLL_INPUT_DIVIDER,
> +		       (best_n & 0x0f));
> +
> +	/* m - low nibble PLL_Loop_Divider_Ratio[4:0]
> +	 * pll_m_ovr[4:0]
> +	 */
> +	test_mode_send(kmb, dphy_no, TEST_CODE_PLL_FEEDBACK_DIVIDER,
> +		       (best_m & 0x1f));
> +
> +	/* m - high nibble PLL_Loop_Divider_Ratio[4:0]
> +	 * pll_m_ovr[9:5]
> +	 */
> +	test_mode_send(kmb, dphy_no, TEST_CODE_PLL_FEEDBACK_DIVIDER,
> +		       ((best_m >> 5) & 0x1f) | PLL_FEEDBACK_DIVIDER_HIGH);
> +
> +	/* Enable overwrite of n,m parameters :pll_n_ovr_en, pll_m_ovr_en */
> +	test_mode_send(kmb, dphy_no, TEST_CODE_PLL_OUTPUT_CLK_SEL,
> +		       (PLL_N_OVR_EN | PLL_M_OVR_EN));
> +
> +	/* Program Charge-Pump parameters */
> +
> +	/* pll_prop_cntrl-fixed values for prop_cntrl from DPHY doc */
> +	t_freq = target_freq_mhz * vco_p.divider;
> +	test_mode_send(kmb, dphy_no,
> +		       TEST_CODE_PLL_PROPORTIONAL_CHARGE_PUMP_CTRL,
> +		       ((t_freq > 1150) ? 0x0C : 0x0B));
> +
> +	/* pll_int_cntrl-fixed value for int_cntrl from DPHY doc */
> +	test_mode_send(kmb, dphy_no, TEST_CODE_PLL_INTEGRAL_CHARGE_PUMP_CTRL,
> +		       0x00);
> +
> +	/* pll_gmp_cntrl-fixed value for gmp_cntrl from DPHY doci */
> +	test_mode_send(kmb, dphy_no, TEST_CODE_PLL_GMP_CTRL, 0x10);
> +
> +	/* pll_cpbias_cntrl-fixed value for cpbias_cntrl from DPHY doc */
> +	test_mode_send(kmb, dphy_no, TEST_CODE_PLL_CHARGE_PUMP_BIAS, 0x10);
> +
> +	/* pll_th1 -Lock Detector Phase error threshold,
> +	 * document gives fixed value
> +	 */
> +	test_mode_send(kmb, dphy_no, TEST_CODE_PLL_PHASE_ERR_CTRL, 0x02);
> +
> +	/* PLL Lock Configuration */
> +
> +	/* pll_th2 - Lock Filter length, document gives fixed value */
> +	test_mode_send(kmb, dphy_no, TEST_CODE_PLL_LOCK_FILTER, 0x60);
> +
> +	/* pll_th3- PLL Unlocking filter, document gives fixed value */
> +	test_mode_send(kmb, dphy_no, TEST_CODE_PLL_UNLOCK_FILTER, 0x03);
> +
> +	/* pll_lock_sel-PLL Lock Detector Selection,
> +	 * document gives fixed value
> +	 */
> +	test_mode_send(kmb, dphy_no, TEST_CODE_PLL_LOCK_DETECTOR, 0x02);
> +}
> +
> +static void set_slewrate_gt_1500(struct kmb_drm_private *kmb, u32 dphy_no)
> +{
> +	u32 test_code = 0, test_data = 0;
> +	/* Bypass slew rate calibration algorithm
> +	 * bits[1:0} srcal_en_ovr_en, srcal_en_ovr
> +	 */
> +	test_code = TEST_CODE_SLEW_RATE_OVERRIDE_CTRL;
> +	test_data = 0x02;
> +	test_mode_send(kmb, dphy_no, test_code, test_data);
> +
> +	/* Disable slew rate calibration */
> +	test_code = TEST_CODE_SLEW_RATE_DDL_LOOP_CTRL;
> +	test_data = 0x00;
> +	test_mode_send(kmb, dphy_no, test_code, test_data);
> +}
> +
> +static void set_slewrate_gt_1000(struct kmb_drm_private *kmb, u32 dphy_no)
> +{
> +	u32 test_code = 0, test_data = 0;
> +
> +	/* BitRate: > 1 Gbps && <= 1.5 Gbps: - slew rate control ON
> +	 * typical rise/fall times: 166 ps
> +	 */
> +
> +	/* Do not bypass slew rate calibration algorithm
> +	 * bits[1:0}=srcal_en_ovr_en, srcal_en_ovr, bit[6]=sr_range
> +	 */
> +	test_code = TEST_CODE_SLEW_RATE_OVERRIDE_CTRL;
> +	test_data = (0x03 | (1 << 6));
> +	test_mode_send(kmb, dphy_no, test_code, test_data);
> +
> +	/* Enable slew rate calibration */
> +	test_code = TEST_CODE_SLEW_RATE_DDL_LOOP_CTRL;
> +	test_data = 0x01;
> +	test_mode_send(kmb, dphy_no, test_code, test_data);
> +
> +	/* Set sr_osc_freq_target[6:0] low nibble
> +	 * typical rise/fall time=166, refer Table 1207 databook
> +	 */
> +	test_code = TEST_CODE_SLEW_RATE_DDL_CYCLES;
> +	test_data = (0x72f & 0x7f);
> +	test_mode_send(kmb, dphy_no, test_code, test_data);
> +
> +	/* Set sr_osc_freq_target[11:7] high nibble
> +	 * Typical rise/fall time=166, refer Table 1207 databook
> +	 */
> +	test_code = TEST_CODE_SLEW_RATE_DDL_CYCLES;
> +	test_data = ((0x72f >> 6) & 0x1f) | (1 << 7);
> +	test_mode_send(kmb, dphy_no, test_code, test_data);
> +}
> +
> +static void set_slewrate_lt_1000(struct kmb_drm_private *kmb, u32 dphy_no)
> +{
> +	u32 test_code = 0, test_data = 0;
> +
> +	/* lane_rate_mbps <= 1000 Mbps
> +	 * BitRate:  <= 1 Gbps:
> +	 * - slew rate control ON
> +	 * - typical rise/fall times: 225 ps
> +	 */
> +
> +	/* Do not bypass slew rate calibration algorithm */
> +	test_code = TEST_CODE_SLEW_RATE_OVERRIDE_CTRL;
> +	test_data = (0x03 | (1 << 6));
> +	test_mode_send(kmb, dphy_no, test_code, test_data);
> +
> +	/* Enable slew rate calibration */
> +	test_code = TEST_CODE_SLEW_RATE_DDL_LOOP_CTRL;
> +	test_data = 0x01;
> +	test_mode_send(kmb, dphy_no, test_code, test_data);
> +
> +	/* Typical rise/fall time=255, refer Table 1207 databook */
> +	test_code = TEST_CODE_SLEW_RATE_DDL_CYCLES;
> +	test_data = (0x523 & 0x7f);
> +	test_mode_send(kmb, dphy_no, test_code, test_data);
> +
> +	/* Set sr_osc_freq_target[11:7] high nibble */
> +	test_code = TEST_CODE_SLEW_RATE_DDL_CYCLES;
> +	test_data = ((0x523 >> 6) & 0x1f) | (1 << 7);
> +	test_mode_send(kmb, dphy_no, test_code, test_data);
> +}
> +
> +static void setup_pll(struct kmb_drm_private *kmb, u32 dphy_no,
> +		      struct mipi_ctrl_cfg *cfg)
> +{
> +	u32 test_code = 0, test_data = 0;
> +
> +	/* Set PLL regulator in bypass */
> +	test_code = TEST_CODE_PLL_ANALOG_PROG;
> +	test_data = 0x01;
> +	test_mode_send(kmb, dphy_no, test_code, test_data);
> +
> +	/* PLL Parameters Setup */
> +	mipi_tx_pll_setup(kmb, dphy_no, cfg->ref_clk_khz / 1000,
> +			  cfg->lane_rate_mbps / 2);
> +
> +	/* Set clksel */
> +	kmb_write_bits_mipi(kmb, DPHY_INIT_CTRL1, PLL_CLKSEL_0, 2, 0x01);
> +
> +	/* Set pll_shadow_control */
> +	kmb_set_bit_mipi(kmb, DPHY_INIT_CTRL1, PLL_SHADOW_CTRL);
> +}
> +
> +static void set_lane_data_rate(struct kmb_drm_private *kmb, u32 dphy_no,
> +			       struct mipi_ctrl_cfg *cfg)
> +{
> +	u32 i, test_code = 0, test_data = 0;
> +
> +	for (i = 0; i < MIPI_DPHY_DEFAULT_BIT_RATES; i++) {
> +		if (mipi_hs_freq_range[i].default_bit_rate_mbps <
> +		    cfg->lane_rate_mbps)
> +			continue;
> +
> +		/* Send the test code and data */
> +		/* bit[6:0] = hsfreqrange_ovr bit[7] = hsfreqrange_ovr_en */
> +		test_code = TEST_CODE_HS_FREQ_RANGE_CFG;
> +		test_data = (mipi_hs_freq_range[i].hsfreqrange_code & 0x7f) |
> +		    (1 << 7);
> +		test_mode_send(kmb, dphy_no, test_code, test_data);
> +		break;
> +	}
> +}
> +
> +static void dphy_init_sequence(struct kmb_drm_private *kmb,
> +			       struct mipi_ctrl_cfg *cfg, u32 dphy_no,
> +			       int active_lanes, enum dphy_mode mode)
> +{
> +	u32 test_code = 0, test_data = 0, val;
> +
> +	/* Set D-PHY in shutdown mode */
> +	/* Assert RSTZ signal */
> +	CLR_DPHY_INIT_CTRL0(kmb, dphy_no, RESETZ);
> +
> +	/* Assert SHUTDOWNZ signal */
> +	CLR_DPHY_INIT_CTRL0(kmb, dphy_no, SHUTDOWNZ);
> +	val = kmb_read_mipi(kmb, DPHY_INIT_CTRL0);
> +
> +	/* Init D-PHY_n
> +	 * Pulse testclear signal to make sure the d-phy configuration
> +	 * starts from a clean base
> +	 */
> +	CLR_DPHY_TEST_CTRL0(kmb, dphy_no);
> +	ndelay(15);
> +	SET_DPHY_TEST_CTRL0(kmb, dphy_no);
> +	ndelay(15);
> +	CLR_DPHY_TEST_CTRL0(kmb, dphy_no);
> +	ndelay(15);
> +
> +	/* Set mastermacro bit - Master or slave mode */
> +	test_code = TEST_CODE_MULTIPLE_PHY_CTRL;
> +
> +	/* DPHY has its own clock lane enabled (master) */
> +	if (mode == MIPI_DPHY_MASTER)
> +		test_data = 0x01;
> +	else
> +		test_data = 0x00;
> +
> +	/* Send the test code and data */
> +	test_mode_send(kmb, dphy_no, test_code, test_data);
> +
> +	/* Set the lane data rate */
> +	set_lane_data_rate(kmb, dphy_no, cfg);
> +
> +	/* High-Speed Tx Slew Rate Calibration
> +	 * BitRate: > 1.5 Gbps && <= 2.5 Gbps: slew rate control OFF
> +	 */
> +	if (cfg->lane_rate_mbps > 1500)
> +		set_slewrate_gt_1500(kmb, dphy_no);
> +	else if (cfg->lane_rate_mbps > 1000)
> +		set_slewrate_gt_1000(kmb, dphy_no);
> +	else
> +		set_slewrate_lt_1000(kmb, dphy_no);
> +
> +	/* Set cfgclkfreqrange */
> +	val = (((cfg->cfg_clk_khz / 1000) - 17) * 4) & 0x3f;
> +	SET_DPHY_FREQ_CTRL0_3(kmb, dphy_no, val);
> +
> +	/* Enable config clk for the corresponding d-phy */
> +	kmb_set_bit_mipi(kmb, DPHY_CFG_CLK_EN, dphy_no);
> +
> +	/* PLL setup */
> +	if (mode == MIPI_DPHY_MASTER)
> +		setup_pll(kmb, dphy_no, cfg);
> +
> +	/* Send NORMAL OPERATION test code */
> +	test_code = 0x0;
> +	test_data = 0x0;
> +	test_mode_send(kmb, dphy_no, test_code, test_data);
> +
> +	/* Configure BASEDIR for data lanes
> +	 * NOTE: basedir only applies to LANE_0 of each D-PHY.
> +	 * The other lanes keep their direction based on the D-PHY type,
> +	 * either Rx or Tx.
> +	 * bits[5:0]  - BaseDir: 1 = Rx
> +	 * bits[9:6] - BaseDir: 0 = Tx
> +	 */
> +	kmb_write_bits_mipi(kmb, DPHY_INIT_CTRL2, 0, 9, 0x03f);
> +	ndelay(15);
> +
> +	/* Enable CLOCK LANE
> +	 * Clock lane should be enabled regardless of the direction
> +	 * set for the D-PHY (Rx/Tx)
> +	 */
> +	kmb_set_bit_mipi(kmb, DPHY_INIT_CTRL2, 12 + dphy_no);
> +
> +	/* Enable DATA LANES */
> +	kmb_write_bits_mipi(kmb, DPHY_ENABLE, dphy_no * 2, 2,
> +			    ((1 << active_lanes) - 1));
> +
> +	ndelay(15);
> +
> +	/* Take D-PHY out of shutdown mode */
> +	/* Deassert SHUTDOWNZ signal */
> +	SET_DPHY_INIT_CTRL0(kmb, dphy_no, SHUTDOWNZ);
> +	ndelay(15);
> +
> +	/* Deassert RSTZ signal */
> +	SET_DPHY_INIT_CTRL0(kmb, dphy_no, RESETZ);
> +}
> +
> +static void dphy_wait_fsm(struct kmb_drm_private *kmb, u32 dphy_no,
> +			  enum dphy_tx_fsm fsm_state)
> +{
> +	enum dphy_tx_fsm val = DPHY_TX_POWERDWN;
> +	int i = 0;
> +	int status = 1;
> +
> +	do {
> +		test_mode_send(kmb, dphy_no, TEST_CODE_FSM_CONTROL, 0x80);
> +
> +		val = GET_TEST_DOUT4_7(kmb, dphy_no);
> +		i++;
> +		if (i > TIMEOUT) {
> +			status = 0;
> +			break;
> +		}
> +	} while (val != fsm_state);
> +
> +	drm_dbg(&kmb->drm, "%s: dphy %d val = %x", __func__, dphy_no, val);
> +	drm_dbg(&kmb->drm, "* DPHY %d WAIT_FSM %s *",
> +		dphy_no, status ? "SUCCESS" : "FAILED");
> +}
> +
> +static void wait_init_done(struct kmb_drm_private *kmb, u32 dphy_no,
> +			   u32 active_lanes)
> +{
> +	u32 stopstatedata = 0;
> +	u32 data_lanes = (1 << active_lanes) - 1;
> +	int i = 0, val;
> +	int status = 1;
> +
> +	do {
> +		val = kmb_read_mipi(kmb, MIPI_DPHY_STAT4_7);
> +		stopstatedata = GET_STOPSTATE_DATA(kmb, dphy_no) & data_lanes;
> +
> +		/* TODO-need to add a time out and return failure */
> +		i++;
> +
> +		if (i > TIMEOUT) {
> +			status = 0;
> +			drm_dbg(&kmb->drm,
> +				"! WAIT_INIT_DONE: TIMING OUT!(err_stat=%d)",
> +				kmb_read_mipi(kmb, MIPI_DPHY_ERR_STAT6_7));
> +			break;
> +		}
> +	} while (stopstatedata != data_lanes);
> +
> +	drm_dbg(&kmb->drm, "* DPHY %d INIT - %s *",
> +		dphy_no, status ? "SUCCESS" : "FAILED");
> +}
> +
> +static void wait_pll_lock(struct kmb_drm_private *kmb, u32 dphy_no)
> +{
> +	int i = 0;
> +	int status = 1;
> +
> +	do {
> +		/* TODO-need to add a time out and return failure */
> +		i++;
> +		if (i > TIMEOUT) {
> +			status = 0;
> +			drm_dbg(&kmb->drm, "%s: timing out", __func__);
> +			break;
> +		}
> +	} while (!GET_PLL_LOCK(kmb, dphy_no));
> +
> +	drm_dbg(&kmb->drm, "* PLL Locked for DPHY %d - %s *",
> +		dphy_no, status ? "SUCCESS" : "FAILED");
> +}
> +
> +static u32 mipi_tx_init_dphy(struct kmb_drm_private *kmb,
> +			     struct mipi_ctrl_cfg *cfg)
> +{
> +	u32 dphy_no = MIPI_DPHY6;
> +
> +	/* Multiple D-PHYs needed */
> +	if (cfg->active_lanes > MIPI_DPHY_D_LANES) {
> +		/*
> +		 *Initialization for Tx aggregation mode is done according to
> +		 *a. start init PHY1
> +		 *b. poll for PHY1 FSM state LOCK
> +		 *   b1. reg addr 0x03[3:0] - state_main[3:0] == 5 (LOCK)
> +		 *c. poll for PHY1 calibrations done :
> +		 *   c1. termination calibration lower section: addr 0x22[5]
> +		 *   - rescal_done
> +		 *   c2. slewrate calibration (if data rate < = 1500 Mbps):
> +		 *     addr 0xA7[3:2] - srcal_done, sr_finished
> +		 *d. start init PHY0
> +		 *e. poll for PHY0 stopstate
> +		 *f. poll for PHY1 stopstate
> +		 */
> +		/* PHY #N+1 ('slave') */
> +
> +		dphy_init_sequence(kmb, cfg, dphy_no + 1,
> +				   (cfg->active_lanes - MIPI_DPHY_D_LANES),
> +				   MIPI_DPHY_SLAVE);
> +		dphy_wait_fsm(kmb, dphy_no + 1, DPHY_TX_LOCK);
> +
> +		/* PHY #N master */
> +		dphy_init_sequence(kmb, cfg, dphy_no, MIPI_DPHY_D_LANES,
> +				   MIPI_DPHY_MASTER);
> +
> +		/* Wait for DPHY init to complete */
> +		wait_init_done(kmb, dphy_no, MIPI_DPHY_D_LANES);
> +		wait_init_done(kmb, dphy_no + 1,
> +			       cfg->active_lanes - MIPI_DPHY_D_LANES);
> +		wait_pll_lock(kmb, dphy_no);
> +		wait_pll_lock(kmb, dphy_no + 1);
> +		dphy_wait_fsm(kmb, dphy_no, DPHY_TX_IDLE);
> +	} else {		/* Single DPHY */
> +		dphy_init_sequence(kmb, cfg, dphy_no, cfg->active_lanes,
> +				   MIPI_DPHY_MASTER);
> +		dphy_wait_fsm(kmb, dphy_no, DPHY_TX_IDLE);
> +		wait_init_done(kmb, dphy_no, cfg->active_lanes);
> +		wait_pll_lock(kmb, dphy_no);
> +	}
> +
> +	return 0;
> +}
> +
> +static void connect_lcd_to_mipi(struct kmb_drm_private *kmb)
> +{
> +	/* DISABLE MIPI->CIF CONNECTION */
> +	kmb_write_msscam(kmb, MSS_MIPI_CIF_CFG, 0);
> +
> +	/* ENABLE LCD->MIPI CONNECTION */
> +	kmb_write_msscam(kmb, MSS_LCD_MIPI_CFG, 1);
> +
> +	/* DISABLE LCD->CIF LOOPBACK */
> +	kmb_write_msscam(kmb, MSS_LOOPBACK_CFG, 0);
> +}
> +
> +int kmb_dsi_hw_init(struct drm_device *dev, struct drm_display_mode *mode)
> +{
> +	struct kmb_drm_private *kmb = to_kmb(dev);
> +	u64 data_rate;
> +
> +	mipi_tx_init_cfg.active_lanes = MIPI_TX_ACTIVE_LANES;
> +
> +	mipi_tx_frame0_sect_cfg.width_pixels = mode->crtc_hdisplay;
> +	mipi_tx_frame0_sect_cfg.height_lines = mode->crtc_vdisplay;
> +	mipitx_frame0_cfg.vsync_width =
> +		mode->crtc_vsync_end - mode->crtc_vsync_start;
> +	mipitx_frame0_cfg.v_backporch =
> +		mode->crtc_vtotal - mode->crtc_vsync_end;
> +	mipitx_frame0_cfg.v_frontporch =
> +		mode->crtc_vsync_start - mode->crtc_vdisplay;
> +	mipitx_frame0_cfg.hsync_width =
> +		mode->crtc_hsync_end - mode->crtc_hsync_start;
> +	mipitx_frame0_cfg.h_backporch =
> +		mode->crtc_htotal - mode->crtc_hsync_end;
> +	mipitx_frame0_cfg.h_frontporch =
> +		mode->crtc_hsync_start - mode->crtc_hdisplay;
> +
> +	/* Lane rate = (vtotal*htotal*fps*bpp)/4 / 1000000
> +	 * to convert to Mbps
> +	 */
> +	data_rate = ((((u32)mode->crtc_vtotal *	(u32)mode->crtc_htotal) *
> +			(u32)(drm_mode_vrefresh(mode)) *
> +			MIPI_TX_BPP) / mipi_tx_init_cfg.active_lanes) /	1000000;
> +
> +	drm_dbg(&kmb->drm, "data_rate=%u active_lanes=%d\n",
> +		(u32)data_rate, mipi_tx_init_cfg.active_lanes);
> +
> +	/* When late rate < 800, modeset fails with 4 lanes,
> +	 * so switch to 2 lanes
> +	 */
> +	if (data_rate < 800) {
> +		mipi_tx_init_cfg.active_lanes = 2;
> +		mipi_tx_init_cfg.lane_rate_mbps = data_rate * 2;
> +	} else {
> +		mipi_tx_init_cfg.lane_rate_mbps = data_rate;
> +	}
> +
> +	kmb_write_mipi(kmb, DPHY_ENABLE, 0);
> +	kmb_write_mipi(kmb, DPHY_INIT_CTRL0, 0);
> +	kmb_write_mipi(kmb, DPHY_INIT_CTRL1, 0);
> +	kmb_write_mipi(kmb, DPHY_INIT_CTRL2, 0);
> +
> +	/* Initialize mipi controller */
> +	mipi_tx_init_cntrl(kmb, &mipi_tx_init_cfg);
> +
> +	/* Dphy initialization */
> +	mipi_tx_init_dphy(kmb, &mipi_tx_init_cfg);
> +
> +	connect_lcd_to_mipi(kmb);
> +
> +	drm_info(&kmb->drm, "mipi hw_initialized");
> +
> +	return 0;
> +}
> +
> +int kmb_dsi_init(struct drm_device *dev, struct drm_bridge *bridge)
> +{
> +	struct kmb_dsi *kmb_dsi;
> +	struct drm_encoder *encoder;
> +	struct drm_connector *connector;
> +	struct kmb_dsi_host *host;
> +	int ret = 0;
> +
> +	kmb_dsi = kzalloc(sizeof(*kmb_dsi), GFP_KERNEL);
> +	if (!kmb_dsi) {
> +		DRM_ERROR("failed to allocate kmb_dsi\n");
> +		return -ENOMEM;
> +	}
> +
> +	host = kmb_dsi_host_init(dev, kmb_dsi);
> +	if (!host) {
> +		DRM_ERROR("Failed to allocate host\n");
> +		kfree(kmb_dsi);
> +		return -ENOMEM;
> +	}
> +
> +	kmb_dsi->dsi_host = host;
> +	encoder = &kmb_dsi->base;
> +	encoder->possible_crtcs = 1;
> +	encoder->possible_clones = 0;
> +
> +	drm_encoder_init(dev, encoder, &kmb_dsi_funcs, DRM_MODE_ENCODER_DSI,
> +			 "MIPI-DSI");
> +
> +	/* Link drm_bridge to encoder */
> +	ret = drm_bridge_attach(encoder, bridge, NULL,
> +			DRM_BRIDGE_ATTACH_NO_CONNECTOR);
> +	if (ret) {
> +		DRM_ERROR("failed to attach bridge to MIPI\n");
> +		drm_encoder_cleanup(encoder);
> +		return ret;
> +	}
> +	drm_info(dev, "Bridge attached : SUCCESS");
> +	connector = drm_bridge_connector_init(dev, encoder);
> +	if (IS_ERR(connector)) {
> +		DRM_ERROR("Unable to create bridge connector");
> +		drm_encoder_cleanup(encoder);
> +		return PTR_ERR(connector);
> +	}
> +	drm_connector_attach_encoder(connector, encoder);
> +
> +	return 0;
> +}
> diff --git a/drivers/gpu/drm/kmb/kmb_dsi.h b/drivers/gpu/drm/kmb/kmb_dsi.h
> new file mode 100644
> index 0000000..fc4366d
> --- /dev/null
> +++ b/drivers/gpu/drm/kmb/kmb_dsi.h
> @@ -0,0 +1,350 @@
> +/* SPDX-License-Identifier: GPL-2.0-only
> + *
> + * Copyright © 2019-2020 Intel Corporation
> + */
> +
> +#ifndef __KMB_DSI_H__
> +#define __KMB_DSI_H__
> +
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_mipi_dsi.h>
> +#include <drm/drm_modes.h>
> +#include "kmb_drv.h"
> +
> +/* MIPI TX CFG*/
> +#define MIPI_TX_LANE_DATA_RATE_MBPS 891
> +#define MIPI_TX_REF_CLK_KHZ         24000
> +#define MIPI_TX_CFG_CLK_KHZ         24000
> +#define MIPI_TX_BPP		    24
> +
> +/* DPHY Tx test codes*/
> +#define TEST_CODE_FSM_CONTROL				0x03
> +#define TEST_CODE_MULTIPLE_PHY_CTRL			0x0C
> +#define TEST_CODE_PLL_PROPORTIONAL_CHARGE_PUMP_CTRL	0x0E
> +#define TEST_CODE_PLL_INTEGRAL_CHARGE_PUMP_CTRL		0x0F
> +#define TEST_CODE_PLL_VCO_CTRL				0x12
> +#define TEST_CODE_PLL_GMP_CTRL				0x13
> +#define TEST_CODE_PLL_PHASE_ERR_CTRL			0x14
> +#define TEST_CODE_PLL_LOCK_FILTER			0x15
> +#define TEST_CODE_PLL_UNLOCK_FILTER			0x16
> +#define TEST_CODE_PLL_INPUT_DIVIDER			0x17
> +#define TEST_CODE_PLL_FEEDBACK_DIVIDER			0x18
> +#define   PLL_FEEDBACK_DIVIDER_HIGH			BIT(7)
> +#define TEST_CODE_PLL_OUTPUT_CLK_SEL			0x19
> +#define   PLL_N_OVR_EN					BIT(4)
> +#define   PLL_M_OVR_EN					BIT(5)
> +#define TEST_CODE_VOD_LEVEL				0x24
> +#define TEST_CODE_PLL_CHARGE_PUMP_BIAS			0x1C
> +#define TEST_CODE_PLL_LOCK_DETECTOR			0x1D
> +#define TEST_CODE_HS_FREQ_RANGE_CFG			0x44
> +#define TEST_CODE_PLL_ANALOG_PROG			0x1F
> +#define TEST_CODE_SLEW_RATE_OVERRIDE_CTRL		0xA0
> +#define TEST_CODE_SLEW_RATE_DDL_LOOP_CTRL		0xA3
> +#define TEST_CODE_SLEW_RATE_DDL_CYCLES			0xA4
> +
> +/* DPHY params */
> +#define PLL_N_MIN	0
> +#define PLL_N_MAX	15
> +#define PLL_M_MIN	62
> +#define PLL_M_MAX	623
> +#define PLL_FVCO_MAX	1250
> +
> +#define TIMEOUT		600
> +
> +#define MIPI_TX_FRAME_GEN				4
> +#define MIPI_TX_FRAME_GEN_SECTIONS			4
> +#define MIPI_CTRL_VIRTUAL_CHANNELS			4
> +#define MIPI_D_LANES_PER_DPHY				2
> +#define MIPI_CTRL_2LANE_MAX_MC_FIFO_LOC			255
> +#define MIPI_CTRL_4LANE_MAX_MC_FIFO_LOC			511
> +/* 2 Data Lanes per D-PHY */
> +#define MIPI_DPHY_D_LANES				2
> +#define MIPI_DPHY_DEFAULT_BIT_RATES			63
> +
> +#define to_kmb_connector(x) container_of(x, struct kmb_connector, base)
> +#define to_kmb_host(x) container_of(x, struct kmb_dsi_host, base)
> +#define to_kmb_dsi(x) container_of(x, struct kmb_dsi, base)
> +
> +struct kmb_connector;
> +struct kmb_dsi_host;
> +
> +struct kmb_dsi {
> +	struct drm_encoder base;
> +	struct kmb_dsi_host *dsi_host;
> +	struct drm_bridge *bridge;
> +};
> +
> +struct kmb_dsi_host {
> +	struct mipi_dsi_host *base;
> +	struct kmb_dsi *kmb_dsi;
> +	struct mipi_dsi_device *device;
> +};
> +
> +struct kmb_connector {
> +	struct drm_connector base;
> +	struct drm_encoder *encoder;
> +	struct drm_display_mode *fixed_mode;
> +};
> +
> +/* DPHY Tx test codes */
> +
> +enum mipi_ctrl_num {
> +	MIPI_CTRL0 = 0,
> +	MIPI_CTRL1,
> +	MIPI_CTRL2,
> +	MIPI_CTRL3,
> +	MIPI_CTRL4,
> +	MIPI_CTRL5,
> +	MIPI_CTRL6,
> +	MIPI_CTRL7,
> +	MIPI_CTRL8,
> +	MIPI_CTRL9,
> +	MIPI_CTRL_NA
> +};
> +
> +enum mipi_dphy_num {
> +	MIPI_DPHY0 = 0,
> +	MIPI_DPHY1,
> +	MIPI_DPHY2,
> +	MIPI_DPHY3,
> +	MIPI_DPHY4,
> +	MIPI_DPHY5,
> +	MIPI_DPHY6,
> +	MIPI_DPHY7,
> +	MIPI_DPHY8,
> +	MIPI_DPHY9,
> +	MIPI_DPHY_NA
> +};
> +
> +enum mipi_dir {
> +	MIPI_RX,
> +	MIPI_TX
> +};
> +
> +enum mipi_ctrl_type {
> +	MIPI_DSI,
> +	MIPI_CSI
> +};
> +
> +enum mipi_data_if {
> +	MIPI_IF_DMA,
> +	MIPI_IF_PARALLEL
> +};
> +
> +enum mipi_data_mode {
> +	MIPI_DATA_MODE0,
> +	MIPI_DATA_MODE1,
> +	MIPI_DATA_MODE2,
> +	MIPI_DATA_MODE3
> +};
> +
> +enum mipi_dsi_video_mode {
> +	DSI_VIDEO_MODE_NO_BURST_PULSE,
> +	DSI_VIDEO_MODE_NO_BURST_EVENT,
> +	DSI_VIDEO_MODE_BURST
> +};
> +
> +enum mipi_dsi_blanking_mode {
> +	TRANSITION_TO_LOW_POWER,
> +	SEND_BLANK_PACKET
> +};
> +
> +enum mipi_dsi_eotp {
> +	DSI_EOTP_DISABLED,
> +	DSI_EOTP_ENABLES
> +};
> +
> +enum mipi_dsi_data_type {
> +	DSI_SP_DT_RESERVED_00 = 0x00,
> +	DSI_SP_DT_VSYNC_START = 0x01,
> +	DSI_SP_DT_COLOR_MODE_OFF = 0x02,
> +	DSI_SP_DT_GENERIC_SHORT_WR = 0x03,
> +	DSI_SP_DT_GENERIC_RD = 0x04,
> +	DSI_SP_DT_DCS_SHORT_WR = 0x05,
> +	DSI_SP_DT_DCS_RD = 0x06,
> +	DSI_SP_DT_EOTP = 0x08,
> +	DSI_LP_DT_NULL = 0x09,
> +	DSI_LP_DT_RESERVED_0A = 0x0a,
> +	DSI_LP_DT_RESERVED_0B = 0x0b,
> +	DSI_LP_DT_LPPS_YCBCR422_20B = 0x0c,
> +	DSI_LP_DT_PPS_RGB101010_30B = 0x0d,
> +	DSI_LP_DT_PPS_RGB565_16B = 0x0e,
> +	DSI_LP_DT_RESERVED_0F = 0x0f,
> +
> +	DSI_SP_DT_RESERVED_10 = 0x10,
> +	DSI_SP_DT_VSYNC_END = 0x11,
> +	DSI_SP_DT_COLOR_MODE_ON = 0x12,
> +	DSI_SP_DT_GENERIC_SHORT_WR_1PAR = 0x13,
> +	DSI_SP_DT_GENERIC_RD_1PAR = 0x14,
> +	DSI_SP_DT_DCS_SHORT_WR_1PAR = 0x15,
> +	DSI_SP_DT_RESERVED_16 = 0x16,
> +	DSI_SP_DT_RESERVED_17 = 0x17,
> +	DSI_SP_DT_RESERVED_18 = 0x18,
> +	DSI_LP_DT_BLANK = 0x19,
> +	DSI_LP_DT_RESERVED_1A = 0x1a,
> +	DSI_LP_DT_RESERVED_1B = 0x1b,
> +	DSI_LP_DT_PPS_YCBCR422_24B = 0x1c,
> +	DSI_LP_DT_PPS_RGB121212_36B = 0x1d,
> +	DSI_LP_DT_PPS_RGB666_18B = 0x1e,
> +	DSI_LP_DT_RESERVED_1F = 0x1f,
> +
> +	DSI_SP_DT_RESERVED_20 = 0x20,
> +	DSI_SP_DT_HSYNC_START = 0x21,
> +	DSI_SP_DT_SHUT_DOWN_PERIPH_CMD = 0x22,
> +	DSI_SP_DT_GENERIC_SHORT_WR_2PAR = 0x23,
> +	DSI_SP_DT_GENERIC_RD_2PAR = 0x24,
> +	DSI_SP_DT_RESERVED_25 = 0x25,
> +	DSI_SP_DT_RESERVED_26 = 0x26,
> +	DSI_SP_DT_RESERVED_27 = 0x27,
> +	DSI_SP_DT_RESERVED_28 = 0x28,
> +	DSI_LP_DT_GENERIC_LONG_WR = 0x29,
> +	DSI_LP_DT_RESERVED_2A = 0x2a,
> +	DSI_LP_DT_RESERVED_2B = 0x2b,
> +	DSI_LP_DT_PPS_YCBCR422_16B = 0x2c,
> +	DSI_LP_DT_RESERVED_2D = 0x2d,
> +	DSI_LP_DT_LPPS_RGB666_18B = 0x2e,
> +	DSI_LP_DT_RESERVED_2F = 0x2f,
> +
> +	DSI_SP_DT_RESERVED_30 = 0x30,
> +	DSI_SP_DT_HSYNC_END = 0x31,
> +	DSI_SP_DT_TURN_ON_PERIPH_CMD = 0x32,
> +	DSI_SP_DT_RESERVED_33 = 0x33,
> +	DSI_SP_DT_RESERVED_34 = 0x34,
> +	DSI_SP_DT_RESERVED_35 = 0x35,
> +	DSI_SP_DT_RESERVED_36 = 0x36,
> +	DSI_SP_DT_SET_MAX_RETURN_PKT_SIZE = 0x37,
> +	DSI_SP_DT_RESERVED_38 = 0x38,
> +	DSI_LP_DT_DSC_LONG_WR = 0x39,
> +	DSI_LP_DT_RESERVED_3A = 0x3a,
> +	DSI_LP_DT_RESERVED_3B = 0x3b,
> +	DSI_LP_DT_RESERVED_3C = 0x3c,
> +	DSI_LP_DT_PPS_YCBCR420_12B = 0x3d,
> +	DSI_LP_DT_PPS_RGB888_24B = 0x3e,
> +	DSI_LP_DT_RESERVED_3F = 0x3f
> +};
> +
> +enum mipi_tx_hs_tp_sel {
> +	MIPI_TX_HS_TP_WHOLE_FRAME_COLOR0 = 0,
> +	MIPI_TX_HS_TP_WHOLE_FRAME_COLOR1,
> +	MIPI_TX_HS_TP_V_STRIPES,
> +	MIPI_TX_HS_TP_H_STRIPES,
> +};
> +
> +enum dphy_mode {
> +	MIPI_DPHY_SLAVE = 0,
> +	MIPI_DPHY_MASTER
> +};
> +
> +enum dphy_tx_fsm {
> +	DPHY_TX_POWERDWN = 0,
> +	DPHY_TX_BGPON,
> +	DPHY_TX_TERMCAL,
> +	DPHY_TX_TERMCALUP,
> +	DPHY_TX_OFFSETCAL,
> +	DPHY_TX_LOCK,
> +	DPHY_TX_SRCAL,
> +	DPHY_TX_IDLE,
> +	DPHY_TX_ULP,
> +	DPHY_TX_LANESTART,
> +	DPHY_TX_CLKALIGN,
> +	DPHY_TX_DDLTUNNING,
> +	DPHY_TX_ULP_FORCE_PLL,
> +	DPHY_TX_LOCK_LOSS
> +};
> +
> +struct mipi_data_type_params {
> +	u8 size_constraint_pixels;
> +	u8 size_constraint_bytes;
> +	u8 pixels_per_pclk;
> +	u8 bits_per_pclk;
> +};
> +
> +struct mipi_tx_dsi_cfg {
> +	u8 hfp_blank_en;	/*horizontal front porch blanking enable */
> +	u8 eotp_en;	/*End of transmission packet enable */
> +	/*last vertical front porch blanking mode */
> +	u8 lpm_last_vfp_line;
> +	/*first vertical sync active blanking mode */
> +	u8 lpm_first_vsa_line;
> +	u8 sync_pulse_eventn;	/*sync type */
> +	u8 hfp_blanking;	/*horizontal front porch blanking mode */
> +	u8 hbp_blanking;	/*horizontal back porch blanking mode */
> +	u8 hsa_blanking;	/*horizontal sync active blanking mode */
> +	u8 v_blanking;	/*vertical timing blanking mode */
> +};
> +
> +struct mipi_tx_frame_section_cfg {
> +	u32 dma_v_stride;
> +	u16 dma_v_scale_cfg;
> +	u16 width_pixels;
> +	u16 height_lines;
> +	u8 dma_packed;
> +	u8 bpp;
> +	u8 bpp_unpacked;
> +	u8 dma_h_stride;
> +	u8 data_type;
> +	u8 data_mode;
> +	u8 dma_flip_rotate_sel;
> +};
> +
> +struct mipi_tx_frame_timing_cfg {
> +	u32 bpp;
> +	u32 lane_rate_mbps;
> +	u32 hsync_width;
> +	u32 h_backporch;
> +	u32 h_frontporch;
> +	u32 h_active;
> +	u16 vsync_width;
> +	u16 v_backporch;
> +	u16 v_frontporch;
> +	u16 v_active;
> +	u8 active_lanes;
> +};
> +
> +struct mipi_tx_frame_sect_phcfg {
> +	u32 wc;
> +	enum mipi_data_mode data_mode;
> +	enum mipi_dsi_data_type data_type;
> +	u8 vchannel;
> +	u8 dma_packed;
> +};
> +
> +struct mipi_tx_frame_cfg {
> +	struct mipi_tx_frame_section_cfg *sections[MIPI_TX_FRAME_GEN_SECTIONS];
> +	u32 hsync_width;	/*in pixels */
> +	u32 h_backporch;	/*in pixels */
> +	u32 h_frontporch;	/*in pixels */
> +	u16 vsync_width;	/*in lines */
> +	u16 v_backporch;	/*in lines */
> +	u16 v_frontporch;	/*in lines */
> +};
> +
> +struct mipi_tx_ctrl_cfg {
> +	struct mipi_tx_frame_cfg *frames[MIPI_TX_FRAME_GEN];
> +	struct mipi_tx_dsi_cfg *tx_dsi_cfg;
> +	u8 line_sync_pkt_en;
> +	u8 line_counter_active;
> +	u8 frame_counter_active;
> +	u8 tx_hsclkkidle_cnt;
> +	u8 tx_hsexit_cnt;
> +	u8 tx_crc_en;
> +	u8 tx_hact_wait_stop;
> +	u8 tx_always_use_hact;
> +	u8 tx_wait_trig;
> +	u8 tx_wait_all_sect;
> +};
> +
> +/*configuration structure for MIPI control */
> +struct mipi_ctrl_cfg {
> +	u8 active_lanes;	/* # active lanes per controller 2/4 */
> +	u32 lane_rate_mbps;	/*MBPS */
> +	u32 ref_clk_khz;
> +	u32 cfg_clk_khz;
> +	struct mipi_tx_ctrl_cfg tx_ctrl_cfg;
> +};
> +
> +struct drm_bridge *kmb_dsi_host_bridge_init(struct device *dev);
> +int kmb_dsi_init(struct drm_device *dev, struct drm_bridge *bridge);
> +void kmb_dsi_host_unregister(void);
> +int kmb_dsi_hw_init(struct drm_device *dev, struct drm_display_mode *mode);
> +#endif /* __KMB_DSI_H__ */
> -- 
> 2.7.4
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch


More information about the dri-devel mailing list