1485 lines
38 KiB
C
1485 lines
38 KiB
C
/*
|
|
* 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_dbi.h"
|
|
#include "mdfld_dsi_dpi.h"
|
|
|
|
#define MDFLD_DSI_DBI_FIFO_TIMEOUT 100
|
|
#define MDFLD_DSI_MAX_RETURN_PACKET_SIZE 512
|
|
#define MDFLD_DSI_READ_MAX_COUNT 5000
|
|
|
|
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 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);
|
|
}
|
|
dev_err(dev->dev, "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, (1 << 2) | (1 << 10) | (1 << 18)
|
|
| (1 << 26) | (1 << 27) | (1 << 28));
|
|
}
|
|
|
|
static int wait_for_lp_fifos_empty(struct mdfld_dsi_pkg_sender *sender)
|
|
{
|
|
return wait_for_gen_fifo_empty(sender, (1 << 10) | (1 << 26));
|
|
}
|
|
|
|
static int wait_for_hs_fifos_empty(struct mdfld_dsi_pkg_sender *sender)
|
|
{
|
|
return wait_for_gen_fifo_empty(sender, (1 << 2) | (1 << 18));
|
|
}
|
|
|
|
static int wait_for_dbi_fifo_empty(struct mdfld_dsi_pkg_sender *sender)
|
|
{
|
|
return wait_for_gen_fifo_empty(sender, (1 << 27));
|
|
}
|
|
|
|
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;
|
|
|
|
switch (mask) {
|
|
case (1 << 0):
|
|
case (1 << 1):
|
|
case (1 << 2):
|
|
case (1 << 3):
|
|
case (1 << 4):
|
|
case (1 << 5):
|
|
case (1 << 6):
|
|
case (1 << 7):
|
|
case (1 << 8):
|
|
case (1 << 9):
|
|
case (1 << 10):
|
|
case (1 << 11):
|
|
case (1 << 12):
|
|
case (1 << 13):
|
|
break;
|
|
case (1 << 14):
|
|
/*wait for all fifo empty*/
|
|
/*wait_for_all_fifos_empty(sender)*/;
|
|
break;
|
|
case (1 << 15):
|
|
break;
|
|
case (1 << 16):
|
|
break;
|
|
case (1 << 17):
|
|
break;
|
|
case (1 << 18):
|
|
case (1 << 19):
|
|
/*wait for contention recovery time*/
|
|
/*mdelay(10);*/
|
|
/*wait for all fifo empty*/
|
|
if (0)
|
|
wait_for_all_fifos_empty(sender);
|
|
break;
|
|
case (1 << 20):
|
|
break;
|
|
case (1 << 21):
|
|
/*wait for all fifo empty*/
|
|
/*wait_for_all_fifos_empty(sender);*/
|
|
break;
|
|
case (1 << 22):
|
|
break;
|
|
case (1 << 23):
|
|
case (1 << 24):
|
|
case (1 << 25):
|
|
case (1 << 26):
|
|
case (1 << 27):
|
|
/* HS Gen fifo full */
|
|
REG_WRITE(intr_stat_reg, mask);
|
|
wait_for_hs_fifos_empty(sender);
|
|
break;
|
|
case (1 << 28):
|
|
/* LP Gen fifo full\n */
|
|
REG_WRITE(intr_stat_reg, mask);
|
|
wait_for_lp_fifos_empty(sender);
|
|
break;
|
|
case (1 << 29):
|
|
case (1 << 30):
|
|
case (1 << 31):
|
|
break;
|
|
}
|
|
|
|
if (mask & REG_READ(intr_stat_reg))
|
|
dev_warn(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(dev->dev, "[DSI]: %s\n", dsi_errors[i]);
|
|
err = handle_dsi_error(sender, mask);
|
|
if (err)
|
|
dev_err(dev->dev, "Cannot handle error\n");
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static inline int dbi_cmd_sent(struct mdfld_dsi_pkg_sender *sender)
|
|
{
|
|
struct drm_device *dev = sender->dev;
|
|
u32 retry = 0xffff;
|
|
u32 dbi_cmd_addr_reg = sender->mipi_cmd_addr_reg;
|
|
|
|
/* Query the command execution status */
|
|
while (retry--) {
|
|
if (!(REG_READ(dbi_cmd_addr_reg) & (1 << 0)))
|
|
break;
|
|
}
|
|
|
|
if (!retry) {
|
|
dev_err(dev->dev, "Timeout waiting for DBI Command status\n");
|
|
return -EAGAIN;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* NOTE: this interface is abandoned expect for write_mem_start DCS
|
|
* other DCS are sent via generic pkg interfaces
|
|
*/
|
|
static int send_dcs_pkg(struct mdfld_dsi_pkg_sender *sender,
|
|
struct mdfld_dsi_pkg *pkg)
|
|
{
|
|
struct drm_device *dev = sender->dev;
|
|
struct mdfld_dsi_dcs_pkg *dcs_pkg = &pkg->pkg.dcs_pkg;
|
|
u32 dbi_cmd_len_reg = sender->mipi_cmd_len_reg;
|
|
u32 dbi_cmd_addr_reg = sender->mipi_cmd_addr_reg;
|
|
u32 cb_phy = sender->dbi_cb_phy;
|
|
u32 index = 0;
|
|
u8 *cb = (u8 *)sender->dbi_cb_addr;
|
|
int i;
|
|
int ret;
|
|
|
|
if (!sender->dbi_pkg_support) {
|
|
dev_err(dev->dev, "Trying to send DCS on a non DBI output, abort!\n");
|
|
return -ENOTSUPP;
|
|
}
|
|
|
|
/*wait for DBI fifo empty*/
|
|
wait_for_dbi_fifo_empty(sender);
|
|
|
|
*(cb + (index++)) = dcs_pkg->cmd;
|
|
if (dcs_pkg->param_num) {
|
|
for (i = 0; i < dcs_pkg->param_num; i++)
|
|
*(cb + (index++)) = *(dcs_pkg->param + i);
|
|
}
|
|
|
|
REG_WRITE(dbi_cmd_len_reg, (1 + dcs_pkg->param_num));
|
|
REG_WRITE(dbi_cmd_addr_reg,
|
|
(cb_phy << CMD_MEM_ADDR_OFFSET)
|
|
| (1 << 0)
|
|
| ((dcs_pkg->data_src == CMD_DATA_SRC_PIPE) ? (1 << 1) : 0));
|
|
|
|
ret = dbi_cmd_sent(sender);
|
|
if (ret) {
|
|
dev_err(dev->dev, "command 0x%x not complete\n", dcs_pkg->cmd);
|
|
return -EAGAIN;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int __send_short_pkg(struct mdfld_dsi_pkg_sender *sender,
|
|
struct mdfld_dsi_pkg *pkg)
|
|
{
|
|
struct drm_device *dev = sender->dev;
|
|
u32 hs_gen_ctrl_reg = sender->mipi_hs_gen_ctrl_reg;
|
|
u32 lp_gen_ctrl_reg = sender->mipi_lp_gen_ctrl_reg;
|
|
u32 gen_ctrl_val = 0;
|
|
struct mdfld_dsi_gen_short_pkg *short_pkg = &pkg->pkg.short_pkg;
|
|
|
|
gen_ctrl_val |= short_pkg->cmd << MCS_COMMANDS_POS;
|
|
gen_ctrl_val |= 0 << DCS_CHANNEL_NUMBER_POS;
|
|
gen_ctrl_val |= pkg->pkg_type;
|
|
gen_ctrl_val |= short_pkg->param << MCS_PARAMETER_POS;
|
|
|
|
if (pkg->transmission_type == MDFLD_DSI_HS_TRANSMISSION) {
|
|
/* wait for hs fifo empty */
|
|
/* wait_for_hs_fifos_empty(sender); */
|
|
/* Send pkg */
|
|
REG_WRITE(hs_gen_ctrl_reg, gen_ctrl_val);
|
|
} else if (pkg->transmission_type == MDFLD_DSI_LP_TRANSMISSION) {
|
|
/* wait_for_lp_fifos_empty(sender); */
|
|
/* Send pkg*/
|
|
REG_WRITE(lp_gen_ctrl_reg, gen_ctrl_val);
|
|
} else {
|
|
dev_err(dev->dev, "Unknown transmission type %d\n",
|
|
pkg->transmission_type);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __send_long_pkg(struct mdfld_dsi_pkg_sender *sender,
|
|
struct mdfld_dsi_pkg *pkg)
|
|
{
|
|
struct drm_device *dev = sender->dev;
|
|
u32 hs_gen_ctrl_reg = sender->mipi_hs_gen_ctrl_reg;
|
|
u32 hs_gen_data_reg = sender->mipi_hs_gen_data_reg;
|
|
u32 lp_gen_ctrl_reg = sender->mipi_lp_gen_ctrl_reg;
|
|
u32 lp_gen_data_reg = sender->mipi_lp_gen_data_reg;
|
|
u32 gen_ctrl_val = 0;
|
|
u32 *dp;
|
|
int i;
|
|
struct mdfld_dsi_gen_long_pkg *long_pkg = &pkg->pkg.long_pkg;
|
|
|
|
dp = long_pkg->data;
|
|
|
|
/*
|
|
* Set up word count for long pkg
|
|
* FIXME: double check word count field.
|
|
* currently, using the byte counts of the payload as the word count.
|
|
* ------------------------------------------------------------
|
|
* | DI | WC | ECC| PAYLOAD |CHECKSUM|
|
|
* ------------------------------------------------------------
|
|
*/
|
|
gen_ctrl_val |= (long_pkg->len << 2) << WORD_COUNTS_POS;
|
|
gen_ctrl_val |= 0 << DCS_CHANNEL_NUMBER_POS;
|
|
gen_ctrl_val |= pkg->pkg_type;
|
|
|
|
if (pkg->transmission_type == MDFLD_DSI_HS_TRANSMISSION) {
|
|
/* Wait for hs ctrl and data fifos to be empty */
|
|
/* wait_for_hs_fifos_empty(sender); */
|
|
for (i = 0; i < long_pkg->len; i++)
|
|
REG_WRITE(hs_gen_data_reg, *(dp + i));
|
|
REG_WRITE(hs_gen_ctrl_reg, gen_ctrl_val);
|
|
} else if (pkg->transmission_type == MDFLD_DSI_LP_TRANSMISSION) {
|
|
/* wait_for_lp_fifos_empty(sender); */
|
|
for (i = 0; i < long_pkg->len; i++)
|
|
REG_WRITE(lp_gen_data_reg, *(dp + i));
|
|
REG_WRITE(lp_gen_ctrl_reg, gen_ctrl_val);
|
|
} else {
|
|
dev_err(dev->dev, "Unknown transmission type %d\n",
|
|
pkg->transmission_type);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static int send_mcs_short_pkg(struct mdfld_dsi_pkg_sender *sender,
|
|
struct mdfld_dsi_pkg *pkg)
|
|
{
|
|
return __send_short_pkg(sender, pkg);
|
|
}
|
|
|
|
static int send_mcs_long_pkg(struct mdfld_dsi_pkg_sender *sender,
|
|
struct mdfld_dsi_pkg *pkg)
|
|
{
|
|
return __send_long_pkg(sender, pkg);
|
|
}
|
|
|
|
static int send_gen_short_pkg(struct mdfld_dsi_pkg_sender *sender,
|
|
struct mdfld_dsi_pkg *pkg)
|
|
{
|
|
return __send_short_pkg(sender, pkg);
|
|
}
|
|
|
|
static int send_gen_long_pkg(struct mdfld_dsi_pkg_sender *sender,
|
|
struct mdfld_dsi_pkg *pkg)
|
|
{
|
|
return __send_long_pkg(sender, pkg);
|
|
}
|
|
|
|
static int send_pkg_prepare(struct mdfld_dsi_pkg_sender *sender,
|
|
struct mdfld_dsi_pkg *pkg)
|
|
{
|
|
u8 cmd;
|
|
u8 *data;
|
|
|
|
switch (pkg->pkg_type) {
|
|
case MDFLD_DSI_PKG_DCS:
|
|
cmd = pkg->pkg.dcs_pkg.cmd;
|
|
break;
|
|
case MDFLD_DSI_PKG_MCS_SHORT_WRITE_0:
|
|
case MDFLD_DSI_PKG_MCS_SHORT_WRITE_1:
|
|
cmd = pkg->pkg.short_pkg.cmd;
|
|
break;
|
|
case MDFLD_DSI_PKG_MCS_LONG_WRITE:
|
|
data = (u8 *)pkg->pkg.long_pkg.data;
|
|
cmd = *data;
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
/* This prevents other package sending while doing msleep */
|
|
sender->status = MDFLD_DSI_PKG_SENDER_BUSY;
|
|
|
|
/* Check panel mode v.s. sending command */
|
|
if ((sender->panel_mode & MDFLD_DSI_PANEL_MODE_SLEEP) &&
|
|
cmd != exit_sleep_mode) {
|
|
dev_err(sender->dev->dev,
|
|
"sending 0x%x when panel sleep in\n", cmd);
|
|
sender->status = MDFLD_DSI_PKG_SENDER_FREE;
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Wait for 120 milliseconds in case exit_sleep_mode just be sent */
|
|
if (cmd == DCS_ENTER_SLEEP_MODE) {
|
|
/*TODO: replace it with msleep later*/
|
|
mdelay(120);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int send_pkg_done(struct mdfld_dsi_pkg_sender *sender,
|
|
struct mdfld_dsi_pkg *pkg)
|
|
{
|
|
u8 cmd;
|
|
u8 *data;
|
|
|
|
switch (pkg->pkg_type) {
|
|
case MDFLD_DSI_PKG_DCS:
|
|
cmd = pkg->pkg.dcs_pkg.cmd;
|
|
break;
|
|
case MDFLD_DSI_PKG_MCS_SHORT_WRITE_0:
|
|
case MDFLD_DSI_PKG_MCS_SHORT_WRITE_1:
|
|
cmd = pkg->pkg.short_pkg.cmd;
|
|
break;
|
|
case MDFLD_DSI_PKG_MCS_LONG_WRITE:
|
|
data = (u8 *)pkg->pkg.long_pkg.data;
|
|
cmd = *data;
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
/* Update panel status */
|
|
if (cmd == DCS_ENTER_SLEEP_MODE) {
|
|
sender->panel_mode |= MDFLD_DSI_PANEL_MODE_SLEEP;
|
|
/*TODO: replace it with msleep later*/
|
|
mdelay(120);
|
|
} else if (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 do_send_pkg(struct mdfld_dsi_pkg_sender *sender,
|
|
struct mdfld_dsi_pkg *pkg)
|
|
{
|
|
int ret;
|
|
|
|
if (sender->status == MDFLD_DSI_PKG_SENDER_BUSY) {
|
|
dev_err(sender->dev->dev, "sender is busy\n");
|
|
return -EAGAIN;
|
|
}
|
|
|
|
ret = send_pkg_prepare(sender, pkg);
|
|
if (ret) {
|
|
dev_err(sender->dev->dev, "send_pkg_prepare error\n");
|
|
return ret;
|
|
}
|
|
|
|
switch (pkg->pkg_type) {
|
|
case MDFLD_DSI_PKG_DCS:
|
|
ret = send_dcs_pkg(sender, pkg);
|
|
break;
|
|
case MDFLD_DSI_PKG_GEN_SHORT_WRITE_0:
|
|
case MDFLD_DSI_PKG_GEN_SHORT_WRITE_1:
|
|
case MDFLD_DSI_PKG_GEN_SHORT_WRITE_2:
|
|
case MDFLD_DSI_PKG_GEN_READ_0:
|
|
case MDFLD_DSI_PKG_GEN_READ_1:
|
|
case MDFLD_DSI_PKG_GEN_READ_2:
|
|
ret = send_gen_short_pkg(sender, pkg);
|
|
break;
|
|
case MDFLD_DSI_PKG_GEN_LONG_WRITE:
|
|
ret = send_gen_long_pkg(sender, pkg);
|
|
break;
|
|
case MDFLD_DSI_PKG_MCS_SHORT_WRITE_0:
|
|
case MDFLD_DSI_PKG_MCS_SHORT_WRITE_1:
|
|
case MDFLD_DSI_PKG_MCS_READ:
|
|
ret = send_mcs_short_pkg(sender, pkg);
|
|
break;
|
|
case MDFLD_DSI_PKG_MCS_LONG_WRITE:
|
|
ret = send_mcs_long_pkg(sender, pkg);
|
|
break;
|
|
default:
|
|
dev_err(sender->dev->dev, "Invalid pkg type 0x%x\n",
|
|
pkg->pkg_type);
|
|
ret = -EINVAL;
|
|
}
|
|
send_pkg_done(sender, pkg);
|
|
return ret;
|
|
}
|
|
|
|
static int send_pkg(struct mdfld_dsi_pkg_sender *sender,
|
|
struct mdfld_dsi_pkg *pkg)
|
|
{
|
|
int err ;
|
|
|
|
/* Handle DSI error */
|
|
err = dsi_error_handler(sender);
|
|
if (err) {
|
|
dev_err(sender->dev->dev, "Error handling failed\n");
|
|
err = -EAGAIN;
|
|
goto send_pkg_err;
|
|
}
|
|
|
|
/* Send pkg */
|
|
err = do_send_pkg(sender, pkg);
|
|
if (err) {
|
|
dev_err(sender->dev->dev, "sent pkg failed\n");
|
|
err = -EAGAIN;
|
|
goto send_pkg_err;
|
|
}
|
|
|
|
/* FIXME: should I query complete and fifo empty here? */
|
|
send_pkg_err:
|
|
return err;
|
|
}
|
|
|
|
static struct mdfld_dsi_pkg *pkg_sender_get_pkg_locked(
|
|
struct mdfld_dsi_pkg_sender *sender)
|
|
{
|
|
struct mdfld_dsi_pkg *pkg;
|
|
|
|
if (list_empty(&sender->free_list)) {
|
|
dev_err(sender->dev->dev, "No free pkg left\n");
|
|
return NULL;
|
|
}
|
|
pkg = list_first_entry(&sender->free_list, struct mdfld_dsi_pkg, entry);
|
|
/* Detach from free list */
|
|
list_del_init(&pkg->entry);
|
|
return pkg;
|
|
}
|
|
|
|
static void pkg_sender_put_pkg_locked(struct mdfld_dsi_pkg_sender *sender,
|
|
struct mdfld_dsi_pkg *pkg)
|
|
{
|
|
memset(pkg, 0, sizeof(struct mdfld_dsi_pkg));
|
|
INIT_LIST_HEAD(&pkg->entry);
|
|
list_add_tail(&pkg->entry, &sender->free_list);
|
|
}
|
|
|
|
static int mdfld_dbi_cb_init(struct mdfld_dsi_pkg_sender *sender,
|
|
struct psb_gtt *pg, int pipe)
|
|
{
|
|
unsigned long phys;
|
|
void *virt_addr = NULL;
|
|
|
|
switch (pipe) {
|
|
case 0:
|
|
/* FIXME: Doesn't this collide with stolen space ? */
|
|
phys = pg->gtt_phys_start - 0x1000;
|
|
break;
|
|
case 2:
|
|
phys = pg->gtt_phys_start - 0x800;
|
|
break;
|
|
default:
|
|
dev_err(sender->dev->dev, "Unsupported channel %d\n", pipe);
|
|
return -EINVAL;
|
|
}
|
|
|
|
virt_addr = ioremap_nocache(phys, 0x800);
|
|
if (!virt_addr) {
|
|
dev_err(sender->dev->dev, "Map DBI command buffer error\n");
|
|
return -ENOMEM;
|
|
}
|
|
sender->dbi_cb_phy = phys;
|
|
sender->dbi_cb_addr = virt_addr;
|
|
return 0;
|
|
}
|
|
|
|
static void mdfld_dbi_cb_destroy(struct mdfld_dsi_pkg_sender *sender)
|
|
{
|
|
if (sender && sender->dbi_cb_addr)
|
|
iounmap(sender->dbi_cb_addr);
|
|
}
|
|
|
|
static void pkg_sender_queue_pkg(struct mdfld_dsi_pkg_sender *sender,
|
|
struct mdfld_dsi_pkg *pkg,
|
|
int delay)
|
|
{
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&sender->lock, flags);
|
|
|
|
if (!delay) {
|
|
send_pkg(sender, pkg);
|
|
pkg_sender_put_pkg_locked(sender, pkg);
|
|
} else {
|
|
/* Queue it */
|
|
list_add_tail(&pkg->entry, &sender->pkg_list);
|
|
}
|
|
spin_unlock_irqrestore(&sender->lock, flags);
|
|
}
|
|
|
|
static void process_pkg_list(struct mdfld_dsi_pkg_sender *sender)
|
|
{
|
|
struct mdfld_dsi_pkg *pkg;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&sender->lock, flags);
|
|
|
|
while (!list_empty(&sender->pkg_list)) {
|
|
pkg = list_first_entry(&sender->pkg_list,
|
|
struct mdfld_dsi_pkg, entry);
|
|
send_pkg(sender, pkg);
|
|
list_del_init(&pkg->entry);
|
|
pkg_sender_put_pkg_locked(sender, pkg);
|
|
}
|
|
|
|
spin_unlock_irqrestore(&sender->lock, flags);
|
|
}
|
|
|
|
static int mdfld_dsi_send_mcs_long(struct mdfld_dsi_pkg_sender *sender,
|
|
u32 *data, u32 len, u8 transmission, int delay)
|
|
{
|
|
struct mdfld_dsi_pkg *pkg;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&sender->lock, flags);
|
|
pkg = pkg_sender_get_pkg_locked(sender);
|
|
spin_unlock_irqrestore(&sender->lock, flags);
|
|
|
|
if (!pkg) {
|
|
dev_err(sender->dev->dev, "No memory\n");
|
|
return -ENOMEM;
|
|
}
|
|
pkg->pkg_type = MDFLD_DSI_PKG_MCS_LONG_WRITE;
|
|
pkg->transmission_type = transmission;
|
|
pkg->pkg.long_pkg.data = data;
|
|
pkg->pkg.long_pkg.len = len;
|
|
INIT_LIST_HEAD(&pkg->entry);
|
|
|
|
pkg_sender_queue_pkg(sender, pkg, delay);
|
|
return 0;
|
|
}
|
|
|
|
static int mdfld_dsi_send_mcs_short(struct mdfld_dsi_pkg_sender *sender,
|
|
u8 cmd, u8 param, u8 param_num,
|
|
u8 transmission,
|
|
int delay)
|
|
{
|
|
struct mdfld_dsi_pkg *pkg;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&sender->lock, flags);
|
|
pkg = pkg_sender_get_pkg_locked(sender);
|
|
spin_unlock_irqrestore(&sender->lock, flags);
|
|
|
|
if (!pkg) {
|
|
dev_err(sender->dev->dev, "No memory\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
if (param_num) {
|
|
pkg->pkg_type = MDFLD_DSI_PKG_MCS_SHORT_WRITE_1;
|
|
pkg->pkg.short_pkg.param = param;
|
|
} else {
|
|
pkg->pkg_type = MDFLD_DSI_PKG_MCS_SHORT_WRITE_0;
|
|
pkg->pkg.short_pkg.param = 0;
|
|
}
|
|
pkg->transmission_type = transmission;
|
|
pkg->pkg.short_pkg.cmd = cmd;
|
|
INIT_LIST_HEAD(&pkg->entry);
|
|
|
|
pkg_sender_queue_pkg(sender, pkg, delay);
|
|
return 0;
|
|
}
|
|
|
|
static int mdfld_dsi_send_gen_short(struct mdfld_dsi_pkg_sender *sender,
|
|
u8 param0, u8 param1, u8 param_num,
|
|
u8 transmission,
|
|
int delay)
|
|
{
|
|
struct mdfld_dsi_pkg *pkg;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&sender->lock, flags);
|
|
pkg = pkg_sender_get_pkg_locked(sender);
|
|
spin_unlock_irqrestore(&sender->lock, flags);
|
|
|
|
if (!pkg) {
|
|
dev_err(sender->dev->dev, "No pkg memory\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
switch (param_num) {
|
|
case 0:
|
|
pkg->pkg_type = MDFLD_DSI_PKG_GEN_SHORT_WRITE_0;
|
|
pkg->pkg.short_pkg.cmd = 0;
|
|
pkg->pkg.short_pkg.param = 0;
|
|
break;
|
|
case 1:
|
|
pkg->pkg_type = MDFLD_DSI_PKG_GEN_SHORT_WRITE_1;
|
|
pkg->pkg.short_pkg.cmd = param0;
|
|
pkg->pkg.short_pkg.param = 0;
|
|
break;
|
|
case 2:
|
|
pkg->pkg_type = MDFLD_DSI_PKG_GEN_SHORT_WRITE_2;
|
|
pkg->pkg.short_pkg.cmd = param0;
|
|
pkg->pkg.short_pkg.param = param1;
|
|
break;
|
|
}
|
|
|
|
pkg->transmission_type = transmission;
|
|
INIT_LIST_HEAD(&pkg->entry);
|
|
|
|
pkg_sender_queue_pkg(sender, pkg, delay);
|
|
return 0;
|
|
}
|
|
|
|
static int mdfld_dsi_send_gen_long(struct mdfld_dsi_pkg_sender *sender,
|
|
u32 *data, u32 len, u8 transmission, int delay)
|
|
{
|
|
struct mdfld_dsi_pkg *pkg;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&sender->lock, flags);
|
|
pkg = pkg_sender_get_pkg_locked(sender);
|
|
spin_unlock_irqrestore(&sender->lock, flags);
|
|
|
|
if (!pkg) {
|
|
dev_err(sender->dev->dev, "No pkg memory\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
pkg->pkg_type = MDFLD_DSI_PKG_GEN_LONG_WRITE;
|
|
pkg->transmission_type = transmission;
|
|
pkg->pkg.long_pkg.data = data;
|
|
pkg->pkg.long_pkg.len = len;
|
|
|
|
INIT_LIST_HEAD(&pkg->entry);
|
|
|
|
pkg_sender_queue_pkg(sender, pkg, delay);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __read_panel_data(struct mdfld_dsi_pkg_sender *sender,
|
|
struct mdfld_dsi_pkg *pkg,
|
|
u32 *data,
|
|
u16 len)
|
|
{
|
|
unsigned long flags;
|
|
struct drm_device *dev = sender->dev;
|
|
int i;
|
|
u32 gen_data_reg;
|
|
int retry = MDFLD_DSI_READ_MAX_COUNT;
|
|
u8 transmission = pkg->transmission_type;
|
|
|
|
/*
|
|
* 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, 1 << 29);
|
|
|
|
if ((REG_READ(sender->mipi_intr_stat_reg) & (1 << 29)))
|
|
DRM_ERROR("Can NOT clean read data valid interrupt\n");
|
|
|
|
/*send out read request*/
|
|
send_pkg(sender, pkg);
|
|
|
|
pkg_sender_put_pkg_locked(sender, pkg);
|
|
|
|
/*polling read data avail interrupt*/
|
|
while (retry && !(REG_READ(sender->mipi_intr_stat_reg) & (1 << 29))) {
|
|
udelay(100);
|
|
retry--;
|
|
}
|
|
|
|
if (!retry) {
|
|
spin_unlock_irqrestore(&sender->lock, flags);
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
REG_WRITE(sender->mipi_intr_stat_reg, (1 << 29));
|
|
|
|
/*read data*/
|
|
if (transmission == MDFLD_DSI_HS_TRANSMISSION)
|
|
gen_data_reg = sender->mipi_hs_gen_data_reg;
|
|
else if (transmission == MDFLD_DSI_LP_TRANSMISSION)
|
|
gen_data_reg = sender->mipi_lp_gen_data_reg;
|
|
else {
|
|
DRM_ERROR("Unknown transmission");
|
|
spin_unlock_irqrestore(&sender->lock, flags);
|
|
return -EINVAL;
|
|
}
|
|
|
|
for (i=0; i<len; i++)
|
|
*(data + i) = REG_READ(gen_data_reg);
|
|
|
|
spin_unlock_irqrestore(&sender->lock, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mdfld_dsi_read_gen(struct mdfld_dsi_pkg_sender *sender,
|
|
u8 param0,
|
|
u8 param1,
|
|
u8 param_num,
|
|
u32 *data,
|
|
u16 len,
|
|
u8 transmission)
|
|
{
|
|
struct mdfld_dsi_pkg *pkg;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&sender->lock, flags);
|
|
|
|
pkg = pkg_sender_get_pkg_locked(sender);
|
|
|
|
spin_unlock_irqrestore(&sender->lock,flags);
|
|
|
|
if (!pkg) {
|
|
dev_err(sender->dev->dev, "No pkg memory\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
switch (param_num) {
|
|
case 0:
|
|
pkg->pkg_type = MDFLD_DSI_PKG_GEN_READ_0;
|
|
pkg->pkg.short_pkg.cmd = 0;
|
|
pkg->pkg.short_pkg.param = 0;
|
|
break;
|
|
case 1:
|
|
pkg->pkg_type = MDFLD_DSI_PKG_GEN_READ_1;
|
|
pkg->pkg.short_pkg.cmd = param0;
|
|
pkg->pkg.short_pkg.param = 0;
|
|
break;
|
|
case 2:
|
|
pkg->pkg_type = MDFLD_DSI_PKG_GEN_READ_2;
|
|
pkg->pkg.short_pkg.cmd = param0;
|
|
pkg->pkg.short_pkg.param = param1;
|
|
break;
|
|
}
|
|
|
|
pkg->transmission_type = transmission;
|
|
|
|
INIT_LIST_HEAD(&pkg->entry);
|
|
|
|
return __read_panel_data(sender, pkg, data, len);
|
|
}
|
|
|
|
static int mdfld_dsi_read_mcs(struct mdfld_dsi_pkg_sender *sender,
|
|
u8 cmd,
|
|
u32 *data,
|
|
u16 len,
|
|
u8 transmission)
|
|
{
|
|
struct mdfld_dsi_pkg *pkg;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&sender->lock, flags);
|
|
|
|
pkg = pkg_sender_get_pkg_locked(sender);
|
|
|
|
spin_unlock_irqrestore(&sender->lock, flags);
|
|
|
|
if (!pkg) {
|
|
dev_err(sender->dev->dev, "No pkg memory\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
pkg->pkg_type = MDFLD_DSI_PKG_MCS_READ;
|
|
pkg->pkg.short_pkg.cmd = cmd;
|
|
pkg->pkg.short_pkg.param = 0;
|
|
|
|
pkg->transmission_type = transmission;
|
|
|
|
INIT_LIST_HEAD(&pkg->entry);
|
|
|
|
return __read_panel_data(sender, pkg, data, len);
|
|
}
|
|
|
|
void dsi_controller_dbi_init(struct mdfld_dsi_config * dsi_config, int pipe)
|
|
{
|
|
struct drm_device * dev = dsi_config->dev;
|
|
u32 reg_offset = pipe ? MIPIC_REG_OFFSET : 0;
|
|
int lane_count = dsi_config->lane_count;
|
|
u32 val = 0;
|
|
|
|
/*un-ready device*/
|
|
REG_WRITE((MIPIA_DEVICE_READY_REG + reg_offset), 0x00000000);
|
|
|
|
/*init dsi adapter before kicking off*/
|
|
REG_WRITE((MIPIA_CONTROL_REG + reg_offset), 0x00000018);
|
|
|
|
/*TODO: figure out how to setup these registers*/
|
|
REG_WRITE((MIPIA_DPHY_PARAM_REG + reg_offset), 0x150c3408);
|
|
REG_WRITE((MIPIA_CLK_LANE_SWITCH_TIME_CNT_REG + reg_offset), 0x000a0014);
|
|
REG_WRITE((MIPIA_DBI_BW_CTRL_REG + reg_offset), 0x00000400);
|
|
REG_WRITE((MIPIA_DBI_FIFO_THROTTLE_REG + reg_offset), 0x00000001);
|
|
REG_WRITE((MIPIA_HS_LS_DBI_ENABLE_REG + reg_offset), 0x00000000);
|
|
|
|
/*enable all interrupts*/
|
|
REG_WRITE((MIPIA_INTR_EN_REG + reg_offset), 0xffffffff);
|
|
/*max value: 20 clock cycles of txclkesc*/
|
|
REG_WRITE((MIPIA_TURN_AROUND_TIMEOUT_REG + reg_offset), 0x0000001f);
|
|
/*min 21 txclkesc, max: ffffh*/
|
|
REG_WRITE((MIPIA_DEVICE_RESET_TIMER_REG + reg_offset), 0x0000ffff);
|
|
/*min: 7d0 max: 4e20*/
|
|
REG_WRITE((MIPIA_INIT_COUNT_REG + reg_offset), 0x00000fa0);
|
|
|
|
/*set up max return packet size*/
|
|
REG_WRITE((MIPIA_MAX_RETURN_PACK_SIZE_REG + reg_offset),
|
|
MDFLD_DSI_MAX_RETURN_PACKET_SIZE);
|
|
|
|
/*set up func_prg*/
|
|
val |= lane_count;
|
|
val |= (dsi_config->channel_num << DSI_DBI_VIRT_CHANNEL_OFFSET);
|
|
val |= DSI_DBI_COLOR_FORMAT_OPTION2;
|
|
REG_WRITE((MIPIA_DSI_FUNC_PRG_REG + reg_offset), val);
|
|
|
|
REG_WRITE((MIPIA_HS_TX_TIMEOUT_REG + reg_offset), 0x3fffff);
|
|
REG_WRITE((MIPIA_LP_RX_TIMEOUT_REG + reg_offset), 0xffff);
|
|
|
|
REG_WRITE((MIPIA_HIGH_LOW_SWITCH_COUNT_REG + reg_offset), 0x46);
|
|
REG_WRITE((MIPIA_EOT_DISABLE_REG + reg_offset), 0x00000000);
|
|
REG_WRITE((MIPIA_LP_BYTECLK_REG + reg_offset), 0x00000004);
|
|
REG_WRITE((MIPIA_DEVICE_READY_REG + reg_offset), 0x00000001);
|
|
}
|
|
|
|
void dsi_controller_dpi_init(struct mdfld_dsi_config * dsi_config, int pipe)
|
|
{
|
|
struct drm_device * dev = dsi_config->dev;
|
|
u32 reg_offset = pipe ? MIPIC_REG_OFFSET : 0;
|
|
int lane_count = dsi_config->lane_count;
|
|
struct mdfld_dsi_dpi_timing dpi_timing;
|
|
struct drm_display_mode * mode = dsi_config->mode;
|
|
u32 val = 0;
|
|
|
|
/*un-ready device*/
|
|
REG_WRITE((MIPIA_DEVICE_READY_REG + reg_offset), 0x00000000);
|
|
|
|
/*init dsi adapter before kicking off*/
|
|
REG_WRITE((MIPIA_CONTROL_REG + reg_offset), 0x00000018);
|
|
|
|
/*enable all interrupts*/
|
|
REG_WRITE((MIPIA_INTR_EN_REG + reg_offset), 0xffffffff);
|
|
|
|
/*set up func_prg*/
|
|
val |= lane_count;
|
|
val |= dsi_config->channel_num << DSI_DPI_VIRT_CHANNEL_OFFSET;
|
|
|
|
switch(dsi_config->bpp) {
|
|
case 16:
|
|
val |= DSI_DPI_COLOR_FORMAT_RGB565;
|
|
break;
|
|
case 18:
|
|
val |= DSI_DPI_COLOR_FORMAT_RGB666;
|
|
break;
|
|
case 24:
|
|
val |= DSI_DPI_COLOR_FORMAT_RGB888;
|
|
break;
|
|
default:
|
|
DRM_ERROR("unsupported color format, bpp = %d\n", dsi_config->bpp);
|
|
}
|
|
|
|
REG_WRITE((MIPIA_DSI_FUNC_PRG_REG + reg_offset), val);
|
|
|
|
REG_WRITE((MIPIA_HS_TX_TIMEOUT_REG + reg_offset),
|
|
(mode->vtotal * mode->htotal * dsi_config->bpp / (8 * lane_count)) & DSI_HS_TX_TIMEOUT_MASK);
|
|
REG_WRITE((MIPIA_LP_RX_TIMEOUT_REG + reg_offset), 0xffff & DSI_LP_RX_TIMEOUT_MASK);
|
|
|
|
/*max value: 20 clock cycles of txclkesc*/
|
|
REG_WRITE((MIPIA_TURN_AROUND_TIMEOUT_REG + reg_offset), 0x14 & DSI_TURN_AROUND_TIMEOUT_MASK);
|
|
|
|
/*min 21 txclkesc, max: ffffh*/
|
|
REG_WRITE((MIPIA_DEVICE_RESET_TIMER_REG + reg_offset), 0xffff & DSI_RESET_TIMER_MASK);
|
|
|
|
REG_WRITE((MIPIA_DPI_RESOLUTION_REG + reg_offset), mode->vdisplay << 16 | mode->hdisplay);
|
|
|
|
/*set DPI timing registers*/
|
|
mdfld_dsi_dpi_timing_calculation(mode, &dpi_timing, dsi_config->lane_count, dsi_config->bpp);
|
|
|
|
REG_WRITE((MIPIA_HSYNC_COUNT_REG + reg_offset), dpi_timing.hsync_count & DSI_DPI_TIMING_MASK);
|
|
REG_WRITE((MIPIA_HBP_COUNT_REG + reg_offset), dpi_timing.hbp_count & DSI_DPI_TIMING_MASK);
|
|
REG_WRITE((MIPIA_HFP_COUNT_REG + reg_offset), dpi_timing.hfp_count & DSI_DPI_TIMING_MASK);
|
|
REG_WRITE((MIPIA_HACTIVE_COUNT_REG + reg_offset), dpi_timing.hactive_count & DSI_DPI_TIMING_MASK);
|
|
REG_WRITE((MIPIA_VSYNC_COUNT_REG + reg_offset), dpi_timing.vsync_count & DSI_DPI_TIMING_MASK);
|
|
REG_WRITE((MIPIA_VBP_COUNT_REG + reg_offset), dpi_timing.vbp_count & DSI_DPI_TIMING_MASK);
|
|
REG_WRITE((MIPIA_VFP_COUNT_REG + reg_offset), dpi_timing.vfp_count & DSI_DPI_TIMING_MASK);
|
|
|
|
REG_WRITE((MIPIA_HIGH_LOW_SWITCH_COUNT_REG + reg_offset), 0x46);
|
|
|
|
/*min: 7d0 max: 4e20*/
|
|
REG_WRITE((MIPIA_INIT_COUNT_REG + reg_offset), 0x000007d0);
|
|
|
|
/*set up video mode*/
|
|
val = dsi_config->video_mode | DSI_DPI_COMPLETE_LAST_LINE;
|
|
REG_WRITE((MIPIA_VIDEO_MODE_FORMAT_REG + reg_offset), val);
|
|
|
|
REG_WRITE((MIPIA_EOT_DISABLE_REG + reg_offset), 0x00000000);
|
|
|
|
REG_WRITE((MIPIA_LP_BYTECLK_REG + reg_offset), 0x00000004);
|
|
|
|
/*TODO: figure out how to setup these registers*/
|
|
REG_WRITE((MIPIA_DPHY_PARAM_REG + reg_offset), 0x150c3408);
|
|
|
|
REG_WRITE((MIPIA_CLK_LANE_SWITCH_TIME_CNT_REG + reg_offset), (0xa << 16) | 0x14);
|
|
|
|
/*set device ready*/
|
|
REG_WRITE((MIPIA_DEVICE_READY_REG + reg_offset), 0x00000001);
|
|
}
|
|
|
|
static void dsi_controller_init(struct mdfld_dsi_config * dsi_config, int pipe)
|
|
{
|
|
if (!dsi_config || ((pipe != 0) && (pipe != 2))) {
|
|
DRM_ERROR("Invalid parameters\n");
|
|
return;
|
|
}
|
|
|
|
if (dsi_config->type == MDFLD_DSI_ENCODER_DPI)
|
|
dsi_controller_dpi_init(dsi_config, pipe);
|
|
else if (dsi_config->type == MDFLD_DSI_ENCODER_DBI)
|
|
dsi_controller_dbi_init(dsi_config, pipe);
|
|
else
|
|
DRM_ERROR("Bad DSI encoder type\n");
|
|
}
|
|
|
|
void mdfld_dsi_cmds_kick_out(struct mdfld_dsi_pkg_sender *sender)
|
|
{
|
|
process_pkg_list(sender);
|
|
}
|
|
|
|
int mdfld_dsi_send_dcs(struct mdfld_dsi_pkg_sender *sender,
|
|
u8 dcs, u8 *param, u32 param_num, u8 data_src,
|
|
int delay)
|
|
{
|
|
struct mdfld_dsi_pkg *pkg;
|
|
u32 cb_phy = sender->dbi_cb_phy;
|
|
struct drm_device *dev = sender->dev;
|
|
u32 index = 0;
|
|
u8 *cb = (u8 *)sender->dbi_cb_addr;
|
|
unsigned long flags;
|
|
int retry;
|
|
u8 *dst = NULL;
|
|
u32 len;
|
|
|
|
if (!sender) {
|
|
WARN_ON(1);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!sender->dbi_pkg_support) {
|
|
dev_err(dev->dev, "No DBI pkg sending on this sender\n");
|
|
return -ENOTSUPP;
|
|
}
|
|
|
|
if (param_num > MDFLD_MAX_DCS_PARAM) {
|
|
dev_err(dev->dev, "Sender only supports up to %d DCS params\n",
|
|
MDFLD_MAX_DCS_PARAM);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*
|
|
* If dcs is write_mem_start, send it directly using DSI adapter
|
|
* interface
|
|
*/
|
|
if (dcs == DCS_WRITE_MEM_START) {
|
|
if (!spin_trylock(&sender->lock))
|
|
return -EAGAIN;
|
|
|
|
/*
|
|
* query whether DBI FIFO is empty,
|
|
* if not wait it becoming empty
|
|
*/
|
|
retry = MDFLD_DSI_DBI_FIFO_TIMEOUT;
|
|
while (retry &&
|
|
!(REG_READ(sender->mipi_gen_fifo_stat_reg) & (1 << 27))) {
|
|
udelay(500);
|
|
retry--;
|
|
}
|
|
|
|
/* If DBI FIFO timeout, drop this frame */
|
|
if (!retry) {
|
|
spin_unlock(&sender->lock);
|
|
return 0;
|
|
}
|
|
|
|
*(cb + (index++)) = write_mem_start;
|
|
|
|
REG_WRITE(sender->mipi_cmd_len_reg, 1);
|
|
REG_WRITE(sender->mipi_cmd_addr_reg,
|
|
cb_phy | (1 << 0) | (1 << 1));
|
|
|
|
retry = MDFLD_DSI_DBI_FIFO_TIMEOUT;
|
|
while (retry &&
|
|
(REG_READ(sender->mipi_cmd_addr_reg) & (1 << 0))) {
|
|
udelay(1);
|
|
retry--;
|
|
}
|
|
|
|
spin_unlock(&sender->lock);
|
|
return 0;
|
|
}
|
|
|
|
/* Get a free pkg */
|
|
spin_lock_irqsave(&sender->lock, flags);
|
|
pkg = pkg_sender_get_pkg_locked(sender);
|
|
spin_unlock_irqrestore(&sender->lock, flags);
|
|
|
|
if (!pkg) {
|
|
dev_err(dev->dev, "No packages memory\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
dst = pkg->pkg.dcs_pkg.param;
|
|
memcpy(dst, param, param_num);
|
|
|
|
pkg->pkg_type = MDFLD_DSI_PKG_DCS;
|
|
pkg->transmission_type = MDFLD_DSI_DCS;
|
|
pkg->pkg.dcs_pkg.cmd = dcs;
|
|
pkg->pkg.dcs_pkg.param_num = param_num;
|
|
pkg->pkg.dcs_pkg.data_src = data_src;
|
|
|
|
INIT_LIST_HEAD(&pkg->entry);
|
|
|
|
if (param_num == 0)
|
|
return mdfld_dsi_send_mcs_short_hs(sender, dcs, 0, 0, delay);
|
|
else if (param_num == 1)
|
|
return mdfld_dsi_send_mcs_short_hs(sender, dcs,
|
|
param[0], 1, delay);
|
|
else if (param_num > 1) {
|
|
len = (param_num + 1) / 4;
|
|
if ((param_num + 1) % 4)
|
|
len++;
|
|
return mdfld_dsi_send_mcs_long_hs(sender,
|
|
(u32 *)&pkg->pkg.dcs_pkg, len, delay);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int mdfld_dsi_send_mcs_short_hs(struct mdfld_dsi_pkg_sender *sender,
|
|
u8 cmd, u8 param, u8 param_num, int delay)
|
|
{
|
|
if (!sender) {
|
|
WARN_ON(1);
|
|
return -EINVAL;
|
|
}
|
|
return mdfld_dsi_send_mcs_short(sender, cmd, param, param_num,
|
|
MDFLD_DSI_HS_TRANSMISSION, delay);
|
|
}
|
|
|
|
int mdfld_dsi_send_mcs_short_lp(struct mdfld_dsi_pkg_sender *sender,
|
|
u8 cmd, u8 param, u8 param_num, int delay)
|
|
{
|
|
if (!sender) {
|
|
WARN_ON(1);
|
|
return -EINVAL;
|
|
}
|
|
return mdfld_dsi_send_mcs_short(sender, cmd, param, param_num,
|
|
MDFLD_DSI_LP_TRANSMISSION, delay);
|
|
}
|
|
|
|
int mdfld_dsi_send_mcs_long_hs(struct mdfld_dsi_pkg_sender *sender,
|
|
u32 *data,
|
|
u32 len,
|
|
int delay)
|
|
{
|
|
if (!sender || !data || !len) {
|
|
DRM_ERROR("Invalid parameters\n");
|
|
return -EINVAL;
|
|
}
|
|
return mdfld_dsi_send_mcs_long(sender, data, len,
|
|
MDFLD_DSI_HS_TRANSMISSION, delay);
|
|
}
|
|
|
|
int mdfld_dsi_send_mcs_long_lp(struct mdfld_dsi_pkg_sender *sender,
|
|
u32 *data,
|
|
u32 len,
|
|
int delay)
|
|
{
|
|
if (!sender || !data || !len) {
|
|
WARN_ON(1);
|
|
return -EINVAL;
|
|
}
|
|
return mdfld_dsi_send_mcs_long(sender, data, len,
|
|
MDFLD_DSI_LP_TRANSMISSION, delay);
|
|
}
|
|
|
|
int mdfld_dsi_send_gen_short_hs(struct mdfld_dsi_pkg_sender *sender,
|
|
u8 param0, u8 param1, u8 param_num, int delay)
|
|
{
|
|
if (!sender) {
|
|
WARN_ON(1);
|
|
return -EINVAL;
|
|
}
|
|
return mdfld_dsi_send_gen_short(sender, param0, param1, param_num,
|
|
MDFLD_DSI_HS_TRANSMISSION, delay);
|
|
}
|
|
|
|
int mdfld_dsi_send_gen_short_lp(struct mdfld_dsi_pkg_sender *sender,
|
|
u8 param0, u8 param1, u8 param_num, int delay)
|
|
{
|
|
if (!sender || param_num < 0 || param_num > 2) {
|
|
WARN_ON(1);
|
|
return -EINVAL;
|
|
}
|
|
return mdfld_dsi_send_gen_short(sender, param0, param1, param_num,
|
|
MDFLD_DSI_LP_TRANSMISSION, delay);
|
|
}
|
|
|
|
int mdfld_dsi_send_gen_long_hs(struct mdfld_dsi_pkg_sender *sender,
|
|
u32 *data,
|
|
u32 len,
|
|
int delay)
|
|
{
|
|
if (!sender || !data || !len) {
|
|
WARN_ON(1);
|
|
return -EINVAL;
|
|
}
|
|
return mdfld_dsi_send_gen_long(sender, data, len,
|
|
MDFLD_DSI_HS_TRANSMISSION, delay);
|
|
}
|
|
|
|
int mdfld_dsi_send_gen_long_lp(struct mdfld_dsi_pkg_sender *sender,
|
|
u32 *data,
|
|
u32 len,
|
|
int delay)
|
|
{
|
|
if (!sender || !data || !len) {
|
|
WARN_ON(1);
|
|
return -EINVAL;
|
|
}
|
|
return mdfld_dsi_send_gen_long(sender, data, len,
|
|
MDFLD_DSI_LP_TRANSMISSION, delay);
|
|
}
|
|
|
|
int mdfld_dsi_read_gen_hs(struct mdfld_dsi_pkg_sender *sender,
|
|
u8 param0,
|
|
u8 param1,
|
|
u8 param_num,
|
|
u32 *data,
|
|
u16 len)
|
|
{
|
|
if (!sender || !data || param_num < 0 || param_num > 2
|
|
|| !data || !len) {
|
|
DRM_ERROR("Invalid parameters\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
return mdfld_dsi_read_gen(sender, param0, param1, param_num,
|
|
data, len, MDFLD_DSI_HS_TRANSMISSION);
|
|
|
|
}
|
|
|
|
int mdfld_dsi_read_gen_lp(struct mdfld_dsi_pkg_sender *sender,
|
|
u8 param0,
|
|
u8 param1,
|
|
u8 param_num,
|
|
u32 *data,
|
|
u16 len)
|
|
{
|
|
if (!sender || !data || param_num < 0 || param_num > 2
|
|
|| !data || !len) {
|
|
DRM_ERROR("Invalid parameters\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
return mdfld_dsi_read_gen(sender, param0, param1, param_num,
|
|
data, len, MDFLD_DSI_LP_TRANSMISSION);
|
|
}
|
|
|
|
int mdfld_dsi_read_mcs_hs(struct mdfld_dsi_pkg_sender *sender,
|
|
u8 cmd,
|
|
u32 *data,
|
|
u16 len)
|
|
{
|
|
if (!sender || !data || !len) {
|
|
DRM_ERROR("Invalid parameters\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
return mdfld_dsi_read_mcs(sender, cmd, data, len,
|
|
MDFLD_DSI_HS_TRANSMISSION);
|
|
}
|
|
|
|
int mdfld_dsi_read_mcs_lp(struct mdfld_dsi_pkg_sender *sender,
|
|
u8 cmd,
|
|
u32 *data,
|
|
u16 len)
|
|
{
|
|
if (!sender || !data || !len) {
|
|
WARN_ON(1);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return mdfld_dsi_read_mcs(sender, cmd, data, len,
|
|
MDFLD_DSI_LP_TRANSMISSION);
|
|
}
|
|
|
|
int mdfld_dsi_pkg_sender_init(struct mdfld_dsi_connector *dsi_connector,
|
|
int pipe)
|
|
{
|
|
int ret;
|
|
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;
|
|
struct psb_gtt *pg = &dev_priv->gtt;
|
|
int i;
|
|
struct mdfld_dsi_pkg *pkg, *tmp;
|
|
u32 mipi_val = 0;
|
|
|
|
if (!dsi_connector) {
|
|
WARN_ON(1);
|
|
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) {
|
|
dev_err(dev->dev, "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 dbi command buffer*/
|
|
|
|
if (dsi_config->type == MDFLD_DSI_ENCODER_DBI) {
|
|
pkg_sender->dbi_pkg_support = 1;
|
|
ret = mdfld_dbi_cb_init(pkg_sender, pg, pipe);
|
|
if (ret) {
|
|
dev_err(dev->dev, "DBI command buffer map failed\n");
|
|
goto mapping_err;
|
|
}
|
|
}
|
|
|
|
/* Init regs */
|
|
if (pipe == 0) {
|
|
pkg_sender->dpll_reg = MRST_DPLL_A;
|
|
pkg_sender->dspcntr_reg = DSPACNTR;
|
|
pkg_sender->pipeconf_reg = PIPEACONF;
|
|
pkg_sender->dsplinoff_reg = DSPALINOFF;
|
|
pkg_sender->dspsurf_reg = DSPASURF;
|
|
pkg_sender->pipestat_reg = PIPEASTAT;
|
|
|
|
pkg_sender->mipi_intr_stat_reg = MIPIA_INTR_STAT_REG;
|
|
pkg_sender->mipi_lp_gen_data_reg = MIPIA_LP_GEN_DATA_REG;
|
|
pkg_sender->mipi_hs_gen_data_reg = MIPIA_HS_GEN_DATA_REG;
|
|
pkg_sender->mipi_lp_gen_ctrl_reg = MIPIA_LP_GEN_CTRL_REG;
|
|
pkg_sender->mipi_hs_gen_ctrl_reg = MIPIA_HS_GEN_CTRL_REG;
|
|
pkg_sender->mipi_gen_fifo_stat_reg = MIPIA_GEN_FIFO_STAT_REG;
|
|
pkg_sender->mipi_data_addr_reg = MIPIA_DATA_ADD_REG;
|
|
pkg_sender->mipi_data_len_reg = MIPIA_DATA_LEN_REG;
|
|
pkg_sender->mipi_cmd_addr_reg = MIPIA_CMD_ADD_REG;
|
|
pkg_sender->mipi_cmd_len_reg = MIPIA_CMD_LEN_REG;
|
|
} else if (pipe == 2) {
|
|
pkg_sender->dpll_reg = MRST_DPLL_A;
|
|
pkg_sender->dspcntr_reg = DSPCCNTR;
|
|
pkg_sender->pipeconf_reg = PIPECCONF;
|
|
pkg_sender->dsplinoff_reg = DSPCLINOFF;
|
|
pkg_sender->dspsurf_reg = DSPCSURF;
|
|
pkg_sender->pipestat_reg = PIPECSTAT;
|
|
|
|
pkg_sender->mipi_intr_stat_reg =
|
|
MIPIA_INTR_STAT_REG + MIPIC_REG_OFFSET;
|
|
pkg_sender->mipi_lp_gen_data_reg =
|
|
MIPIA_LP_GEN_DATA_REG + MIPIC_REG_OFFSET;
|
|
pkg_sender->mipi_hs_gen_data_reg =
|
|
MIPIA_HS_GEN_DATA_REG + MIPIC_REG_OFFSET;
|
|
pkg_sender->mipi_lp_gen_ctrl_reg =
|
|
MIPIA_LP_GEN_CTRL_REG + MIPIC_REG_OFFSET;
|
|
pkg_sender->mipi_hs_gen_ctrl_reg =
|
|
MIPIA_HS_GEN_CTRL_REG + MIPIC_REG_OFFSET;
|
|
pkg_sender->mipi_gen_fifo_stat_reg =
|
|
MIPIA_GEN_FIFO_STAT_REG + MIPIC_REG_OFFSET;
|
|
pkg_sender->mipi_data_addr_reg =
|
|
MIPIA_DATA_ADD_REG + MIPIC_REG_OFFSET;
|
|
pkg_sender->mipi_data_len_reg =
|
|
MIPIA_DATA_LEN_REG + MIPIC_REG_OFFSET;
|
|
pkg_sender->mipi_cmd_addr_reg =
|
|
MIPIA_CMD_ADD_REG + MIPIC_REG_OFFSET;
|
|
pkg_sender->mipi_cmd_len_reg =
|
|
MIPIA_CMD_LEN_REG + MIPIC_REG_OFFSET;
|
|
}
|
|
|
|
/* Init pkg list */
|
|
INIT_LIST_HEAD(&pkg_sender->pkg_list);
|
|
INIT_LIST_HEAD(&pkg_sender->free_list);
|
|
|
|
spin_lock_init(&pkg_sender->lock);
|
|
|
|
/* Allocate free pkg pool */
|
|
for (i = 0; i < MDFLD_MAX_PKG_NUM; i++) {
|
|
pkg = kzalloc(sizeof(struct mdfld_dsi_pkg), GFP_KERNEL);
|
|
if (!pkg) {
|
|
dev_err(dev->dev, "Out of memory allocating pkg pool");
|
|
ret = -ENOMEM;
|
|
goto pkg_alloc_err;
|
|
}
|
|
INIT_LIST_HEAD(&pkg->entry);
|
|
list_add_tail(&pkg->entry, &pkg_sender->free_list);
|
|
}
|
|
|
|
/*
|
|
* For video mode, don't enable DPI timing output here,
|
|
* will init the DPI timing output during mode setting.
|
|
*/
|
|
if (dsi_config->type == MDFLD_DSI_ENCODER_DPI)
|
|
mipi_val = PASS_FROM_SPHY_TO_AFE | SEL_FLOPPED_HSTX;
|
|
else if (dsi_config->type == MDFLD_DSI_ENCODER_DBI)
|
|
mipi_val = PASS_FROM_SPHY_TO_AFE | SEL_FLOPPED_HSTX
|
|
| TE_TRIGGER_GPIO_PIN;
|
|
else
|
|
DRM_ERROR("Bad DSI encoder type\n");
|
|
|
|
if (pipe == 0) {
|
|
mipi_val |= 0x2;
|
|
REG_WRITE(MIPI, mipi_val);
|
|
REG_READ(MIPI);
|
|
} else if (pipe == 2) {
|
|
REG_WRITE(MIPI_C, mipi_val);
|
|
REG_READ(MIPI_C);
|
|
}
|
|
|
|
/*do dsi controller init*/
|
|
dsi_controller_init(dsi_config, pipe);
|
|
|
|
return 0;
|
|
|
|
pkg_alloc_err:
|
|
list_for_each_entry_safe(pkg, tmp, &pkg_sender->free_list, entry) {
|
|
list_del(&pkg->entry);
|
|
kfree(pkg);
|
|
}
|
|
|
|
/* Free mapped command buffer */
|
|
mdfld_dbi_cb_destroy(pkg_sender);
|
|
mapping_err:
|
|
kfree(pkg_sender);
|
|
dsi_connector->pkg_sender = NULL;
|
|
return ret;
|
|
}
|
|
|
|
void mdfld_dsi_pkg_sender_destroy(struct mdfld_dsi_pkg_sender *sender)
|
|
{
|
|
struct mdfld_dsi_pkg *pkg, *tmp;
|
|
|
|
if (!sender || IS_ERR(sender))
|
|
return;
|
|
|
|
/* Free pkg pool */
|
|
list_for_each_entry_safe(pkg, tmp, &sender->free_list, entry) {
|
|
list_del(&pkg->entry);
|
|
kfree(pkg);
|
|
}
|
|
/* Free pkg list */
|
|
list_for_each_entry_safe(pkg, tmp, &sender->pkg_list, entry) {
|
|
list_del(&pkg->entry);
|
|
kfree(pkg);
|
|
}
|
|
mdfld_dbi_cb_destroy(sender); /* free mapped command buffer */
|
|
kfree(sender);
|
|
}
|