Skip to content

Add ap1302 isp sensor driver#103541

Open
yongxu-wang15 wants to merge 3 commits intozephyrproject-rtos:mainfrom
nxp-upstream:Add-ap1302-isp-sensor-driver
Open

Add ap1302 isp sensor driver#103541
yongxu-wang15 wants to merge 3 commits intozephyrproject-rtos:mainfrom
nxp-upstream:Add-ap1302-isp-sensor-driver

Conversation

@yongxu-wang15
Copy link
Contributor

@yongxu-wang15 yongxu-wang15 commented Feb 5, 2026

Add onnn ap1302 isp driver

Add basic driver for ON Semiconductor AP1302 Image Signal Processor.
Advanced features are not implemented in this initial version.

Signed-off-by: Yongxu Wang <yongxu.wang@nxp.com>
Add lpi2c3 and cmko1 pin for imx95_evk board

Signed-off-by: Yongxu Wang <yongxu.wang@nxp.com>
Add device tree configuration for AP1302 ISP on i.MX95 EVK:
- AP1302 on LPI2C3 (I2C address 0x3C)
- ADP5585 and PCAL6408A GPIO expanders
- Camera power regulators
- MCLK clock configuration

Signed-off-by: Yongxu Wang <yongxu.wang@nxp.com>
Copy link
Contributor

@josuah josuah left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good to see ISPs being contributed to Zephyr.

Here are a few feedback points for you to consider, feel free to discuss on each of them or suggest.

Thank you!

Comment on lines +125 to +162
/* Register access functions */
static int ap1302_write_reg16(const struct device *dev, uint16_t reg, uint16_t val)
{
const struct ap1302_config *cfg = dev->config;
uint8_t buf[4];
int ret;

sys_put_be16(reg, &buf[0]);
sys_put_be16(val, &buf[2]);

ret = i2c_write_dt(&cfg->i2c, buf, sizeof(buf));
if (ret < 0) {
LOG_ERR("Failed to write reg 0x%04x: %d", reg, ret);
}

return ret;
}

