blob: 50d3ee3c9fa66c7c56fb4d3860120da86c2f534c [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
*
* Copyright (C) 2019 Amlogic, Inc. All rights reserved.
*
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/device.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/spinlock.h>
#include <linux/irq.h>
#include <linux/notifier.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/of_address.h>
#include <linux/amlogic/media/vout/lcd/aml_ldim.h>
#include <linux/amlogic/media/vout/lcd/aml_bl.h>
#include "../../lcd_common.h"
#include "ldim_drv.h"
#include "ldim_dev_drv.h"
#define NORMAL_MSG (0 << 7)
#define BROADCAST_MSG BIT(7)
#define BLOCK_DATA (0 << 6)
#define SINGLE_DATA BIT(6)
#define IW7027_CLASS_NAME "iw7027"
#define IW7027_REG_MAX 0x100
#define IW7027_REG_BRIGHTNESS_CHK 0x00
#define IW7027_REG_BRIGHTNESS 0x01
#define IW7027_REG_CHIPID 0x7f
#define IW7027_CHIPID 0x28
#define VSYNC_INFO_FREQUENT 300
/* for spi xfer */
static spinlock_t spi_lock;
static DEFINE_MUTEX(spi_mutex);
static DEFINE_MUTEX(dev_mutex);
struct iw7027_s {
unsigned int dev_on_flag;
unsigned int dma_support;
unsigned short vsync_cnt;
unsigned short fault_cnt;
unsigned int fault_cnt_save;
unsigned int fault_check_cnt;
unsigned int reg_buf_size;
unsigned int tbuf_size;
unsigned int rbuf_size;
unsigned char *reg_buf; /* local dimming driver smr api usage */
/* spi api internal used, don't use outside!!! */
unsigned char *tbuf;
unsigned char *rbuf;
};
struct iw7027_s *bl_iw7027;
/* write single device */
static int spi_wregs(struct spi_device *spi, unsigned char chip_id, unsigned int chip_cnt,
unsigned char reg, unsigned char *data_buf, unsigned int tlen)
{
unsigned char *tbuf;
int n, xlen, ret;
if (!bl_iw7027 || !bl_iw7027->tbuf) {
LDIMERR("%s: bl_iw7027 or tbuf is null\n", __func__);
return -1;
}
tbuf = bl_iw7027->tbuf;
if (chip_id == 0x00 || chip_id == 0x3f) {
LDIMERR("%s: chip_id 0x%02x is invalid\n", __func__, chip_id);
return -1;
}
if (tlen == 0) {
LDIMERR("%s: tlen is 0\n", __func__);
return -1;
}
n = chip_id - 1;
if (tlen == 1)
xlen = 2 + 1 + n;
else
xlen = 3 + tlen + n;
if (bl_iw7027->tbuf_size < xlen) {
LDIMERR("%s: tbuf_size %d is not enough\n", __func__, bl_iw7027->tbuf_size);
return -1;
}
if (tlen == 1) {
tbuf[0] = NORMAL_MSG | SINGLE_DATA | (chip_id & 0x3f);
tbuf[1] = reg & 0x7f;
tbuf[2] = data_buf[0];
memset(&tbuf[3], 0, n);
} else {
tbuf[0] = NORMAL_MSG | BLOCK_DATA | (chip_id & 0x3f);
tbuf[1] = tlen;
tbuf[2] = reg & 0x7f;
memcpy(&tbuf[3], data_buf, tlen);
memset(&tbuf[3 + tlen], 0, n);
}
ret = ldim_spi_write(spi, tbuf, xlen);
return ret;
}
static int spi_high_reg_switch(struct spi_device *spi, unsigned char chip_id, unsigned int chip_cnt,
int flag)
{
unsigned char temp;
int ret;
if (flag)
temp = 0x80;
else
temp = 0x00;
ret = spi_wregs(spi, chip_id, chip_cnt, 0x78, &temp, 1);
if (ret)
return -1;
return 0;
}
/* read single device */
static int spi_rregs(struct spi_device *spi, unsigned char chip_id, unsigned int chip_cnt,
unsigned char reg, unsigned char *data_buf, unsigned int rlen)
{
unsigned char *tbuf, *rbuf;
int n, xlen, ret;
int i;
if (!bl_iw7027 || !bl_iw7027->tbuf || !bl_iw7027->rbuf) {
LDIMERR("%s: bl_iw7027 or tbuf or rbuf is null\n", __func__);
return -1;
}
tbuf = bl_iw7027->tbuf;
rbuf = bl_iw7027->rbuf;
if (chip_id == 0x00 || chip_id == 0x3f) {
LDIMERR("%s: chip_id 0x%02x is invalid\n", __func__, chip_id);
return -1;
}
if (rlen == 0) {
LDIMERR("%s: rlen is 0\n", __func__);
return -1;
}
n = chip_cnt + rlen;
if (rlen == 1)
xlen = 2 + n;
else
xlen = 3 + n;
if (bl_iw7027->tbuf_size < xlen) {
LDIMERR("%s: tbuf_size %d is not enough\n", __func__, bl_iw7027->tbuf_size);
return -1;
}
if (bl_iw7027->rbuf_size < xlen) {
LDIMERR("%s: rbuf_size %d is not enough\n", __func__, bl_iw7027->rbuf_size);
return -1;
}
if (reg >= 0x80)
spi_high_reg_switch(spi, chip_id, chip_cnt, 1);
if (rlen == 1) {
tbuf[0] = NORMAL_MSG | SINGLE_DATA | (chip_id & 0x3f);
tbuf[1] = (reg & 0x7f) | 0x80;
memset(&tbuf[2], 0, n);
} else {
tbuf[0] = NORMAL_MSG | BLOCK_DATA | (chip_id & 0x3f);
tbuf[1] = rlen;
tbuf[2] = (reg & 0x7f) | 0x80;
memset(&tbuf[3], 0, n);
}
ret = ldim_spi_read_sync(spi, tbuf, rbuf, xlen);
memcpy(data_buf, &rbuf[xlen - rlen], rlen);
if (reg >= 0x80)
spi_high_reg_switch(spi, chip_id, chip_cnt, 0);
if (lcd_debug_print_flag & LCD_DBG_PR_BL_ADV) {
LDIMPR("%s: reg=0x%02x, rlen=%d, data_buf:\n", __func__, reg, rlen);
for (i = 0; i < rlen; i++)
pr_info(" 0x%02x\n", data_buf[i]);
}
return ret;
}
/* write same data to all device */
static int spi_wregs_all(struct spi_device *spi, unsigned int chip_cnt,
unsigned char reg, unsigned char *data_buf, int tlen)
{
unsigned char *tbuf;
int n, xlen, ret;
if (!bl_iw7027 || !bl_iw7027->tbuf) {
LDIMERR("%s: bl_iw7027 or tbuf is null\n", __func__);
return -1;
}
tbuf = bl_iw7027->tbuf;
if (tlen == 0) {
LDIMERR("%s: tlen is 0\n", __func__);
return -1;
}
n = chip_cnt - 1;
if (tlen == 1)
xlen = 2 + 1 + n;
else
xlen = 3 + tlen + n;
if (bl_iw7027->tbuf_size < xlen) {
LDIMERR("%s: tbuf_size %d is not enough\n", __func__, bl_iw7027->tbuf_size);
return -1;
}
if (tlen == 1) {
tbuf[0] = BROADCAST_MSG | SINGLE_DATA | 0x00;
tbuf[1] = reg & 0x7f;
tbuf[2] = data_buf[0];
memset(&tbuf[3], 0, n);
} else {
tbuf[0] = BROADCAST_MSG | BLOCK_DATA | 0x00;
tbuf[1] = tlen;
tbuf[2] = reg & 0x7f;
memcpy(&tbuf[3], data_buf, tlen);
memset(&tbuf[3 + tlen], 0, n);
}
ret = ldim_spi_write(spi, tbuf, xlen);
return ret;
}
/* write diff data to all device */
static int spi_wregs_duty(struct spi_device *spi, unsigned int chip_cnt,
unsigned char reg, unsigned char *data_buf, int tlen)
{
unsigned char *tbuf, *rbuf;
int i, n, xlen, ret;
if (!bl_iw7027 || !bl_iw7027->tbuf) {
LDIMERR("%s: bl_iw7027 or tbuf is null\n", __func__);
return -1;
}
tbuf = bl_iw7027->tbuf;
rbuf = bl_iw7027->rbuf;
if (tlen == 0) {
LDIMERR("%s: tlen is 0\n", __func__);
return -1;
}
n = chip_cnt - 1;
if (tlen == 1)
xlen = 2 + chip_cnt + n;
else
xlen = 3 + tlen * chip_cnt + n;
if (bl_iw7027->tbuf_size < xlen) {
LDIMERR("%s: tbuf_size %d is not enough\n", __func__, bl_iw7027->tbuf_size);
return -1;
}
if (bl_iw7027->rbuf_size < xlen) {
LDIMERR("%s: rbuf_size %d is not enough\n", __func__, bl_iw7027->rbuf_size);
return -1;
}
if (tlen == 1) {
tbuf[0] = BROADCAST_MSG | SINGLE_DATA | 0x3f;
tbuf[1] = reg & 0x7f;
for (i = 0; i < chip_cnt; i++)
tbuf[2 + i] = data_buf[0];
memset(&tbuf[2 + chip_cnt], 0, n);
} else {
tbuf[0] = BROADCAST_MSG | BLOCK_DATA | 0x3f;
tbuf[1] = tlen;
tbuf[2] = reg & 0x7f;
for (i = 0; i < chip_cnt; i++)
memcpy(&tbuf[3 + i * tlen], &data_buf[i * tlen], tlen);
memset(&tbuf[3 + tlen * chip_cnt], 0, n);
}
ret = ldim_spi_write_async(spi, tbuf, xlen,
bl_iw7027->dma_support, bl_iw7027->tbuf_size);
return ret;
}
static int iw7027_reg_write(struct ldim_dev_driver_s *dev_drv, unsigned char chip_id,
unsigned char reg, unsigned char *buf, unsigned int len)
{
int ret;
mutex_lock(&spi_mutex);
ret = spi_wregs(dev_drv->spi_dev, chip_id, dev_drv->chip_cnt, reg, buf, len);
if (ret)
LDIMERR("%s: chip_id[%d] reg 0x%x error\n", __func__, chip_id, reg);
mutex_unlock(&spi_mutex);
return ret;
}
static int iw7027_reg_read(struct ldim_dev_driver_s *dev_drv, unsigned char chip_id,
unsigned char reg, unsigned char *buf, unsigned int len)
{
int ret;
mutex_lock(&spi_mutex);
memset(buf, 0, len);
ret = spi_rregs(dev_drv->spi_dev, chip_id, dev_drv->chip_cnt, reg, buf, len);
if (ret)
LDIMERR("%s: chip_id[%d] reg 0x%x error\n", __func__, chip_id, reg);
mutex_unlock(&spi_mutex);
return ret;
}
static int iw7027_reg_write_all(struct ldim_dev_driver_s *dev_drv,
unsigned char reg, unsigned char *buf, unsigned int len)
{
int ret;
mutex_lock(&spi_mutex);
ret = spi_wregs_all(dev_drv->spi_dev, dev_drv->chip_cnt, reg, buf, len);
if (ret)
LDIMERR("%s: reg 0x%x, len %d error\n", __func__, reg, len);
mutex_unlock(&spi_mutex);
return ret;
}
static int iw7027_reg_write_duty(struct ldim_dev_driver_s *dev_drv,
unsigned char reg, unsigned char *buf, unsigned int len)
{
int ret;
#ifdef LDIM_SPI_DUTY_VSYNC_DIRECT
unsigned long flags = 0;
#endif
#ifdef LDIM_SPI_DUTY_VSYNC_DIRECT
spin_lock_irqsave(&spi_lock, flags);
#elif
mutex_lock(&spi_mutex);
#endif
ret = spi_wregs_duty(dev_drv->spi_dev, dev_drv->chip_cnt, reg, buf, len);
if (ret)
LDIMERR("%s: reg 0x%x, len %d error\n", __func__, reg, len);
#ifdef LDIM_SPI_DUTY_VSYNC_DIRECT
spin_unlock_irqrestore(&spi_lock, flags);
#elif
mutex_unlock(&spi_mutex);
#endif
return ret;
}
static int ldim_power_cmd_dynamic_size(struct ldim_dev_driver_s *dev_drv)
{
unsigned char *table;
int i = 0, j, step = 0, max_len = 0;
unsigned char type, cmd_size;
int delay_ms, ret = 0;
table = dev_drv->init_on;
max_len = dev_drv->init_on_cnt;
while ((i + 1) < max_len) {
type = table[i];
if (type == LCD_EXT_CMD_TYPE_END)
break;
if (ldim_debug_print) {
LDIMPR("%s: step %d: type=0x%02x, cmd_size=%d\n",
__func__, step, type, table[i + 1]);
}
cmd_size = table[i + 1];
if (cmd_size == 0)
goto power_cmd_dynamic_next;
if ((i + 2 + cmd_size) > max_len)
break;
if (type == LCD_EXT_CMD_TYPE_NONE) {
/* do nothing */
} else if (type == LCD_EXT_CMD_TYPE_DELAY) {
delay_ms = 0;
for (j = 0; j < cmd_size; j++)
delay_ms += table[i + 2 + j];
if (delay_ms > 0)
lcd_delay_ms(delay_ms);
} else if (type == LCD_EXT_CMD_TYPE_CMD) {
ret = iw7027_reg_write_all(dev_drv, table[i + 2],
&table[i + 3], (cmd_size - 1));
udelay(1);
} else if (type == LCD_EXT_CMD_TYPE_CMD_DELAY) {
ret = iw7027_reg_write_all(dev_drv, table[i + 2],
&table[i + 3], (cmd_size - 2));
udelay(1);
if (table[i + 2 + cmd_size - 1] > 0)
lcd_delay_ms(table[i + 2 + cmd_size - 1]);
} else {
LDIMERR("%s: type 0x%02x invalid\n", __func__, type);
}
power_cmd_dynamic_next:
i += (cmd_size + 2);
step++;
}
return ret;
}
static int iw7027_power_on_init(struct ldim_dev_driver_s *dev_drv)
{
int ret = 0;
if (dev_drv->cmd_size < 1) {
LDIMERR("%s: cmd_size %d is invalid\n",
__func__, dev_drv->cmd_size);
return -1;
}
if (!dev_drv->init_on) {
LDIMERR("%s: init_on is null\n", __func__);
return -1;
}
if (dev_drv->cmd_size == LCD_EXT_CMD_SIZE_DYNAMIC)
ret = ldim_power_cmd_dynamic_size(dev_drv);
return ret;
}
static int iw7027_hw_init_on(struct ldim_dev_driver_s *dev_drv)
{
unsigned char chip_id, reg, temp[2];
int i, retry_cnt = 0;
LDIMPR("%s\n", __func__);
chip_id = 0x01;
/* step 1: system power_on */
ldim_gpio_set(dev_drv, dev_drv->en_gpio, dev_drv->en_gpio_on);
/* step 2: Keep Vsync signal at low level */
/* step 3: delay for internal logic stable */
lcd_delay_ms(10);
iw7027_hw_init_on_retry:
/* SPI communication check */
for (i = 1; i <= 10; i++) {
/* step 4: Write 0X00=0X06 */
reg = 0x00;
temp[0] = 0x06;
iw7027_reg_write_all(dev_drv, reg, temp, 1);
/* step 5: Read if 0x00=0x06,go on to step 6,or step 4 loop until 0x00 = 0x06 */
iw7027_reg_read(dev_drv, chip_id, reg, temp, 1);
if (temp[0] == 0x06)
break;
}
if (i == 10)
LDIMERR("%s: SPI communication check error\n", __func__);
/* step 6: configure initial registers */
iw7027_power_on_init(dev_drv);
/* step 7: Generate external VSYNC to VSYNC/PWM pin */
ldim_set_duty_pwm(&dev_drv->ldim_pwm_config);
ldim_set_duty_pwm(&dev_drv->analog_pwm_config);
dev_drv->pinmux_ctrl(dev_drv, 1);
/* step 8: delay for system clock and light bar PSU stable */
lcd_delay_ms(520);
/* start calibration */
/* step 9: set [SOFT_RST_N] = 1 to exit reset state */
reg = 0x00;
temp[0] = 0x07;
iw7027_reg_write_all(dev_drv, reg, temp, 1);
//lcd_delay_ms(200);
/* step 10: Set 0X78[7]=1; no need here */
/* step 11: Wait until reg0xb3 LSB 4bit is 0101//It means iW7027 enter OTF state */
i = 0;
while (i++ < 500) {
reg = 0xb3;
iw7027_reg_read(dev_drv, chip_id, reg, temp, 1);
/*VDAC statue reg :FB1=[0x5] FB2=[0x50]*/
/*The current platform using FB1*/
if ((temp[0] & 0xf) == 0x05)
break;
lcd_delay_ms(1);
}
/* step 12: Read 0x85,0x86 */
reg = 0x85;
iw7027_reg_read(dev_drv, chip_id, reg, temp, 2);
/* step 13: Set 0X78[7]=0, no need */
/* step 14: check if power on vaoltage exist */
if (temp[0] || temp[1]) {
LDIMERR("%s: 0x85,0x86 is not zero\n", __func__);
if (retry_cnt++ < 3) {
LDIMPR("%s: retry %d\n", __func__, retry_cnt);
goto iw7027_hw_init_on_retry;
}
} else {
/* step 15: calibration done */
LDIMPR("%s: calibration done\n", __func__);
}
//reg = 0x30;
//temp[0] = 0x1d;
//iw7027_reg_write_all(dev_drv, chip_id, reg, temp, 1);
//reg = 0x29;
//temp[0] = 0x84;
//iw7027_reg_write_all(dev_drv, chip_id, reg, temp, 1);
return 0;
}
static int iw7027_hw_init_off(struct ldim_dev_driver_s *dev_drv)
{
ldim_gpio_set(dev_drv, dev_drv->en_gpio, dev_drv->en_gpio_off);
dev_drv->pinmux_ctrl(dev_drv, 0);
ldim_pwm_off(&dev_drv->ldim_pwm_config);
ldim_pwm_off(&dev_drv->analog_pwm_config);
return 0;
}
static int iw7027_spi_dump_reg(struct ldim_dev_driver_s *dev_drv, char *buf)
{
unsigned char *data;
int i, len, ret;
if (!buf)
return 0;
mutex_lock(&dev_mutex);
data = kcalloc(0x80, sizeof(unsigned char), GFP_KERNEL);
if (!data) {
mutex_unlock(&dev_mutex);
return sprintf(buf, "data buf malloc failed\n");
}
len = sprintf(buf, "iw7027 reg dump:\n");
ret = iw7027_reg_read(dev_drv, 0x1, 0x0, data, 0x80);
if (ret) {
kfree(data);
len += sprintf(buf + len, "%s error\n", __func__);
mutex_unlock(&dev_mutex);
return len;
}
for (i = 0; i < 0x80; i++)
len += sprintf(buf + len, " 0x%02x=0x%02x\n", i, data[i]);
ret = iw7027_reg_read(dev_drv, 0x1, 0x80, data, 0x80);
if (ret) {
kfree(data);
len += sprintf(buf + len, "%s error\n", __func__);
mutex_unlock(&dev_mutex);
return len;
}
for (i = 0; i < 0x80; i++)
len += sprintf(buf + len, " 0x%02x=0x%02x\n", (i + 0x80), data[i]);
len += sprintf(buf + len, "\n");
kfree(data);
mutex_unlock(&dev_mutex);
return len;
}
static int iw7027_spi_dump_duty(struct ldim_dev_driver_s *dev_drv, char *buf)
{
unsigned char *data, id;
int i, len, ret;
if (!buf)
return 0;
mutex_lock(&dev_mutex);
data = kcalloc(0x20, sizeof(unsigned char), GFP_KERNEL);
if (!data) {
mutex_unlock(&dev_mutex);
return sprintf(buf, "data buf malloc failed\n");
}
len = sprintf(buf, "iw7027 duty dump:\n");
for (id = 1; id <= dev_drv->chip_cnt; id++) {
len += sprintf(buf, "chip_id[%d]:\n", id);
ret = iw7027_reg_read(dev_drv, id, 0x40, data, 0x20);
if (ret) {
kfree(data);
len += sprintf(buf + len, "%s error\n", __func__);
mutex_unlock(&dev_mutex);
return len;
}
for (i = 0; i < 0x20; i++)
len += sprintf(buf + len, " 0x%02x=0x%02x\n", (i + 0x40), data[i]);
}
len += sprintf(buf + len, "\n");
kfree(data);
mutex_unlock(&dev_mutex);
return len;
}
static void ldim_vs_debug_info(struct aml_ldim_driver_s *ldim_drv)
{
struct ldim_dev_driver_s *dev_drv = ldim_drv->dev_drv;
unsigned int i, j, zone_max;
if (bl_iw7027->vsync_cnt)
return;
if (ldim_debug_print != 3)
return;
LDIMPR("%s:\n", __func__);
zone_max = bl_iw7027->reg_buf_size / 2;
for (i = 0; i < dev_drv->zone_num; i++) {
j = dev_drv->bl_mapping[i];
if (j >= zone_max) {
LDIMERR("mapping[%d]=%d invalid, max %d\n",
i, j, zone_max);
return;
}
LDIMPR("zone[%d]: reg_buf:[%d]=0x%02x, [%d]=0x%02x\n",
i, 2 * j, bl_iw7027->reg_buf[2 * j],
2 * j + 1, bl_iw7027->reg_buf[2 * j + 1]);
}
pr_info("\n");
}
static inline void ldim_data_mapping(unsigned int *duty_buf,
unsigned int max, unsigned int min,
unsigned int zone_num,
unsigned short *mapping)
{
unsigned int i, j, val, zone_max;
zone_max = bl_iw7027->reg_buf_size / 2;
for (i = 0; i < zone_num; i++) {
val = min + ((duty_buf[i] * (max - min)) / LD_DATA_MAX);
j = mapping[i];
if (j >= zone_max) {
if (bl_iw7027->vsync_cnt == 0) {
LDIMPR("%s: mapping[%d]=%d invalid, max %d\n",
__func__, i, j, zone_max);
}
return;
}
bl_iw7027->reg_buf[2 * j] = (val >> 8) & 0xf;
bl_iw7027->reg_buf[2 * j + 1] = val & 0xff;
}
}
static int iw7027_smr(struct aml_ldim_driver_s *ldim_drv, unsigned int *buf,
unsigned int len)
{
struct ldim_dev_driver_s *dev_drv = ldim_drv->dev_drv;
unsigned int value_flag = 0;
int i;
if (!bl_iw7027)
return -1;
if (bl_iw7027->vsync_cnt++ >= VSYNC_INFO_FREQUENT)
bl_iw7027->vsync_cnt = 0;
if (bl_iw7027->dev_on_flag == 0) {
if (bl_iw7027->vsync_cnt == 0)
LDIMPR("%s: on_flag=%d\n", __func__, bl_iw7027->dev_on_flag);
return 0;
}
if (len != dev_drv->zone_num) {
if (bl_iw7027->vsync_cnt == 0)
LDIMERR("%s: data len %d invalid\n", __func__, len);
return -1;
}
if (!bl_iw7027->reg_buf) {
if (bl_iw7027->vsync_cnt == 0)
LDIMERR("%s: reg_buf is null\n", __func__);
return -1;
}
/* this function will cause backlight flicker */
/*if (ldim_drv->vsync_change_flag == 50) {
* spi_wreg(dev_drv->spi_dev, 0x31, 0xd7);
* ldim_drv->vsync_change_flag = 0;
*} else if (ldim_drv->vsync_change_flag == 60) {
* spi_wreg(dev_drv->spi_dev, 0x31, 0xd3);
* ldim_drv->vsync_change_flag = 0;
*}
*/
for (i = 0; i < dev_drv->zone_num; i++)
value_flag += buf[i];
if (value_flag == 0) {
for (i = 0; i < (dev_drv->zone_num * 2); i++)
bl_iw7027->reg_buf[i] = 0;
} else {
ldim_data_mapping(buf, dev_drv->dim_max, dev_drv->dim_min,
dev_drv->zone_num, dev_drv->bl_mapping);
}
ldim_vs_debug_info(ldim_drv);
iw7027_reg_write_duty(dev_drv, 0x40, bl_iw7027->reg_buf, 0x20);
return 0;
}
static int iw7027_smr_dummy(struct aml_ldim_driver_s *ldim_drv)
{
if (!bl_iw7027)
return -1;
if (bl_iw7027->vsync_cnt++ >= VSYNC_INFO_FREQUENT)
bl_iw7027->vsync_cnt = 0;
return 0;
}
static int iw7027_fault_handler(struct aml_ldim_driver_s *ldim_drv)
{
struct ldim_dev_driver_s *dev_drv = ldim_drv->dev_drv;
int ret = 0;
if (dev_drv->fault_check == 0)
return 0;
if (!bl_iw7027)
return 0;
if (bl_iw7027->dev_on_flag == 0) {
if (bl_iw7027->vsync_cnt == 0)
LDIMPR("%s: on_flag=%d\n", __func__, bl_iw7027->dev_on_flag);
return 0;
}
mutex_lock(&dev_mutex);
//step1: check dev_drv->lamp_err_gpio
//step2: if error, change bl_iw7027->dev_on_flag to 0, and reset iw7027
//step3: ret = -1, so main driver can refresh bl_duty after reset dev
mutex_unlock(&dev_mutex);
return ret;
}
static int iw7027_power_on(struct aml_ldim_driver_s *ldim_drv)
{
if (!bl_iw7027)
return -1;
if (bl_iw7027->dev_on_flag) {
LDIMPR("%s: iw7027 is already on, exit\n", __func__);
return 0;
}
mutex_lock(&dev_mutex);
iw7027_hw_init_on(ldim_drv->dev_drv);
bl_iw7027->dev_on_flag = 1;
bl_iw7027->vsync_cnt = 0;
bl_iw7027->fault_cnt = 0;
mutex_unlock(&dev_mutex);
LDIMPR("%s: ok\n", __func__);
return 0;
}
static int iw7027_power_off(struct aml_ldim_driver_s *ldim_drv)
{
if (!bl_iw7027)
return -1;
mutex_lock(&dev_mutex);
bl_iw7027->dev_on_flag = 0;
iw7027_hw_init_off(ldim_drv->dev_drv);
mutex_unlock(&dev_mutex);
LDIMPR("%s: ok\n", __func__);
return 0;
}
static int iw7027_config_update(struct aml_ldim_driver_s *ldim_drv)
{
int ret = 0;
LDIMPR("%s: func_en = %d\n", __func__, ldim_drv->func_en);
return ret;
}
static ssize_t iw7027_show(struct class *class, struct class_attribute *attr, char *buf)
{
struct aml_ldim_driver_s *ldim_drv = aml_ldim_get_driver();
struct ldim_dev_driver_s *dev_drv;
int i, j, temp, len = 0;
if (!bl_iw7027)
return sprintf(buf, "bl_iw7027 is null\n");
dev_drv = ldim_drv->dev_drv;
if (!strcmp(attr->attr.name, "iw7027_status")) {
len = sprintf(buf, "iw7027 status:\n"
"dev_index = %d\n"
"on_flag = %d\n"
"vsync_cnt = %d\n"
"fault_cnt = %d\n"
"reg_buf_size = %d\n"
"tbuf_size = %d\n"
"rbuf_size = %d\n"
"en_on = %d\n"
"en_off = %d\n"
"cs_hold_delay = %d\n"
"cs_clk_delay = %d\n"
"dim_max = 0x%03x\n"
"dim_min = 0x%03x\n",
dev_drv->index,
bl_iw7027->dev_on_flag,
bl_iw7027->vsync_cnt,
bl_iw7027->fault_cnt,
bl_iw7027->reg_buf_size,
bl_iw7027->tbuf_size,
bl_iw7027->rbuf_size,
dev_drv->en_gpio_on,
dev_drv->en_gpio_off,
dev_drv->cs_hold_delay,
dev_drv->cs_clk_delay,
dev_drv->dim_max,
dev_drv->dim_min);
} else if (!strcmp(attr->attr.name, "dump_buf")) {
if (!bl_iw7027->reg_buf || bl_iw7027->reg_buf_size == 0)
return sprintf(buf, "bl_iw7027 reg_buf is null\n");
len = sprintf(buf, "bl_matrix spi buffer:\n");
for (i = 0 ; i < dev_drv->chip_cnt; i++) {
for (j = 0; j < 16; j++) {
temp = (i * 16 + j) * 2;
if (temp >= bl_iw7027->reg_buf_size)
break;
len += sprintf(buf + len, " 0x%02x", bl_iw7027->reg_buf[temp]);
if ((temp + 1) >= bl_iw7027->reg_buf_size)
break;
len += sprintf(buf + len, " 0x%02x", bl_iw7027->reg_buf[temp + 1]);
}
len += sprintf(buf + len, "\n");
}
} else if (!strcmp(attr->attr.name, "dump_reg")) {
len = iw7027_spi_dump_reg(dev_drv, buf);
} else if (!strcmp(attr->attr.name, "dump_duty")) {
len = iw7027_spi_dump_duty(dev_drv, buf);
} else {
return sprintf(buf, "invalid node\n");
}
return len;
}
static ssize_t iw7027_store(struct class *class, struct class_attribute *attr,
const char *buf, size_t count)
{
struct aml_ldim_driver_s *ldim_drv = aml_ldim_get_driver();
struct ldim_dev_driver_s *dev_drv;
unsigned int val, val2;
unsigned char reg, data;
int n;
dev_drv = ldim_drv->dev_drv;
if (!strcmp(attr->attr.name, "init")) {
mutex_lock(&dev_mutex);
iw7027_hw_init_on(dev_drv);
mutex_unlock(&dev_mutex);
} else if (!strcmp(attr->attr.name, "chip")) {
n = sscanf(buf, "all %x %x", &val, &val2);
if (n == 2) {
mutex_lock(&dev_mutex);
reg = (unsigned char)val;
data = (unsigned char)val2;
iw7027_reg_write_all(dev_drv, reg, &data, 1);
mutex_unlock(&dev_mutex);
} else {
LDIMERR("%s: invalid args\n", __func__);
}
} else if (!strcmp(attr->attr.name, "dma")) {
n = sscanf(buf, "set %d", &val);
if (n == 1)
bl_iw7027->dma_support = val;
LDIMPR("dma_support: %d\n", bl_iw7027->dma_support);
} else {
LDIMERR("argment error!\n");
}
return count;
}
static struct class_attribute iw7027_class_attrs[] = {
__ATTR(init, 0644, NULL, iw7027_store),
__ATTR(dma, 0644, NULL, iw7027_store),
__ATTR(iw7027_status, 0644, iw7027_show, NULL),
__ATTR(dump_buf, 0644, iw7027_show, NULL),
__ATTR(dump_reg, 0644, iw7027_show, NULL),
__ATTR(dump_duty, 0644, iw7027_show, NULL)
};
static int iw7027_ldim_dev_update(struct ldim_dev_driver_s *dev_drv)
{
dev_drv->power_on = iw7027_power_on;
dev_drv->power_off = iw7027_power_off;
dev_drv->dev_smr = iw7027_smr;
dev_drv->dev_smr_dummy = iw7027_smr_dummy;
dev_drv->dev_err_handler = iw7027_fault_handler;
dev_drv->config_update = iw7027_config_update;
dev_drv->reg_write = iw7027_reg_write;
dev_drv->reg_read = iw7027_reg_read;
return 0;
}
int ldim_dev_iw7027_probe(struct aml_ldim_driver_s *ldim_drv)
{
struct ldim_dev_driver_s *dev_drv = ldim_drv->dev_drv;
int n, i;
if (!dev_drv) {
LDIMERR("%s: dev_drv is null\n", __func__);
return -1;
}
if (!dev_drv->spi_dev) {
LDIMERR("%s: spi_dev is null\n", __func__);
return -1;
}
if (dev_drv->chip_cnt == 0) {
LDIMERR("%s: chip_cnt is 0\n", __func__);
return -1;
}
bl_iw7027 = kzalloc(sizeof(*bl_iw7027), GFP_KERNEL);
if (!bl_iw7027)
return -1;
bl_iw7027->dev_on_flag = 0;
bl_iw7027->vsync_cnt = 0;
bl_iw7027->fault_cnt = 0;
bl_iw7027->dma_support = dev_drv->dma_support;
/* 16 each device, each zone 2 bytes */
bl_iw7027->reg_buf_size = 16 * 2 * dev_drv->chip_cnt;
bl_iw7027->reg_buf = kcalloc(bl_iw7027->reg_buf_size, sizeof(unsigned char), GFP_KERNEL);
if (!bl_iw7027->reg_buf)
goto ldim_dev_iw7027_probe_err0;
/* spi transfer buffer: header + reg_max_cnt + chip_cnt */
bl_iw7027->tbuf_size = 3 + IW7027_REG_MAX + dev_drv->chip_cnt;
if (bl_iw7027->dma_support) {
n = bl_iw7027->tbuf_size;
bl_iw7027->tbuf_size = ldim_spi_dma_cycle_align_byte(n);
}
bl_iw7027->tbuf = kcalloc(bl_iw7027->tbuf_size, sizeof(unsigned char), GFP_KERNEL);
if (!bl_iw7027->tbuf)
goto ldim_dev_iw7027_probe_err1;
/* spi transfer buffer: header + reg_max_cnt + chip_cnt + dev_id_max(=chip_cnt) */
bl_iw7027->rbuf_size = 3 + IW7027_REG_MAX + dev_drv->chip_cnt * 2;
if (bl_iw7027->rbuf_size < bl_iw7027->tbuf_size) /* for dma use */
bl_iw7027->rbuf_size = bl_iw7027->tbuf_size;
bl_iw7027->rbuf = kcalloc(bl_iw7027->rbuf_size, sizeof(unsigned char), GFP_KERNEL);
if (!bl_iw7027->rbuf)
goto ldim_dev_iw7027_probe_err2;
iw7027_ldim_dev_update(dev_drv);
if (dev_drv->class) {
for (i = 0; i < ARRAY_SIZE(iw7027_class_attrs); i++) {
if (class_create_file(dev_drv->class, &iw7027_class_attrs[i])) {
LDIMERR("create ldim_dev class attribute %s fail\n",
iw7027_class_attrs[i].attr.name);
}
}
}
#ifdef LDIM_SPI_DUTY_VSYNC_DIRECT
spin_lock_init(&spi_lock);
#endif
bl_iw7027->dev_on_flag = 1; /* default enable in uboot */
LDIMPR("%s ok\n", __func__);
return 0;
ldim_dev_iw7027_probe_err2:
kfree(bl_iw7027->tbuf);
bl_iw7027->tbuf_size = 0;
ldim_dev_iw7027_probe_err1:
kfree(bl_iw7027->reg_buf);
bl_iw7027->reg_buf_size = 0;
ldim_dev_iw7027_probe_err0:
kfree(bl_iw7027);
bl_iw7027 = NULL;
return -1;
}
int ldim_dev_iw7027_remove(struct aml_ldim_driver_s *ldim_drv)
{
if (!bl_iw7027)
return 0;
kfree(bl_iw7027->rbuf);
bl_iw7027->rbuf_size = 0;
kfree(bl_iw7027->tbuf);
bl_iw7027->tbuf_size = 0;
kfree(bl_iw7027->reg_buf);
bl_iw7027->reg_buf_size = 0;
kfree(bl_iw7027);
bl_iw7027 = NULL;
return 0;
}