diff --git a/drivers/media/platform/tegra/cdi/cdi-dev-priv.h b/drivers/media/platform/tegra/cdi/cdi-dev-priv.h index 043ed0be..26e5c13d 100644 --- a/drivers/media/platform/tegra/cdi/cdi-dev-priv.h +++ b/drivers/media/platform/tegra/cdi/cdi-dev-priv.h @@ -5,6 +5,7 @@ #define __CDI_DEV_PRIV_H__ #include +#include "cdi-tca-priv.h" #define MAX_POWER_LINKS_PER_BLOCK (4U) @@ -32,6 +33,8 @@ struct cdi_dev_info { u8 cam_pwr_method; u8 cam_pwr_i2c_addr; struct max20087_priv max20087; + struct tca9539_priv tca9539; + u8 cim_ver; /* 1 - P3714 A01, 2 - P3714 A02/A03 */ }; int cdi_dev_raw_rd(struct cdi_dev_info *info, unsigned int offset, diff --git a/drivers/media/platform/tegra/cdi/cdi-mgr-priv.h b/drivers/media/platform/tegra/cdi/cdi-mgr-priv.h index ecc9b183..946ec2a0 100644 --- a/drivers/media/platform/tegra/cdi/cdi-mgr-priv.h +++ b/drivers/media/platform/tegra/cdi/cdi-mgr-priv.h @@ -6,6 +6,7 @@ #include #include +#include "cdi-tca-priv.h" #define CDI_MGR_STOP_GPIO_INTR_EVENT_WAIT (~(0u)) #define CDI_MGR_TCA9539_REGISTER_COUNT (8) @@ -24,17 +25,6 @@ struct cam_gpio_config { int gpio_intr_irq; }; -struct tca9539_priv { - struct i2c_adapter *adap; - int bus; - u32 addr; - u32 reg_len; - u32 dat_len; - u8 init_val[12]; - u32 power_port; - bool enable; -}; - struct cdi_mgr_priv { struct device *pdev; /* parent device */ struct device *dev; /* this device */ @@ -65,7 +55,7 @@ struct cdi_mgr_priv { uint32_t gpio_count; uint32_t err_irq_recvd_status_mask; bool stop_err_irq_wait; - u8 cim_ver; /* 1 - P3714 A01, 2 - P3714 A02 */ + u8 cim_ver; /* 1 - P3714 A01, 2 - P3714 A02/A03 */ u32 cim_frsync[3]; /* FRSYNC source selection for each muxer */ u8 pre_suspend_tca9539_regvals[CDI_MGR_TCA9539_REGISTER_COUNT]; }; diff --git a/drivers/media/platform/tegra/cdi/cdi-tca-priv.h b/drivers/media/platform/tegra/cdi/cdi-tca-priv.h new file mode 100644 index 00000000..d569d689 --- /dev/null +++ b/drivers/media/platform/tegra/cdi/cdi-tca-priv.h @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + +#ifndef __CDI_TCA9539_PRIV_H__ +#define __CDI_TCA9539_PRIV_H__ + +/* i2c payload size is only 12 bit */ +#define MAX_MSG_SIZE (0xFFF - 1) + +struct tca9539_priv { + struct i2c_adapter *adap; + int bus; + u32 addr; + u32 reg_len; + u32 dat_len; + u8 init_val[12]; + u32 power_port; + bool enable; +}; + +static int tca9539_raw_wr( + struct device *dev, struct tca9539_priv *tca9539, unsigned int offset, u8 val) +{ + int ret = -ENODEV; + u8 *buf_start = NULL; + struct i2c_msg *i2cmsg; + unsigned int num_msgs = 0, total_size, i; + u8 data[3]; + size_t size = 1; + + dev_dbg(dev, "%s\n", __func__); + + if (tca9539->reg_len == 2) { + data[0] = (u8)((offset >> 8) & 0xff); + data[1] = (u8)(offset & 0xff); + data[2] = val; + size += 2; + } else if (tca9539->reg_len == 1) { + data[0] = (u8)(offset & 0xff); + data[1] = val; + size += 1; + } else if ((tca9539->reg_len == 0) || + (tca9539->reg_len > 3)) { + return 0; + } + + num_msgs = size / MAX_MSG_SIZE; + num_msgs += (size % MAX_MSG_SIZE) ? 1 : 0; + + i2cmsg = kzalloc((sizeof(struct i2c_msg)*num_msgs), GFP_KERNEL); + if (!i2cmsg) { + return -ENOMEM; + } + + buf_start = data; + total_size = size; + + dev_dbg(dev, "%s: num_msgs: %d\n", __func__, num_msgs); + for (i = 0; i < num_msgs; i++) { + i2cmsg[i].addr = tca9539->addr; + i2cmsg[i].buf = (__u8 *)buf_start; + + if (i > 0) + i2cmsg[i].flags = I2C_M_NOSTART; + else + i2cmsg[i].flags = 0; + + if (total_size > MAX_MSG_SIZE) { + i2cmsg[i].len = MAX_MSG_SIZE; + buf_start += MAX_MSG_SIZE; + total_size -= MAX_MSG_SIZE; + } else { + i2cmsg[i].len = total_size; + } + dev_dbg(dev, "%s: addr:%x buf:%p, flags:%u len:%u\n", + __func__, i2cmsg[i].addr, (void *)i2cmsg[i].buf, + i2cmsg[i].flags, i2cmsg[i].len); + } + + ret = i2c_transfer(tca9539->adap, i2cmsg, num_msgs); + if (ret > 0) + ret = 0; + + kfree(i2cmsg); + return ret; +} + +static int tca9539_raw_rd( + struct device *dev, struct tca9539_priv *tca9539, unsigned int offset, u8 *val) +{ + int ret = -ENODEV; + u8 data[2]; + size_t size = 1; + struct i2c_msg i2cmsg[2]; + + dev_dbg(dev, "%s\n", __func__); + + if (tca9539->reg_len == 2) { + data[0] = (u8)((offset >> 8) & 0xff); + data[1] = (u8)(offset & 0xff); + } else if (tca9539->reg_len == 1) + data[0] = (u8)(offset & 0xff); + + i2cmsg[0].addr = tca9539->addr; + i2cmsg[0].len = tca9539->reg_len; + i2cmsg[0].buf = (__u8 *)data; + i2cmsg[0].flags = I2C_M_NOSTART; + + i2cmsg[1].addr = tca9539->addr; + i2cmsg[1].flags = I2C_M_RD; + i2cmsg[1].len = size; + i2cmsg[1].buf = (__u8 *)val; + + ret = i2c_transfer(tca9539->adap, i2cmsg, 2); + if (ret > 0) + ret = 0; + + return ret; +} + +#endif /* __CDI_TCA9539_PRIV_H__ */ + diff --git a/drivers/media/platform/tegra/cdi/cdi_dev.c b/drivers/media/platform/tegra/cdi/cdi_dev.c index 75da67a2..715638cb 100644 --- a/drivers/media/platform/tegra/cdi/cdi_dev.c +++ b/drivers/media/platform/tegra/cdi/cdi_dev.c @@ -394,6 +394,81 @@ static int cdi_dev_get_pwr_info( return 0; } +static int tca9539_wr( + struct cdi_dev_info *info, unsigned int offset, u8 val) +{ + int ret = -ENODEV; + + dev_dbg(info->dev, "%s\n", __func__); + mutex_lock(&info->mutex); + + ret = tca9539_raw_wr(info->dev, &info->tca9539, offset, val); + + mutex_unlock(&info->mutex); + return ret; +} + +static int tca9539_rd( + struct cdi_dev_info *info, unsigned int offset, u8 *val) +{ + int ret = -ENODEV; + + dev_dbg(info->dev, "%s\n", __func__); + mutex_lock(&info->mutex); + + ret = tca9539_raw_rd(info->dev, &info->tca9539, offset, val); + + mutex_unlock(&info->mutex); + + return ret; +} + +static int cdi_dev_set_fsync_mux( + struct cdi_dev_info *info, + void __user *arg) +{ + u8 val, shift; + struct cdi_dev_fsync_mux fsync_mux; + + if (copy_from_user(&fsync_mux, arg, sizeof(fsync_mux))) { + dev_err(info->dev, + "%s: failed to copy from user\n", __func__); + return -EFAULT; + } + + if (info->cim_ver == 2U) { + /* P01:P00 for the camera group B. cam_grp 1. + * P03:P02 for the camera group C. cam_grp 2. + * P05:P04 for the camera group D. cam_grp 3. + */ + if ((fsync_mux.cam_grp > 0U) && (fsync_mux.cam_grp < 4U)) { + if (tca9539_rd(info, 0x02, &val) != 0) + return -EFAULT; + switch (fsync_mux.cam_grp) { + case 1U: + shift = 0U; + break; + case 2U: + shift = 2U; + break; + case 3U: + shift = 4U; + break; + default: + shift = 0U; + break; + } + + val &= ~(0x3 << shift); + val |= (fsync_mux.mux_sel << shift); + if (tca9539_wr(info, 0x02, val) != 0) + return -EFAULT; + } + } + + return 0; +} + static long cdi_dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -411,6 +486,9 @@ static long cdi_dev_ioctl(struct file *file, case CDI_DEV_IOCTL_GET_PWR_INFO: err = cdi_dev_get_pwr_info(info, (void __user *)arg); break; + case CDI_DEV_IOCTL_FRSYNC_MUX: + err = cdi_dev_set_fsync_mux(info, (void __user *)arg); + break; default: dev_dbg(info->dev, "%s: invalid cmd %x\n", __func__, cmd); return -EINVAL; @@ -453,6 +531,38 @@ static const struct file_operations cdi_dev_fileops = { .release = cdi_dev_release, }; +static void cdi_dev_get_cim_ver(struct device_node *np, struct cdi_dev_info *info) +{ + int err = 0; + struct device_node *child = NULL; + struct device_node *cim = NULL; + const char *cim_ver; + + child = of_get_parent(np); + if (child != NULL) { + cim = of_get_compatible_child(child, + "nvidia,cim_ver"); + if (cim != NULL) { + err = of_property_read_string(cim, + "cim_ver", + &cim_ver); + if (!err) { + if (!strncmp(cim_ver, + "cim_ver_a01", + sizeof("cim_ver_a01"))) { + dev_info(info->dev, + "CIM A01\n"); + info->cim_ver = 1U; + } else { + dev_info(info->dev, + "CIM A02\n"); + info->cim_ver = 2U; + } + } + } + } +} + static int cdi_dev_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -460,6 +570,7 @@ static int cdi_dev_probe(struct i2c_client *client, struct cdi_mgr_priv *cdi_mgr = NULL; struct device *pdev; struct device_node *child = NULL, *child_max20087 = NULL; + struct device_node *child_tca9539 = NULL; int err; int numLinks = 0; int i; @@ -483,7 +594,10 @@ static int cdi_dev_probe(struct i2c_client *client, dev_notice(&client->dev, "%s NO platform data\n", __func__); return -ENODEV; } + if (info->pdata->np != NULL) { + cdi_dev_get_cim_ver(info->pdata->np, info); + child = of_get_child_by_name(info->pdata->np, "pwr_ctrl"); if (child != NULL) { @@ -553,6 +667,68 @@ static int cdi_dev_probe(struct i2c_client *client, } } } + + if (info->cim_ver == 2U) { + /* get the I/O expander information */ + child_tca9539 = of_get_child_by_name(child, "tca9539"); + if (child_tca9539 != NULL) { + err = of_property_read_u32(child_tca9539, "i2c-bus", + &info->tca9539.bus); + if (err) { + dev_err(info->dev, + "%s: Failed to get I2C bus number, ERROR %d\n", + __func__, err); + return -ENODEV; + } + err = of_property_read_u32(child_tca9539, "addr", + &info->tca9539.addr); + if (err || !info->tca9539.addr) { + dev_err(info->dev, + "%s: ERROR %d addr = %d\n", + __func__, err, + info->tca9539.addr); + return -ENODEV; + } + err = of_property_read_u32(child_tca9539, "reg_len", + &info->tca9539.reg_len); + if (err || !info->tca9539.reg_len) { + dev_err(info->dev, + "%s: ERROR %d reg_len = %d\n", + __func__, err, + info->tca9539.reg_len); + return -ENODEV; + } + err = of_property_read_u32(child_tca9539, "dat_len", + &info->tca9539.dat_len); + if (err || !info->tca9539.dat_len) { + dev_err(info->dev, + "%s: ERROR %d dat_len = %d\n", + __func__, err, + info->tca9539.dat_len); + return -ENODEV; + } + err = of_property_read_u32(child_tca9539->parent, + "power_port", + &info->tca9539.power_port); + if (err) { + dev_err(info->dev, + "%s: ERROR %d power_port = %d\n", + __func__, err, + info->tca9539.power_port); + return -ENODEV; + } + + info->tca9539.reg_len /= 8; + info->tca9539.dat_len /= 8; + info->tca9539.enable = 1; + info->tca9539.adap = i2c_get_adapter(info->tca9539.bus); + if (!info->tca9539.adap) { + dev_err(info->dev, "%s no such i2c bus %d\n", + __func__, info->tca9539.bus); + return -ENODEV; + } + } + } } if (info->pdata->reg_bits) diff --git a/drivers/media/platform/tegra/cdi/cdi_mgr.c b/drivers/media/platform/tegra/cdi/cdi_mgr.c index 36f6085d..c2669d57 100644 --- a/drivers/media/platform/tegra/cdi/cdi_mgr.c +++ b/drivers/media/platform/tegra/cdi/cdi_mgr.c @@ -146,107 +146,30 @@ static int pwr_off_set(void *data, u64 val) DEFINE_SIMPLE_ATTRIBUTE(pwr_off_fops, pwr_off_get, pwr_off_set, "0x%02llx\n"); -static int tca9539_raw_wr( +static int tca9539_wr( struct cdi_mgr_priv *info, unsigned int offset, u8 val) { int ret = -ENODEV; - u8 *buf_start = NULL; - struct i2c_msg *i2cmsg; - unsigned int num_msgs = 0, total_size, i; - u8 data[3]; - size_t size = 1; dev_dbg(info->dev, "%s\n", __func__); mutex_lock(&info->mutex); - if (info->tca9539.reg_len == 2) { - data[0] = (u8)((offset >> 8) & 0xff); - data[1] = (u8)(offset & 0xff); - data[2] = val; - size += 2; - } else if (info->tca9539.reg_len == 1) { - data[0] = (u8)(offset & 0xff); - data[1] = val; - size += 1; - } else if ((info->tca9539.reg_len == 0) || - (info->tca9539.reg_len > 3)) { - mutex_unlock(&info->mutex); - return 0; - } + ret = tca9539_raw_wr(info->dev, &info->tca9539, offset, val); - num_msgs = size / MAX_MSG_SIZE; - num_msgs += (size % MAX_MSG_SIZE) ? 1 : 0; - - i2cmsg = kzalloc((sizeof(struct i2c_msg)*num_msgs), GFP_KERNEL); - if (!i2cmsg) { - mutex_unlock(&info->mutex); - return -ENOMEM; - } - - buf_start = data; - total_size = size; - - dev_dbg(info->dev, "%s: num_msgs: %d\n", __func__, num_msgs); - for (i = 0; i < num_msgs; i++) { - i2cmsg[i].addr = info->tca9539.addr; - i2cmsg[i].buf = (__u8 *)buf_start; - - if (i > 0) - i2cmsg[i].flags = I2C_M_NOSTART; - else - i2cmsg[i].flags = 0; - - if (total_size > MAX_MSG_SIZE) { - i2cmsg[i].len = MAX_MSG_SIZE; - buf_start += MAX_MSG_SIZE; - total_size -= MAX_MSG_SIZE; - } else { - i2cmsg[i].len = total_size; - } - dev_dbg(info->dev, "%s: addr:%x buf:%p, flags:%u len:%u\n", - __func__, i2cmsg[i].addr, (void *)i2cmsg[i].buf, - i2cmsg[i].flags, i2cmsg[i].len); - } - - ret = i2c_transfer(info->tca9539.adap, i2cmsg, num_msgs); - if (ret > 0) - ret = 0; - - kfree(i2cmsg); mutex_unlock(&info->mutex); return ret; } -static int tca9539_raw_rd( +static int tca9539_rd( struct cdi_mgr_priv *info, unsigned int offset, u8 *val) { int ret = -ENODEV; - u8 data[2]; - size_t size = 1; - struct i2c_msg i2cmsg[2]; dev_dbg(info->dev, "%s\n", __func__); mutex_lock(&info->mutex); - if (info->tca9539.reg_len == 2) { - data[0] = (u8)((offset >> 8) & 0xff); - data[1] = (u8)(offset & 0xff); - } else if (info->tca9539.reg_len == 1) - data[0] = (u8)(offset & 0xff); + ret = tca9539_raw_rd(info->dev, &info->tca9539, offset, val); - i2cmsg[0].addr = info->tca9539.addr; - i2cmsg[0].len = info->tca9539.reg_len; - i2cmsg[0].buf = (__u8 *)data; - i2cmsg[0].flags = I2C_M_NOSTART; - - i2cmsg[1].addr = info->tca9539.addr; - i2cmsg[1].flags = I2C_M_RD; - i2cmsg[1].len = size; - i2cmsg[1].buf = (__u8 *)val; - - ret = i2c_transfer(info->tca9539.adap, i2cmsg, 2); - if (ret > 0) - ret = 0; mutex_unlock(&info->mutex); return ret; @@ -901,16 +824,16 @@ static int cdi_mgr_open(struct inode *inode, struct file *file) "%s: failed to wait for the semaphore\n", __func__); if (cdi_mgr->cim_ver == 1U) { /* P3714 A01 */ - if (tca9539_raw_rd(cdi_mgr, 0x02, &val) != 0) + if (tca9539_rd(cdi_mgr, 0x02, &val) != 0) return -EFAULT; val |= (0x10 << cdi_mgr->tca9539.power_port); - if (tca9539_raw_wr(cdi_mgr, 0x02, val) != 0) + if (tca9539_wr(cdi_mgr, 0x02, val) != 0) return -EFAULT; } else if (cdi_mgr->cim_ver == 2U) { /* P3714 A02 */ - if (tca9539_raw_rd(cdi_mgr, 0x03, &val) != 0) + if (tca9539_rd(cdi_mgr, 0x03, &val) != 0) return -EFAULT; val |= (0x1 << cdi_mgr->tca9539.power_port); - if (tca9539_raw_wr(cdi_mgr, 0x03, val) != 0) + if (tca9539_wr(cdi_mgr, 0x03, val) != 0) return -EFAULT; } up(&tca9539_sem); @@ -933,16 +856,16 @@ static int cdi_mgr_release(struct inode *inode, struct file *file) "%s: failed to wait for the semaphore\n", __func__); if (cdi_mgr->cim_ver == 1U) { /* P3714 A01 */ - if (tca9539_raw_rd(cdi_mgr, 0x02, &val) != 0) + if (tca9539_rd(cdi_mgr, 0x02, &val) != 0) return -EFAULT; val &= ~(0x10 << cdi_mgr->tca9539.power_port); - if (tca9539_raw_wr(cdi_mgr, 0x02, val) != 0) + if (tca9539_wr(cdi_mgr, 0x02, val) != 0) return -EFAULT; } else if (cdi_mgr->cim_ver == 2U) { /* P3714 A02 */ - if (tca9539_raw_rd(cdi_mgr, 0x03, &val) != 0) + if (tca9539_rd(cdi_mgr, 0x03, &val) != 0) return -EFAULT; val &= ~(0x1 << cdi_mgr->tca9539.power_port); - if (tca9539_raw_wr(cdi_mgr, 0x03, val) != 0) + if (tca9539_wr(cdi_mgr, 0x03, val) != 0) return -EFAULT; } up(&tca9539_sem); @@ -1242,7 +1165,7 @@ static int cdi_mgr_suspend(struct device *dev) if (cdi_mgr->tca9539.enable) { reg_addr = CDI_MGR_TCA9539_BASE_REG_ADDR; while (reg_addr < CDI_MGR_TCA9539_REGISTER_COUNT) { - rc = tca9539_raw_rd(cdi_mgr, reg_addr, + rc = tca9539_rd(cdi_mgr, reg_addr, &cdi_mgr->pre_suspend_tca9539_regvals[reg_addr]); if (rc != 0) { dev_err(dev, "%s: tca9539_raw_rd failed reading reg[0x%x]\n", @@ -1276,7 +1199,7 @@ static int cdi_mgr_resume(struct device *dev) if (cdi_mgr->tca9539.enable) { reg_addr = CDI_MGR_TCA9539_BASE_REG_ADDR; while (reg_addr < CDI_MGR_TCA9539_REGISTER_COUNT) { - rc = tca9539_raw_wr(cdi_mgr, reg_addr, + rc = tca9539_wr(cdi_mgr, reg_addr, cdi_mgr->pre_suspend_tca9539_regvals[reg_addr]); if (rc != 0) { dev_err(dev, "%s: tca9539_raw_wr failed setting reg[0x%x] = 0x%x\n", @@ -1689,27 +1612,27 @@ static int cdi_mgr_probe(struct platform_device *pdev) /* the registers in TCA9539 */ /* Use the IO expander to control PWDN signals */ if (cdi_mgr->cim_ver == 1U) { /* P3714 A01 */ - if (tca9539_raw_wr(cdi_mgr, 0x6, 0x0E) != 0) { + if (tca9539_wr(cdi_mgr, 0x6, 0x0E) != 0) { dev_err(&pdev->dev, "%s: ERR %d: TCA9539: Failed to select PWDN signal source\n", __func__, err); goto err_probe; } /* Output low for AGGA/B/C/D_PWRDN */ - if (tca9539_raw_wr(cdi_mgr, 0x2, 0x0E) != 0) { + if (tca9539_wr(cdi_mgr, 0x2, 0x0E) != 0) { dev_err(&pdev->dev, "%s: ERR %d: TCA9539: Failed to set the output level\n", __func__, err); goto err_probe; } } else if (cdi_mgr->cim_ver == 2U) { /* P3714 A02 */ - if (tca9539_raw_wr(cdi_mgr, 0x6, 0xC0) != 0) { + if (tca9539_wr(cdi_mgr, 0x6, 0xC0) != 0) { dev_err(&pdev->dev, "%s: ERR %d: TCA9539: Failed to select FS selection signal source\n", __func__, err); goto err_probe; } - if (tca9539_raw_wr(cdi_mgr, 0x7, 0x70) != 0) { + if (tca9539_wr(cdi_mgr, 0x7, 0x70) != 0) { dev_err(&pdev->dev, "%s: ERR %d: TCA9539: Failed to select PWDN signal source\n", __func__, err); @@ -1722,7 +1645,7 @@ static int cdi_mgr_probe(struct platform_device *pdev) cdi_mgr->cim_frsync[0], cdi_mgr->cim_frsync[1], cdi_mgr->cim_frsync[2]); - if (tca9539_raw_wr(cdi_mgr, 0x2, + if (tca9539_wr(cdi_mgr, 0x2, (cdi_mgr->cim_frsync[2] << 4) | (cdi_mgr->cim_frsync[1] << 2) | (cdi_mgr->cim_frsync[0])) < 0) { @@ -1732,7 +1655,7 @@ static int cdi_mgr_probe(struct platform_device *pdev) goto err_probe; } /* Output low for AGGA/B/C/D_PWRDN */ - if (tca9539_raw_wr(cdi_mgr, 0x3, 0x00) != 0) { + if (tca9539_wr(cdi_mgr, 0x3, 0x00) != 0) { dev_err(&pdev->dev, "%s: ERR %d: TCA9539: Failed to set the output level\n", __func__, err); diff --git a/include/uapi/media/cdi-dev.h b/include/uapi/media/cdi-dev.h index c5edfaa5..66527a44 100644 --- a/include/uapi/media/cdi-dev.h +++ b/include/uapi/media/cdi-dev.h @@ -11,6 +11,7 @@ #define CDI_DEV_IOCTL_RW _IOW('o', 1, struct cdi_dev_package) #define CDI_DEV_IOCTL_GET_PWR_INFO _IOW('o', 2, struct cdi_dev_pwr_ctrl_info) +#define CDI_DEV_IOCTL_FRSYNC_MUX _IOW('o', 3, struct cdi_dev_fsync_mux) #define DES_PWR_NVCCP 0U #define DES_PWR_GPIO 1U @@ -27,6 +28,11 @@ struct __attribute__ ((__packed__)) cdi_dev_pwr_ctrl_info { __u8 cam_pwr_links[MAX_POWER_LINKS_PER_BLOCK]; }; +struct cdi_dev_fsync_mux { + __s8 mux_sel; + __s8 cam_grp; +}; + struct __attribute__ ((__packed__)) cdi_dev_package { __u16 offset; __u16 offset_len;