mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-24 10:11:26 +03:00
Compare commits
5 Commits
IGX_OS-1.1
...
jetson_36.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4212fbd97f | ||
|
|
4e63d195c6 | ||
|
|
4ad0e0c6c8 | ||
|
|
969619a872 | ||
|
|
ad7508c52a |
@@ -19,6 +19,134 @@
|
||||
|
||||
#include "../platform/tegra/camera/camera_gpio.h"
|
||||
|
||||
#define lt6911uxc_reg struct reg_8
|
||||
#define LT6911_TABLE_WAIT_MS 0
|
||||
#define LT6911_TABLE_END 1
|
||||
|
||||
static lt6911uxc_reg lt_enable_i2c[] = {
|
||||
{0xff, 0x80},
|
||||
{0xee, 0x01},
|
||||
{0x10, 0x00},
|
||||
{LT6911_TABLE_END, 0x00}
|
||||
};
|
||||
|
||||
static lt6911uxc_reg lt_configure_param[] = {
|
||||
{0x5e, 0xdf},
|
||||
{0x58, 0x00},
|
||||
{0x59, 0x50},
|
||||
{0x5a, 0x10},
|
||||
{LT6911_TABLE_WAIT_MS, 1},
|
||||
{0x5a, 0x00},
|
||||
{0x58, 0x21},
|
||||
{LT6911_TABLE_END, 0x00}
|
||||
};
|
||||
|
||||
static lt6911uxc_reg lt_block_erase[] = {
|
||||
{0x5a, 0x04},
|
||||
{LT6911_TABLE_WAIT_MS, 1},
|
||||
{0x5a, 0x00},
|
||||
{0x5b, 0x01},
|
||||
{0x5c, 0x80},
|
||||
{0x5d, 0x00},
|
||||
{0x5a, 0x01},
|
||||
{LT6911_TABLE_WAIT_MS, 1},
|
||||
{0x5a, 0x00},
|
||||
{LT6911_TABLE_END, 0x00}
|
||||
};
|
||||
|
||||
static lt6911uxc_reg lt_fifo_rst[] = {
|
||||
{0xff, 0x81},
|
||||
{0x08, 0xbf},
|
||||
{LT6911_TABLE_WAIT_MS, 1},
|
||||
{0x08, 0xff},
|
||||
{LT6911_TABLE_END, 0x00}
|
||||
};
|
||||
|
||||
static lt6911uxc_reg lt_write_enable[] = {
|
||||
{0xff, 0x80},
|
||||
{0x5a, 0x04},
|
||||
{LT6911_TABLE_WAIT_MS, 1},
|
||||
{0x5a, 0x00},
|
||||
{LT6911_TABLE_END, 0x00}
|
||||
};
|
||||
|
||||
static lt6911uxc_reg lt_data_to_fifo[] = {
|
||||
{0x5e, 0xdf},
|
||||
{0x5a, 0x20},
|
||||
{LT6911_TABLE_WAIT_MS, 1},
|
||||
{0x5a, 0x00},
|
||||
{LT6911_TABLE_END, 0x00}
|
||||
};
|
||||
|
||||
static lt6911uxc_reg lt_write_disable[] = {
|
||||
{0xff, 0x80},
|
||||
{0x5a, 0x08},
|
||||
{LT6911_TABLE_WAIT_MS, 1},
|
||||
{0x5a, 0x00},
|
||||
{0x58, 0x00},
|
||||
{LT6911_TABLE_END, 0x00}
|
||||
};
|
||||
|
||||
static u8 LT6911_EDIDs[][256] = {
|
||||
// EDID with default 1920x1080 60fps
|
||||
{
|
||||
0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x3A,0xC4,0x04,0xED,0x01,0x01,0x01,0x01,
|
||||
0x1E,0x21,0x01,0x03,0x80,0x6F,0x3E,0x78,0x0A,0xEE,0x91,0xA3,0x54,0x4C,0x99,0x26,
|
||||
0x0F,0x50,0x54,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
|
||||
0x01,0x01,0x01,0x01,0x01,0x01,0x02,0x3A,0x80,0x18,0x71,0x38,0x2D,0x40,0x58,0x2C,
|
||||
0x45,0x00,0xBA,0x88,0x21,0x00,0x00,0x1E,0x02,0x3A,0x80,0x18,0x71,0x38,0x2D,0x40,
|
||||
0x58,0x2C,0x45,0x00,0xBA,0x88,0x21,0x00,0x00,0x1E,0x00,0x00,0x00,0xFD,0x00,0x18,
|
||||
0x4B,0x0F,0x87,0x22,0x00,0x0A,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0xFC,
|
||||
0x00,0x44,0x75,0x61,0x6C,0x20,0x50,0x6F,0x72,0x74,0x20,0x52,0x47,0x42,0x01,0x50,
|
||||
0x02,0x03,0x18,0x71,0x43,0x04,0x01,0x03,0x23,0x09,0x07,0x01,0x83,0x01,0x00,0x00,
|
||||
0x67,0x03,0x0C,0x00,0x10,0x00,0x00,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xAD
|
||||
},
|
||||
// EDID with default 3840x2160 60fps
|
||||
{
|
||||
0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x3A,0xC4,0x01,0xED,0x01,0x01,0x01,0x01,
|
||||
0x1E,0x21,0x01,0x03,0x80,0x6F,0x3E,0x78,0x0A,0xEE,0x91,0xA3,0x54,0x4C,0x99,0x26,
|
||||
0x0F,0x50,0x54,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
|
||||
0x01,0x01,0x01,0x01,0x01,0x01,0x08,0xE8,0x00,0x30,0xF2,0x70,0x5A,0x80,0xB0,0x58,
|
||||
0x8A,0x00,0x50,0x1D,0x74,0x00,0x00,0x1E,0x08,0xE8,0x00,0x30,0xF2,0x70,0x5A,0x80,
|
||||
0xB0,0x58,0x8A,0x00,0x50,0x1D,0x74,0x00,0x00,0x1E,0x00,0x00,0x00,0xFD,0x00,0x18,
|
||||
0x4B,0x0F,0x87,0x3C,0x00,0x0A,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0xFC,
|
||||
0x00,0x44,0x75,0x61,0x6C,0x20,0x50,0x6F,0x72,0x74,0x20,0x52,0x47,0x42,0x01,0xC7,
|
||||
0x02,0x03,0x20,0x71,0x43,0x61,0x01,0x03,0x23,0x09,0x07,0x01,0x83,0x01,0x00,0x00,
|
||||
0x67,0x03,0x0C,0x00,0x10,0x00,0x00,0x3C,0x67,0xD8,0x5D,0xC4,0x01,0x78,0x80,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xEF
|
||||
},
|
||||
// EDID with default 1280x720 60fps
|
||||
{
|
||||
0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x3A,0xC4,0x05,0xED,0x01,0x01,0x01,0x01,
|
||||
0x1E,0x21,0x01,0x03,0x80,0x6F,0x3E,0x78,0x0A,0xEE,0x91,0xA3,0x54,0x4C,0x99,0x26,
|
||||
0x0F,0x50,0x54,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
|
||||
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x1D,0x00,0x72,0x51,0xD0,0x1E,0x20,0x6E,0x28,
|
||||
0x55,0x00,0xC4,0x8E,0x21,0x00,0x00,0x1E,0x01,0x1D,0x00,0x72,0x51,0xD0,0x1E,0x20,
|
||||
0x6E,0x28,0x55,0x00,0xC4,0x8E,0x21,0x00,0x00,0x1E,0x00,0x00,0x00,0xFD,0x00,0x18,
|
||||
0x4B,0x0F,0x87,0x22,0x00,0x0A,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0xFC,
|
||||
0x00,0x44,0x75,0x61,0x6C,0x20,0x50,0x6F,0x72,0x74,0x20,0x52,0x47,0x42,0x01,0xE1,
|
||||
0x02,0x03,0x18,0x71,0x43,0x04,0x01,0x03,0x23,0x09,0x07,0x01,0x83,0x01,0x00,0x00,
|
||||
0x67,0x03,0x0C,0x00,0x10,0x00,0x00,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xAD
|
||||
}
|
||||
};
|
||||
|
||||
static const struct of_device_id lt6911uxc_of_match[] = {
|
||||
{ .compatible = "nvidia,lt6911uxc", },
|
||||
{ },
|
||||
@@ -37,13 +165,18 @@ static const int lt6911uxc_30fps[] = {
|
||||
30,
|
||||
};
|
||||
|
||||
static const u8 lt6911uxc_HDMI_Signal = 0xa3;
|
||||
|
||||
static const u8 lt6911uxc_FW_version[] = { 0xa7, 0xa8, 0xa9, 0xaa };
|
||||
|
||||
struct lt6911uxc {
|
||||
struct i2c_client *i2c_client;
|
||||
struct v4l2_subdev *subdev;
|
||||
u16 fine_integ_time;
|
||||
u32 frame_length;
|
||||
u16 fine_integ_time;
|
||||
u32 frame_length;
|
||||
struct camera_common_data *s_data;
|
||||
struct tegracam_device *tc_dev;
|
||||
struct regmap *regmap;
|
||||
};
|
||||
|
||||
static const struct camera_common_frmfmt lt6911uxc_frmfmt[] = {
|
||||
@@ -53,11 +186,50 @@ static const struct camera_common_frmfmt lt6911uxc_frmfmt[] = {
|
||||
};
|
||||
|
||||
static const struct regmap_config sensor_regmap_config = {
|
||||
.reg_bits = 16,
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
|
||||
static inline int lt6911uxc_read_reg(struct camera_common_data *s_data,
|
||||
u16 addr, u8 *val)
|
||||
{
|
||||
int err = 0;
|
||||
u32 reg_val = 0;
|
||||
|
||||
err = regmap_read(s_data->regmap, addr, ®_val);
|
||||
if (err) {
|
||||
dev_err(s_data->dev, "%s: i2c read , 0x%x = %x",
|
||||
__func__, addr, reg_val);
|
||||
return err;
|
||||
}
|
||||
|
||||
*val = reg_val & 0xff;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline int lt6911uxc_write_reg(struct camera_common_data *s_data,
|
||||
u16 addr, u8 val)
|
||||
{
|
||||
int err = 0;
|
||||
err = regmap_write(s_data->regmap, addr, val);
|
||||
if (err)
|
||||
dev_err(s_data->dev, "%s: i2c write failed, 0x%x = %x",
|
||||
__func__, addr, val);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int lt6911uxc_write_table(struct lt6911uxc *priv,
|
||||
const lt6911uxc_reg table[])
|
||||
{
|
||||
return regmap_util_write_table_8(priv->s_data->regmap,
|
||||
table,
|
||||
NULL, 0,
|
||||
LT6911_TABLE_WAIT_MS,
|
||||
LT6911_TABLE_END);
|
||||
}
|
||||
|
||||
static int lt6911uxc_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
@@ -84,6 +256,8 @@ static int lt6911uxc_start_streaming(struct tegracam_device *tc_dev)
|
||||
else
|
||||
gpio_set_value(pw->reset_gpio, 1);
|
||||
}
|
||||
msleep(300);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -101,29 +275,7 @@ static int lt6911uxc_stop_streaming(struct tegracam_device *tc_dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lt6911uxc_set_mode(struct tegracam_device *tc_dev)
|
||||
{
|
||||
/* Width and Height taken care of by the firmware */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int lt6911uxc_read_reg(struct camera_common_data *s_data,
|
||||
u16 addr, u8 *val)
|
||||
{
|
||||
int err = 0;
|
||||
u32 reg_val = 0;
|
||||
|
||||
err = regmap_read(s_data->regmap, addr, ®_val);
|
||||
*val = reg_val & 0xff;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline int lt6911uxc_write_reg(struct camera_common_data *s_data,
|
||||
u16 addr, u8 val)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lt6911uxc_power_get(struct tegracam_device *tc_dev)
|
||||
{
|
||||
@@ -309,9 +461,9 @@ static int lt6911uxc_power_on(struct camera_common_data *s_data)
|
||||
|
||||
if (pw->reset_gpio) {
|
||||
if (gpiod_cansleep(gpio_to_desc(pw->reset_gpio)))
|
||||
gpio_set_value_cansleep(pw->reset_gpio, 0);
|
||||
gpio_set_value_cansleep(pw->reset_gpio, 1);
|
||||
else
|
||||
gpio_set_value(pw->reset_gpio, 0);
|
||||
gpio_set_value(pw->reset_gpio, 1);
|
||||
}
|
||||
|
||||
usleep_range(10, 20);
|
||||
@@ -335,7 +487,7 @@ static int lt6911uxc_power_on(struct camera_common_data *s_data)
|
||||
goto lt6911uxc_dvdd_fail;
|
||||
}
|
||||
|
||||
usleep_range(10, 20);
|
||||
msleep(300);
|
||||
|
||||
pw->state = SWITCH_ON;
|
||||
|
||||
@@ -389,11 +541,64 @@ static int lt6911uxc_power_off(struct camera_common_data *s_data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lt6911uxc_set_mode(struct tegracam_device *tc_dev)
|
||||
{
|
||||
int err = 0;
|
||||
int i, j;
|
||||
|
||||
struct camera_common_data *s_data = tc_dev->s_data;
|
||||
struct lt6911uxc *priv = (struct lt6911uxc *)s_data->priv;
|
||||
struct camera_common_power_rail *pw = s_data->power;
|
||||
|
||||
// Write the corresponding Shadow EDID
|
||||
err |= lt6911uxc_write_table(priv, lt_enable_i2c);
|
||||
err |= lt6911uxc_write_table(priv, lt_configure_param);
|
||||
err |= lt6911uxc_write_table(priv, lt_block_erase);
|
||||
msleep(600);
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
err |= lt6911uxc_write_table(priv, lt_fifo_rst);
|
||||
err |= lt6911uxc_write_table(priv, lt_write_enable);
|
||||
err |= lt6911uxc_write_table(priv, lt_data_to_fifo);
|
||||
|
||||
for(j = 0; j < 32; j++)
|
||||
{
|
||||
lt6911uxc_write_reg(priv->s_data, 0x59,
|
||||
LT6911_EDIDs[s_data->mode][(i * 32) + j]);
|
||||
}
|
||||
err |= lt6911uxc_write_reg(priv->s_data, 0x5b, 0x01);
|
||||
err |= lt6911uxc_write_reg(priv->s_data, 0x5c, 0x80);
|
||||
err |= lt6911uxc_write_reg(priv->s_data, 0x5d, (i) * 32);
|
||||
err |= lt6911uxc_write_reg(priv->s_data, 0x5a, 0x10);
|
||||
msleep(1);
|
||||
err |= lt6911uxc_write_reg(priv->s_data, 0x5a, 0x00);
|
||||
msleep(1);
|
||||
}
|
||||
err |= lt6911uxc_write_table(priv, lt_write_disable);
|
||||
if (err)
|
||||
dev_err(tc_dev->dev, "%s Error in writing shadow EDID \n", __func__);
|
||||
|
||||
// Toggle the reset pin to low, stream on will again set it to high
|
||||
if (pw->reset_gpio) {
|
||||
if (gpiod_cansleep(gpio_to_desc(pw->reset_gpio)))
|
||||
gpio_set_value_cansleep(pw->reset_gpio, 0);
|
||||
else
|
||||
gpio_set_value(pw->reset_gpio, 0);
|
||||
}
|
||||
// Keep reset pin low for the changes to take effect
|
||||
msleep(800);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int lt6911uxc_board_setup(struct lt6911uxc *priv)
|
||||
{
|
||||
struct camera_common_data *s_data = priv->s_data;
|
||||
struct camera_common_pdata *pdata = s_data->pdata;
|
||||
struct device *dev = s_data->dev;
|
||||
u8 version;
|
||||
int i = 0, fw_ver;
|
||||
int err = 0;
|
||||
|
||||
if (pdata->mclk_name) {
|
||||
@@ -409,11 +614,31 @@ static int lt6911uxc_board_setup(struct lt6911uxc *priv)
|
||||
dev_err(dev, "error during power on sensor (%d)\n", err);
|
||||
goto err_power_on;
|
||||
}
|
||||
msleep(300);
|
||||
|
||||
err |= lt6911uxc_write_table(priv, lt_enable_i2c);
|
||||
err |= lt6911uxc_write_reg(s_data, 0xff, 0x86);
|
||||
|
||||
for (i = 0; i < sizeof(lt6911uxc_FW_version); i++) {
|
||||
fw_ver = 0;
|
||||
err = lt6911uxc_read_reg(s_data, lt6911uxc_FW_version[i], &version);
|
||||
if (err)
|
||||
dev_err(s_data->dev, "%s: Error in reading FW version \n",
|
||||
__func__);
|
||||
fw_ver = (fw_ver << 1) | version;
|
||||
}
|
||||
dev_info(s_data->dev, "Lontium FW version %d \n",fw_ver);
|
||||
|
||||
err_power_on:
|
||||
if (pdata->mclk_name)
|
||||
camera_common_mclk_disable(s_data);
|
||||
|
||||
err = lt6911uxc_power_off(s_data);
|
||||
if (err) {
|
||||
dev_err(dev, "error during power off sensor (%d)\n", err);
|
||||
goto err_power_on;
|
||||
}
|
||||
|
||||
done:
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -326,7 +326,7 @@ static int tegracam_set_ctrls(struct tegracam_ctrl_handler *handler,
|
||||
/* For controls that require sensor to be on */
|
||||
switch (ctrl->id) {
|
||||
case TEGRA_CAMERA_CID_GAIN:
|
||||
if (*ctrl->p_new.p_s64 == ctrlprops->min_gain_val - 1)
|
||||
if (*ctrl->p_new.p_s64 == ctrlprops->max_gain_val + 1)
|
||||
return 0;
|
||||
err = ops->set_gain(tc_dev, *ctrl->p_new.p_s64);
|
||||
break;
|
||||
@@ -334,7 +334,7 @@ static int tegracam_set_ctrls(struct tegracam_ctrl_handler *handler,
|
||||
err = ops->set_frame_rate(tc_dev, *ctrl->p_new.p_s64);
|
||||
break;
|
||||
case TEGRA_CAMERA_CID_EXPOSURE:
|
||||
if (*ctrl->p_new.p_s64 == ctrlprops->min_exp_time.val - 1)
|
||||
if (*ctrl->p_new.p_s64 == ctrlprops->max_exp_time.val + 1)
|
||||
return 0;
|
||||
err = ops->set_exposure(tc_dev, *ctrl->p_new.p_s64);
|
||||
break;
|
||||
@@ -607,8 +607,8 @@ int tegracam_init_ctrl_ranges_by_mode(
|
||||
switch (ctrl->id) {
|
||||
case TEGRA_CAMERA_CID_GAIN:
|
||||
err = v4l2_ctrl_modify_range(ctrl,
|
||||
ctrlprops->min_gain_val - 1,
|
||||
ctrlprops->max_gain_val,
|
||||
ctrlprops->min_gain_val,
|
||||
ctrlprops->max_gain_val + 1,
|
||||
ctrlprops->step_gain_val,
|
||||
ctrlprops->default_gain);
|
||||
break;
|
||||
@@ -621,8 +621,8 @@ int tegracam_init_ctrl_ranges_by_mode(
|
||||
break;
|
||||
case TEGRA_CAMERA_CID_EXPOSURE:
|
||||
err = v4l2_ctrl_modify_range(ctrl,
|
||||
ctrlprops->min_exp_time.val - 1,
|
||||
ctrlprops->max_exp_time.val,
|
||||
ctrlprops->min_exp_time.val,
|
||||
ctrlprops->max_exp_time.val + 1,
|
||||
ctrlprops->step_exp_time.val,
|
||||
ctrlprops->default_exp_time.val);
|
||||
break;
|
||||
|
||||
@@ -10,6 +10,7 @@ LINUXINCLUDE += -I$(srctree.nvidia-oot)/drivers/platform/tegra/aon/include
|
||||
ccflags-y += -Werror
|
||||
|
||||
obj-m += tegra234-aon.o
|
||||
obj-m += tegra-aon-ivc-echo.o
|
||||
|
||||
tegra234-aon-objs += \
|
||||
tegra-aon-hsp.o \
|
||||
|
||||
124
drivers/platform/tegra/aon/tegra-aon-ivc-echo.c
Normal file
124
drivers/platform/tegra/aon/tegra-aon-ivc-echo.c
Normal file
@@ -0,0 +1,124 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2017-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/mailbox_client.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/tegra-aon.h>
|
||||
|
||||
#define TX_BLOCK_PERIOD 100
|
||||
#define IVC_FRAME_SIZE 64
|
||||
|
||||
struct tegra_aon_ivc_echo_data {
|
||||
struct mbox_client cl;
|
||||
struct mbox_chan *mbox;
|
||||
char rx_data[IVC_FRAME_SIZE];
|
||||
};
|
||||
|
||||
static ssize_t tegra_aon_ivc_echo_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct tegra_aon_ivc_echo_data *drvdata = dev_get_drvdata(dev);
|
||||
|
||||
memcpy(buf, drvdata->rx_data, IVC_FRAME_SIZE);
|
||||
|
||||
return IVC_FRAME_SIZE;
|
||||
}
|
||||
|
||||
static ssize_t tegra_aon_ivc_echo_tx(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct tegra_aon_ivc_echo_data *drvdata = dev_get_drvdata(dev);
|
||||
struct tegra_aon_mbox_msg msg;
|
||||
int ret;
|
||||
|
||||
if (count > IVC_FRAME_SIZE) {
|
||||
dev_err(dev, "Message is greater than the frame size %d\n",
|
||||
IVC_FRAME_SIZE);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
msg.length = count;
|
||||
msg.data = (void *)buf;
|
||||
ret = mbox_send_message(drvdata->mbox, (void *)&msg);
|
||||
if (ret < 0)
|
||||
dev_err(dev, "mbox_send_message failed %d\n", ret);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const DEVICE_ATTR(data_channel, S_IRUGO | S_IWUSR,
|
||||
tegra_aon_ivc_echo_show, tegra_aon_ivc_echo_tx);
|
||||
|
||||
static void tegra_aon_ivc_echo_rx(struct mbox_client *cl, void *data)
|
||||
{
|
||||
struct tegra_aon_mbox_msg *msg = data;
|
||||
struct tegra_aon_ivc_echo_data *drvdata = container_of(cl,
|
||||
struct tegra_aon_ivc_echo_data,
|
||||
cl);
|
||||
memcpy(drvdata->rx_data, msg->data, IVC_FRAME_SIZE);
|
||||
}
|
||||
|
||||
static int tegra_aon_ivc_echo_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct tegra_aon_ivc_echo_data *drvdata;
|
||||
|
||||
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
|
||||
if (!drvdata)
|
||||
return -ENOMEM;
|
||||
dev_set_drvdata(dev, drvdata);
|
||||
drvdata->cl.dev = dev;
|
||||
drvdata->cl.tx_block = true;
|
||||
drvdata->cl.tx_tout = TX_BLOCK_PERIOD;
|
||||
drvdata->cl.knows_txdone = false;
|
||||
drvdata->cl.rx_callback = tegra_aon_ivc_echo_rx;
|
||||
drvdata->mbox = mbox_request_channel(&drvdata->cl, 0);
|
||||
if (IS_ERR(drvdata->mbox)) {
|
||||
ret = PTR_ERR(drvdata->mbox);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "mbox_request_channel failed. Error %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = device_create_file(dev, &dev_attr_data_channel);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to create device file. Error %d\n", ret);
|
||||
mbox_free_channel(drvdata->mbox);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_aon_ivc_echo_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct tegra_aon_ivc_echo_data *drvdata = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
device_remove_file(&pdev->dev, &dev_attr_data_channel);
|
||||
mbox_free_channel(drvdata->mbox);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id tegra_aon_ivc_echo_match[] = {
|
||||
{ .compatible = "nvidia,tegra186-aon-ivc-echo", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tegra_aon_ivc_echo_match);
|
||||
|
||||
static struct platform_driver tegra_aon_ivc_echo_driver = {
|
||||
.probe = tegra_aon_ivc_echo_probe,
|
||||
.remove = tegra_aon_ivc_echo_remove,
|
||||
.driver = {
|
||||
.name = "tegra-aon-ivc-echo",
|
||||
.of_match_table = tegra_aon_ivc_echo_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(tegra_aon_ivc_echo_driver);
|
||||
MODULE_LICENSE("GPL v2");
|
||||
@@ -4,5 +4,6 @@
|
||||
ifeq ($(findstring ack_src,$(NV_BUILD_KERNEL_OPTIONS)),)
|
||||
obj-m += pex9749-thermal.o
|
||||
obj-m += tegra234-oc-event.o
|
||||
obj-m += thermal-trip-event.o
|
||||
endif
|
||||
obj-m += max77851_thermal.o
|
||||
|
||||
257
drivers/thermal/thermal-trip-event.c
Normal file
257
drivers/thermal/thermal-trip-event.c
Normal file
@@ -0,0 +1,257 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/thermal.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
enum cdev_states {
|
||||
CDEV_INACTIVE,
|
||||
CDEV_ACTIVE,
|
||||
CDEV_DESTROY,
|
||||
};
|
||||
|
||||
struct therm_trip_event {
|
||||
unsigned int cur_state;
|
||||
unsigned int max_state;
|
||||
unsigned int event_timeout_ms;
|
||||
struct mutex cur_state_lock;
|
||||
struct mutex event_timeout_lock;
|
||||
struct thermal_cooling_device *cdev;
|
||||
wait_queue_head_t waitq_head;
|
||||
};
|
||||
|
||||
static int tte_cdev_get_max_state(struct thermal_cooling_device *tcd,
|
||||
unsigned long *state)
|
||||
{
|
||||
struct therm_trip_event *tte = tcd->devdata;
|
||||
|
||||
*state = tte->max_state;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tte_cdev_get_cur_state(struct thermal_cooling_device *tcd,
|
||||
unsigned long *state)
|
||||
{
|
||||
struct therm_trip_event *tte = tcd->devdata;
|
||||
|
||||
*state = tte->cur_state;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tte_cdev_set_cur_state(struct thermal_cooling_device *tcd,
|
||||
unsigned long state)
|
||||
{
|
||||
struct therm_trip_event *tte = tcd->devdata;
|
||||
struct device *dev = &tcd->device;
|
||||
|
||||
if (state > tte->max_state)
|
||||
return -EINVAL;
|
||||
|
||||
if (state == tte->cur_state)
|
||||
return 0;
|
||||
|
||||
dev_notice(dev, "%s cooling state: %u -> %lu\n", tcd->type,
|
||||
tte->cur_state, state);
|
||||
|
||||
/*
|
||||
* Although tcd->lock is already held in the thermal framework, a lock
|
||||
* is added here to avoid a race condition between set_cur_state() and
|
||||
* driver removal.
|
||||
*/
|
||||
mutex_lock(&tte->cur_state_lock);
|
||||
tte->cur_state = state;
|
||||
mutex_unlock(&tte->cur_state_lock);
|
||||
|
||||
if (tte->cur_state != CDEV_INACTIVE) {
|
||||
if (wq_has_sleeper(&tte->waitq_head)) {
|
||||
wake_up_interruptible_all(&tte->waitq_head);
|
||||
dev_dbg(dev, "THERMAL_EVENT_TRIPPED!\n");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t thermal_trip_event_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct therm_trip_event *tte = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%d\n", tte->cur_state);
|
||||
}
|
||||
|
||||
static ssize_t thermal_trip_event_block_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct therm_trip_event *tte = dev_get_drvdata(dev);
|
||||
unsigned int event_timeout_ms = tte->event_timeout_ms;
|
||||
int ret;
|
||||
|
||||
if (event_timeout_ms > 0)
|
||||
ret = wait_event_interruptible_timeout(
|
||||
tte->waitq_head, tte->cur_state != CDEV_INACTIVE,
|
||||
msecs_to_jiffies(event_timeout_ms));
|
||||
else
|
||||
ret = wait_event_interruptible(tte->waitq_head,
|
||||
tte->cur_state != CDEV_INACTIVE);
|
||||
|
||||
/*
|
||||
* -ERESTARTSYS is returned to avoid false alarm and to resume the call.
|
||||
*/
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return sprintf(buf, "%d\n", tte->cur_state);
|
||||
}
|
||||
|
||||
static ssize_t thermal_trip_event_block_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
unsigned int val;
|
||||
struct therm_trip_event *tte = dev_get_drvdata(dev);
|
||||
|
||||
if (kstrtouint(buf, 0, &val))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&tte->event_timeout_lock);
|
||||
tte->event_timeout_ms = val;
|
||||
mutex_unlock(&tte->event_timeout_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct thermal_cooling_device_ops tte_cdev_ops = {
|
||||
.get_max_state = tte_cdev_get_max_state,
|
||||
.get_cur_state = tte_cdev_get_cur_state,
|
||||
.set_cur_state = tte_cdev_set_cur_state,
|
||||
};
|
||||
|
||||
static DEVICE_ATTR_RO(thermal_trip_event);
|
||||
static DEVICE_ATTR_RW(thermal_trip_event_block);
|
||||
|
||||
static const struct attribute *tte_cdev_attr[] = {
|
||||
&dev_attr_thermal_trip_event.attr,
|
||||
&dev_attr_thermal_trip_event_block.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static int thermal_trip_event_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct therm_trip_event *tte;
|
||||
const char *cdev_type;
|
||||
int ret;
|
||||
|
||||
tte = devm_kzalloc(dev, sizeof(struct therm_trip_event), GFP_KERNEL);
|
||||
if (!tte)
|
||||
return -ENOMEM;
|
||||
|
||||
if (of_property_read_string(np, "cdev-type", &cdev_type) != 0) {
|
||||
dev_err(dev, "invalid cdev-type property of %pOFn\n", np);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
tte->cur_state = CDEV_INACTIVE;
|
||||
tte->max_state = CDEV_DESTROY;
|
||||
mutex_init(&tte->cur_state_lock);
|
||||
mutex_init(&tte->event_timeout_lock);
|
||||
init_waitqueue_head(&tte->waitq_head);
|
||||
dev_set_drvdata(dev, tte);
|
||||
|
||||
tte->cdev = thermal_of_cooling_device_register(np, cdev_type, tte,
|
||||
&tte_cdev_ops);
|
||||
if (IS_ERR(tte->cdev)) {
|
||||
ret = PTR_ERR(tte->cdev);
|
||||
goto destroy_lock;
|
||||
}
|
||||
|
||||
ret = sysfs_create_files(&dev->kobj, tte_cdev_attr);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to create sysfs files\n");
|
||||
goto free_cdev;
|
||||
}
|
||||
|
||||
ret = sysfs_create_link(&tte->cdev->device.kobj, &dev->kobj,
|
||||
"thermal_trip_event");
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to create sysfs symlink\n");
|
||||
goto free_sysfs_files;
|
||||
}
|
||||
|
||||
dev_info(dev, "cooling device registered.\n");
|
||||
return 0;
|
||||
|
||||
free_sysfs_files:
|
||||
sysfs_remove_files(&dev->kobj, tte_cdev_attr);
|
||||
free_cdev:
|
||||
thermal_cooling_device_unregister(tte->cdev);
|
||||
destroy_lock:
|
||||
mutex_destroy(&tte->event_timeout_lock);
|
||||
mutex_destroy(&tte->cur_state_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int thermal_trip_event_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct therm_trip_event *tte = dev_get_drvdata(dev);
|
||||
struct thermal_cooling_device *cdev = tte->cdev;
|
||||
|
||||
/*
|
||||
* There could be processes waiting for the event to happen while the
|
||||
* driver is being removed. To have smooth removal, wake them up by
|
||||
* updating the cur_state. The cooling state change here is not
|
||||
* perceived by the thermal framework, but it's not a big deal as the
|
||||
* cooling device is going to be destroyed soon.
|
||||
*/
|
||||
mutex_lock(&tte->cur_state_lock);
|
||||
tte->cur_state = CDEV_DESTROY;
|
||||
mutex_unlock(&tte->cur_state_lock);
|
||||
|
||||
if (wq_has_sleeper(&tte->waitq_head)) {
|
||||
wake_up_interruptible_all(&tte->waitq_head);
|
||||
dev_dbg(dev, "THERMAL_EVENT_DESTROYED!\n");
|
||||
}
|
||||
|
||||
sysfs_remove_link(&cdev->device.kobj, "thermal_trip_event");
|
||||
sysfs_remove_files(&dev->kobj, tte_cdev_attr);
|
||||
thermal_cooling_device_unregister(cdev);
|
||||
mutex_destroy(&tte->event_timeout_lock);
|
||||
mutex_destroy(&tte->cur_state_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id thermal_trip_event_of_match[] = {
|
||||
{
|
||||
.compatible = "thermal-trip-event",
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, thermal_trip_event_of_match);
|
||||
|
||||
static struct platform_driver thermal_trip_event_driver = {
|
||||
.driver = {
|
||||
.name = "thermal-trip-event",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = thermal_trip_event_of_match,
|
||||
},
|
||||
.probe = thermal_trip_event_probe,
|
||||
.remove = thermal_trip_event_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(thermal_trip_event_driver);
|
||||
|
||||
MODULE_AUTHOR("Sreenivasulu Velpula <svelpula@nvidia.com>");
|
||||
MODULE_AUTHOR("Yi-Wei Wang <yiweiw@nvidia.com>");
|
||||
MODULE_DESCRIPTION("Thermal Trip Event Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
@@ -434,10 +434,12 @@ int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
|
||||
* and suspend-resume functionality on Tegra30, although audio mclk is
|
||||
* only needed for audio.
|
||||
*/
|
||||
ret = clk_prepare_enable(data->clk_cdev1);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Can't enable cdev1: %d\n", ret);
|
||||
return ret;
|
||||
if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA30) {
|
||||
ret = clk_prepare_enable(data->clk_cdev1);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Can't enable cdev1: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user