2012-03-09 00:02:20 +08:00
|
|
|
/*
|
|
|
|
* 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>
|
2014-08-05 14:22:48 +08:00
|
|
|
#include <video/mipi_display.h>
|
2012-03-09 00:02:20 +08:00
|
|
|
|
|
|
|
#include "mdfld_dsi_output.h"
|
|
|
|
#include "mdfld_dsi_pkg_sender.h"
|
|
|
|
#include "mdfld_dsi_dpi.h"
|
|
|
|
|
|
|
|
#define MDFLD_DSI_READ_MAX_COUNT 5000
|
|
|
|
|
|
|
|
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",
|
2015-11-03 06:20:56 +08:00
|
|
|
"Generic Read Data Avail",
|
2012-03-09 00:02:20 +08:00
|
|
|
"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*/
|
2014-06-09 21:21:08 +08:00
|
|
|
/*wait_for_all_fifos_empty(sender)*/
|
2012-03-09 00:02:20 +08:00
|
|
|
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) {
|
2014-08-05 14:22:48 +08:00
|
|
|
case MIPI_DSI_DCS_SHORT_WRITE:
|
|
|
|
case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
|
|
|
|
case MIPI_DSI_DCS_LONG_WRITE:
|
2012-03-09 00:02:20 +08:00
|
|
|
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*/
|
2014-08-05 14:22:48 +08:00
|
|
|
if (unlikely(cmd == MIPI_DCS_ENTER_SLEEP_MODE)) {
|
2012-03-09 00:02:20 +08:00
|
|
|
/*TODO: replace it with msleep later*/
|
|
|
|
mdelay(120);
|
|
|
|
}
|
|
|
|
|
2014-08-05 14:22:48 +08:00
|
|
|
if (unlikely(cmd == MIPI_DCS_EXIT_SLEEP_MODE)) {
|
2012-03-09 00:02:20 +08:00
|
|
|
/*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) {
|
2014-08-05 14:22:48 +08:00
|
|
|
case MIPI_DSI_DCS_SHORT_WRITE:
|
|
|
|
case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
|
|
|
|
case MIPI_DSI_DCS_LONG_WRITE:
|
2012-03-09 00:02:20 +08:00
|
|
|
cmd = *data;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*update panel status*/
|
2014-08-05 14:22:48 +08:00
|
|
|
if (unlikely(cmd == MIPI_DCS_ENTER_SLEEP_MODE)) {
|
2012-03-09 00:02:20 +08:00
|
|
|
sender->panel_mode |= MDFLD_DSI_PANEL_MODE_SLEEP;
|
|
|
|
/*TODO: replace it with msleep later*/
|
|
|
|
mdelay(120);
|
2014-08-05 14:22:48 +08:00
|
|
|
} else if (unlikely(cmd == MIPI_DCS_EXIT_SLEEP_MODE)) {
|
2012-03-09 00:02:20 +08:00
|
|
|
sender->panel_mode &= ~MDFLD_DSI_PANEL_MODE_SLEEP;
|
|
|
|
/*TODO: replace it with msleep later*/
|
|
|
|
mdelay(120);
|
2014-08-05 14:22:48 +08:00
|
|
|
} else if (unlikely(cmd == MIPI_DCS_SOFT_RESET)) {
|
2012-03-09 00:02:20 +08:00
|
|
|
/*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) {
|
2014-08-05 14:22:48 +08:00
|
|
|
case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:
|
|
|
|
case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:
|
|
|
|
case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:
|
|
|
|
case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM:
|
|
|
|
case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM:
|
|
|
|
case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM:
|
|
|
|
case MIPI_DSI_DCS_SHORT_WRITE:
|
|
|
|
case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
|
|
|
|
case MIPI_DSI_DCS_READ:
|
2012-03-09 00:02:20 +08:00
|
|
|
ret = send_short_pkg(sender, data_type, data[0], data[1], hs);
|
|
|
|
break;
|
2014-08-05 14:22:48 +08:00
|
|
|
case MIPI_DSI_GENERIC_LONG_WRITE:
|
|
|
|
case MIPI_DSI_DCS_LONG_WRITE:
|
2012-03-09 00:02:20 +08:00
|
|
|
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);
|
2014-08-05 14:22:48 +08:00
|
|
|
send_pkg(sender, MIPI_DSI_DCS_LONG_WRITE, data, len, hs);
|
2012-03-09 00:02:20 +08:00
|
|
|
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) {
|
2014-08-05 14:22:48 +08:00
|
|
|
data_type = MIPI_DSI_DCS_SHORT_WRITE_PARAM;
|
2012-03-09 00:02:20 +08:00
|
|
|
data[1] = param;
|
|
|
|
} else {
|
2014-08-05 14:22:48 +08:00
|
|
|
data_type = MIPI_DSI_DCS_SHORT_WRITE;
|
2012-03-09 00:02:20 +08:00
|
|
|
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;
|
|
|
|
|
2012-03-09 00:11:38 +08:00
|
|
|
if (!sender || param_num > 2) {
|
2012-03-09 00:02:20 +08:00
|
|
|
DRM_ERROR("Invalid parameter\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (param_num) {
|
|
|
|
case 0:
|
2014-08-05 14:22:48 +08:00
|
|
|
data_type = MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM;
|
2012-03-09 00:02:20 +08:00
|
|
|
data[0] = 0;
|
|
|
|
data[1] = 0;
|
|
|
|
break;
|
|
|
|
case 1:
|
2014-08-05 14:22:48 +08:00
|
|
|
data_type = MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM;
|
2012-03-09 00:02:20 +08:00
|
|
|
data[0] = param0;
|
|
|
|
data[1] = 0;
|
|
|
|
break;
|
|
|
|
case 2:
|
2014-08-05 14:22:48 +08:00
|
|
|
data_type = MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM;
|
2012-03-09 00:02:20 +08:00
|
|
|
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);
|
2014-08-05 14:22:48 +08:00
|
|
|
send_pkg(sender, MIPI_DSI_GENERIC_LONG_WRITE, data, len, hs);
|
2012-03-09 00:02:20 +08:00
|
|
|
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;
|
2017-08-12 09:55:15 +08:00
|
|
|
struct drm_device *dev;
|
2012-03-09 00:02:20 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-08-12 09:55:15 +08:00
|
|
|
dev = sender->dev;
|
|
|
|
|
2012-03-09 00:02:20 +08:00
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
2014-08-05 14:22:48 +08:00
|
|
|
return __read_panel_data(sender, MIPI_DSI_DCS_READ, &cmd, 1,
|
2012-03-09 00:02:20 +08:00
|
|
|
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;
|
2012-05-11 18:31:22 +08:00
|
|
|
struct drm_psb_private *dev_priv = dev->dev_private;
|
|
|
|
const struct psb_offset *map = &dev_priv->regmap[pipe];
|
2012-03-09 00:02:20 +08:00
|
|
|
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*/
|
2012-05-11 18:31:22 +08:00
|
|
|
/* 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;
|
2012-03-09 00:02:20 +08:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|