Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 65ded36c authored by Ken Zhang's avatar Ken Zhang
Browse files

msm9x35: display: qpic: enable interrupt for sending command



Enable interrupt usage and use it by default. Redefine
command data structure to reduce call stack. SW mode
and panel read function have been added for debugging.

Change-Id: I5eb9e5ad2df8e1cd8af868f5aa9ededafb3307f1
Signed-off-by: default avatarKen Zhang <kenz@codeaurora.org>
parent 0af4da69
Loading
Loading
Loading
Loading
+177 −69
Original line number Diff line number Diff line
@@ -40,14 +40,14 @@

static int mdss_qpic_probe(struct platform_device *pdev);
static int mdss_qpic_remove(struct platform_device *pdev);
static void qpic_interrupt_en(u32 en);

struct qpic_data_type *qpic_res;

/* for tuning */
/* for debugging */
static u32 use_bam = true;
static u32 use_irq;
static u32 use_irq = true;
static u32 use_vsync;
static u32 bus_handle;

static const struct of_device_id mdss_qpic_dt_match[] = {
	{ .compatible = "qcom,mdss_qpic",},
@@ -79,6 +79,8 @@ int qpic_off(struct msm_fb_data_type *mfd)
{
	int ret;
	ret = mdss_qpic_panel_off(qpic_res->panel_data, &qpic_res->panel_io);
	if (use_irq)
		qpic_interrupt_en(false);
	return ret;
}

@@ -86,13 +88,13 @@ static int msm_qpic_bus_set_vote(u32 vote)
{
	int ret;

	if (!bus_handle)
	if (!qpic_res->bus_handle)
		return 0;
	ret = msm_bus_scale_client_update_request(bus_handle,
	ret = msm_bus_scale_client_update_request(qpic_res->bus_handle,
			vote);
	if (ret)
		pr_err("msm_bus_scale_client_update_request() failed, bus_handle=0x%x, vote=%d, err=%d\n",
			bus_handle, vote, ret);
			qpic_res->bus_handle, vote, ret);
	return ret;
}

@@ -119,13 +121,16 @@ static void mdss_qpic_pan_display(struct msm_fb_data_type *mfd)
		       offset, fbi->fix.smem_len);
		return;
	}
	if (use_bam)
		fb_offset = (u32)fbi->fix.smem_start + offset;
	else
		fb_offset = (u32)mfd->fbi->screen_base + offset;

	msm_qpic_bus_set_vote(1);
	mdss_qpic_panel_on(qpic_res->panel_data, &qpic_res->panel_io);
	size = fbi->var.xres * fbi->var.yres * bpp;

	qpic_send_frame(0, 0, fbi->var.xres, fbi->var.yres,
	qpic_send_frame(0, 0, fbi->var.xres - 1, fbi->var.yres - 1,
		(u32 *)fb_offset, size);
	msm_qpic_bus_set_vote(0);
}
@@ -328,7 +333,8 @@ void mdss_qpic_reset(void)
	QPIC_OUTP(QPIC_REG_QPIC_LCDC_RESET, 1 << 0);
	/* wait 100 us after reset as suggested by hw */
	usleep(100);
	time_end = (u32)ktime_to_ms(ktime_get()) + QPIC_MAX_VSYNC_WAIT_TIME;
	time_end = (u32)ktime_to_ms(ktime_get()) +
		QPIC_MAX_VSYNC_WAIT_TIME;
	while (((QPIC_INP(QPIC_REG_QPIC_LCDC_STTS) & (1 << 8)) == 0)) {
		if ((u32)ktime_to_ms(ktime_get()) > time_end) {
			pr_err("%s reset not finished", __func__);
@@ -339,15 +345,14 @@ void mdss_qpic_reset(void)
	}
}