static int ap1302_read_reg16(const struct device *dev, uint16_t reg, uint16_t *val)
{
const struct ap1302_config *cfg = dev->config;
uint8_t reg_buf[2];
uint8_t val_buf[2];
int ret;

sys_put_be16(reg, reg_buf);

ret = i2c_write_read_dt(&cfg->i2c, reg_buf, sizeof(reg_buf),
val_buf, sizeof(val_buf));
if (ret < 0) {
LOG_ERR("Failed to read reg 0x%04x: %d", reg, ret);
return ret;
}

*val = sys_get_be16(val_buf);

return 0;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems compatible with the MIPI CCI format, and there are utilities to send registers to camera in Zephyr which saves you the need to implement such wrapper function. Would that be interesting for you to use?

You may define the size your I2C target uses like so:

#define IMX335_REG8(addr) ((addr) | VIDEO_REG_ADDR16_DATA8)
#define IMX335_REG16(addr) ((addr) | VIDEO_REG_ADDR16_DATA16_LE)
#define IMX335_REG24(addr) ((addr) | VIDEO_REG_ADDR16_DATA24_LE)

Then define register names along with their size (here only 16-bit it seems):

#define IMX335_BCWAIT_TIME IMX335_REG8(0x300c)
#define IMX335_CPWAIT_TIME IMX335_REG8(0x300d)
#define IMX335_WINMODE IMX335_REG8(0x3018)
#define IMX335_HTRIMMING_START IMX335_REG16(0x302c)
#define IMX335_HNUM IMX335_REG16(0x302e)
#define IMX335_VMAX IMX335_REG24(0x3030)
#define IMX335_HMAX IMX335_REG16(0x3034)

Then use the functions from the CCI library to sendn I2C commands directly from uint16_t and so forth (passed as 32-bit values):

ret = video_write_cci_reg(&cfg->i2c, IMX335_REGHOLD, 1);
if (ret < 0) {
return ret;
}

You do not need to specify the register size (i.e. 16-bit) anywhere, as it is already encoded in the register definition (as seen above).

.reset_gpio = GPIO_DT_SPEC_INST_GET_OR(n, reset_gpios, {0}), \
.standby_gpio = GPIO_DT_SPEC_INST_GET_OR(n, standby_gpios, {0}), \
.isp_en_gpio = GPIO_DT_SPEC_INST_GET_OR(n, isp_en_gpios, {0}), \
.firmware_address = DT_INST_PROP(n, firmware_address), \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this firmware something loaded in a memory region?

It is possible that the user cannot know which memory region the firmware is stored in, for instance if their application only knows the name of the flash storage partition it is stored in, but not the actual address.

How about using a storage partition phandle?

Comment on lines +36 to +39
mipi-data-lanes:
type: int
required: true
description: Number of MIPI CSI-2 data lanes (1-4)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This video driver is what corresponds to a "subdevice" in Linux: a device without a "enqueue()" and "dequeue()" interface, which allows users to control an external chip located upstream to the MIPI receiver of the local SoC.

For instance: an image sensor, an ISP.

This is handled via remote-endpoint-label, port, and endpoint like in Linux.

You might be able to look at MIPID02 which is a MIPI in DVP out device in the same situation as this driver firmware-wise:

https://github.com/zephyrproject-rtos/zephyr/blob/main/dts/bindings/video/st%2Cmipid02.yaml

Then mipi-data-lanes and other properties can be taken from the existing .yaml files in Zephyr and just marked required: true.

Comment on lines +296 to +297
static int ap1302_load_firmware(const struct device *dev)
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the access to this binary blob is restricted, this means that this driver is not usable and I do not know if it is possible to merge it (I just need to check and discuss).

If the binary blob can be downloaded publicly, then here is the documentation in integrating binary blobs such as firmware into Zephyr https://docs.zephyrproject.org/latest/contribute/bin_blobs.html

Feel free to ask as many questions as you wish.

Comment on lines +490 to +492
if (fmt == NULL) {
return -EINVAL;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is often not required to test the pointers passed at runtime as these are conventionally left for the caller to validate.

return -EINVAL;
}

info = ap1302_find_format(fmt->pixelformat);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there many formats to support in the long term? If not, maybe a table with a single entry is not needed.

You might also be interested in validating that the format passed is valid according to the table of capabilities, which you can do using this:

/**
* @brief Search for a format that matches in a list of capabilities
*
* @param fmts The format capability list to search.
* @param fmt The format to find in the list.
* @param idx The pointer to a number of the first format that matches.
*
* @return 0 when a format is found.
* @return -ENOENT when no matching format is found.
*/
int video_format_caps_index(const struct video_format_cap *fmts, const struct video_format *fmt,
size_t *idx);

return -ENOTSUP;
}

ap1302_sanitize_format(fmt);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once video_format_caps_index() is called, and the return value is 0, the format is expected to be valid according to the list of supported formats of your driver.

Comment on lines +506 to +508
/* Calculate buffer parameters for YUYV (2 bytes per pixel) */
fmt->pitch = fmt->width * 2;
fmt->size = fmt->pitch * fmt->height;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a helper for doing it available in video_common.h (or maybe video.h).

# SPDX-License-Identifier: Apache-2.0

description: ONNN AP1302 Camera ISP

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about adding an example section to show how to integrate it on devicetree?

examples:
  - |
    ...
Copy link
Contributor

@ngphibang ngphibang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding this interesting driver.
Could you add a build_all test case for the driver ?
Could you also add support for this driver in the video (capture) sample ?
The ap1302 supports multi-sensors so it would be great to see this first multi-sensor use case in Zephyr, the video framework need to be extended for multi-sensor support, we can work together to see what is missing ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

4 participants