diff options
Diffstat (limited to 'drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c')
| -rw-r--r-- | drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c | 688 | 
1 files changed, 688 insertions, 0 deletions
diff --git a/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c b/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c new file mode 100644 index 00000000000..87885d8c06e --- /dev/null +++ b/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c @@ -0,0 +1,688 @@ +/* + * Copyright © 2010 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Jackie Li<yaodong.li@intel.com> + */ + +#include <linux/freezer.h> + +#include "mdfld_dsi_output.h" +#include "mdfld_dsi_pkg_sender.h" +#include "mdfld_dsi_dpi.h" + +#define MDFLD_DSI_READ_MAX_COUNT		5000 + +enum data_type { +	DSI_DT_GENERIC_SHORT_WRITE_0	= 0x03, +	DSI_DT_GENERIC_SHORT_WRITE_1	= 0x13, +	DSI_DT_GENERIC_SHORT_WRITE_2	= 0x23, +	DSI_DT_GENERIC_READ_0		= 0x04, +	DSI_DT_GENERIC_READ_1		= 0x14, +	DSI_DT_GENERIC_READ_2		= 0x24, +	DSI_DT_GENERIC_LONG_WRITE	= 0x29, +	DSI_DT_DCS_SHORT_WRITE_0	= 0x05, +	DSI_DT_DCS_SHORT_WRITE_1	= 0x15, +	DSI_DT_DCS_READ			= 0x06, +	DSI_DT_DCS_LONG_WRITE		= 0x39, +}; + +enum { +	MDFLD_DSI_PANEL_MODE_SLEEP = 0x1, +}; + +enum { +	MDFLD_DSI_PKG_SENDER_FREE = 0x0, +	MDFLD_DSI_PKG_SENDER_BUSY = 0x1, +}; + +static const char *const dsi_errors[] = { +	"RX SOT Error", +	"RX SOT Sync Error", +	"RX EOT Sync Error", +	"RX Escape Mode Entry Error", +	"RX LP TX Sync Error", +	"RX HS Receive Timeout Error", +	"RX False Control Error", +	"RX ECC Single Bit Error", +	"RX ECC Multibit Error", +	"RX Checksum Error", +	"RX DSI Data Type Not Recognised", +	"RX DSI VC ID Invalid", +	"TX False Control Error", +	"TX ECC Single Bit Error", +	"TX ECC Multibit Error", +	"TX Checksum Error", +	"TX DSI Data Type Not Recognised", +	"TX DSI VC ID invalid", +	"High Contention", +	"Low contention", +	"DPI FIFO Under run", +	"HS TX Timeout", +	"LP RX Timeout", +	"Turn Around ACK Timeout", +	"ACK With No Error", +	"RX Invalid TX Length", +	"RX Prot Violation", +	"HS Generic Write FIFO Full", +	"LP Generic Write FIFO Full", +	"Generic Read Data Avail" +	"Special Packet Sent", +	"Tearing Effect", +}; + +static inline int wait_for_gen_fifo_empty(struct mdfld_dsi_pkg_sender *sender, +						u32 mask) +{ +	struct drm_device *dev = sender->dev; +	u32 gen_fifo_stat_reg = sender->mipi_gen_fifo_stat_reg; +	int retry = 0xffff; + +	while (retry--) { +		if ((mask & REG_READ(gen_fifo_stat_reg)) == mask) +			return 0; +		udelay(100); +	} +	DRM_ERROR("fifo is NOT empty 0x%08x\n", REG_READ(gen_fifo_stat_reg)); +	return -EIO; +} + +static int wait_for_all_fifos_empty(struct mdfld_dsi_pkg_sender *sender) +{ +	return wait_for_gen_fifo_empty(sender, (BIT(2) | BIT(10) | BIT(18) | +						BIT(26) | BIT(27) | BIT(28))); +} + +static int wait_for_lp_fifos_empty(struct mdfld_dsi_pkg_sender *sender) +{ +	return wait_for_gen_fifo_empty(sender, (BIT(10) | BIT(26))); +} + +static int wait_for_hs_fifos_empty(struct mdfld_dsi_pkg_sender *sender) +{ +	return wait_for_gen_fifo_empty(sender, (BIT(2) | BIT(18))); +} + +static int handle_dsi_error(struct mdfld_dsi_pkg_sender *sender, u32 mask) +{ +	u32 intr_stat_reg = sender->mipi_intr_stat_reg; +	struct drm_device *dev = sender->dev; + +	dev_dbg(sender->dev->dev, "Handling error 0x%08x\n", mask); + +	switch (mask) { +	case BIT(0): +	case BIT(1): +	case BIT(2): +	case BIT(3): +	case BIT(4): +	case BIT(5): +	case BIT(6): +	case BIT(7): +	case BIT(8): +	case BIT(9): +	case BIT(10): +	case BIT(11): +	case BIT(12): +	case BIT(13): +		dev_dbg(sender->dev->dev, "No Action required\n"); +		break; +	case BIT(14): +		/*wait for all fifo empty*/ +		/*wait_for_all_fifos_empty(sender)*/ +		break; +	case BIT(15): +		dev_dbg(sender->dev->dev, "No Action required\n"); +		break; +	case BIT(16): +		break; +	case BIT(17): +		break; +	case BIT(18): +	case BIT(19): +		dev_dbg(sender->dev->dev, "High/Low contention detected\n"); +		/*wait for contention recovery time*/ +		/*mdelay(10);*/ +		/*wait for all fifo empty*/ +		if (0) +			wait_for_all_fifos_empty(sender); +		break; +	case BIT(20): +		dev_dbg(sender->dev->dev, "No Action required\n"); +		break; +	case BIT(21): +		/*wait for all fifo empty*/ +		/*wait_for_all_fifos_empty(sender);*/ +		break; +	case BIT(22): +		break; +	case BIT(23): +	case BIT(24): +	case BIT(25): +	case BIT(26): +	case BIT(27): +		dev_dbg(sender->dev->dev, "HS Gen fifo full\n"); +		REG_WRITE(intr_stat_reg, mask); +		wait_for_hs_fifos_empty(sender); +		break; +	case BIT(28): +		dev_dbg(sender->dev->dev, "LP Gen fifo full\n"); +		REG_WRITE(intr_stat_reg, mask); +		wait_for_lp_fifos_empty(sender); +		break; +	case BIT(29): +	case BIT(30): +	case BIT(31): +		dev_dbg(sender->dev->dev, "No Action required\n"); +		break; +	} + +	if (mask & REG_READ(intr_stat_reg)) +		dev_dbg(sender->dev->dev, +				"Cannot clean interrupt 0x%08x\n", mask); +	return 0; +} + +static int dsi_error_handler(struct mdfld_dsi_pkg_sender *sender) +{ +	struct drm_device *dev = sender->dev; +	u32 intr_stat_reg = sender->mipi_intr_stat_reg; +	u32 mask; +	u32 intr_stat; +	int i; +	int err = 0; + +	intr_stat = REG_READ(intr_stat_reg); + +	for (i = 0; i < 32; i++) { +		mask = (0x00000001UL) << i; +		if (intr_stat & mask) { +			dev_dbg(sender->dev->dev, "[DSI]: %s\n", dsi_errors[i]); +			err = handle_dsi_error(sender, mask); +			if (err) +				DRM_ERROR("Cannot handle error\n"); +		} +	} +	return err; +} + +static int send_short_pkg(struct mdfld_dsi_pkg_sender *sender, u8 data_type, +			u8 cmd, u8 param, bool hs) +{ +	struct drm_device *dev = sender->dev; +	u32 ctrl_reg; +	u32 val; +	u8 virtual_channel = 0; + +	if (hs) { +		ctrl_reg = sender->mipi_hs_gen_ctrl_reg; + +		/* FIXME: wait_for_hs_fifos_empty(sender); */ +	} else { +		ctrl_reg = sender->mipi_lp_gen_ctrl_reg; + +		/* FIXME: wait_for_lp_fifos_empty(sender); */ +	} + +	val = FLD_VAL(param, 23, 16) | FLD_VAL(cmd, 15, 8) | +		FLD_VAL(virtual_channel, 7, 6) | FLD_VAL(data_type, 5, 0); + +	REG_WRITE(ctrl_reg, val); + +	return 0; +} + +static int send_long_pkg(struct mdfld_dsi_pkg_sender *sender, u8 data_type, +			u8 *data, int len, bool hs) +{ +	struct drm_device *dev = sender->dev; +	u32 ctrl_reg; +	u32 data_reg; +	u32 val; +	u8 *p; +	u8 b1, b2, b3, b4; +	u8 virtual_channel = 0; +	int i; + +	if (hs) { +		ctrl_reg = sender->mipi_hs_gen_ctrl_reg; +		data_reg = sender->mipi_hs_gen_data_reg; + +		/* FIXME: wait_for_hs_fifos_empty(sender); */ +	} else { +		ctrl_reg = sender->mipi_lp_gen_ctrl_reg; +		data_reg = sender->mipi_lp_gen_data_reg; + +		/* FIXME: wait_for_lp_fifos_empty(sender); */ +	} + +	p = data; +	for (i = 0; i < len / 4; i++) { +		b1 = *p++; +		b2 = *p++; +		b3 = *p++; +		b4 = *p++; + +		REG_WRITE(data_reg, b4 << 24 | b3 << 16 | b2 << 8 | b1); +	} + +	i = len % 4; +	if (i) { +		b1 = 0; b2 = 0; b3 = 0; + +		switch (i) { +		case 3: +			b1 = *p++; +			b2 = *p++; +			b3 = *p++; +			break; +		case 2: +			b1 = *p++; +			b2 = *p++; +			break; +		case 1: +			b1 = *p++; +			break; +		} + +		REG_WRITE(data_reg, b3 << 16 | b2 << 8 | b1); +	} + +	val = FLD_VAL(len, 23, 8) | FLD_VAL(virtual_channel, 7, 6) | +		FLD_VAL(data_type, 5, 0); + +	REG_WRITE(ctrl_reg, val); + +	return 0; +} + +static int send_pkg_prepare(struct mdfld_dsi_pkg_sender *sender, u8 data_type, +			u8 *data, u16 len) +{ +	u8 cmd; + +	switch (data_type) { +	case DSI_DT_DCS_SHORT_WRITE_0: +	case DSI_DT_DCS_SHORT_WRITE_1: +	case DSI_DT_DCS_LONG_WRITE: +		cmd = *data; +		break; +	default: +		return 0; +	} + +	/*this prevents other package sending while doing msleep*/ +	sender->status = MDFLD_DSI_PKG_SENDER_BUSY; + +	/*wait for 120 milliseconds in case exit_sleep_mode just be sent*/ +	if (unlikely(cmd == DCS_ENTER_SLEEP_MODE)) { +		/*TODO: replace it with msleep later*/ +		mdelay(120); +	} + +	if (unlikely(cmd == DCS_EXIT_SLEEP_MODE)) { +		/*TODO: replace it with msleep later*/ +		mdelay(120); +	} +	return 0; +} + +static int send_pkg_done(struct mdfld_dsi_pkg_sender *sender, u8 data_type, +			u8 *data, u16 len) +{ +	u8 cmd; + +	switch (data_type) { +	case DSI_DT_DCS_SHORT_WRITE_0: +	case DSI_DT_DCS_SHORT_WRITE_1: +	case DSI_DT_DCS_LONG_WRITE: +		cmd = *data; +		break; +	default: +		return 0; +	} + +	/*update panel status*/ +	if (unlikely(cmd == DCS_ENTER_SLEEP_MODE)) { +		sender->panel_mode |= MDFLD_DSI_PANEL_MODE_SLEEP; +		/*TODO: replace it with msleep later*/ +		mdelay(120); +	} else if (unlikely(cmd == DCS_EXIT_SLEEP_MODE)) { +		sender->panel_mode &= ~MDFLD_DSI_PANEL_MODE_SLEEP; +		/*TODO: replace it with msleep later*/ +		mdelay(120); +	} else if (unlikely(cmd == DCS_SOFT_RESET)) { +		/*TODO: replace it with msleep later*/ +		mdelay(5); +	} + +	sender->status = MDFLD_DSI_PKG_SENDER_FREE; + +	return 0; +} + +static int send_pkg(struct mdfld_dsi_pkg_sender *sender, u8 data_type, +		u8 *data, u16 len, bool hs) +{ +	int ret; + +	/*handle DSI error*/ +	ret = dsi_error_handler(sender); +	if (ret) { +		DRM_ERROR("Error handling failed\n"); +		return -EAGAIN; +	} + +	/* send pkg */ +	if (sender->status == MDFLD_DSI_PKG_SENDER_BUSY) { +		DRM_ERROR("sender is busy\n"); +		return -EAGAIN; +	} + +	ret = send_pkg_prepare(sender, data_type, data, len); +	if (ret) { +		DRM_ERROR("send_pkg_prepare error\n"); +		return ret; +	} + +	switch (data_type) { +	case DSI_DT_GENERIC_SHORT_WRITE_0: +	case DSI_DT_GENERIC_SHORT_WRITE_1: +	case DSI_DT_GENERIC_SHORT_WRITE_2: +	case DSI_DT_GENERIC_READ_0: +	case DSI_DT_GENERIC_READ_1: +	case DSI_DT_GENERIC_READ_2: +	case DSI_DT_DCS_SHORT_WRITE_0: +	case DSI_DT_DCS_SHORT_WRITE_1: +	case DSI_DT_DCS_READ: +		ret = send_short_pkg(sender, data_type, data[0], data[1], hs); +		break; +	case DSI_DT_GENERIC_LONG_WRITE: +	case DSI_DT_DCS_LONG_WRITE: +		ret = send_long_pkg(sender, data_type, data, len, hs); +		break; +	} + +	send_pkg_done(sender, data_type, data, len); + +	/*FIXME: should I query complete and fifo empty here?*/ + +	return ret; +} + +int mdfld_dsi_send_mcs_long(struct mdfld_dsi_pkg_sender *sender, u8 *data, +			u32 len, bool hs) +{ +	unsigned long flags; + +	if (!sender || !data || !len) { +		DRM_ERROR("Invalid parameters\n"); +		return -EINVAL; +	} + +	spin_lock_irqsave(&sender->lock, flags); +	send_pkg(sender, DSI_DT_DCS_LONG_WRITE, data, len, hs); +	spin_unlock_irqrestore(&sender->lock, flags); + +	return 0; +} + +int mdfld_dsi_send_mcs_short(struct mdfld_dsi_pkg_sender *sender, u8 cmd, +			u8 param, u8 param_num, bool hs) +{ +	u8 data[2]; +	unsigned long flags; +	u8 data_type; + +	if (!sender) { +		DRM_ERROR("Invalid parameter\n"); +		return -EINVAL; +	} + +	data[0] = cmd; + +	if (param_num) { +		data_type = DSI_DT_DCS_SHORT_WRITE_1; +		data[1] = param; +	} else { +		data_type = DSI_DT_DCS_SHORT_WRITE_0; +		data[1] = 0; +	} + +	spin_lock_irqsave(&sender->lock, flags); +	send_pkg(sender, data_type, data, sizeof(data), hs); +	spin_unlock_irqrestore(&sender->lock, flags); + +	return 0; +} + +int mdfld_dsi_send_gen_short(struct mdfld_dsi_pkg_sender *sender, u8 param0, +			u8 param1, u8 param_num, bool hs) +{ +	u8 data[2]; +	unsigned long flags; +	u8 data_type; + +	if (!sender || param_num > 2) { +		DRM_ERROR("Invalid parameter\n"); +		return -EINVAL; +	} + +	switch (param_num) { +	case 0: +		data_type = DSI_DT_GENERIC_SHORT_WRITE_0; +		data[0] = 0; +		data[1] = 0; +		break; +	case 1: +		data_type = DSI_DT_GENERIC_SHORT_WRITE_1; +		data[0] = param0; +		data[1] = 0; +		break; +	case 2: +		data_type = DSI_DT_GENERIC_SHORT_WRITE_2; +		data[0] = param0; +		data[1] = param1; +		break; +	} + +	spin_lock_irqsave(&sender->lock, flags); +	send_pkg(sender, data_type, data, sizeof(data), hs); +	spin_unlock_irqrestore(&sender->lock, flags); + +	return 0; +} + +int mdfld_dsi_send_gen_long(struct mdfld_dsi_pkg_sender *sender, u8 *data, +			u32 len, bool hs) +{ +	unsigned long flags; + +	if (!sender || !data || !len) { +		DRM_ERROR("Invalid parameters\n"); +		return -EINVAL; +	} + +	spin_lock_irqsave(&sender->lock, flags); +	send_pkg(sender, DSI_DT_GENERIC_LONG_WRITE, data, len, hs); +	spin_unlock_irqrestore(&sender->lock, flags); + +	return 0; +} + +static int __read_panel_data(struct mdfld_dsi_pkg_sender *sender, u8 data_type, +			u8 *data, u16 len, u32 *data_out, u16 len_out, bool hs) +{ +	unsigned long flags; +	struct drm_device *dev = sender->dev; +	int i; +	u32 gen_data_reg; +	int retry = MDFLD_DSI_READ_MAX_COUNT; + +	if (!sender || !data_out || !len_out) { +		DRM_ERROR("Invalid parameters\n"); +		return -EINVAL; +	} + +	/** +	 * do reading. +	 * 0) send out generic read request +	 * 1) polling read data avail interrupt +	 * 2) read data +	 */ +	spin_lock_irqsave(&sender->lock, flags); + +	REG_WRITE(sender->mipi_intr_stat_reg, BIT(29)); + +	if ((REG_READ(sender->mipi_intr_stat_reg) & BIT(29))) +		DRM_ERROR("Can NOT clean read data valid interrupt\n"); + +	/*send out read request*/ +	send_pkg(sender, data_type, data, len, hs); + +	/*polling read data avail interrupt*/ +	while (retry && !(REG_READ(sender->mipi_intr_stat_reg) & BIT(29))) { +		udelay(100); +		retry--; +	} + +	if (!retry) { +		spin_unlock_irqrestore(&sender->lock, flags); +		return -ETIMEDOUT; +	} + +	REG_WRITE(sender->mipi_intr_stat_reg, BIT(29)); + +	/*read data*/ +	if (hs) +		gen_data_reg = sender->mipi_hs_gen_data_reg; +	else +		gen_data_reg = sender->mipi_lp_gen_data_reg; + +	for (i = 0; i < len_out; i++) +		*(data_out + i) = REG_READ(gen_data_reg); + +	spin_unlock_irqrestore(&sender->lock, flags); + +	return 0; +} + +int mdfld_dsi_read_mcs(struct mdfld_dsi_pkg_sender *sender, u8 cmd, +		u32 *data, u16 len, bool hs) +{ +	if (!sender || !data || !len) { +		DRM_ERROR("Invalid parameters\n"); +		return -EINVAL; +	} + +	return __read_panel_data(sender, DSI_DT_DCS_READ, &cmd, 1, +				data, len, hs); +} + +int mdfld_dsi_pkg_sender_init(struct mdfld_dsi_connector *dsi_connector, +								int pipe) +{ +	struct mdfld_dsi_pkg_sender *pkg_sender; +	struct mdfld_dsi_config *dsi_config = +				mdfld_dsi_get_config(dsi_connector); +	struct drm_device *dev = dsi_config->dev; +	struct drm_psb_private *dev_priv = dev->dev_private; +	const struct psb_offset *map = &dev_priv->regmap[pipe]; +	u32 mipi_val = 0; + +	if (!dsi_connector) { +		DRM_ERROR("Invalid parameter\n"); +		return -EINVAL; +	} + +	pkg_sender = dsi_connector->pkg_sender; + +	if (!pkg_sender || IS_ERR(pkg_sender)) { +		pkg_sender = kzalloc(sizeof(struct mdfld_dsi_pkg_sender), +								GFP_KERNEL); +		if (!pkg_sender) { +			DRM_ERROR("Create DSI pkg sender failed\n"); +			return -ENOMEM; +		} +		dsi_connector->pkg_sender = (void *)pkg_sender; +	} + +	pkg_sender->dev = dev; +	pkg_sender->dsi_connector = dsi_connector; +	pkg_sender->pipe = pipe; +	pkg_sender->pkg_num = 0; +	pkg_sender->panel_mode = 0; +	pkg_sender->status = MDFLD_DSI_PKG_SENDER_FREE; + +	/*init regs*/ +	/* FIXME: should just copy the regmap ptr ? */ +	pkg_sender->dpll_reg = map->dpll; +	pkg_sender->dspcntr_reg = map->cntr; +	pkg_sender->pipeconf_reg = map->conf; +	pkg_sender->dsplinoff_reg = map->linoff; +	pkg_sender->dspsurf_reg = map->surf; +	pkg_sender->pipestat_reg = map->status; + +	pkg_sender->mipi_intr_stat_reg = MIPI_INTR_STAT_REG(pipe); +	pkg_sender->mipi_lp_gen_data_reg = MIPI_LP_GEN_DATA_REG(pipe); +	pkg_sender->mipi_hs_gen_data_reg = MIPI_HS_GEN_DATA_REG(pipe); +	pkg_sender->mipi_lp_gen_ctrl_reg = MIPI_LP_GEN_CTRL_REG(pipe); +	pkg_sender->mipi_hs_gen_ctrl_reg = MIPI_HS_GEN_CTRL_REG(pipe); +	pkg_sender->mipi_gen_fifo_stat_reg = MIPI_GEN_FIFO_STAT_REG(pipe); +	pkg_sender->mipi_data_addr_reg = MIPI_DATA_ADD_REG(pipe); +	pkg_sender->mipi_data_len_reg = MIPI_DATA_LEN_REG(pipe); +	pkg_sender->mipi_cmd_addr_reg = MIPI_CMD_ADD_REG(pipe); +	pkg_sender->mipi_cmd_len_reg = MIPI_CMD_LEN_REG(pipe); + +	/*init lock*/ +	spin_lock_init(&pkg_sender->lock); + +	if (mdfld_get_panel_type(dev, pipe) != TC35876X) { +		/** +		 * For video mode, don't enable DPI timing output here, +		 * will init the DPI timing output during mode setting. +		 */ +		mipi_val = PASS_FROM_SPHY_TO_AFE | SEL_FLOPPED_HSTX; + +		if (pipe == 0) +			mipi_val |= 0x2; + +		REG_WRITE(MIPI_PORT_CONTROL(pipe), mipi_val); +		REG_READ(MIPI_PORT_CONTROL(pipe)); + +		/* do dsi controller init */ +		mdfld_dsi_controller_init(dsi_config, pipe); +	} + +	return 0; +} + +void mdfld_dsi_pkg_sender_destroy(struct mdfld_dsi_pkg_sender *sender) +{ +	if (!sender || IS_ERR(sender)) +		return; + +	/*free*/ +	kfree(sender); +} + +  | 