void qpic_interrupt_en(u32 en)
static void qpic_interrupt_en(u32 en)
{
	QPIC_OUTP(QPIC_REG_QPIC_LCDC_IRQ_CLR, 0xff);
	if (en) {
		if (!qpic_res->irq_ena) {
			init_completion(&qpic_res->fifo_eof_comp);
			qpic_res->irq_ena = true;
			enable_irq(qpic_res->irq);
			QPIC_OUTP(QPIC_REG_QPIC_LCDC_IRQ_EN,
				(1 << 0) | (1 << 2));
		}
	} else {
		QPIC_OUTP(QPIC_REG_QPIC_LCDC_IRQ_EN, 0);
@@ -361,20 +366,25 @@ static irqreturn_t qpic_irq_handler(int irq, void *ptr)
	u32 data;
	data = QPIC_INP(QPIC_REG_QPIC_LCDC_IRQ_STTS);
	QPIC_OUTP(QPIC_REG_QPIC_LCDC_IRQ_CLR, 0xff);
	return 0;
	QPIC_OUTP(QPIC_REG_QPIC_LCDC_IRQ_EN, 0);

	if (data & ((1 << 2) | (1 << 4)))
		complete(&qpic_res->fifo_eof_comp);
	return IRQ_HANDLED;
}

int qpic_flush_buffer_bam(u32 cmd, u32 len, u32 *param, u32 is_cmd)
static int qpic_send_pkt_bam(u32 cmd, u32 len, u8 *param)
{
	int  ret = 0;
	u32 phys_addr, cfg2, block_len , flags;
	if (is_cmd) {

	if ((cmd != OP_WRITE_MEMORY_START) &&
		(cmd != OP_WRITE_MEMORY_CONTINUE)) {
		memcpy((u8 *)qpic_res->cmd_buf_virt, param, len);
		phys_addr = qpic_res->cmd_buf_phys;
	} else {
		phys_addr = (u32)param;
	}

	cfg2 = QPIC_INP(QPIC_REG_QPIC_LCDC_CFG2);
	cfg2 &= ~0xFF;
	cfg2 |= cmd;
@@ -405,55 +415,155 @@ int qpic_flush_buffer_bam(u32 cmd, u32 len, u32 *param, u32 is_cmd)
	return ret;
}

int qpic_flush_buffer_sw(u32 cmd, u32 len, u32 *param, u32 is_cmd)
void qpic_dump_reg(void)
{
	pr_info("%s\n", __func__);
	pr_info("QPIC_REG_QPIC_LCDC_CTRL = %x\n",
		QPIC_INP(QPIC_REG_QPIC_LCDC_CTRL));
	pr_info("QPIC_REG_QPIC_LCDC_CMD_DATA_CYCLE_CNT = %x\n",
		QPIC_INP(QPIC_REG_QPIC_LCDC_CMD_DATA_CYCLE_CNT));
	pr_info("QPIC_REG_QPIC_LCDC_CFG0 = %x\n",
		QPIC_INP(QPIC_REG_QPIC_LCDC_CFG0));
	pr_info("QPIC_REG_QPIC_LCDC_CFG1 = %x\n",
		QPIC_INP(QPIC_REG_QPIC_LCDC_CFG1));
	pr_info("QPIC_REG_QPIC_LCDC_CFG2 = %x\n",
		QPIC_INP(QPIC_REG_QPIC_LCDC_CFG2));
	pr_info("QPIC_REG_QPIC_LCDC_IRQ_EN = %x\n",
		QPIC_INP(QPIC_REG_QPIC_LCDC_IRQ_EN));
	pr_info("QPIC_REG_QPIC_LCDC_IRQ_STTS = %x\n",
		QPIC_INP(QPIC_REG_QPIC_LCDC_IRQ_STTS));
	pr_info("QPIC_REG_QPIC_LCDC_STTS = %x\n",
		QPIC_INP(QPIC_REG_QPIC_LCDC_STTS));
	pr_info("QPIC_REG_QPIC_LCDC_FIFO_SOF = %x\n",
		QPIC_INP(QPIC_REG_QPIC_LCDC_FIFO_SOF));
}

static int qpic_wait_for_fifo(void)
{
	u32 data, time_end;
	int ret = 0;

	if (use_irq) {
		data = QPIC_INP(QPIC_REG_QPIC_LCDC_STTS);
		data &= 0x3F;
		if (data == 0)
			return ret;
		INIT_COMPLETION(qpic_res->fifo_eof_comp);
		QPIC_OUTP(QPIC_REG_QPIC_LCDC_IRQ_EN, (1 << 4));
		ret = wait_for_completion_timeout(&qpic_res->fifo_eof_comp,
				msecs_to_jiffies(QPIC_MAX_VSYNC_WAIT_TIME));
		if (ret > 0) {
			ret = 0;
		} else {
			pr_err("%s timeout %x\n", __func__, ret);
			ret = -ETIMEDOUT;
		}
		QPIC_OUTP(QPIC_REG_QPIC_LCDC_IRQ_EN, 0);
	} else {
		time_end = (u32)ktime_to_ms(ktime_get()) +
			QPIC_MAX_VSYNC_WAIT_TIME;
		while (1) {
			data = QPIC_INP(QPIC_REG_QPIC_LCDC_STTS);
			data &= 0x3F;
			if (data == 0)
				break;
			/* yield 10 us for next polling by experiment*/
			usleep(10);
			if (ktime_to_ms(ktime_get()) > time_end) {
				pr_err("%s time out", __func__);
				ret = -EBUSY;
				break;
			}
		}
	}
	return ret;
}

static int qpic_wait_for_eof(void)
{
	u32 data, time_end;
	int ret = 0;
	if (use_irq) {
		data = QPIC_INP(QPIC_REG_QPIC_LCDC_IRQ_STTS);
		if (data & (1 << 2))
			return ret;
		INIT_COMPLETION(qpic_res->fifo_eof_comp);
		QPIC_OUTP(QPIC_REG_QPIC_LCDC_IRQ_EN, (1 << 2));
		ret = wait_for_completion_timeout(&qpic_res->fifo_eof_comp,
				msecs_to_jiffies(QPIC_MAX_VSYNC_WAIT_TIME));
		if (ret > 0) {
			ret = 0;
		} else {
			pr_err("%s timeout %x\n", __func__, ret);
			ret = -ETIMEDOUT;
		}
		QPIC_OUTP(QPIC_REG_QPIC_LCDC_IRQ_EN, 0);
	} else {
		time_end = (u32)ktime_to_ms(ktime_get()) +
			QPIC_MAX_VSYNC_WAIT_TIME;
		while (1) {
			data = QPIC_INP(QPIC_REG_QPIC_LCDC_IRQ_STTS);
			if (data & (1 << 2))
				break;
			/* yield 10 us for next polling by experiment*/
			usleep(10);
			if (ktime_to_ms(ktime_get()) > time_end) {
				pr_err("%s wait for eof time out\n", __func__);
				qpic_dump_reg();
				ret = -EBUSY;
				break;
			}
		}
	}
	return ret;
}

static int qpic_send_pkt_sw(u32 cmd, u32 len, u8 *param)
{
	u32 bytes_left, space, data, cfg2, time_end;
	u32 bytes_left, space, data, cfg2;
	int i, ret = 0;
	if ((len <= (sizeof(u32) * 4)) && (is_cmd)) {
		len >>= 2;/* len in dwords */
	if (len <= 4) {
		len = (len + 3) / 4; /* len in dwords */
		data = 0;
		for (i = 0; i < len; i++)
			data |= param[i] << (8 * i);
			data |= (u32)param[i] << (8 * i);
		QPIC_OUTP(QPIC_REG_QPIC_LCDC_CMD_DATA_CYCLE_CNT, len);
		QPIC_OUTP(QPIC_REG_LCD_DEVICE_CMD0 + (4 * cmd), data);
		return 0;
	}

	if ((len & 0x1) != 0) {
		pr_err("%s: number of bytes needs be even", __func__);
		pr_debug("%s: number of bytes needs be even", __func__);
		len = (len + 1) & (~0x1);
	}
	QPIC_OUTP(QPIC_REG_QPIC_LCDC_IRQ_CLR, 0xff);
	QPIC_OUTP(QPIC_REG_QPIC_LCDC_CMD_DATA_CYCLE_CNT, 0);
	cfg2 = QPIC_INP(QPIC_REG_QPIC_LCDC_CFG2);
	if ((cmd != OP_WRITE_MEMORY_START) &&
		(cmd != OP_WRITE_MEMORY_CONTINUE))
		cfg2 |= (1 << 24); /* transparent mode */
	else
		cfg2 &= ~(1 << 24);

	cfg2 &= ~0xFF;
	cfg2 |= cmd;
	QPIC_OUTP(QPIC_REG_QPIC_LCDC_CFG2, cfg2);
	QPIC_OUTP(QPIC_REG_QPIC_LCDC_FIFO_SOF, 0x0);
	bytes_left = len;

	while (bytes_left > 0) {
		time_end = (u32)ktime_to_ms(ktime_get()) +
			QPIC_MAX_VSYNC_WAIT_TIME;
		while (1) {
			data = QPIC_INP(QPIC_REG_QPIC_LCDC_STTS);
			data &= 0x3F;
			if (data == 0)
				break;
			/* yield 10 us for next polling by experiment*/
			usleep(10);
			if (ktime_to_ms(ktime_get()) > time_end) {
				pr_err("%s time out", __func__);
				ret = -EBUSY;
		ret = qpic_wait_for_fifo();
		if (ret)
			goto exit_send_cmd_sw;
			}
		}
		space = (16 - data);

		space = 16;

		while ((space > 0) && (bytes_left > 0)) {
			/* write to fifo */
			if (bytes_left >= 4) {
				QPIC_OUTP(QPIC_REG_QPIC_LCDC_FIFO_DATA_PORT0,
					param[0]);
				param++;
					*(u32 *)param);
				param += 4;
				bytes_left -= 4;
				space--;
			} else if (bytes_left == 2) {
@@ -465,36 +575,20 @@ int qpic_flush_buffer_sw(u32 cmd, u32 len, u32 *param, u32 is_cmd)
	}
	/* finished */
	QPIC_OUTP(QPIC_REG_QPIC_LCDC_FIFO_EOF, 0x0);

	time_end = (u32)ktime_to_ms(ktime_get()) + QPIC_MAX_VSYNC_WAIT_TIME;
	while (1) {
		data = QPIC_INP(QPIC_REG_QPIC_LCDC_IRQ_STTS);
		if (data & (1 << 2))
			break;
		/* yield 10 us for next polling by experiment*/
		usleep(10);
		if (ktime_to_ms(ktime_get()) > time_end) {
			pr_err("%s wait for eof time out", __func__);
			ret = -EBUSY;
			goto exit_send_cmd_sw;
		}
	}
	ret = qpic_wait_for_eof();
exit_send_cmd_sw:
	cfg2 &= ~(1 << 24);
	QPIC_OUTP(QPIC_REG_QPIC_LCDC_CFG2, cfg2);
	return ret;
}

int qpic_flush_buffer(u32 cmd, u32 len, u32 *param, u32 is_cmd)
int qpic_send_pkt(u32 cmd, u8 *param, u32 len)
{
	if (use_bam) {
		if (is_cmd)
			return qpic_flush_buffer_sw(cmd, len, param, is_cmd);
	if (!use_bam || ((cmd != OP_WRITE_MEMORY_CONTINUE) &&
		(cmd != OP_WRITE_MEMORY_START)))
		return qpic_send_pkt_sw(cmd, len, param);
	else
			return qpic_flush_buffer_bam(cmd, len, param, is_cmd);
	} else {
		return qpic_flush_buffer_sw(cmd, len, param, is_cmd);
	}
		return qpic_send_pkt_bam(cmd, len, param);
}

int mdss_qpic_init(void)
@@ -514,18 +608,21 @@ int mdss_qpic_init(void)
	data |= (1 << 9); /* threshold */
	QPIC_OUTP(QPIC_REG_QPIC_LCDC_CTRL, data);

	if (use_irq && qpic_res->irq_requested) {
	if (use_irq && (!qpic_res->irq_requested)) {
		ret = devm_request_irq(&qpic_res->pdev->dev,
			qpic_res->irq, qpic_irq_handler,
			IRQF_DISABLED,	"QPIC", qpic_res);
		if (ret) {
			pr_err("qpic request_irq() failed!\n");
			use_irq = false;
		} else {
			disable_irq(qpic_res->irq);
		}
		qpic_res->irq_requested = true;
	}

	qpic_interrupt_en(use_irq);

	QPIC_OUTP(QPIC_REG_QPIC_LCDC_CFG0, 0x02108501);
	data = QPIC_INP(QPIC_REG_QPIC_LCDC_CFG2);
	data &= ~(0xFFF);
@@ -549,6 +646,16 @@ int mdss_qpic_init(void)
	return ret;
}

u32 qpic_read_data(u32 cmd_index, u32 size)
{
	u32 data = 0;
	if (size <= 4) {
		QPIC_OUTP(QPIC_REG_QPIC_LCDC_CMD_DATA_CYCLE_CNT, size);
		data = QPIC_INP(QPIC_REG_LCD_DEVICE_CMD0 + (cmd_index * 4));
	}
	return data;
}

static int msm_qpic_bus_register(struct platform_device *pdev)
{
	int ret = 0;
@@ -559,9 +666,9 @@ static int msm_qpic_bus_register(struct platform_device *pdev)
		pr_err("msm_bus_cl_get_pdata failed\n");
		return -EINVAL;
	}
	bus_handle =
	qpic_res->bus_handle =
		msm_bus_scale_register_client(use_cases);
	if (!bus_handle) {
	if (!qpic_res->bus_handle) {
		ret = -EINVAL;
		pr_err("msm_bus_scale_register_client failed\n");
	}
@@ -645,8 +752,9 @@ probe_done:

static int mdss_qpic_remove(struct platform_device *pdev)
{
	if (bus_handle)
		msm_bus_scale_unregister_client(bus_handle);
	if (qpic_res->bus_handle)
		msm_bus_scale_unregister_client(qpic_res->bus_handle);
	qpic_res->bus_handle = 0;
	return 0;
}

+4 −2
Original line number Diff line number Diff line
@@ -48,8 +48,8 @@
#define QPIC_MAX_CMD_BUF_SIZE				512

int mdss_qpic_init(void);
int qpic_flush_buffer(u32 cmd, u32 len, u32 *param, u32 is_cmd);

int qpic_send_pkt(u32 cmd, u8 *param, u32 len);
u32 qpic_read_data(u32 cmd_index, u32 size);
u32 msm_qpic_get_bam_hdl(struct sps_bam_props *bam);
int mdss_qpic_panel_on(struct mdss_panel_data *pdata,
	struct qpic_panel_io_desc *panel_io);
@@ -83,6 +83,8 @@ struct qpic_data_type {
	u32 irq_requested;
	struct mdss_panel_data *panel_data;
	struct qpic_panel_io_desc panel_io;
	u32 bus_handle;
	struct completion fifo_eof_comp;
};

u32 qpic_send_frame(
+29 −51
Original line number Diff line number Diff line
@@ -41,44 +41,6 @@ u32 qpic_panel_get_framerate(void)
	return panel_refresh_rate;
}

u32 qpic_send_panel_cmd(u32 cmd, u32 *val, u32 length)
{
	u32 ret;
	u32 cmd_index;
	u32 size;
	u32 buffer[LCDC_INTERNAL_BUFFER_SIZE];
	u32 i;

	cmd_index = LCDC_EXTRACT_OP_CMD(cmd);
	size = LCDC_EXTRACT_OP_SIZE(cmd);
	if (size == INV_SIZE)
		size = length;
	/* short or pixel data commands need not conversion */
	if ((cmd == OP_WRITE_MEMORY_CONTINUE) ||
		(cmd == OP_WRITE_MEMORY_START)) {
		ret = qpic_flush_buffer(cmd_index, size, val, false);
	} else {
		if (size > LCDC_INTERNAL_BUFFER_SIZE)
			size = LCDC_INTERNAL_BUFFER_SIZE;
		/* correcting for encoding issues */
		for (i = 0; i < size; i += sizeof(u32)) {
			buffer[i] = (val[(i>>2)] >> 0) & 0xff;
			buffer[i+1] = (val[(i>>2)] >> 8) & 0xff;
			buffer[i+2] = (val[(i>>2)] >> 16) & 0xff;
			buffer[i+3] = (val[(i>>2)] >> 24) & 0xff;
		}
		ret = qpic_flush_buffer(cmd_index,
				size * sizeof(u32), buffer, true);
	}
	return ret;
}

u32 qpic_panel_set_cmd_only(u32 command)
{
	u32 param;
	return qpic_send_panel_cmd(command, &param, 0);
}

/* write a frame of pixels to a MIPI screen */
u32 qpic_send_frame(u32 x_start,
				u32 y_start,
@@ -87,7 +49,7 @@ u32 qpic_send_frame(u32 x_start,
				u32 *data,
				u32 total_bytes)
{
	u32 param;
	u8 param[4];
	u32 status;
	u32 start_0_7;
	u32 end_0_7;
@@ -105,9 +67,11 @@ u32 qpic_send_frame(u32 x_start,
	end_0_7 = x_end & 0xff;
	start_8_15 = (x_start >> 8) & 0xff;
	end_8_15 = (x_end >> 8) & 0xff;
	param = (start_8_15 << 0) | (start_0_7 << 8) |
		(end_8_15 << 16) | (end_0_7 << 24U);
	status = qpic_send_panel_cmd(OP_SET_COLUMN_ADDRESS, &param, 0);
	param[0] = start_8_15;
	param[1] = start_0_7;
	param[2] = end_8_15;
	param[3] = end_0_7;
	status = qpic_send_pkt(OP_SET_COLUMN_ADDRESS, param, 4);
	if (status) {
		pr_err("Failed to set column address");
		return status;
@@ -117,16 +81,17 @@ u32 qpic_send_frame(u32 x_start,
	end_0_7 = y_end & 0xff;
	start_8_15 = (y_start >> 8) & 0xff;
	end_8_15 = (y_end >> 8) & 0xff;
	param = (start_8_15 << 0) | (start_0_7 << 8) |
		(end_8_15 << 16) | (end_0_7 << 24U);
	status = qpic_send_panel_cmd(OP_SET_PAGE_ADDRESS, &param, 0);
	param[0] = start_8_15;
	param[1] = start_0_7;
	param[2] = end_8_15;
	param[3] = end_0_7;
	status = qpic_send_pkt(OP_SET_PAGE_ADDRESS, param, 4);
	if (status) {
		pr_err("Failed to set page address");
		return status;
	}

	status = qpic_send_panel_cmd(OP_WRITE_MEMORY_START,
		&(data[0]), total_bytes);
	status = qpic_send_pkt(OP_WRITE_MEMORY_START, (u8 *)data, total_bytes);
	if (status) {
		pr_err("Failed to start memory write");
		return status;
@@ -262,13 +227,26 @@ static int mdss_qpic_panel_probe(struct platform_device *pdev)
	else
		pr_info("%s: Panel Name = %s\n", __func__, panel_name);

	rc = mdss_panel_parse_dt(pdev, &vendor_pdata);
	if (rc)
		return rc;

	/* select panel according to label */
	if (!strcmp(panel_name, "ili qvga lcdc panel")) {
		qpic_panel_on = ili9341_on;
		qpic_panel_off = ili9341_off;
	} else {
		/* select default panel driver */
		pr_info("%s: select default panel driver\n", __func__);
		qpic_panel_on = ili9341_on;
		qpic_panel_off = ili9341_off;
	}

	if (qpic_panel_on == ili9341_on) {
		vendor_pdata.panel_info.xres = 240;
		vendor_pdata.panel_info.yres = 320;
	}

	rc = mdss_panel_parse_dt(pdev, &vendor_pdata);
	if (rc)
		return rc;
	rc = qpic_register_panel(&vendor_pdata);
	if (rc)
		return rc;
+42 −39
Original line number Diff line number Diff line
@@ -67,43 +67,47 @@
/**
 * LDCc MIPI Type B supported commands
 */
enum {
	OP_ENTER_IDLE_MODE        = OP_SIZE_PAIR(0x39, 0),
	OP_ENTER_INVERT_MODE      = OP_SIZE_PAIR(0x21, 0),
	OP_ENTER_NORMAL_MODE      = OP_SIZE_PAIR(0x13, 0),
	OP_ENTER_PARTIAL_MODE     = OP_SIZE_PAIR(0x12, 0),
	OP_ENTER_SLEEP_MODE       = OP_SIZE_PAIR(0x10, 0),
	OP_EXIT_INVERT_MODE       = OP_SIZE_PAIR(0x20, 0),
	OP_EXIT_SLEEP_MODE        = OP_SIZE_PAIR(0x11, 0),
	OP_EXIT_IDLE_MODE         = OP_SIZE_PAIR(0x38, 0),
	OP_GET_ADDRESS_MODE       = OP_SIZE_PAIR(0x0B, 1),
	OP_GET_BLUE_CHANNEL       = OP_SIZE_PAIR(0x08, 1),
	OP_GET_DIAGNOSTIC_RESULT  = OP_SIZE_PAIR(0x0F, 2),
	OP_GET_DISPLAY_MODE       = OP_SIZE_PAIR(0x0D, 1),
	OP_GET_GREEN_CHANNEL      = OP_SIZE_PAIR(0x07, 1),
	OP_GET_PIXEL_FORMAT       = OP_SIZE_PAIR(0x0C, 1),
	OP_GET_POWER_MODE         = OP_SIZE_PAIR(0x0A, 1),
	OP_GET_RED_CHANNEL        = OP_SIZE_PAIR(0x06, 1),
	OP_GET_SCANLINE           = OP_SIZE_PAIR(0x45, 2),
	OP_GET_SIGNAL_MODE        = OP_SIZE_PAIR(0x0E, 1),
	OP_NOP                    = OP_SIZE_PAIR(0x00, 0),
	OP_READ_DDB_CONTINUE      = OP_SIZE_PAIR(0xA8, INV_SIZE),
	OP_READ_DDB_START         = OP_SIZE_PAIR(0xA1, INV_SIZE),
	OP_READ_MEMORY_CONTINUE   = OP_SIZE_PAIR(0x3E, INV_SIZE),
	OP_READ_MEMORY_START      = OP_SIZE_PAIR(0x2E, INV_SIZE),
	OP_SET_ADDRESS_MODE       = OP_SIZE_PAIR(0x36, 1),
	OP_SET_COLUMN_ADDRESS     = OP_SIZE_PAIR(0x2A, 4),
	OP_SET_DISPLAY_OFF        = OP_SIZE_PAIR(0x28, 0),
	OP_SET_DISPLAY_ON         = OP_SIZE_PAIR(0x29, 0),
	OP_SET_GAMMA_CURVE        = OP_SIZE_PAIR(0x26, 1),
	OP_SET_PAGE_ADDRESS       = OP_SIZE_PAIR(0x2B, 4),
	OP_SET_PARTIAL_COLUMNS    = OP_SIZE_PAIR(0x31, 4),
	OP_SET_PARTIAL_ROWS       = OP_SIZE_PAIR(0x30, 4),
	OP_SET_PIXEL_FORMAT       = OP_SIZE_PAIR(0x3A, 1),
	OP_SOFT_RESET             = OP_SIZE_PAIR(0x01, 0),
	OP_WRITE_MEMORY_CONTINUE  = OP_SIZE_PAIR(0x3C, INV_SIZE),
	OP_WRITE_MEMORY_START     = OP_SIZE_PAIR(0x2C, INV_SIZE),
};
#define	OP_ENTER_IDLE_MODE      0x39
#define	OP_ENTER_INVERT_MODE    0x21
#define	OP_ENTER_NORMAL_MODE    0x13
#define	OP_ENTER_PARTIAL_MODE   0x12
#define	OP_ENTER_SLEEP_MODE     0x10
#define	OP_EXIT_INVERT_MODE     0x20
#define	OP_EXIT_SLEEP_MODE      0x11
#define	OP_EXIT_IDLE_MODE       0x38
#define	OP_GET_ADDRESS_MODE     0x0B /* size 1 */
#define	OP_GET_BLUE_CHANNEL     0x08 /* size 1 */
#define	OP_GET_DIAGNOSTIC       0x0F /* size 2 */
#define	OP_GET_DISPLAY_MODE     0x0D /* size 1 */
#define	OP_GET_GREEN_CHANNEL    0x07 /* size 1 */
#define	OP_GET_PIXEL_FORMAT     0x0C /* size 1 */
#define	OP_GET_POWER_MODE       0x0A /* size 1 */
#define	OP_GET_RED_CHANNEL      0x06 /* size 1 */
#define	OP_GET_SCANLINE         0x45 /* size 1 */
#define	OP_GET_SIGNAL_MODE      0x0E /* size 1 */
#define	OP_NOP                  0x00
#define	OP_READ_DDB_CONTINUE    0xA8 /* size not fixed */
#define	OP_READ_DDB_START       0xA1 /* size not fixed */
#define	OP_READ_MEMORY_CONTINUE 0x3E /* size not fixed */
#define	OP_READ_MEMORY_START    0x2E /* size not fixed */
#define	OP_SET_ADDRESS_MODE     0x36 /* size 1 */
#define	OP_SET_COLUMN_ADDRESS   0x2A /* size 4 */
#define	OP_SET_DISPLAY_OFF      0x28
#define	OP_SET_DISPLAY_ON       0x29
#define	OP_SET_GAMMA_CURVE      0x26 /* size 1 */
#define	OP_SET_PAGE_ADDRESS     0x2B /* size 4 */
#define	OP_SET_PARTIAL_COLUMNS  0x31 /* size 4 */
#define	OP_SET_PARTIAL_ROWS     0x30 /* size 4 */
#define	OP_SET_PIXEL_FORMAT     0x3A /* size 1 */
#define	OP_SOFT_RESET           0x01
#define	OP_WRITE_MEMORY_CONTINUE  0x3C /* size not fixed */
#define	OP_WRITE_MEMORY_START   0x2C /* size not fixed */

/**
 * ILI9341 commands
 */
#define OP_ILI9341_INTERFACE_CONTROL	0xf6
#define OP_ILI9341_TEARING_EFFECT_LINE_ON	0x35

struct qpic_panel_io_desc {
	int rst_gpio;
@@ -118,8 +122,7 @@ struct qpic_panel_io_desc {

int mdss_qpic_panel_io_init(struct platform_device *pdev,
	struct qpic_panel_io_desc *qpic_panel_io);
u32 qpic_panel_set_cmd_only(u32 command);
u32 qpic_send_panel_cmd(u32 cmd, u32 *val, u32 length);
u32 qpic_panel_get_cmd(u32 command, u32 size);
int ili9341_on(struct qpic_panel_io_desc *qpic_panel_io);
void ili9341_off(struct qpic_panel_io_desc *qpic_panel_io);

+31 −51
Original line number Diff line number Diff line
@@ -29,34 +29,7 @@
#include "mdss_qpic.h"
#include "mdss_qpic_panel.h"

enum {
	OP_ILI9341_TEARING_EFFECT_LINE_ON = OP_SIZE_PAIR(0x35, 1),
	OP_ILI9341_INTERFACE_CONTROL = OP_SIZE_PAIR(0xf6, 3),
	OP_ILI9341_WRITE_CTRL_DISPLAY  = OP_SIZE_PAIR(0x53, 1),
	OP_ILI9341_POWER_CONTROL_A  = OP_SIZE_PAIR(0xcb, 5),
	OP_ILI9341_POWER_CONTROL_B  = OP_SIZE_PAIR(0xcf, 3),
	OP_ILI9341_DRIVER_TIMING_CONTROL_A  = OP_SIZE_PAIR(0xe8, 3),
	OP_ILI9341_DRIVER_TIMING_CONTROL_B  = OP_SIZE_PAIR(0xea, 3),
	OP_ILI9341_POWER_ON_SEQUENCE_CONTROL  = OP_SIZE_PAIR(0xed, 4),
	OP_ILI9341_PUMP_RATIO_CONTROL  = OP_SIZE_PAIR(0xf7, 1),
	OP_ILI9341_POWER_CONTROL_1  = OP_SIZE_PAIR(0xc0, 1),
	OP_ILI9341_POWER_CONTROL_2  = OP_SIZE_PAIR(0xc1, 1),
	OP_ILI9341_VCOM_CONTROL_1  = OP_SIZE_PAIR(0xc5, 2),
	OP_ILI9341_VCOM_CONTROL_2  = OP_SIZE_PAIR(0xc7, 1),
	OP_ILI9341_MEMORY_ACCESS_CONTROL  = OP_SIZE_PAIR(0x36, 1),
	OP_ILI9341_FRAME_RATE_CONTROL  = OP_SIZE_PAIR(0xb1, 2),
	OP_ILI9341_DISPLAY_FUNCTION_CONTROL = OP_SIZE_PAIR(0xb6, 4),
	OP_ILI9341_ENABLE_3G = OP_SIZE_PAIR(0xf2, 1),
	OP_ILI9341_COLMOD_PIXEL_FORMAT_SET = OP_SIZE_PAIR(0x3a, 1),
	OP_ILI9341_GAMMA_SET = OP_SIZE_PAIR(0x26, 1),
	OP_ILI9341_POSITIVE_GAMMA_CORRECTION = OP_SIZE_PAIR(0xe0, 15),
	OP_ILI9341_NEGATIVE_GAMMA_CORRECTION = OP_SIZE_PAIR(0xe1, 15),
	OP_ILI9341_READ_DISPLAY_ID = OP_SIZE_PAIR(0x04, 4),
	OP_ILI9341_READ_DISPLAY_POWER_MODE = OP_SIZE_PAIR(0x0a, 2),
	OP_ILI9341_READ_DISPLAY_MADCTL = OP_SIZE_PAIR(0x0b, 2),
};

static int ili9341_init(struct qpic_panel_io_desc *panel_io)
static int panel_io_init(struct qpic_panel_io_desc *panel_io)
{
	int rc;
	if (panel_io->vdd_vreg) {
@@ -78,7 +51,7 @@ static int ili9341_init(struct qpic_panel_io_desc *panel_io)
	return 0;
}

void ili9341_off(struct qpic_panel_io_desc *qpic_panel_io)
static void panel_io_off(struct qpic_panel_io_desc *qpic_panel_io)
{
	if (qpic_panel_io->ad8_gpio)
		gpio_free(qpic_panel_io->ad8_gpio);
@@ -96,7 +69,12 @@ void ili9341_off(struct qpic_panel_io_desc *qpic_panel_io)
		regulator_disable(qpic_panel_io->avdd_vreg);
}

static int ili9341_panel_power_on(struct qpic_panel_io_desc *qpic_panel_io)
void ili9341_off(struct qpic_panel_io_desc *qpic_panel_io)
{
	panel_io_off(qpic_panel_io);
}

static int panel_io_on(struct qpic_panel_io_desc *qpic_panel_io)
{
	int rc;
	if (qpic_panel_io->vdd_vreg) {
@@ -148,66 +126,68 @@ static int ili9341_panel_power_on(struct qpic_panel_io_desc *qpic_panel_io)
	msleep(20);
	return 0;
power_on_error:
	ili9341_off(qpic_panel_io);
	panel_io_off(qpic_panel_io);
	return -EINVAL;
}

int ili9341_on(struct qpic_panel_io_desc *qpic_panel_io)
{
	u32 param[20];
	u8 param[4];
	int ret;
	if (!qpic_panel_io->init) {
		ili9341_init(qpic_panel_io);
		panel_io_init(qpic_panel_io);
		qpic_panel_io->init = true;
	}
	ret = ili9341_panel_power_on(qpic_panel_io);
	ret = panel_io_on(qpic_panel_io);
	if (ret)
		return ret;
	qpic_panel_set_cmd_only(OP_SOFT_RESET);
	qpic_send_pkt(OP_SOFT_RESET, NULL, 0);
	/* wait for 120 ms after reset as panel spec suggests */
	msleep(120);
	qpic_panel_set_cmd_only(OP_SET_DISPLAY_OFF);
	qpic_send_pkt(OP_SET_DISPLAY_OFF, NULL, 0);
	/* wait for 20 ms after disply off */
	msleep(20);

	/* set memory access control */
	param[0] = ((0x48)<<0) | ((0x00)<<8) | ((0x00)<<16) | ((0x00U)<<24U);
	qpic_send_panel_cmd(OP_ILI9341_MEMORY_ACCESS_CONTROL, param, 0);
	param[0] = 0x48;
	qpic_send_pkt(OP_SET_ADDRESS_MODE, param, 1);
	/* wait for 20 ms after command sent as panel spec suggests */
	msleep(20);

	/* set COLMOD: Pixel Format Set */
	param[0] = ((0x66)<<0) | ((0x00)<<8) | ((0x00)<<16) | ((0x00U)<<24U);
	qpic_send_panel_cmd(OP_ILI9341_COLMOD_PIXEL_FORMAT_SET, param, 0);
	param[0] = 0x66;
	qpic_send_pkt(OP_SET_PIXEL_FORMAT, param, 1);
	/* wait for 20 ms after command sent as panel spec suggests */
	msleep(20);

	/* set interface */
	param[0] = ((0x01)<<0) | ((0x00)<<8) | ((0x00)<<16) | ((0x00U)<<24U);
	qpic_send_panel_cmd(OP_ILI9341_INTERFACE_CONTROL, &param[0], 0);
	param[0] = 1;
	param[1] = 0;
	param[2] = 0;
	qpic_send_pkt(OP_ILI9341_INTERFACE_CONTROL, param, 3);
	/* wait for 20 ms after command sent */
	msleep(20);

	/* exit sleep mode */
	qpic_panel_set_cmd_only(OP_EXIT_SLEEP_MODE);
	qpic_send_pkt(OP_EXIT_SLEEP_MODE, NULL, 0);
	/* wait for 20 ms after command sent as panel spec suggests */
	msleep(20);

	/* normal mode */
	qpic_panel_set_cmd_only(OP_ENTER_NORMAL_MODE);
	qpic_send_pkt(OP_ENTER_NORMAL_MODE, NULL, 0);
	/* wait for 20 ms after command sent as panel spec suggests */
	msleep(20);

	/* display on */
	qpic_panel_set_cmd_only(OP_SET_DISPLAY_ON);
	qpic_send_pkt(OP_SET_DISPLAY_ON, NULL, 0);
	/* wait for 20 ms after command sent as panel spec suggests */
	msleep(20);

	/* tearing effect  */
	param[0] = ((0x00)<<0) | ((0x00)<<8) | ((0x00)<<16) | ((0x00U)<<24U);
	qpic_send_panel_cmd(OP_ILI9341_TEARING_EFFECT_LINE_ON, param, 0);
	/* wait for 20 ms after command sent as panel spec suggests */
	msleep(20);
	param[0] = 0;
	qpic_send_pkt(OP_ILI9341_TEARING_EFFECT_LINE_ON, param, 1);

	/* test */
	param[0] = qpic_read_data(OP_GET_PIXEL_FORMAT, 1);
	pr_debug("Pixel format =%x", param[0]);

	return 0;
}