From: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
To: Sakari Ailus <sakari.ailus@iki.fi>
Cc: linux-media@vger.kernel.org,
Paul Elder <paul.elder@ideasonboard.com>,
Hans Verkuil <hverkuil-cisco@xs4all.nl>,
Rob Herring <robh+dt@kernel.org>,
Krzysztof Kozlowski <krzysztof.kozlowski+dt@linaro.org>,
Conor Dooley <conor+dt@kernel.org>,
Matthias Brugger <matthias.bgg@gmail.com>,
AngeloGioacchino Del Regno
<angelogioacchino.delregno@collabora.com>,
Julien Stephan <jstephan@baylibre.com>,
devicetree@vger.kernel.org, linux-mediatek@lists.infradead.org
Subject: Re: [PATCH v4 3/3] media: i2c: Add driver for THine THP7312
Date: Fri, 27 Oct 2023 16:59:31 +0300 [thread overview]
Message-ID: <20231027135931.GA20465@pendragon.ideasonboard.com> (raw)
In-Reply-To: <20231027124529.GA19539@pendragon.ideasonboard.com>
On Fri, Oct 27, 2023 at 03:45:30PM +0300, Laurent Pinchart wrote:
> On Fri, Oct 27, 2023 at 12:30:37PM +0000, Sakari Ailus wrote:
> > On Tue, Oct 17, 2023 at 04:21:03PM +0300, Laurent Pinchart wrote:
> > > From: Paul Elder <paul.elder@ideasonboard.com>
> > >
> > > The THP7312 is an external camera ISP from THine. Add a V4L2 subdev
> > > driver for it.
> > >
> > > Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
> > > Co-developed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > > ---
> > > Changes since v3:
> > >
> > > - Move thp7312_get_regulators() to probe section
> > > - Turn firmware update handlers static
> > > - Wire up power management in struct driver
> > > - Remove unnecessary double underscore function prefixes
> > > - Configure CSI-2 lanes at stream on time
> > > - Clean up naming of power management functions
> > >
> > > Changes since v2:
> > >
> > > - Make boot-mode property optional
> > > - Fix dev_err_probe() usage in DT parsing
> > > - Additional dev_err_probe() usage
> > > - Use %u instead of %d for unsigned values
> > > - Don't split lines unnecessarily
> > > - Fix error handling in firmware upload initialization
> > > - Use CCI helpers in firmware update code
> > > - Fix runtime PM usage count
> > > ---
> > > MAINTAINERS | 1 +
> > > drivers/media/i2c/Kconfig | 16 +
> > > drivers/media/i2c/Makefile | 1 +
> > > drivers/media/i2c/thp7312.c | 2339 +++++++++++++++++++++++++++++++++++
> > > 4 files changed, 2357 insertions(+)
> > > create mode 100644 drivers/media/i2c/thp7312.c
[snip]
> > > diff --git a/drivers/media/i2c/thp7312.c b/drivers/media/i2c/thp7312.c
> > > new file mode 100644
> > > index 000000000000..7d3de929079d
> > > --- /dev/null
> > > +++ b/drivers/media/i2c/thp7312.c
> > > @@ -0,0 +1,2339 @@
[snip]
> > > +static int thp7312_sensor_init(struct thp7312_sensor *sensor, unsigned int index)
> > > +{
> > > + struct thp7312_device *thp7312 = sensor->thp7312;
> > > + struct device *dev = thp7312->dev;
> > > + unsigned int i;
> > > + int ret;
> > > +
> > > + sensor->index = index;
> > > +
> > > + /*
> > > + * Register a device for the sensor, to support usage of the regulator
> > > + * API.
> > > + */
> > > + sensor->dev = kzalloc(sizeof(*sensor->dev), GFP_KERNEL);
> > > + if (!sensor->dev)
> > > + return -ENOMEM;
> > > +
> > > + sensor->dev->parent = dev;
> > > + sensor->dev->of_node = of_node_get(sensor->of_node);
> >
> > This device could well find its way to a non-OF system. Could you use the
> > fwnode property API instead?
>
> I'm pretty sure there will be problems if someone was using this driver
> on an ACPI-based system, so trying to pretend it's supported without
> being able to test it may not be the best use of development time. I'll
> try, but if I hit any issue, I'll keep using the OF-specific functions
> in the next version.
>
> > > + sensor->dev->release = &thp7312_sensor_dev_release;
> > > + dev_set_name(sensor->dev, "%s-%s.%u", dev_name(dev),
> > > + thp7312->sensor_info->name, index);
> > > +
> > > + ret = device_register(sensor->dev);
> > > + if (ret < 0) {
> > > + dev_err(dev, "Failed to register device for sensor %u\n", index);
> > > + put_device(sensor->dev);
> > > + sensor->dev = NULL;
> > > + return ret;
> > > + }
> > > +
> > > + /* Retrieve the power supplies for the sensor, if any. */
> > > + if (thp7312->sensor_info->supplies) {
> > > + const struct thp7312_sensor_supply *supplies =
> > > + thp7312->sensor_info->supplies;
> > > + unsigned int num_supplies;
> > > +
> > > + for (num_supplies = 0; supplies[num_supplies].name; ++num_supplies)
> > > + ;
> > > +
> > > + sensor->supplies = devm_kcalloc(dev, num_supplies,
> > > + sizeof(*sensor->supplies),
> > > + GFP_KERNEL);
> > > + if (!sensor->supplies) {
> > > + ret = -ENOMEM;
> > > + goto error;
> > > + }
> > > +
> > > + for (i = 0; i < num_supplies; ++i)
> > > + sensor->supplies[i].supply = supplies[i].name;
> > > +
> > > + ret = regulator_bulk_get(sensor->dev, num_supplies,
> > > + sensor->supplies);
> > > + if (ret < 0) {
> > > + dev_err(dev, "Failed to get supplies for sensor %u\n", index);
> > > + goto error;
> > > + }
> > > +
> > > + sensor->num_supplies = i;
> > > + }
> > > +
> > > + return 0;
> > > +
> > > +error:
> > > + device_unregister(sensor->dev);
> > > + return ret;
> > > +}
> > > +
> > > +static int thp7312_init_sensors(struct thp7312_device *thp7312)
> > > +{
> > > + unsigned int i;
> > > + int ret;
> > > +
> > > + for (i = 0; i < ARRAY_SIZE(thp7312->sensors); i++) {
> > > + struct thp7312_sensor *sensor = &thp7312->sensors[i];
> > > +
> > > + if (!sensor->thp7312)
> > > + continue;
> > > +
> > > + ret = thp7312_sensor_init(sensor, i);
> > > + if (ret < 0)
> > > + return ret;
> > > + }
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static void thp7312_sensor_cleanup(struct thp7312_sensor *sensor)
> > > +{
> > > + if (sensor->num_supplies)
> > > + regulator_bulk_free(sensor->num_supplies, sensor->supplies);
> > > +
> > > + if (sensor->dev)
> > > + device_unregister(sensor->dev);
> > > + of_node_put(sensor->of_node);
> > > +}
> > > +
> > > +static void thp7312_remove_sensors(struct thp7312_device *thp7312)
> > > +{
> > > + unsigned int i;
> > > +
> > > + for (i = 0; i < ARRAY_SIZE(thp7312->sensors); i++) {
> > > + struct thp7312_sensor *sensor = &thp7312->sensors[i];
> > > +
> > > + if (!sensor->thp7312)
> > > + continue;
> > > +
> > > + thp7312_sensor_cleanup(sensor);
> > > + }
> > > +}
> > > +
> > > +static int thp7312_sensor_parse_dt(struct thp7312_device *thp7312,
> > > + struct device_node *node)
> > > +{
> > > + struct device *dev = thp7312->dev;
> > > + struct thp7312_sensor *sensor;
> > > + u32 data_lanes_rx[4];
> > > + const char *model;
> > > + unsigned int i;
> > > + u32 reg;
> > > + int ret;
> > > +
> > > + if (!of_device_is_available(node))
> > > + return -ENODEV;
> > > +
> > > + /* Retrieve the sensor index from the reg property. */
> > > + ret = of_property_read_u32(node, "reg", ®);
> > > + if (ret < 0) {
> > > + dev_err(dev, "'reg' property missing in sensor node\n");
> >
> > Shouldn't you assume it's zero instead?
>
> The property is mandatory.
>
> > > + return -EINVAL;
> > > + }
> > > +
> > > + if (reg >= ARRAY_SIZE(thp7312->sensors)) {
> > > + dev_err(dev, "Out-of-bounds 'reg' value %u\n", reg);
> > > + return -EINVAL;
> > > + }
> > > +
> > > + sensor = &thp7312->sensors[reg];
> > > + if (sensor->thp7312) {
> > > + dev_err(dev, "Duplicate entry for sensor %u\n", reg);
> > > + return -EINVAL;
> > > + }
> > > +
> > > + ret = of_property_read_string(node, "thine,model", &model);
> > > + if (ret < 0) {
> > > + dev_err(dev, "'thine,model' property missing in sensor node\n");
> > > + return -EINVAL;
> > > + }
> > > +
> > > + for (i = 0; i < ARRAY_SIZE(thp7312_sensor_info); i++) {
> > > + const struct thp7312_sensor_info *info =
> > > + &thp7312_sensor_info[i];
> > > +
> > > + if (!strcmp(info->model, model)) {
> > > + thp7312->sensor_info = info;
> > > + break;
> > > + }
> > > + }
> > > +
> > > + if (!thp7312->sensor_info) {
> > > + dev_err(dev, "Unsupported sensor model %s\n", model);
> > > + return -EINVAL;
> > > + }
> > > +
> > > + ret = of_property_read_u32_array(node, "data-lanes",
> > > + data_lanes_rx, ARRAY_SIZE(data_lanes_rx));
> > > + if (ret < 0) {
> > > + dev_err(dev, "Failed to read property data-lanes: %d\n", ret);
> > > + return ret;
> > > + }
> > > +
> > > + for (i = 0; i < ARRAY_SIZE(sensor->data_lanes); i++)
> > > + sensor->data_lanes[i] = (u8)data_lanes_rx[i];
> >
> > I don't think you need the cast here.
> >
> > > +
> > > + sensor->thp7312 = thp7312;
> > > + sensor->of_node = of_node_get(node);
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static int thp7312_parse_dt(struct thp7312_device *thp7312)
> > > +{
> > > + struct device *dev = thp7312->dev;
> > > + struct fwnode_handle *endpoint;
> > > + struct device_node *sensors;
> > > + unsigned int num_sensors = 0;
> > > + struct device_node *node;
> > > + int ret;
> > > +
> > > + endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
> > > + if (!endpoint)
> > > + return dev_err_probe(dev, -EINVAL, "Endpoint node not found\n");
> > > +
> > > + ret = v4l2_fwnode_endpoint_parse(endpoint, &thp7312->ep);
> >
> > You should assign the bus_type before parsing. It is deprecated to guess
> > it --- there's no universal guarantee it'll be successful.
>
> As only CSI-2 is supported for now, I'll do so.
>
> > > + fwnode_handle_put(endpoint);
> > > + if (ret)
> > > + return dev_err_probe(dev, ret, "Could not parse endpoint\n");
> > > +
> > > + if (thp7312->ep.bus_type != V4L2_MBUS_CSI2_DPHY)
> > > + return dev_err_probe(dev, -EINVAL, "Unsupported bus type %d\n",
> > > + thp7312->ep.bus_type);
> > > +
> > > + /*
> > > + * The thine,boot-mode property is optional and default to
> > > + * THP7312_BOOT_MODE_SPI_MASTER (1).
> > > + */
> > > + thp7312->boot_mode = THP7312_BOOT_MODE_SPI_MASTER;
> > > + ret = of_property_read_u32(dev->of_node, "thine,boot-mode",
> > > + &thp7312->boot_mode);
> > > + if (ret && ret != -EINVAL)
> > > + return dev_err_probe(dev, ret, "Property '%s' is invalid\n",
> > > + "thine,boot-mode");
> > > +
> > > + if (thp7312->boot_mode != THP7312_BOOT_MODE_2WIRE_SLAVE &&
> > > + thp7312->boot_mode != THP7312_BOOT_MODE_SPI_MASTER)
> > > + return dev_err_probe(dev, -EINVAL, "Invalid '%s' value %u\n",
> > > + "thine,boot-mode", thp7312->boot_mode);
> > > +
> > > + /* Sensors */
> > > + sensors = of_get_child_by_name(dev->of_node, "sensors");
> > > + if (!sensors) {
> > > + dev_err(dev, "'sensors' child node not found\n");
> > > + return -EINVAL;
> > > + }
> > > +
> > > + for_each_child_of_node(sensors, node) {
> > > + if (of_node_name_eq(node, "sensor")) {
I couldn't find a fwnode equivalent to this, so I'll keep using the OF
API in the next version. If someone ever wants to use this device on a
non-OF system, they will have to implement support for it on top.
> > > + if (!thp7312_sensor_parse_dt(thp7312, node))
> > > + num_sensors++;
> > > + }
> > > + }
> > > +
> > > + of_node_put(sensors);
> > > +
> > > + if (!num_sensors) {
> > > + dev_err(dev, "No sensor found\n");
> > > + return -EINVAL;
> > > + }
> > > +
> > > + return 0;
> > > +}
[snip]
--
Regards,
Laurent Pinchart
next prev parent reply other threads:[~2023-10-27 13:59 UTC|newest]
Thread overview: 24+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-10-17 13:21 [PATCH v4 0/3] media: i2c: Add driver for THine THP7312 ISP Laurent Pinchart
2023-10-17 13:21 ` [PATCH v4 1/3] dt-bindings: media: Add bindings " Laurent Pinchart
2023-10-18 15:02 ` Krzysztof Kozlowski
2023-10-27 11:55 ` Sakari Ailus
2023-10-27 12:19 ` Laurent Pinchart
2023-10-27 12:35 ` Sakari Ailus
2023-10-27 12:46 ` Laurent Pinchart
2023-10-27 14:52 ` Sakari Ailus
2023-10-27 15:36 ` Laurent Pinchart
2023-10-27 16:27 ` Sakari Ailus
2023-10-28 15:21 ` Laurent Pinchart
2023-10-17 13:21 ` [PATCH v4 2/3] media: uapi: Add controls for the " Laurent Pinchart
2023-10-17 13:21 ` [PATCH v4 3/3] media: i2c: Add driver for THine THP7312 Laurent Pinchart
2023-10-27 12:30 ` Sakari Ailus
2023-10-27 12:45 ` Laurent Pinchart
2023-10-27 13:59 ` Laurent Pinchart [this message]
2023-10-27 14:50 ` Sakari Ailus
2023-10-28 15:18 ` Laurent Pinchart
2023-10-30 8:09 ` Sakari Ailus
2023-10-30 10:42 ` Laurent Pinchart
2023-10-31 10:45 ` Sakari Ailus
2023-10-31 13:41 ` Andy Shevchenko
2023-10-31 14:24 ` Laurent Pinchart
2023-10-31 16:43 ` Andy Shevchenko
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20231027135931.GA20465@pendragon.ideasonboard.com \
--to=laurent.pinchart@ideasonboard.com \
--cc=angelogioacchino.delregno@collabora.com \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=hverkuil-cisco@xs4all.nl \
--cc=jstephan@baylibre.com \
--cc=krzysztof.kozlowski+dt@linaro.org \
--cc=linux-media@vger.kernel.org \
--cc=linux-mediatek@lists.infradead.org \
--cc=matthias.bgg@gmail.com \
--cc=paul.elder@ideasonboard.com \
--cc=robh+dt@kernel.org \
--cc=sakari.ailus@iki.fi \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).