blob: c0d547ea33842fe8c5fef434082dc33e5d4d8c8a [file] [log] [blame]
/* Driver for the TC358764 Parallel to MIPI bridge
*
* Copyright (C) 2014 Parrot S.A.
*
* @author Lionel Flandrin <lionel.flandrin@parrot.com>
* @date 12-Dec-2014
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <media/tc358764.h>
#define DEVICE_NAME "tc358764"
#define TC358764_REG_VERSION 0x0000
/* Expected chip revision */
#define TC358764_VERSION 0x4401
/* These registers can be written using tc358764_write_dsi_config */
#define TC358764_REG_DSI_CONTROL 0x040C
#define TC358764_REG_DSI_INT_ENA 0x0418
#define TC358764_REG_DSI_ACKERR_INTENA 0x0438
#define TC358764_REG_DSI_ACKERR_HALT 0x043C
#define TC358764_REG_DSI_RXERR_INTENA 0x0444
#define TC358764_REG_DSI_RXERR_HALT 0x0448
#define TC358764_REG_DSI_ERR_INTENA 0x0450
#define TC358764_REG_DSI_ERR_HALT 0x0454
#define TC358764_REG_DSI_CONFW 0x0500
#define TC358764_REG_DSICMD_TX 0x0600
#define TC358764_REG_DSICMD_TYPE 0x0602
#define TC358764_REG_DSICMD_WC 0x0604
#define TC358764_REG_DSICMD_WD0 0x0610
#define TC358764_REG_DSICMD_WD1 0x0612
#define TC358764_REG_DSICMD_WD2 0x0614
#define TC358764_REG_DSICMD_WD3 0x0616
struct tc358764 {
struct i2c_client *client;
};
static int tc358764_read16(struct tc358764 *tc358764, u16 reg, u16 *val)
{
struct i2c_client *client = tc358764->client;
u8 regb[2];
u8 valb[2];
int ret;
struct i2c_msg msg[] = {
[0] = {
.addr = client->addr,
.flags = 0,
.len = ARRAY_SIZE(regb),
.buf = regb,
},
[1] = {
.addr = client->addr,
.flags = I2C_M_RD,
.len = ARRAY_SIZE(valb),
.buf = valb,
},
};
regb[0] = (u8)(reg >> 8);
regb[1] = (u8)(reg);
ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
if (ret < 0) {
dev_err(&client->dev, "I2C read failed with error %d", ret);
return ret;
}
*val = (((u16)valb[0]) << 8) | ((u16)valb[1]);
return 0;
}
static int tc358764_write16(struct tc358764 *tc358764, u16 reg, u16 val)
{
struct i2c_client *client = tc358764->client;
u8 data[4];
int ret;
struct i2c_msg msg[] = {
[0] = {
.addr = client->addr,
.flags = 0,
.len = ARRAY_SIZE(data),
.buf = data,
},
};
data[0] = (u8)(reg >> 8);
data[1] = (u8)(reg);
data[2] = (u8)(val >> 8);
data[3] = (u8)(val);
ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
if (ret < 0) {
dev_err(&client->dev, "I2C write failed with error %d", ret);
return ret;
}
return 0;
}
static int tc358764_write32(struct tc358764 *tc358764, u16 reg, u32 val)
{
struct i2c_client *client = tc358764->client;
u8 data[6];
int ret;
struct i2c_msg msg[] = {
[0] = {
.addr = client->addr,
.flags = 0,
.len = ARRAY_SIZE(data),
.buf = data,
},
};
data[0] = (u8)(reg >> 8);
data[1] = (u8)(reg);
// The chip uses this weird "PDP-endian" style byte organisation for
// 32byte writes.
data[2] = (u8)(val >> 8);
data[3] = (u8)(val);
data[4] = (u8)(val >> 24);
data[5] = (u8)(val >> 16);
ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
if (ret < 0) {
dev_err(&client->dev, "I2C write failed with error %d", ret);
return ret;
}
return 0;
}
/* Certain registers have to be written indirectly using the DSI_CONFW
* register. It lets us set or clear individual bits (depending on whether set
* is true or false) data in reg */
static int tc358764_write_dsi_config(struct tc358764 *tc358764,
u16 reg,
int set,
u16 data)
{
u32 id = 0;
u32 r;
int i;
/* The DSI_CONFW register can be used to write to a specific list of
* registers selected with a 5bit identifier */
static const struct { u16 reg; u32 id; } id_list[] = {
{ TC358764_REG_DSI_CONTROL, 0x03 },
{ TC358764_REG_DSI_INT_ENA, 0x06 },
{ TC358764_REG_DSI_ACKERR_INTENA, 0x0e },
{ TC358764_REG_DSI_ACKERR_HALT, 0x0f },
{ TC358764_REG_DSI_RXERR_INTENA, 0x11 },
{ TC358764_REG_DSI_RXERR_HALT, 0x12 },
{ TC358764_REG_DSI_ERR_INTENA, 0x14 },
{ TC358764_REG_DSI_ERR_HALT, 0x15 },
};
for (i = 0; i < ARRAY_SIZE(id_list); i++) {
if (id_list[i].reg == reg) {
id = id_list[i].id;
break;
}
}
/* Crash if we're called with an invalid register */
BUG_ON(id == 0);
/* Build the DSI_CONFW command value */
r = id << 24;
r |= (set ? 0x5 : 0x6) << 29;
r |= (u32)data;
return tc358764_write32(tc358764, TC358764_REG_DSI_CONFW, r);
}
/* Send a short command with no parameters */
static int tc358764_dsi_write_no_param(struct tc358764 *tc358764, u8 cmd)
{
int ret;
/* Short write without parameter */
ret = tc358764_write16(tc358764, TC358764_REG_DSICMD_TYPE, 0x1005);
if (ret)
goto err;
/* Word count is always 0 for short writes */
ret = tc358764_write16(tc358764, TC358764_REG_DSICMD_WC, 0);
if (ret)
goto err;
/* Command byte to be sent */
ret = tc358764_write16(tc358764, TC358764_REG_DSICMD_WD0, cmd);
if (ret)
goto err;
/* Start DSI transfer */
ret = tc358764_write16(tc358764, TC358764_REG_DSICMD_TX, 1);
if (ret)
goto err;
err:
return ret;
}
/* Send a short command with one parameters */
static int tc358764_dsi_write_one_param(struct tc358764 *tc358764,
u8 cmd,
u8
param)
{
int ret;
/* Short write with one parameter */
ret = tc358764_write16(tc358764, TC358764_REG_DSICMD_TYPE, 0x1015);
if (ret)
goto err;
/* Word count is always 0 for short writes */
ret = tc358764_write16(tc358764, TC358764_REG_DSICMD_WC, 0);
if (ret)
goto err;
/* Command and parameter to be sent */
ret = tc358764_write16(tc358764,
TC358764_REG_DSICMD_WD0,
(((u16)param) << 8) | (u16)cmd);
if (ret)
goto err;
/* Start DSI transfer */
ret = tc358764_write16(tc358764, TC358764_REG_DSICMD_TX, 1);
if (ret)
goto err;
err:
return ret;
}
/* Send a long command with up to 7 parameters */
static int tc358764_dsi_write_long(struct tc358764 *tc358764,
u8 cmd,
size_t nparams,
u8 *params)
{
int ret;
BUG_ON(nparams > 7);
/* DCS long write */
ret = tc358764_write16(tc358764, TC358764_REG_DSICMD_TYPE, 0x4039);
if (ret)
goto err;
/* Set number of parameters + 1 for the command byte */
ret = tc358764_write16(tc358764, TC358764_REG_DSICMD_WC, nparams + 1);
if (ret)
goto err;
/* Store the command + parameters to be sent. Unused parameters are
* padded with 0s */
#define GET_PARAM(n) ((n) < nparams ? params[(n)] : 0)
#define INTO_U16(l, h) ((u16)(l) | (((u16)(h)) << 8))
ret = tc358764_write16(tc358764,
TC358764_REG_DSICMD_WD0,
INTO_U16(cmd, GET_PARAM(0)));
if (ret)
goto err;
ret = tc358764_write16(tc358764,
TC358764_REG_DSICMD_WD1,
INTO_U16(GET_PARAM(1), GET_PARAM(2)));
if (ret)
goto err;
ret = tc358764_write16(tc358764,
TC358764_REG_DSICMD_WD2,
INTO_U16(GET_PARAM(3), GET_PARAM(4)));
if (ret)
goto err;
ret = tc358764_write16(tc358764,
TC358764_REG_DSICMD_WD3,
INTO_U16(GET_PARAM(5), GET_PARAM(6)));
if (ret)
goto err;
#undef INTO_U16
#undef GET_PARAM
/* Start DSI transfer */
ret = tc358764_write16(tc358764, TC358764_REG_DSICMD_TX, 1);
if (ret)
goto err;
err:
return ret;
}
/// Send DSI command with a variable amount of parameters
static int tc358764_dsi_write(struct tc358764 *tc358764,
u8 cmd,
size_t nparams,
u8 *params)
{
if (nparams == 0) {
return tc358764_dsi_write_no_param(tc358764, cmd);
} else if (nparams == 1) {
return tc358764_dsi_write_one_param(tc358764, cmd, params[0]);
} else if (nparams <= 7) {
return tc358764_dsi_write_long(tc358764, cmd, nparams, params);
} else {
/* Sending commands with more parameters is possible with this
* bridge but it's tricky. You need to use the video RAM to
* store the command (so you have to stop the video streaming)
* and we never managed to find the correct sequence to get it
* to work reliably. */
dev_err(&tc358764->client->dev,
"DSI command with more than 7 "
"parameters are not supported");
return -EINVAL;
}
/* Not sure if that's entirely necessary but it shouldn't hurt */
msleep(1);
}
/* XXX Hardcoded init for RNB6 */
static int tc358764_rnb6_init(struct tc358764 *tc358764) {
int ret;
#define TRY(exp) do { ret = (exp); if (ret < 0) { goto err; } } while (0)
// Disable Parallel Input and activate Automatic I2C address increment
TRY(tc358764_write16(tc358764, 0x04, 0x04));
// Software reset
TRY(tc358764_write16(tc358764, 0x2, 0x1));
msleep(10);
TRY(tc358764_write16(tc358764, 0x2, 0x0));
// PLL config
TRY(tc358764_write16(tc358764, 0x16, 0x507e));
TRY(tc358764_write16(tc358764, 0x18, 0x0203));
msleep(1);
TRY(tc358764_write16(tc358764, 0x18, 0x0213));
TRY(tc358764_write16(tc358764, 0x06, 0x0D0));
TRY(tc358764_write16(tc358764, 0x08, 0x37)); //?
TRY(tc358764_write16(tc358764, 0x50, 0x3e)); //?
TRY(tc358764_write32(tc358764, 0x140, 0));
TRY(tc358764_write32(tc358764, 0x144, 0));
TRY(tc358764_write32(tc358764, 0x148, 0));
TRY(tc358764_write32(tc358764, 0x14c, 0));
TRY(tc358764_write32(tc358764, 0x150, 0));
TRY(tc358764_write32(tc358764, 0x210, 0x2c88));
TRY(tc358764_write32(tc358764, 0x214, 0x5));
TRY(tc358764_write32(tc358764, 0x218, 0x1f06));
TRY(tc358764_write32(tc358764, 0x21c, 0x3));
TRY(tc358764_write32(tc358764, 0x220, 0x606));
TRY(tc358764_write32(tc358764, 0x224, 0x4a88));
TRY(tc358764_write32(tc358764, 0x228, 0xb));
TRY(tc358764_write32(tc358764, 0x22c, 0x4));
TRY(tc358764_write32(tc358764, 0x234, 0x1f));
TRY(tc358764_write32(tc358764, 0x238, 0x1));
TRY(tc358764_write32(tc358764, 0x23c, 0x00050005));
TRY(tc358764_write32(tc358764, 0x204, 0x1));
TRY(tc358764_write16(tc358764, 0x620, 0x1));
TRY(tc358764_write16(tc358764, 0x622, 0x11));
TRY(tc358764_write16(tc358764, 0x626, 0x500));
TRY(tc358764_write16(tc358764, 0x628, 0xd8));
TRY(tc358764_write16(tc358764, 0x62c, 0x870));
//DSI Start
TRY(tc358764_write32(tc358764, 0x518, 0x1));
//Set bit DSI CTRL
TRY(tc358764_write_dsi_config(tc358764,
TC358764_REG_DSI_CONTROL,
1,
0x0126));
//Clear bit DSI CTRL
TRY(tc358764_write_dsi_config(tc358764,
TC358764_REG_DSI_CONTROL,
0,
0xfe81));
// INIT LCD
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0x00}));
TRY(tc358764_dsi_write(tc358764, 0xff, 3, (u8[]){0x12, 0x84, 0x01}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0x80}));
TRY(tc358764_dsi_write(tc358764, 0xff, 2, (u8[]){0x12, 0x84}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0x80}));
TRY(tc358764_dsi_write(tc358764, 0xc0, 7, (u8[]){0x00, 0x64, 0x00, 0x10, 0x10, 0x00, 0x64}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0x87}));
TRY(tc358764_dsi_write(tc358764, 0xc0, 2, (u8[]){0x10, 0x10}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0x90}));
TRY(tc358764_dsi_write(tc358764, 0xc0, 6, (u8[]){0x00, 0x5c, 0x00, 0x01, 0x00, 0x04}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xb3}));
TRY(tc358764_dsi_write(tc358764, 0xc0, 2, (u8[]){0x00, 0x55}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0x81}));
TRY(tc358764_dsi_write(tc358764, 0xc1, 1, (u8[]){0x55}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xa0}));
TRY(tc358764_dsi_write(tc358764, 0xc4, 7, (u8[]){0x05, 0x10, 0x06, 0x02, 0x05, 0x15, 0x10}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xa7}));
TRY(tc358764_dsi_write(tc358764, 0xc4, 7, (u8[]){0x05, 0x10, 0x07, 0x02, 0x05, 0x15, 0x10}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xb0}));
TRY(tc358764_dsi_write(tc358764, 0xc4, 2, (u8[]){0x00, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0x91}));
TRY(tc358764_dsi_write(tc358764, 0xc5, 2, (u8[]){0x46, 0x42}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0x00}));
TRY(tc358764_dsi_write(tc358764, 0xd8, 2, (u8[]){0xc7, 0xc7}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0x00}));
TRY(tc358764_dsi_write(tc358764, 0xd9, 1, (u8[]){0x68}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xb3}));
TRY(tc358764_dsi_write(tc358764, 0xc5, 1, (u8[]){0x84}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xbb}));
TRY(tc358764_dsi_write(tc358764, 0xc5, 1, (u8[]){0x8a}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0x82}));
TRY(tc358764_dsi_write(tc358764, 0xC4, 1, (u8[]){0x0a}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xc6}));
TRY(tc358764_dsi_write(tc358764, 0xB0, 1, (u8[]){0x03}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xc2}));
TRY(tc358764_dsi_write(tc358764, 0xf5, 1, (u8[]){0x40}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xc3}));
TRY(tc358764_dsi_write(tc358764, 0xf5, 1, (u8[]){0x85}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0x00}));
TRY(tc358764_dsi_write(tc358764, 0xd0, 1, (u8[]){0x40}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0x00}));
TRY(tc358764_dsi_write(tc358764, 0xd1, 2, (u8[]){0x00, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0x80}));
TRY(tc358764_dsi_write(tc358764, 0xcb, 7, (u8[]){0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0x87}));
TRY(tc358764_dsi_write(tc358764, 0xcb, 4, (u8[]){0x00, 0x00, 0x00, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0x90}));
TRY(tc358764_dsi_write(tc358764, 0xcb, 7, (u8[]){0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0x97}));
TRY(tc358764_dsi_write(tc358764, 0xcb, 7, (u8[]){0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0x9e}));
TRY(tc358764_dsi_write(tc358764, 0xcb, 1, (u8[]){0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xa0}));
TRY(tc358764_dsi_write(tc358764, 0xcb, 7, (u8[]){0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xa7}));
TRY(tc358764_dsi_write(tc358764, 0xcb, 7, (u8[]){0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xae}));
TRY(tc358764_dsi_write(tc358764, 0xcb, 1, (u8[]){0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xb0}));
TRY(tc358764_dsi_write(tc358764, 0xcb, 7, (u8[]){0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xb7}));
TRY(tc358764_dsi_write(tc358764, 0xcb, 7, (u8[]){0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xbe}));
TRY(tc358764_dsi_write(tc358764, 0xcb, 1, (u8[]){0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xc0}));
TRY(tc358764_dsi_write(tc358764, 0xcb, 7, (u8[]){0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xc7}));
TRY(tc358764_dsi_write(tc358764, 0xcb, 7, (u8[]){0x00, 0x00, 0x00, 0x05, 0x05, 0x00, 0x05}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xce}));
TRY(tc358764_dsi_write(tc358764, 0xcb, 1, (u8[]){0x05}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xd0}));
TRY(tc358764_dsi_write(tc358764, 0xcb, 7, (u8[]){0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xd7}));
TRY(tc358764_dsi_write(tc358764, 0xcb, 7, (u8[]){0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xde}));
TRY(tc358764_dsi_write(tc358764, 0xcb, 1, (u8[]){0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xe0}));
TRY(tc358764_dsi_write(tc358764, 0xcb, 7, (u8[]){0x00, 0x00, 0x05, 0x05, 0x00, 0x05, 0x05}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xe7}));
TRY(tc358764_dsi_write(tc358764, 0xcb, 7, (u8[]){0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xf0}));
TRY(tc358764_dsi_write(tc358764, 0xcb, 7, (u8[]){0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xf7}));
TRY(tc358764_dsi_write(tc358764, 0xcb, 4, (u8[]){0xff, 0xff, 0xff, 0xff}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0x80}));
TRY(tc358764_dsi_write(tc358764, 0xcc, 7, (u8[]){0x0e, 0x10, 0x0a, 0x0c, 0x02, 0x04, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0x87}));
TRY(tc358764_dsi_write(tc358764, 0xcc, 7, (u8[]){0x00, 0x00, 0x00, 0x2e, 0x2d, 0x00, 0x29}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0x8e}));
TRY(tc358764_dsi_write(tc358764, 0xcc, 1, (u8[]){0x2a}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0x90}));
TRY(tc358764_dsi_write(tc358764, 0xcc, 7, (u8[]){0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0x97}));
TRY(tc358764_dsi_write(tc358764, 0xcc, 7, (u8[]){0x0d, 0x0f, 0x09, 0x0b, 0x01, 0x03, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0x9e}));
TRY(tc358764_dsi_write(tc358764, 0xcc, 1, (u8[]){0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xa0}));
TRY(tc358764_dsi_write(tc358764, 0xcc, 7, (u8[]){0x00, 0x00, 0x2e, 0x2d, 0x00, 0x29, 0x2a}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xa7}));
TRY(tc358764_dsi_write(tc358764, 0xcc, 7, (u8[]){0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xb0}));
TRY(tc358764_dsi_write(tc358764, 0xcc, 7, (u8[]){0x0b, 0x09, 0x0f, 0x0d, 0x03, 0x01, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xb7}));
TRY(tc358764_dsi_write(tc358764, 0xcc, 7, (u8[]){0x00, 0x00, 0x00, 0x2d, 0x2e, 0x00, 0x29}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xbe}));
TRY(tc358764_dsi_write(tc358764, 0xcc, 1, (u8[]){0x2a}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xc0}));
TRY(tc358764_dsi_write(tc358764, 0xcc, 7, (u8[]){0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xc7}));
TRY(tc358764_dsi_write(tc358764, 0xcc, 7, (u8[]){0x0c, 0x0a, 0x10, 0x0e, 0x04, 0x02, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xce}));
TRY(tc358764_dsi_write(tc358764, 0xcc, 1, (u8[]){0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xd0}));
TRY(tc358764_dsi_write(tc358764, 0xcc, 7, (u8[]){0x00, 0x00, 0x2d, 0x2e, 0x00, 0x29, 0x2a}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xd7}));
TRY(tc358764_dsi_write(tc358764, 0xcc, 7, (u8[]){0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0x80}));
TRY(tc358764_dsi_write(tc358764, 0xce, 7, (u8[]){0x8b, 0x03, 0x18, 0x8a, 0x03, 0x18, 0x89}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0x87}));
TRY(tc358764_dsi_write(tc358764, 0xce, 5, (u8[]){0x03, 0x18, 0x88, 0x03, 0x18}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0x90}));
TRY(tc358764_dsi_write(tc358764, 0xce, 7, (u8[]){0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0x97}));
TRY(tc358764_dsi_write(tc358764, 0xce, 7, (u8[]){0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xa0}));
TRY(tc358764_dsi_write(tc358764, 0xce, 7, (u8[]){0x38, 0x07, 0x05, 0x00, 0x00, 0x18, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xa7}));
TRY(tc358764_dsi_write(tc358764, 0xce, 7, (u8[]){0x38, 0x06, 0x05, 0x01, 0x00, 0x18, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xb0}));
TRY(tc358764_dsi_write(tc358764, 0xce, 7, (u8[]){0x38, 0x05, 0x05, 0x02, 0x00, 0x18, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xb7}));
TRY(tc358764_dsi_write(tc358764, 0xce, 7, (u8[]){0x38, 0x04, 0x05, 0x03, 0x00, 0x18, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xc0}));
TRY(tc358764_dsi_write(tc358764, 0xce, 7, (u8[]){0x38, 0x03, 0x05, 0x04, 0x00, 0x18, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xc7}));
TRY(tc358764_dsi_write(tc358764, 0xce, 7, (u8[]){0x38, 0x02, 0x05, 0x05, 0x00, 0x18, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xd0}));
TRY(tc358764_dsi_write(tc358764, 0xce, 7, (u8[]){0x38, 0x01, 0x05, 0x06, 0x00, 0x18, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xd7}));
TRY(tc358764_dsi_write(tc358764, 0xce, 7, (u8[]){0x38, 0x00, 0x05, 0x07, 0x00, 0x18, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0x80}));
TRY(tc358764_dsi_write(tc358764, 0xcf, 7, (u8[]){0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0x87}));
TRY(tc358764_dsi_write(tc358764, 0xcf, 7, (u8[]){0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0x90}));
TRY(tc358764_dsi_write(tc358764, 0xcf, 7, (u8[]){0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0x97}));
TRY(tc358764_dsi_write(tc358764, 0xcf, 7, (u8[]){0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xa0}));
TRY(tc358764_dsi_write(tc358764, 0xcf, 7, (u8[]){0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xa7}));
TRY(tc358764_dsi_write(tc358764, 0xcf, 7, (u8[]){0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xb0}));
TRY(tc358764_dsi_write(tc358764, 0xcf, 7, (u8[]){0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xb7}));
TRY(tc358764_dsi_write(tc358764, 0xcf, 7, (u8[]){0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xc0}));
TRY(tc358764_dsi_write(tc358764, 0xcf, 7, (u8[]){0x3d, 0x02, 0x15, 0x20, 0x00, 0x00, 0x01}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xc7}));
TRY(tc358764_dsi_write(tc358764, 0xcf, 4, (u8[]){0x81, 0x00, 0x03, 0x08}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xb5}));
TRY(tc358764_dsi_write(tc358764, 0xc5, 6, (u8[]){0x3f, 0x6f, 0xff, 0x00, 0x6f, 0xff}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0x90}));
TRY(tc358764_dsi_write(tc358764, 0xf5, 4, (u8[]){0x02, 0x11, 0x02, 0x15}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0x90}));
TRY(tc358764_dsi_write(tc358764, 0xc5, 1, (u8[]){0x50}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0x94}));
TRY(tc358764_dsi_write(tc358764, 0xc5, 1, (u8[]){0x66}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xb2}));
TRY(tc358764_dsi_write(tc358764, 0xf5, 2, (u8[]){0x00, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xb4}));
TRY(tc358764_dsi_write(tc358764, 0xf5, 2, (u8[]){0x00, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xb6}));
TRY(tc358764_dsi_write(tc358764, 0xf5, 2, (u8[]){0x00, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xb8}));
TRY(tc358764_dsi_write(tc358764, 0xf5, 2, (u8[]){0x00, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0x94}));
TRY(tc358764_dsi_write(tc358764, 0xf5, 2, (u8[]){0x00, 0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xd2}));
TRY(tc358764_dsi_write(tc358764, 0xf5, 2, (u8[]){0x06, 0x15}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0xb4}));
TRY(tc358764_dsi_write(tc358764, 0xc5, 1, (u8[]){0xcc}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0x00}));
TRY(tc358764_dsi_write(tc358764, 0xE1, 1, (u8[]){0x00}));
TRY(tc358764_dsi_write(tc358764, 0xE1, 1, (u8[]){0x25}));
TRY(tc358764_dsi_write(tc358764, 0xE1, 1, (u8[]){0x34}));
TRY(tc358764_dsi_write(tc358764, 0xE1, 1, (u8[]){0x41}));
TRY(tc358764_dsi_write(tc358764, 0xE1, 1, (u8[]){0x52}));
TRY(tc358764_dsi_write(tc358764, 0xE1, 1, (u8[]){0x5f}));
TRY(tc358764_dsi_write(tc358764, 0xE1, 1, (u8[]){0x60}));
TRY(tc358764_dsi_write(tc358764, 0xE1, 1, (u8[]){0x88}));
TRY(tc358764_dsi_write(tc358764, 0xE1, 1, (u8[]){0x76}));
TRY(tc358764_dsi_write(tc358764, 0xE1, 1, (u8[]){0x8d}));
TRY(tc358764_dsi_write(tc358764, 0xE1, 1, (u8[]){0x77}));
TRY(tc358764_dsi_write(tc358764, 0xE1, 1, (u8[]){0x63}));
TRY(tc358764_dsi_write(tc358764, 0xE1, 1, (u8[]){0x63}));
TRY(tc358764_dsi_write(tc358764, 0xE1, 1, (u8[]){0x4e}));
TRY(tc358764_dsi_write(tc358764, 0xE1, 1, (u8[]){0x4a}));
TRY(tc358764_dsi_write(tc358764, 0xE1, 1, (u8[]){0x3a}));
TRY(tc358764_dsi_write(tc358764, 0xE1, 1, (u8[]){0x2b}));
TRY(tc358764_dsi_write(tc358764, 0xE1, 1, (u8[]){0x1b}));
TRY(tc358764_dsi_write(tc358764, 0xE1, 1, (u8[]){0x10}));
TRY(tc358764_dsi_write(tc358764, 0xE1, 1, (u8[]){0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0x00}));
TRY(tc358764_dsi_write(tc358764, 0xE2, 1, (u8[]){0x00}));
TRY(tc358764_dsi_write(tc358764, 0xE2, 1, (u8[]){0x25}));
TRY(tc358764_dsi_write(tc358764, 0xE2, 1, (u8[]){0x34}));
TRY(tc358764_dsi_write(tc358764, 0xE2, 1, (u8[]){0x41}));
TRY(tc358764_dsi_write(tc358764, 0xE2, 1, (u8[]){0x52}));
TRY(tc358764_dsi_write(tc358764, 0xE2, 1, (u8[]){0x5f}));
TRY(tc358764_dsi_write(tc358764, 0xE2, 1, (u8[]){0x60}));
TRY(tc358764_dsi_write(tc358764, 0xE2, 1, (u8[]){0x88}));
TRY(tc358764_dsi_write(tc358764, 0xE2, 1, (u8[]){0x76}));
TRY(tc358764_dsi_write(tc358764, 0xE2, 1, (u8[]){0x8d}));
TRY(tc358764_dsi_write(tc358764, 0xE2, 1, (u8[]){0x77}));
TRY(tc358764_dsi_write(tc358764, 0xE2, 1, (u8[]){0x63}));
TRY(tc358764_dsi_write(tc358764, 0xE2, 1, (u8[]){0x74}));
TRY(tc358764_dsi_write(tc358764, 0xE2, 1, (u8[]){0x4e}));
TRY(tc358764_dsi_write(tc358764, 0xE2, 1, (u8[]){0x4a}));
TRY(tc358764_dsi_write(tc358764, 0xE2, 1, (u8[]){0x3a}));
TRY(tc358764_dsi_write(tc358764, 0xE2, 1, (u8[]){0x2b}));
TRY(tc358764_dsi_write(tc358764, 0xE2, 1, (u8[]){0x1b}));
TRY(tc358764_dsi_write(tc358764, 0xE2, 1, (u8[]){0x10}));
TRY(tc358764_dsi_write(tc358764, 0xE2, 1, (u8[]){0x00}));
TRY(tc358764_dsi_write(tc358764, 0x00, 1, (u8[]){0x00}));
TRY(tc358764_dsi_write(tc358764, 0xff, 3, (u8[]){0xff, 0xff, 0xff}));
msleep(1);
TRY(tc358764_dsi_write(tc358764, 0x11, 0, NULL));
msleep(1);
TRY(tc358764_dsi_write(tc358764, 0x29, 0, NULL));
msleep(1);
TRY(tc358764_write_dsi_config(tc358764,
TC358764_REG_DSI_CONTROL,
1,
0x00a7));
TRY(tc358764_write_dsi_config(tc358764,
TC358764_REG_DSI_CONTROL,
0,
0x8000));
TRY(tc358764_write16(tc358764, 0x4, 0x44));
#undef TRY
err:
return ret;
}
static int tc358764_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct tc358764_pdata *pdata = client->dev.platform_data;
struct tc358764 *tc358764;
u16 version;
int ret;
tc358764 = kzalloc(sizeof(*tc358764), GFP_KERNEL);
if (tc358764 == NULL) {
ret = -ENOMEM;
goto kzalloc_failed;
}
tc358764->client = client;
i2c_set_clientdata(client, tc358764);
if (pdata->set_power) {
ret = pdata->set_power(1);
if (ret) {
goto set_power_failed;
}
}
ret = tc358764_read16(tc358764, TC358764_REG_VERSION, &version);
if (ret) {
goto read_failed;
}
if (version != TC358764_VERSION) {
ret = -EINVAL;
dev_err(&client->dev,
"Unexpected device version: expected %04x got %04x\n",
TC358764_VERSION, version);
goto bad_version;
}
ret = tc358764_rnb6_init(tc358764);
if (ret) {
goto init_failed;
}
dev_info(&client->dev, "bridge succesfully initialized\n");
return 0;
init_failed:
bad_version:
read_failed:
pdata->set_power(0);
set_power_failed:
kfree(tc358764);
kzalloc_failed:
i2c_set_clientdata(client, NULL);
return ret;
}
static int tc358764_remove(struct i2c_client *client)
{
struct tc358764 *tc358764 = i2c_get_clientdata(client);
struct tc358764_pdata *pdata = client->dev.platform_data;
if (tc358764 == NULL) {
return 0;
}
if (pdata->set_power) {
pdata->set_power(0);
}
kfree(tc358764);
i2c_set_clientdata(client, NULL);
return 0;
}
static const struct i2c_device_id tc358764_id[] = {
{ DEVICE_NAME, 0 },
{ /* EOT */ }
};
MODULE_DEVICE_TABLE(i2c, tc358764_id);
static struct i2c_driver tc358764_driver = {
.driver = {
.owner = THIS_MODULE,
.name = DEVICE_NAME,
},
.probe = tc358764_probe,
.remove = tc358764_remove,
.id_table = tc358764_id,
};
module_i2c_driver(tc358764_driver);
MODULE_AUTHOR("Lionel Flandrin <lionel.flandrin@parrot.com>");
MODULE_DESCRIPTION("kernel driver for tc358764");
MODULE_LICENSE("GPL");