JVC camera and Hauppauge PVR-150 framegrabber.
With a manual setting of the device path and ID, the V4L2 Test Bench produced this image from a JVC TK-1070U camera on a microscope. http://easthope.ca/JVCtoPVR150screen.jpg Setting the device in the Test Bench each time it is opened becomes tedious and no /etc/*v4l* exists. Can the default configuration be adjusted permanently without recompiling? How? Although too dark, the image from the microscope slide is faintly visible. The upper half of the image is on the bottom of the screen and the lower half is at the top. On a VCR I might try adjusting vertical sync. Is there an equivalent in the Test Bench? Thanks, ... Peter E. -- 123456789 123456789 123456789 123456789 123456789 123456789 123456789 Tel: +1 360 639 0202 Pender Is.: +1 250 629 3757 http://easthope.ca/Peter.html Bcc: peter at easthope. ca
media: dvbsky: issues with DVBSky T680CI
er? Could you please look into this? Thank you. There is also a discontinuity issue with this device when a CAM is initialized. However, I'll address that later. Peter
CI Plus support for TT S2 3200
Does anyone know if the current Technotrend DVB-S2 S2-3200 with the CI daughter board supports the latest generation of CI+ CAMS and specified in http://www.ci-plus.com/ in non-legacy mode? I guess what I am asking is, can the hardware support full CI+, what would be involved software wise and has anyone looked at this? Pete -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [RFC] SW connection between DVB Transport Stream demuxer and I2C-based frontend
On 2019-07-08 13:08, Marc Gonzalez wrote: > Hello everyone, *snip* Focusing on #2... > PROBLEM #2 > > The tuner (si2157) is not on the i2c5 bus, instead it is on a private > i2c bus *behind* si2168, which routes requests to the proper client. > For the time being, I don't know how to model this relationship in DT. > (TODO: check i2c_slave_cb_t slave_cb in struct i2c_client) > I have initialized si2157 in the si2168 driver, but this doesn't feel > right. (Though it seems all(?) users pair 2168 with 2157.) > > > It would really help if I could get some guidance from media and i2c > people for these two issues. (I'd like to upstream the driver in time > for 5.3) > > Regards. > > > > diff --git a/arch/arm64/boot/dts/qcom/apq8098-batfish.dts > b/arch/arm64/boot/dts/qcom/apq8098-batfish.dts > index 29d59ecad138..9353e62375a7 100644 > --- a/arch/arm64/boot/dts/qcom/apq8098-batfish.dts > +++ b/arch/arm64/boot/dts/qcom/apq8098-batfish.dts > @@ -30,6 +30,28 @@ > status = "ok"; > }; > > +&blsp1_i2c5 { > + status = "ok"; > + clock-frequency = <10>; > + pinctrl-names = "default"; > + pinctrl-0 = <&i2c5_default>; > + > + dvb_demod: si2168@64 { > + compatible = "silabs,si2168"; > + reg = <0x64>; > + reset-gpios = <&tlmm 84 GPIO_ACTIVE_LOW>; In principle, I think you should be able to add something like this here: i2c-gate { #address-cells = <1>; #size-cells = <0>; tuner@60 { compatible = "silabs,si2157"; reg = <0x60>; /* whatever else is needed */ }; }; But in practice, I don't know if the si2157 driver understands that or if there is anything else that gets in the way. Totally untested... The i2c-gate child node is examined when you call i2c_mux_add_adapter if you have provided the correct struct device in the second argument when you called i2c_mux_alloc (I think that is already the case). You also need to set the I2C_MUX_GATE flag in the i2c_mux_alloc call, but I do not see a problem with that since I think only one child adapter is added. If it is a problem to add the I2C_MUX_GATE flag, then you can just adjust the above i2c-gate node according to the bindings in Documentation/devicetree/bindings/i2c/i2c-mux.txt. I.e. add this instead of the above i2c-gate node: i2c-mux { #address-cells = <1>; #size-cells = <0>; i2c@0 { #address-cells = <1>; #size-cells = <0>; reg = <0>; tuner@60 { compatible = "silabs,si2157"; reg = <0x60>; /* whatever else is needed */ }; }; }; But it feels *right* to add the I2C_MUX_GATE flag, because a gate is what you have. I think? Cheers, Peter > + }; > +}; > + > +&tsif { > + demod = <&dvb_demod>; > +}; > + > +&i2c5_default { > + drive-strength = <2>; > + bias-disable; > +}; > + > &qusb2phy { > status = "ok"; > vdda-pll-supply = <&vreg_l12a_1p8>;
Re: [PATCH] omap-dma/omap_vout_vrfb: fix off-by-one fi value
Hans, On 30/07/2019 9.14, Hans Verkuil wrote: > The OMAP 4 TRM specifies that when using double-index addressing > the address increases by the ES plus the EI value minus 1 within > a frame. When a full frame is transferred, the address increases > by the ES plus the frame index (FI) value minus 1. > > The omap-dma code didn't account for the 'minus 1' in the FI register. > To get correct addressing, add 1 to the src_icg value. > > This was found when testing a hacked version of the media m2m-deinterlace.c > driver on a Pandaboard. > > The only other source that uses this feature is omap_vout_vrfb.c, > and that adds a + 1 when setting the dst_icg. This is a workaround > for the broken omap-dma.c behavior. So remove the workaround at the > same time that we fix omap-dma.c. > > I tested the omap_vout driver with a Beagle XM board to check that > the '+ 1' in omap_vout_vrfb.c was indeed a workaround for the omap-dma > bug. Thanks for catching it. I have implemented the interleaved support based on the omap_vout_vrfb driver's behavior and hence I have missed the + 1 in the omap-dma. Acked-by: Peter Ujfalusi > > Signed-off-by: Hans Verkuil > --- > It makes sense that this patch goes in through the dmaengine subsystem > (Mauro, can you Ack this patch?), but if preferred it can also go in > through the media subsystem if we get an Ack. > > Regards, > > Hans > --- > drivers/dma/ti/omap-dma.c| 4 ++-- > drivers/media/platform/omap/omap_vout_vrfb.c | 2 +- > 2 files changed, 3 insertions(+), 3 deletions(-) > > diff --git a/drivers/dma/ti/omap-dma.c b/drivers/dma/ti/omap-dma.c > index ba2489d4ea24..ba27802efcd0 100644 > --- a/drivers/dma/ti/omap-dma.c > +++ b/drivers/dma/ti/omap-dma.c > @@ -1234,7 +1234,7 @@ static struct dma_async_tx_descriptor > *omap_dma_prep_dma_interleaved( > if (src_icg) { > d->ccr |= CCR_SRC_AMODE_DBLIDX; > d->ei = 1; > - d->fi = src_icg; > + d->fi = src_icg + 1; > } else if (xt->src_inc) { > d->ccr |= CCR_SRC_AMODE_POSTINC; > d->fi = 0; > @@ -1249,7 +1249,7 @@ static struct dma_async_tx_descriptor > *omap_dma_prep_dma_interleaved( > if (dst_icg) { > d->ccr |= CCR_DST_AMODE_DBLIDX; > sg->ei = 1; > - sg->fi = dst_icg; > + sg->fi = dst_icg + 1; > } else if (xt->dst_inc) { > d->ccr |= CCR_DST_AMODE_POSTINC; > sg->fi = 0; > diff --git a/drivers/media/platform/omap/omap_vout_vrfb.c > b/drivers/media/platform/omap/omap_vout_vrfb.c > index 29e3f5da59c1..729b1bf9395f 100644 > --- a/drivers/media/platform/omap/omap_vout_vrfb.c > +++ b/drivers/media/platform/omap/omap_vout_vrfb.c > @@ -254,7 +254,7 @@ int omap_vout_prepare_vrfb(struct omap_vout_device *vout, > > pixsize = vout->bpp * vout->vrfb_bpp; > dst_icg = ((MAX_PIXELS_PER_LINE * pixsize) - > - (vout->pix.width * vout->bpp)) + 1; > + (vout->pix.width * vout->bpp)); > > xt->src_start = vout->buf_phy_addr[vb->i]; > xt->dst_start = vout->vrfb_context[vb->i].paddr[0]; > - Péter Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki. Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki
try to edit channel in kaffeine 2.0.17 gives a crash
Hello, right-click on certain channels in the dvb-channel list in kaffeine 2.0.17 and selecting "edit" crashes the program without opening the channel-editor. gdb output says: Thread 1 "kaffeine" received signal SIGSEGV, Segmentation fault. 0x555a78c6 in DvbChannelEditor::DvbChannelEditor (this=0x574fce60, model_=, channel_=..., parent=) at /usr/src/debug/kaffeine-2.0.17-lp151.1.1.x86_64/src/dvb/dvbchanneldialog.cpp:961 961 QString text = QString::number(it.first); (gdb) CU
Re: [PATCH 2/2] [media] platform: add video-multiplexer subdevice driver
On 2017-04-28 16:13, Philipp Zabel wrote: > This driver can handle SoC internal and external video bus multiplexers, > controlled by mux controllers provided by the mux controller framework, > such as MMIO register bitfields or GPIOs. The subdevice passes through > the mbus configuration of the active input to the output side. > > Signed-off-by: Sascha Hauer > Signed-off-by: Philipp Zabel > Signed-off-by: Steve Longerbeam > --- > This has been last sent as part of the i.MX media series. > > Changes since https://patchwork.kernel.org/patch/9647869/: > - Split out the actual mux operation to be provided by the mux controller >framework [1]. GPIO and MMIO control can be provided by individual mux >controller drivers [2][3]. >[1] https://patchwork.kernel.org/patch/9695837/ >[2] https://patchwork.kernel.org/patch/9695839/ >[3] https://patchwork.kernel.org/patch/9704509/ > - Shortened 'video-multiplexer' to 'video-mux', replaced all instances of >vidsw with video_mux. > - Made the mux inactive by default, only activated by user interaction. > - Added CONFIG_OF and CONFIG_MULTIPLEXER dependencies. > - Reuse subdev.entity.num_pads instead of keeping our own count. > - Removed implicit link disabling. Instead, trying to enable a second >sink pad link yields -EBUSY. > - Merged _async_init into _probe. > - Removed superfluous pad index check from _set_format. > - Added is_source_pad helper to tell source and sink pads apart. > - Removed test for status property in endpoint nodes. Disable the remote >device or sever the endpoint link to disable a sink pad. > --- > drivers/media/platform/Kconfig | 6 + > drivers/media/platform/Makefile| 2 + > drivers/media/platform/video-mux.c | 341 > + > 3 files changed, 349 insertions(+) > create mode 100644 drivers/media/platform/video-mux.c > > diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig > index c9106e105baba..b046a6d39fee5 100644 > --- a/drivers/media/platform/Kconfig > +++ b/drivers/media/platform/Kconfig > @@ -74,6 +74,12 @@ config VIDEO_M32R_AR_M64278 > To compile this driver as a module, choose M here: the > module will be called arv. > > +config VIDEO_MUX > + tristate "Video Multiplexer" > + depends on OF && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER && > MULTIPLEXER > + help > + This driver provides support for N:1 video bus multiplexers. > + > config VIDEO_OMAP3 > tristate "OMAP 3 Camera support" > depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3 > diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile > index 349ddf6a69da2..fd2735ca3ff75 100644 > --- a/drivers/media/platform/Makefile > +++ b/drivers/media/platform/Makefile > @@ -27,6 +27,8 @@ obj-$(CONFIG_VIDEO_SH_VEU) += sh_veu.o > > obj-$(CONFIG_VIDEO_MEM2MEM_DEINTERLACE) += m2m-deinterlace.o > > +obj-$(CONFIG_VIDEO_MUX) += video-mux.o > + > obj-$(CONFIG_VIDEO_S3C_CAMIF)+= s3c-camif/ > obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS4_IS) += exynos4-is/ > obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG) += s5p-jpeg/ > diff --git a/drivers/media/platform/video-mux.c > b/drivers/media/platform/video-mux.c > new file mode 100644 > index 0..419541729f67e > --- /dev/null > +++ b/drivers/media/platform/video-mux.c > @@ -0,0 +1,341 @@ > +/* > + * video stream multiplexer controlled via mux control > + * > + * Copyright (C) 2013 Pengutronix, Sascha Hauer > + * Copyright (C) 2016 Pengutronix, Philipp Zabel 2017? > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License > + * as published by the Free Software Foundation; either version 2 > + * of the License, or (at your option) any later version. > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +struct video_mux { > + struct v4l2_subdev subdev; > + struct media_pad *pads; > + struct v4l2_mbus_framefmt *format_mbus; > + struct v4l2_of_endpoint *endpoint; > + struct mux_control *mux; > + int active; > +}; > + > +static inline struct video_mux *v4l2_subdev_to_video_mux(struct v4l2_subdev > *sd) > +{ > + return container_of(sd, struct video_mux, subdev); > +} > + > +static inline bool is_source_pad(struct video_mux *vmux, unsigned int pad) > +{ > + return pad == vmux->subdev.entity.num_pads - 1; > +} > + > +static int video_mux_link_setup(struct media_entity *entity, > + const struct media_pad *local, > +
Re: [PATCH 2/2] [media] platform: add video-multiplexer subdevice driver
On 2017-04-29 23:29, Peter Rosin wrote: > On 2017-04-28 16:13, Philipp Zabel wrote: >> This driver can handle SoC internal and external video bus multiplexers, >> controlled by mux controllers provided by the mux controller framework, >> such as MMIO register bitfields or GPIOs. The subdevice passes through >> the mbus configuration of the active input to the output side. >> >> Signed-off-by: Sascha Hauer >> Signed-off-by: Philipp Zabel >> Signed-off-by: Steve Longerbeam >> --- >> This has been last sent as part of the i.MX media series. >> >> Changes since https://patchwork.kernel.org/patch/9647869/: >> - Split out the actual mux operation to be provided by the mux controller >>framework [1]. GPIO and MMIO control can be provided by individual mux >>controller drivers [2][3]. >>[1] https://patchwork.kernel.org/patch/9695837/ >>[2] https://patchwork.kernel.org/patch/9695839/ >>[3] https://patchwork.kernel.org/patch/9704509/ >> - Shortened 'video-multiplexer' to 'video-mux', replaced all instances of >>vidsw with video_mux. >> - Made the mux inactive by default, only activated by user interaction. >> - Added CONFIG_OF and CONFIG_MULTIPLEXER dependencies. >> - Reuse subdev.entity.num_pads instead of keeping our own count. >> - Removed implicit link disabling. Instead, trying to enable a second >>sink pad link yields -EBUSY. >> - Merged _async_init into _probe. >> - Removed superfluous pad index check from _set_format. >> - Added is_source_pad helper to tell source and sink pads apart. >> - Removed test for status property in endpoint nodes. Disable the remote >>device or sever the endpoint link to disable a sink pad. >> --- >> drivers/media/platform/Kconfig | 6 + >> drivers/media/platform/Makefile| 2 + >> drivers/media/platform/video-mux.c | 341 >> + >> 3 files changed, 349 insertions(+) >> create mode 100644 drivers/media/platform/video-mux.c >> >> diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig >> index c9106e105baba..b046a6d39fee5 100644 >> --- a/drivers/media/platform/Kconfig >> +++ b/drivers/media/platform/Kconfig >> @@ -74,6 +74,12 @@ config VIDEO_M32R_AR_M64278 >>To compile this driver as a module, choose M here: the >>module will be called arv. >> >> +config VIDEO_MUX >> +tristate "Video Multiplexer" >> +depends on OF && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER && >> MULTIPLEXER >> +help >> + This driver provides support for N:1 video bus multiplexers. >> + >> config VIDEO_OMAP3 >> tristate "OMAP 3 Camera support" >> depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3 >> diff --git a/drivers/media/platform/Makefile >> b/drivers/media/platform/Makefile >> index 349ddf6a69da2..fd2735ca3ff75 100644 >> --- a/drivers/media/platform/Makefile >> +++ b/drivers/media/platform/Makefile >> @@ -27,6 +27,8 @@ obj-$(CONFIG_VIDEO_SH_VEU) += sh_veu.o >> >> obj-$(CONFIG_VIDEO_MEM2MEM_DEINTERLACE) += m2m-deinterlace.o >> >> +obj-$(CONFIG_VIDEO_MUX) += video-mux.o >> + >> obj-$(CONFIG_VIDEO_S3C_CAMIF) += s3c-camif/ >> obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS4_IS) += exynos4-is/ >> obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG)+= s5p-jpeg/ >> diff --git a/drivers/media/platform/video-mux.c >> b/drivers/media/platform/video-mux.c >> new file mode 100644 >> index 0..419541729f67e >> --- /dev/null >> +++ b/drivers/media/platform/video-mux.c >> @@ -0,0 +1,341 @@ >> +/* >> + * video stream multiplexer controlled via mux control >> + * >> + * Copyright (C) 2013 Pengutronix, Sascha Hauer >> + * Copyright (C) 2016 Pengutronix, Philipp Zabel > > 2017? > >> + * >> + * This program is free software; you can redistribute it and/or >> + * modify it under the terms of the GNU General Public License >> + * as published by the Free Software Foundation; either version 2 >> + * of the License, or (at your option) any later version. >> + * This program is distributed in the hope that it will be useful, >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >> + * GNU General Public License for more details. >> + */ >> + >> +#include >> +#in
Re: [PATCH 2/2] [media] platform: add video-multiplexer subdevice driver
On 2017-05-02 17:21, Philipp Zabel wrote: > On Sat, 2017-04-29 at 23:42 +0200, Peter Rosin wrote: >> On 2017-04-29 23:29, Peter Rosin wrote: >>> On 2017-04-28 16:13, Philipp Zabel wrote: >>>> This driver can handle SoC internal and external video bus multiplexers, >>>> controlled by mux controllers provided by the mux controller framework, >>>> such as MMIO register bitfields or GPIOs. The subdevice passes through >>>> the mbus configuration of the active input to the output side. >>>> >>>> Signed-off-by: Sascha Hauer >>>> Signed-off-by: Philipp Zabel >>>> Signed-off-by: Steve Longerbeam >>>> --- >>>> This has been last sent as part of the i.MX media series. >>>> >>>> Changes since https://patchwork.kernel.org/patch/9647869/: >>>> - Split out the actual mux operation to be provided by the mux controller >>>>framework [1]. GPIO and MMIO control can be provided by individual mux >>>>controller drivers [2][3]. >>>>[1] https://patchwork.kernel.org/patch/9695837/ >>>>[2] https://patchwork.kernel.org/patch/9695839/ >>>>[3] https://patchwork.kernel.org/patch/9704509/ >>>> - Shortened 'video-multiplexer' to 'video-mux', replaced all instances of >>>>vidsw with video_mux. >>>> - Made the mux inactive by default, only activated by user interaction. >>>> - Added CONFIG_OF and CONFIG_MULTIPLEXER dependencies. >>>> - Reuse subdev.entity.num_pads instead of keeping our own count. >>>> - Removed implicit link disabling. Instead, trying to enable a second >>>>sink pad link yields -EBUSY. >>>> - Merged _async_init into _probe. >>>> - Removed superfluous pad index check from _set_format. >>>> - Added is_source_pad helper to tell source and sink pads apart. >>>> - Removed test for status property in endpoint nodes. Disable the remote >>>>device or sever the endpoint link to disable a sink pad. >>>> --- >>>> drivers/media/platform/Kconfig | 6 + >>>> drivers/media/platform/Makefile| 2 + >>>> drivers/media/platform/video-mux.c | 341 >>>> + >>>> 3 files changed, 349 insertions(+) >>>> create mode 100644 drivers/media/platform/video-mux.c >>>> >>>> diff --git a/drivers/media/platform/Kconfig >>>> b/drivers/media/platform/Kconfig >>>> index c9106e105baba..b046a6d39fee5 100644 >>>> --- a/drivers/media/platform/Kconfig >>>> +++ b/drivers/media/platform/Kconfig >>>> @@ -74,6 +74,12 @@ config VIDEO_M32R_AR_M64278 >>>> To compile this driver as a module, choose M here: the >>>> module will be called arv. >>>> >>>> +config VIDEO_MUX >>>> + tristate "Video Multiplexer" >>>> + depends on OF && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER && >>>> MULTIPLEXER >>>> + help >>>> +This driver provides support for N:1 video bus multiplexers. >>>> + >>>> config VIDEO_OMAP3 >>>>tristate "OMAP 3 Camera support" >>>>depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3 >>>> diff --git a/drivers/media/platform/Makefile >>>> b/drivers/media/platform/Makefile >>>> index 349ddf6a69da2..fd2735ca3ff75 100644 >>>> --- a/drivers/media/platform/Makefile >>>> +++ b/drivers/media/platform/Makefile >>>> @@ -27,6 +27,8 @@ obj-$(CONFIG_VIDEO_SH_VEU) += sh_veu.o >>>> >>>> obj-$(CONFIG_VIDEO_MEM2MEM_DEINTERLACE) += m2m-deinterlace.o >>>> >>>> +obj-$(CONFIG_VIDEO_MUX) += video-mux.o >>>> + >>>> obj-$(CONFIG_VIDEO_S3C_CAMIF) += s3c-camif/ >>>> obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS4_IS)+= exynos4-is/ >>>> obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG) += s5p-jpeg/ >>>> diff --git a/drivers/media/platform/video-mux.c >>>> b/drivers/media/platform/video-mux.c >>>> new file mode 100644 >>>> index 0..419541729f67e >>>> --- /dev/null >>>> +++ b/drivers/media/platform/video-mux.c >>>> @@ -0,0 +1,341 @@ >>>> +/* >>>> + * video stream multiplexer controlled via mux control >>>> + * >>>> + * Cop
[RESEND v2 PATCH] v4l: omap_vout: vrfb: Convert to dmaengine
The dmaengine driver for sDMA now have support for interleaved transfer. This trasnfer type was open coded with the legacy omap-dma API, but now we can move it to dmaengine. Signed-off-by: Peter Ujfalusi --- Hi, changes since RESEND (27.10.2016): - rebased on next-20170503 I can not test it on real HW (still), but I have validated [1] that the change is correct and should not cause any regression. Laurent: can you verify the patch on a real hardware? Regards, Peter drivers/media/platform/omap/omap_vout_vrfb.c | 133 --- drivers/media/platform/omap/omap_voutdef.h | 6 +- 2 files changed, 83 insertions(+), 56 deletions(-) diff --git a/drivers/media/platform/omap/omap_vout_vrfb.c b/drivers/media/platform/omap/omap_vout_vrfb.c index 92c4e1826356..45a553d4f5b2 100644 --- a/drivers/media/platform/omap/omap_vout_vrfb.c +++ b/drivers/media/platform/omap/omap_vout_vrfb.c @@ -16,7 +16,6 @@ #include #include -#include #include #include "omap_voutdef.h" @@ -63,7 +62,7 @@ static int omap_vout_allocate_vrfb_buffers(struct omap_vout_device *vout, /* * Wakes up the application once the DMA transfer to VRFB space is completed. */ -static void omap_vout_vrfb_dma_tx_callback(int lch, u16 ch_status, void *data) +static void omap_vout_vrfb_dma_tx_callback(void *data) { struct vid_vrfb_dma *t = (struct vid_vrfb_dma *) data; @@ -94,6 +93,7 @@ int omap_vout_setup_vrfb_bufs(struct platform_device *pdev, int vid_num, int ret = 0, i, j; struct omap_vout_device *vout; struct video_device *vfd; + dma_cap_mask_t mask; int image_width, image_height; int vrfb_num_bufs = VRFB_NUM_BUFS; struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); @@ -131,18 +131,27 @@ int omap_vout_setup_vrfb_bufs(struct platform_device *pdev, int vid_num, /* * Request and Initialize DMA, for DMA based VRFB transfer */ - vout->vrfb_dma_tx.dev_id = OMAP_DMA_NO_DEVICE; - vout->vrfb_dma_tx.dma_ch = -1; - vout->vrfb_dma_tx.req_status = DMA_CHAN_ALLOTED; - ret = omap_request_dma(vout->vrfb_dma_tx.dev_id, "VRFB DMA TX", - omap_vout_vrfb_dma_tx_callback, - (void *) &vout->vrfb_dma_tx, &vout->vrfb_dma_tx.dma_ch); - if (ret < 0) { + dma_cap_zero(mask); + dma_cap_set(DMA_INTERLEAVE, mask); + vout->vrfb_dma_tx.chan = dma_request_chan_by_mask(&mask); + if (IS_ERR(vout->vrfb_dma_tx.chan)) { vout->vrfb_dma_tx.req_status = DMA_CHAN_NOT_ALLOTED; + } else { + size_t xt_size = sizeof(struct dma_interleaved_template) + +sizeof(struct data_chunk); + + vout->vrfb_dma_tx.xt = kzalloc(xt_size, GFP_KERNEL); + if (!vout->vrfb_dma_tx.xt) { + dma_release_channel(vout->vrfb_dma_tx.chan); + vout->vrfb_dma_tx.req_status = DMA_CHAN_NOT_ALLOTED; + } + } + + if (vout->vrfb_dma_tx.req_status == DMA_CHAN_NOT_ALLOTED) dev_info(&pdev->dev, ": failed to allocate DMA Channel for video%d\n", vfd->minor); - } + init_waitqueue_head(&vout->vrfb_dma_tx.wait); /* statically allocated the VRFB buffer is done through @@ -177,7 +186,9 @@ void omap_vout_release_vrfb(struct omap_vout_device *vout) if (vout->vrfb_dma_tx.req_status == DMA_CHAN_ALLOTED) { vout->vrfb_dma_tx.req_status = DMA_CHAN_NOT_ALLOTED; - omap_free_dma(vout->vrfb_dma_tx.dma_ch); + kfree(vout->vrfb_dma_tx.xt); + dmaengine_terminate_sync(vout->vrfb_dma_tx.chan); + dma_release_channel(vout->vrfb_dma_tx.chan); } } @@ -219,70 +230,84 @@ int omap_vout_vrfb_buffer_setup(struct omap_vout_device *vout, } int omap_vout_prepare_vrfb(struct omap_vout_device *vout, - struct videobuf_buffer *vb) + struct videobuf_buffer *vb) { - dma_addr_t dmabuf; - struct vid_vrfb_dma *tx; + struct dma_async_tx_descriptor *tx; + enum dma_ctrl_flags flags; + struct dma_chan *chan = vout->vrfb_dma_tx.chan; + struct dma_device *dmadev = chan->device; + struct dma_interleaved_template *xt = vout->vrfb_dma_tx.xt; + dma_cookie_t cookie; + enum dma_status status; enum dss_rotation rotation; - u32 dest_frame_index = 0, src_element_index = 0; - u32 dest_element_index = 0, src_frame_index = 0; - u32 elem_count = 0, frame_count = 0, pixsize = 2; + size_t dst_icg; + u32 pixsize; if (!is_rotation_enabled(vout)) return 0; - dmabuf = vout->buf_phy_addr[vb->i]; /* If ro
Re: [RESEND v2 PATCH] v4l: omap_vout: vrfb: Convert to dmaengine
On 2017-05-03 14:08, Peter Ujfalusi wrote: The dmaengine driver for sDMA now have support for interleaved transfer. This trasnfer type was open coded with the legacy omap-dma API, but now we can move it to dmaengine. Signed-off-by: Peter Ujfalusi --- Hi, changes since RESEND (27.10.2016): - rebased on next-20170503 I can not test it on real HW (still), but I have validated [1] that the change is correct and should not cause any regression. Laurent: can you verify the patch on a real hardware? [1] https://github.com/omap-audio/linux-audio/blob/peter/linux-next-wip/drivers/misc/ovv_dmaengine.c Regards, Peter drivers/media/platform/omap/omap_vout_vrfb.c | 133 --- drivers/media/platform/omap/omap_voutdef.h | 6 +- 2 files changed, 83 insertions(+), 56 deletions(-) diff --git a/drivers/media/platform/omap/omap_vout_vrfb.c b/drivers/media/platform/omap/omap_vout_vrfb.c index 92c4e1826356..45a553d4f5b2 100644 --- a/drivers/media/platform/omap/omap_vout_vrfb.c +++ b/drivers/media/platform/omap/omap_vout_vrfb.c @@ -16,7 +16,6 @@ #include #include -#include #include #include "omap_voutdef.h" @@ -63,7 +62,7 @@ static int omap_vout_allocate_vrfb_buffers(struct omap_vout_device *vout, /* * Wakes up the application once the DMA transfer to VRFB space is completed. */ -static void omap_vout_vrfb_dma_tx_callback(int lch, u16 ch_status, void *data) +static void omap_vout_vrfb_dma_tx_callback(void *data) { struct vid_vrfb_dma *t = (struct vid_vrfb_dma *) data; @@ -94,6 +93,7 @@ int omap_vout_setup_vrfb_bufs(struct platform_device *pdev, int vid_num, int ret = 0, i, j; struct omap_vout_device *vout; struct video_device *vfd; + dma_cap_mask_t mask; int image_width, image_height; int vrfb_num_bufs = VRFB_NUM_BUFS; struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); @@ -131,18 +131,27 @@ int omap_vout_setup_vrfb_bufs(struct platform_device *pdev, int vid_num, /* * Request and Initialize DMA, for DMA based VRFB transfer */ - vout->vrfb_dma_tx.dev_id = OMAP_DMA_NO_DEVICE; - vout->vrfb_dma_tx.dma_ch = -1; - vout->vrfb_dma_tx.req_status = DMA_CHAN_ALLOTED; - ret = omap_request_dma(vout->vrfb_dma_tx.dev_id, "VRFB DMA TX", - omap_vout_vrfb_dma_tx_callback, - (void *) &vout->vrfb_dma_tx, &vout->vrfb_dma_tx.dma_ch); - if (ret < 0) { + dma_cap_zero(mask); + dma_cap_set(DMA_INTERLEAVE, mask); + vout->vrfb_dma_tx.chan = dma_request_chan_by_mask(&mask); + if (IS_ERR(vout->vrfb_dma_tx.chan)) { vout->vrfb_dma_tx.req_status = DMA_CHAN_NOT_ALLOTED; + } else { + size_t xt_size = sizeof(struct dma_interleaved_template) + +sizeof(struct data_chunk); + + vout->vrfb_dma_tx.xt = kzalloc(xt_size, GFP_KERNEL); + if (!vout->vrfb_dma_tx.xt) { + dma_release_channel(vout->vrfb_dma_tx.chan); + vout->vrfb_dma_tx.req_status = DMA_CHAN_NOT_ALLOTED; + } + } + + if (vout->vrfb_dma_tx.req_status == DMA_CHAN_NOT_ALLOTED) dev_info(&pdev->dev, ": failed to allocate DMA Channel for video%d\n", vfd->minor); - } + init_waitqueue_head(&vout->vrfb_dma_tx.wait); /* statically allocated the VRFB buffer is done through @@ -177,7 +186,9 @@ void omap_vout_release_vrfb(struct omap_vout_device *vout) if (vout->vrfb_dma_tx.req_status == DMA_CHAN_ALLOTED) { vout->vrfb_dma_tx.req_status = DMA_CHAN_NOT_ALLOTED; - omap_free_dma(vout->vrfb_dma_tx.dma_ch); + kfree(vout->vrfb_dma_tx.xt); + dmaengine_terminate_sync(vout->vrfb_dma_tx.chan); + dma_release_channel(vout->vrfb_dma_tx.chan); } } @@ -219,70 +230,84 @@ int omap_vout_vrfb_buffer_setup(struct omap_vout_device *vout, } int omap_vout_prepare_vrfb(struct omap_vout_device *vout, - struct videobuf_buffer *vb) + struct videobuf_buffer *vb) { - dma_addr_t dmabuf; - struct vid_vrfb_dma *tx; + struct dma_async_tx_descriptor *tx; + enum dma_ctrl_flags flags; + struct dma_chan *chan = vout->vrfb_dma_tx.chan; + struct dma_device *dmadev = chan->device; + struct dma_interleaved_template *xt = vout->vrfb_dma_tx.xt; + dma_cookie_t cookie; + enum dma_status status; enum dss_rotation rotation; - u32 dest_frame_index = 0, src_element_index = 0; - u32 dest_element_index = 0, src_frame_index = 0; - u32 elem_count = 0, frame_count = 0, pixsize = 2; + size_t dst_icg; + u32 pix
Re: [PATCH 2/2] [media] platform: add video-multiplexer subdevice driver
On 2017-05-03 10:35, Philipp Zabel wrote: > On Tue, 2017-05-02 at 19:42 +0200, Peter Rosin wrote: >> On 2017-05-02 17:21, Philipp Zabel wrote: >>> Thank you, I've resent a version with a mutex lock around vmux->active. >> >> I had a bunch of ifs in the above message, so I'm not sure it's needed. >> I would expect there to be a lock outside somewhere in the media layer. >> A cursory look gets me to media-entity.c and media_entity_setup_link() >> which does have a mutex. But I'm no media expert, so maybe there are other >> ways of getting to video_mux_link_setup that I'm not aware of? > > link_setup is always called under the graph mutex of the /dev/media > device. That is why I didn't think about locking too hard. In fact, I > initially wrote this expecting mux_control_get_exclusive to exist and > the mux select/deselect not to be locked at all. > > But set_format is called from an unlocked ioctl on a /dev/v4l-subdev > device. Until your comments I didn't notice that it would be possible to > let link_setup set active = -1 in the middle of the set_format call, > causing it to return garbage. Obviously, now that you point it out. I completely missed set_format... Cheers, peda
Re: [PATCH] [BUGREPORT] media: v4l: omap_vout: vrfb: initialize DMA flags
Arnd, sorry for the delayed response, I was away w/o internet connection for the past weeks. On 2017-07-10 14:18, Arnd Bergmann wrote: > Passing uninitialized flags into device_prep_interleaved_dma is clearly > a bad idea, and we get a compiler warning for it: > > drivers/media/platform/omap/omap_vout_vrfb.c: In function > 'omap_vout_prepare_vrfb': > drivers/media/platform/omap/omap_vout_vrfb.c:273:5: error: 'flags' may be > used uninitialized in this function [-Werror=maybe-uninitialized] I can not explain why I have missed this. > It seems that the OMAP dmaengine ignores the flags, but we should > pick the right ones anyway. Unfortunately I don't know what they > should be, so I just picked the most common flags. Please set the > right flags here and fold the modified patch. The flags are fine. > > Fixes: 6a1560ecaa8c ("media: v4l: omap_vout: vrfb: Convert to dmaengine") > Signed-off-by: Arnd Bergmann Acked-by: Peter Ujfalusi > --- > drivers/media/platform/omap/omap_vout_vrfb.c | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > > diff --git a/drivers/media/platform/omap/omap_vout_vrfb.c > b/drivers/media/platform/omap/omap_vout_vrfb.c > index 45a553d4f5b2..fed28b6bbbc0 100644 > --- a/drivers/media/platform/omap/omap_vout_vrfb.c > +++ b/drivers/media/platform/omap/omap_vout_vrfb.c > @@ -233,7 +233,7 @@ int omap_vout_prepare_vrfb(struct omap_vout_device *vout, > struct videobuf_buffer *vb) > { > struct dma_async_tx_descriptor *tx; > - enum dma_ctrl_flags flags; > + enum dma_ctrl_flags flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK; > struct dma_chan *chan = vout->vrfb_dma_tx.chan; > struct dma_device *dmadev = chan->device; > struct dma_interleaved_template *xt = vout->vrfb_dma_tx.xt; > - Péter
[PATCH 3/3] [media] cx231xx: only unregister successfully registered i2c adapters
This prevents potentially scary debug messages from the i2c core. Signed-off-by: Peter Rosin --- drivers/media/usb/cx231xx/cx231xx-core.c | 3 +++ drivers/media/usb/cx231xx/cx231xx-i2c.c | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/media/usb/cx231xx/cx231xx-core.c b/drivers/media/usb/cx231xx/cx231xx-core.c index 46646ecd2dbc..f372ad3917a8 100644 --- a/drivers/media/usb/cx231xx/cx231xx-core.c +++ b/drivers/media/usb/cx231xx/cx231xx-core.c @@ -1311,6 +1311,7 @@ int cx231xx_dev_init(struct cx231xx *dev) dev->i2c_bus[0].i2c_period = I2C_SPEED_100K;/* 100 KHz */ dev->i2c_bus[0].i2c_nostop = 0; dev->i2c_bus[0].i2c_reserve = 0; + dev->i2c_bus[0].i2c_rc = -ENODEV; /* External Master 2 Bus */ dev->i2c_bus[1].nr = 1; @@ -1318,6 +1319,7 @@ int cx231xx_dev_init(struct cx231xx *dev) dev->i2c_bus[1].i2c_period = I2C_SPEED_100K;/* 100 KHz */ dev->i2c_bus[1].i2c_nostop = 0; dev->i2c_bus[1].i2c_reserve = 0; + dev->i2c_bus[1].i2c_rc = -ENODEV; /* Internal Master 3 Bus */ dev->i2c_bus[2].nr = 2; @@ -1325,6 +1327,7 @@ int cx231xx_dev_init(struct cx231xx *dev) dev->i2c_bus[2].i2c_period = I2C_SPEED_100K;/* 100kHz */ dev->i2c_bus[2].i2c_nostop = 0; dev->i2c_bus[2].i2c_reserve = 0; + dev->i2c_bus[2].i2c_rc = -ENODEV; /* register I2C buses */ errCode = cx231xx_i2c_register(&dev->i2c_bus[0]); diff --git a/drivers/media/usb/cx231xx/cx231xx-i2c.c b/drivers/media/usb/cx231xx/cx231xx-i2c.c index 3e49517cb5e0..8ce6b815d16d 100644 --- a/drivers/media/usb/cx231xx/cx231xx-i2c.c +++ b/drivers/media/usb/cx231xx/cx231xx-i2c.c @@ -553,7 +553,8 @@ int cx231xx_i2c_register(struct cx231xx_i2c *bus) */ void cx231xx_i2c_unregister(struct cx231xx_i2c *bus) { - i2c_del_adapter(&bus->i2c_adap); + if (!bus->i2c_rc) + i2c_del_adapter(&bus->i2c_adap); } /* -- 2.11.0
[PATCH 2/3] [media] cx231xx: drop return value of cx231xx_i2c_unregister
Noone cares anyway. Signed-off-by: Peter Rosin --- drivers/media/usb/cx231xx/cx231xx-i2c.c | 3 +-- drivers/media/usb/cx231xx/cx231xx.h | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/media/usb/cx231xx/cx231xx-i2c.c b/drivers/media/usb/cx231xx/cx231xx-i2c.c index 3a0c45ffd40f..3e49517cb5e0 100644 --- a/drivers/media/usb/cx231xx/cx231xx-i2c.c +++ b/drivers/media/usb/cx231xx/cx231xx-i2c.c @@ -551,10 +551,9 @@ int cx231xx_i2c_register(struct cx231xx_i2c *bus) * cx231xx_i2c_unregister() * unregister i2c_bus */ -int cx231xx_i2c_unregister(struct cx231xx_i2c *bus) +void cx231xx_i2c_unregister(struct cx231xx_i2c *bus) { i2c_del_adapter(&bus->i2c_adap); - return 0; } /* diff --git a/drivers/media/usb/cx231xx/cx231xx.h b/drivers/media/usb/cx231xx/cx231xx.h index 27ee035f9f84..72d5937a087e 100644 --- a/drivers/media/usb/cx231xx/cx231xx.h +++ b/drivers/media/usb/cx231xx/cx231xx.h @@ -762,7 +762,7 @@ int cx231xx_reset_analog_tuner(struct cx231xx *dev); /* Provided by cx231xx-i2c.c */ void cx231xx_do_i2c_scan(struct cx231xx *dev, int i2c_port); int cx231xx_i2c_register(struct cx231xx_i2c *bus); -int cx231xx_i2c_unregister(struct cx231xx_i2c *bus); +void cx231xx_i2c_unregister(struct cx231xx_i2c *bus); int cx231xx_i2c_mux_create(struct cx231xx *dev); int cx231xx_i2c_mux_register(struct cx231xx *dev, int mux_no); void cx231xx_i2c_mux_unregister(struct cx231xx *dev); -- 2.11.0
[PATCH 0/3] [media] cx231xx: cleanup i2c adapter handling
Hi! This seems like fairly straight forward cleanups/bugfixes. I don't have the hardware and found the issues by reading code while doing other things. So, it builds for me, but it's untested. 1/3 changes behavior on failure, but I think it's the right thing to do. If it isn't for some reason, then the current code is crap anyway, because as-is it compares with a value that is always zero meaning that the entire "if (0 != bus->i2c_rc)"-clause with its dev_warn can be removed from cx231xx_i2c_register. Cheers, Peter Peter Rosin (3): [media] cx231xx: fail probe if i2c_add_adapter fails [media] cx231xx: drop return value of cx231xx_i2c_unregister [media] cx231xx: only unregister successfully registered i2c adapters drivers/media/usb/cx231xx/cx231xx-core.c | 3 +++ drivers/media/usb/cx231xx/cx231xx-i2c.c | 8 drivers/media/usb/cx231xx/cx231xx.h | 4 ++-- 3 files changed, 9 insertions(+), 6 deletions(-) -- 2.11.0
[PATCH 1/3] [media] cx231xx: fail probe if i2c_add_adapter fails
While at it, change the type of the previously always-zero i2c_rc member to int, matching the returned type from i2c_add_adapter. Signed-off-by: Peter Rosin --- drivers/media/usb/cx231xx/cx231xx-i2c.c | 2 +- drivers/media/usb/cx231xx/cx231xx.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/cx231xx/cx231xx-i2c.c b/drivers/media/usb/cx231xx/cx231xx-i2c.c index 8d95b1154e12..3a0c45ffd40f 100644 --- a/drivers/media/usb/cx231xx/cx231xx-i2c.c +++ b/drivers/media/usb/cx231xx/cx231xx-i2c.c @@ -538,7 +538,7 @@ int cx231xx_i2c_register(struct cx231xx_i2c *bus) bus->i2c_adap.algo_data = bus; i2c_set_adapdata(&bus->i2c_adap, &dev->v4l2_dev); - i2c_add_adapter(&bus->i2c_adap); + bus->i2c_rc = i2c_add_adapter(&bus->i2c_adap); if (0 != bus->i2c_rc) dev_warn(dev->dev, diff --git a/drivers/media/usb/cx231xx/cx231xx.h b/drivers/media/usb/cx231xx/cx231xx.h index 986c64ba5b56..27ee035f9f84 100644 --- a/drivers/media/usb/cx231xx/cx231xx.h +++ b/drivers/media/usb/cx231xx/cx231xx.h @@ -476,7 +476,7 @@ struct cx231xx_i2c { /* i2c i/o */ struct i2c_adapter i2c_adap; - u32 i2c_rc; + int i2c_rc; /* different settings for each bus */ u8 i2c_period; -- 2.11.0
Re: [PATCH 3/3] [media] cx231xx: only unregister successfully registered i2c adapters
On 2017-08-09 16:27, Mauro Carvalho Chehab wrote: > Em Mon, 31 Jul 2017 15:38:52 +0200 > Peter Rosin escreveu: > >> This prevents potentially scary debug messages from the i2c core. >> >> Signed-off-by: Peter Rosin >> --- >> drivers/media/usb/cx231xx/cx231xx-core.c | 3 +++ >> drivers/media/usb/cx231xx/cx231xx-i2c.c | 3 ++- >> 2 files changed, 5 insertions(+), 1 deletion(-) >> >> diff --git a/drivers/media/usb/cx231xx/cx231xx-core.c >> b/drivers/media/usb/cx231xx/cx231xx-core.c >> index 46646ecd2dbc..f372ad3917a8 100644 >> --- a/drivers/media/usb/cx231xx/cx231xx-core.c >> +++ b/drivers/media/usb/cx231xx/cx231xx-core.c >> @@ -1311,6 +1311,7 @@ int cx231xx_dev_init(struct cx231xx *dev) >> dev->i2c_bus[0].i2c_period = I2C_SPEED_100K;/* 100 KHz */ >> dev->i2c_bus[0].i2c_nostop = 0; >> dev->i2c_bus[0].i2c_reserve = 0; >> +dev->i2c_bus[0].i2c_rc = -ENODEV; >> >> /* External Master 2 Bus */ >> dev->i2c_bus[1].nr = 1; >> @@ -1318,6 +1319,7 @@ int cx231xx_dev_init(struct cx231xx *dev) >> dev->i2c_bus[1].i2c_period = I2C_SPEED_100K;/* 100 KHz */ >> dev->i2c_bus[1].i2c_nostop = 0; >> dev->i2c_bus[1].i2c_reserve = 0; >> +dev->i2c_bus[1].i2c_rc = -ENODEV; >> >> /* Internal Master 3 Bus */ >> dev->i2c_bus[2].nr = 2; >> @@ -1325,6 +1327,7 @@ int cx231xx_dev_init(struct cx231xx *dev) >> dev->i2c_bus[2].i2c_period = I2C_SPEED_100K;/* 100kHz */ >> dev->i2c_bus[2].i2c_nostop = 0; >> dev->i2c_bus[2].i2c_reserve = 0; >> +dev->i2c_bus[2].i2c_rc = -ENODEV; >> >> /* register I2C buses */ >> errCode = cx231xx_i2c_register(&dev->i2c_bus[0]); >> diff --git a/drivers/media/usb/cx231xx/cx231xx-i2c.c >> b/drivers/media/usb/cx231xx/cx231xx-i2c.c >> index 3e49517cb5e0..8ce6b815d16d 100644 >> --- a/drivers/media/usb/cx231xx/cx231xx-i2c.c >> +++ b/drivers/media/usb/cx231xx/cx231xx-i2c.c >> @@ -553,7 +553,8 @@ int cx231xx_i2c_register(struct cx231xx_i2c *bus) >> */ >> void cx231xx_i2c_unregister(struct cx231xx_i2c *bus) >> { >> -i2c_del_adapter(&bus->i2c_adap); >> +if (!bus->i2c_rc) >> +i2c_del_adapter(&bus->i2c_adap); > > That doesn't sound right. what happens if i2c_rc is 1 or 2? > > IMHO, the right would would be, instead: > > if (bus->i2c_rc >= 0) > i2c_del_adapter(&bus->i2c_adap); In theory, yes. But in practice i2c_add_adapter never returns >0, and is also documented so. Let me know if you still want an update. In that case I'll also fix the precedent present in the context of patch 1/3, i.e. if (0 != bus->i2c_rc) ... Cheers, peda
Re: [linux-media] Patch notification: 3 patches updated
On 2017-08-09 17:01, Patchwork wrote: > Hello, > > The following patches (submitted by you) have been updated in patchwork: > > * linux-media: [2/3,media] cx231xx: drop return value of > cx231xx_i2c_unregister > - http://patchwork.linuxtv.org/patch/42858/ > - for: Linux Media kernel patches > was: Under Review > now: Superseded > > * linux-media: [3/3,media] cx231xx: only unregister successfully registered > i2c adapters > - http://patchwork.linuxtv.org/patch/42857/ > - for: Linux Media kernel patches > was: Under Review > now: Superseded > > * linux-media: [1/3,media] cx231xx: fail probe if i2c_add_adapter fails > - http://patchwork.linuxtv.org/patch/42859/ > - for: Linux Media kernel patches > was: Under Review > now: Superseded > > This email is a notification only - you do not need to respond. *snip* > If you think any status change is a mistake, please send an email to the ML. I think the correct status should be "Accepted", right? Because I got mails saying all three were queued... Cheers, peda
Re: [STLinux Kernel] [PATCH v6 01/10] Documentation: DT: add bindings for ST DELTA
On Wed, 01 Feb 2017, Hugues Fruchet wrote: > This patch adds DT binding documentation for STMicroelectronics > DELTA V4L2 video decoder. > > Signed-off-by: Hugues Fruchet Acked-by: Peter Griffin > --- > Documentation/devicetree/bindings/media/st,st-delta.txt | 17 > + > 1 file changed, 17 insertions(+) > create mode 100644 Documentation/devicetree/bindings/media/st,st-delta.txt > > diff --git a/Documentation/devicetree/bindings/media/st,st-delta.txt > b/Documentation/devicetree/bindings/media/st,st-delta.txt > new file mode 100644 > index 000..a538ab3 > --- /dev/null > +++ b/Documentation/devicetree/bindings/media/st,st-delta.txt > @@ -0,0 +1,17 @@ > +* STMicroelectronics DELTA multi-format video decoder > + > +Required properties: > +- compatible: should be "st,st-delta". > +- clocks: from common clock binding: handle hardware IP needed clocks, the > + number of clocks may depend on the SoC type. > + See ../clock/clock-bindings.txt for details. > +- clock-names: names of the clocks listed in clocks property in the same > order. > + > +Example: > + delta0 { > + compatible = "st,st-delta"; > + clock-names = "delta", "delta-st231", "delta-flash-promip"; > + clocks = <&clk_s_c0_flexgen CLK_VID_DMU>, > + <&clk_s_c0_flexgen CLK_ST231_DMU>, > + <&clk_s_c0_flexgen CLK_FLASH_PROMIP>; > + }; -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [STLinux Kernel] [PATCH v6 10/10] st-delta: debug: trace stream/frame information & summary
On Wed, 01 Feb 2017, Hugues Fruchet wrote: > Adds some trace points showing input compressed stream or > output decoded frame information. > Adds an unconditional trace point when streaming starts showing > the compressed stream and the decoded frame information. > Adds an unconditional trace point at instance closure summarizing > into a single line the decoding process (stream information, decoded > and output frames number, potential errors observed). > > Signed-off-by: Hugues Fruchet Acked-by: Peter Griffin > --- > drivers/media/platform/sti/delta/Makefile | 2 +- > drivers/media/platform/sti/delta/delta-debug.c | 72 > ++ > drivers/media/platform/sti/delta/delta-debug.h | 18 +++ > drivers/media/platform/sti/delta/delta-v4l2.c | 30 +-- > 4 files changed, 117 insertions(+), 5 deletions(-) > create mode 100644 drivers/media/platform/sti/delta/delta-debug.c > create mode 100644 drivers/media/platform/sti/delta/delta-debug.h > > diff --git a/drivers/media/platform/sti/delta/Makefile > b/drivers/media/platform/sti/delta/Makefile > index b268df6..8d032508 100644 > --- a/drivers/media/platform/sti/delta/Makefile > +++ b/drivers/media/platform/sti/delta/Makefile > @@ -1,5 +1,5 @@ > obj-$(CONFIG_VIDEO_STI_DELTA_DRIVER) := st-delta.o > -st-delta-y := delta-v4l2.o delta-mem.o delta-ipc.o > +st-delta-y := delta-v4l2.o delta-mem.o delta-ipc.o delta-debug.o > > # MJPEG support > st-delta-$(CONFIG_VIDEO_STI_DELTA_MJPEG) += delta-mjpeg-hdr.o > diff --git a/drivers/media/platform/sti/delta/delta-debug.c > b/drivers/media/platform/sti/delta/delta-debug.c > new file mode 100644 > index 000..a7ebf2c > --- /dev/null > +++ b/drivers/media/platform/sti/delta/delta-debug.c > @@ -0,0 +1,72 @@ > +/* > + * Copyright (C) STMicroelectronics SA 2015 > + * Authors: Hugues Fruchet > + * Fabrice Lecoultre > + * for STMicroelectronics. > + * License terms: GNU General Public License (GPL), version 2 > + */ > + > +#include "delta.h" > +#include "delta-debug.h" > + > +char *delta_streaminfo_str(struct delta_streaminfo *s, char *str, > +unsigned int len) > +{ > + if (!s) > + return NULL; > + > + snprintf(str, len, > + "%4.4s %dx%d %s %s dpb=%d %s %s %s%dx%d@(%d,%d) %s%d/%d", > + (char *)&s->streamformat, s->width, s->height, > + s->profile, s->level, s->dpb, > + (s->field == V4L2_FIELD_NONE) ? "progressive" : "interlaced", > + s->other, > + s->flags & DELTA_STREAMINFO_FLAG_CROP ? "crop=" : "", > + s->crop.width, s->crop.height, > + s->crop.left, s->crop.top, > + s->flags & DELTA_STREAMINFO_FLAG_PIXELASPECT ? "par=" : "", > + s->pixelaspect.numerator, > + s->pixelaspect.denominator); > + > + return str; > +} > + > +char *delta_frameinfo_str(struct delta_frameinfo *f, char *str, > + unsigned int len) > +{ > + if (!f) > + return NULL; > + > + snprintf(str, len, > + "%4.4s %dx%d aligned %dx%d %s %s%dx%d@(%d,%d) %s%d/%d", > + (char *)&f->pixelformat, f->width, f->height, > + f->aligned_width, f->aligned_height, > + (f->field == V4L2_FIELD_NONE) ? "progressive" : "interlaced", > + f->flags & DELTA_STREAMINFO_FLAG_CROP ? "crop=" : "", > + f->crop.width, f->crop.height, > + f->crop.left, f->crop.top, > + f->flags & DELTA_STREAMINFO_FLAG_PIXELASPECT ? "par=" : "", > + f->pixelaspect.numerator, > + f->pixelaspect.denominator); > + > + return str; > +} > + > +void delta_trace_summary(struct delta_ctx *ctx) > +{ > + struct delta_dev *delta = ctx->dev; > + struct delta_streaminfo *s = &ctx->streaminfo; > + unsigned char str[100] = ""; > + > + if (!(ctx->flags & DELTA_FLAG_STREAMINFO)) > + return; > + > + dev_dbg(delta->dev, "%s %s, %d frames decoded, %d frames output, %d > frames dropped, %d stream errors, %d decode errors", > + ctx->name, > + delta_streaminfo_str(s, str, sizeof(str)), > + ctx->decoded_frames, > + ctx->output_frames, > + ctx->dropped_frames, >
Re: [STLinux Kernel] [PATCH v6 09/10] [media] st-delta: add mjpeg support
On Wed, 01 Feb 2017, Hugues Fruchet wrote: > Adds support of DELTA MJPEG video decoder back-end, > implemented by calling JPEG_DECODER_HW0 firmware > using RPMSG IPC communication layer. > > Signed-off-by: Hugues Fruchet Acked-by: Peter Griffin > --- > drivers/media/platform/Kconfig | 12 +- > drivers/media/platform/sti/delta/Makefile | 4 + > drivers/media/platform/sti/delta/delta-cfg.h | 3 + > drivers/media/platform/sti/delta/delta-mjpeg-dec.c | 455 > + > drivers/media/platform/sti/delta/delta-mjpeg-fw.h | 225 ++ > drivers/media/platform/sti/delta/delta-mjpeg-hdr.c | 149 +++ > drivers/media/platform/sti/delta/delta-mjpeg.h | 35 ++ > drivers/media/platform/sti/delta/delta-v4l2.c | 3 + > 8 files changed, 885 insertions(+), 1 deletion(-) > create mode 100644 drivers/media/platform/sti/delta/delta-mjpeg-dec.c > create mode 100644 drivers/media/platform/sti/delta/delta-mjpeg-fw.h > create mode 100644 drivers/media/platform/sti/delta/delta-mjpeg-hdr.c > create mode 100644 drivers/media/platform/sti/delta/delta-mjpeg.h > > diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig > index 2e82ec6..20b26ea 100644 > --- a/drivers/media/platform/Kconfig > +++ b/drivers/media/platform/Kconfig > @@ -317,10 +317,20 @@ config VIDEO_STI_DELTA > > if VIDEO_STI_DELTA > > +config VIDEO_STI_DELTA_MJPEG > + bool "STMicroelectronics DELTA MJPEG support" > + default y > + help > + Enables DELTA MJPEG hardware support. > + > + To compile this driver as a module, choose M here: > + the module will be called st-delta. > + > config VIDEO_STI_DELTA_DRIVER > tristate > depends on VIDEO_STI_DELTA > - default n > + depends on VIDEO_STI_DELTA_MJPEG > + default VIDEO_STI_DELTA_MJPEG > select VIDEOBUF2_DMA_CONTIG > select V4L2_MEM2MEM_DEV > select RPMSG > diff --git a/drivers/media/platform/sti/delta/Makefile > b/drivers/media/platform/sti/delta/Makefile > index b791ba0..b268df6 100644 > --- a/drivers/media/platform/sti/delta/Makefile > +++ b/drivers/media/platform/sti/delta/Makefile > @@ -1,2 +1,6 @@ > obj-$(CONFIG_VIDEO_STI_DELTA_DRIVER) := st-delta.o > st-delta-y := delta-v4l2.o delta-mem.o delta-ipc.o > + > +# MJPEG support > +st-delta-$(CONFIG_VIDEO_STI_DELTA_MJPEG) += delta-mjpeg-hdr.o > +st-delta-$(CONFIG_VIDEO_STI_DELTA_MJPEG) += delta-mjpeg-dec.o > diff --git a/drivers/media/platform/sti/delta/delta-cfg.h > b/drivers/media/platform/sti/delta/delta-cfg.h > index f6674f6..c6388f5 100644 > --- a/drivers/media/platform/sti/delta/delta-cfg.h > +++ b/drivers/media/platform/sti/delta/delta-cfg.h > @@ -57,5 +57,8 @@ > #define DELTA_HW_AUTOSUSPEND_DELAY_MS 5 > > #define DELTA_MAX_DECODERS 10 > +#ifdef CONFIG_VIDEO_STI_DELTA_MJPEG > +extern const struct delta_dec mjpegdec; > +#endif > > #endif /* DELTA_CFG_H */ > diff --git a/drivers/media/platform/sti/delta/delta-mjpeg-dec.c > b/drivers/media/platform/sti/delta/delta-mjpeg-dec.c > new file mode 100644 > index 000..e79bdc6 > --- /dev/null > +++ b/drivers/media/platform/sti/delta/delta-mjpeg-dec.c > @@ -0,0 +1,455 @@ > +/* > + * Copyright (C) STMicroelectronics SA 2013 > + * Author: Hugues Fruchet for STMicroelectronics. > + * License terms: GNU General Public License (GPL), version 2 > + */ > + > +#include > + > +#include "delta.h" > +#include "delta-ipc.h" > +#include "delta-mjpeg.h" > +#include "delta-mjpeg-fw.h" > + > +#define DELTA_MJPEG_MAX_RESO DELTA_MAX_RESO > + > +struct delta_mjpeg_ctx { > + /* jpeg header */ > + struct mjpeg_header header_struct; > + struct mjpeg_header *header; > + > + /* ipc */ > + void *ipc_hdl; > + struct delta_buf *ipc_buf; > + > + /* decoded output frame */ > + struct delta_frame *out_frame; > + > + unsigned char str[3000]; > +}; > + > +#define to_ctx(ctx) ((struct delta_mjpeg_ctx *)(ctx)->priv) > + > +static char *ipc_open_param_str(struct jpeg_video_decode_init_params_t *p, > + char *str, unsigned int len) > +{ > + char *b = str; > + > + if (!p) > + return ""; > + > + b += snprintf(b, len, > + "jpeg_video_decode_init_params_t\n" > + "circular_buffer_begin_addr_p 0x%x\n" > + "circular_buffer_end_addr_p 0x%x\n", > + p->circular_buffer_begin_addr_p, > + p->circular_
Re: [STLinux Kernel] [PATCH v6 07/10] [media] st-delta: rpmsg ipc support
On Wed, 01 Feb 2017, Hugues Fruchet wrote: > IPC (Inter Process Communication) support for communication with > DELTA coprocessor firmware using rpmsg kernel framework. > Based on 4 services open/set_stream/decode/close and their associated > rpmsg messages. > The messages structures are duplicated on both host and firmware > side and are packed (use only of 32 bits size fields in messages > structures to ensure packing). > Each service is synchronous; service returns only when firmware > acknowledges the associated command message. > Due to significant parameters size exchanged from host to copro, > parameters are not inserted in rpmsg messages. Instead, parameters are > stored in physical memory shared between host and coprocessor. > Memory is non-cacheable, so no special operation is required > to ensure memory coherency on host and on coprocessor side. > Multi-instance support and re-entrance are ensured using host_hdl and > copro_hdl in message header exchanged between both host and coprocessor. > This avoids to manage tables on both sides to get back the running context > of each instance. > > Signed-off-by: Hugues Fruchet Acked-by: Peter Griffin > --- > drivers/media/platform/Kconfig| 1 + > drivers/media/platform/sti/delta/Makefile | 2 +- > drivers/media/platform/sti/delta/delta-ipc.c | 594 > ++ > drivers/media/platform/sti/delta/delta-ipc.h | 76 > drivers/media/platform/sti/delta/delta-v4l2.c | 11 + > drivers/media/platform/sti/delta/delta.h | 21 + > 6 files changed, 704 insertions(+), 1 deletion(-) > create mode 100644 drivers/media/platform/sti/delta/delta-ipc.c > create mode 100644 drivers/media/platform/sti/delta/delta-ipc.h > > diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig > index 2247d9d..2e82ec6 100644 > --- a/drivers/media/platform/Kconfig > +++ b/drivers/media/platform/Kconfig > @@ -323,6 +323,7 @@ config VIDEO_STI_DELTA_DRIVER > default n > select VIDEOBUF2_DMA_CONTIG > select V4L2_MEM2MEM_DEV > + select RPMSG > > endif # VIDEO_STI_DELTA > > diff --git a/drivers/media/platform/sti/delta/Makefile > b/drivers/media/platform/sti/delta/Makefile > index 93a3037..b791ba0 100644 > --- a/drivers/media/platform/sti/delta/Makefile > +++ b/drivers/media/platform/sti/delta/Makefile > @@ -1,2 +1,2 @@ > obj-$(CONFIG_VIDEO_STI_DELTA_DRIVER) := st-delta.o > -st-delta-y := delta-v4l2.o delta-mem.o > +st-delta-y := delta-v4l2.o delta-mem.o delta-ipc.o > diff --git a/drivers/media/platform/sti/delta/delta-ipc.c > b/drivers/media/platform/sti/delta/delta-ipc.c > new file mode 100644 > index 000..41e4a4c > --- /dev/null > +++ b/drivers/media/platform/sti/delta/delta-ipc.c > @@ -0,0 +1,594 @@ > +/* > + * Copyright (C) STMicroelectronics SA 2015 > + * Author: Hugues Fruchet for STMicroelectronics. > + * License terms: GNU General Public License (GPL), version 2 > + */ > + > +#include > + > +#include "delta.h" > +#include "delta-ipc.h" > +#include "delta-mem.h" > + > +#define IPC_TIMEOUT 100 > +#define IPC_SANITY_TAG 0xDEADBEEF > + > +enum delta_ipc_fw_command { > + DELTA_IPC_OPEN, > + DELTA_IPC_SET_STREAM, > + DELTA_IPC_DECODE, > + DELTA_IPC_CLOSE > +}; > + > +#define to_rpmsg_driver(__drv) container_of(__drv, struct rpmsg_driver, drv) > +#define to_delta(__d) container_of(__d, struct delta_dev, rpmsg_driver) > + > +#define to_ctx(hdl) ((struct delta_ipc_ctx *)hdl) > +#define to_pctx(ctx) container_of(ctx, struct delta_ctx, ipc_ctx) > + > +struct delta_ipc_header_msg { > + u32 tag; > + void *host_hdl; > + u32 copro_hdl; > + u32 command; > +}; > + > +#define to_host_hdl(ctx) ((void *)ctx) > + > +#define msg_to_ctx(msg) ((struct delta_ipc_ctx *)(msg)->header.host_hdl) > +#define msg_to_copro_hdl(msg) ((msg)->header.copro_hdl) > + > +static inline dma_addr_t to_paddr(struct delta_ipc_ctx *ctx, void *vaddr) > +{ > + return (ctx->ipc_buf->paddr + (vaddr - ctx->ipc_buf->vaddr)); > +} > + > +static inline bool is_valid_data(struct delta_ipc_ctx *ctx, > + void *data, u32 size) > +{ > + return ((data >= ctx->ipc_buf->vaddr) && > + ((data + size) <= (ctx->ipc_buf->vaddr + ctx->ipc_buf->size))); > +} > + > +/* > + * IPC shared memory (@ipc_buf_size, @ipc_buf_paddr) is sent to copro > + * at each instance opening. This memory is allocated by IPC client > + * and given through delta_ipc_open(). All messages parameters > + * (open, set_stream, decode) will have their phy a
Re: [STLinux Kernel] [PATCH v6 03/10] ARM: multi_v7_defconfig: enable STMicroelectronics DELTA Support
On Wed, 01 Feb 2017, Hugues Fruchet wrote: > Enables support of STMicroelectronics STiH4xx SoC series > DELTA multi-format video decoder V4L2 driver. > > Signed-off-by: Hugues Fruchet Acked-by: Peter Griffin > --- > arch/arm/configs/multi_v7_defconfig | 1 + > 1 file changed, 1 insertion(+) > > diff --git a/arch/arm/configs/multi_v7_defconfig > b/arch/arm/configs/multi_v7_defconfig > index b01a438..5dff8fe 100644 > --- a/arch/arm/configs/multi_v7_defconfig > +++ b/arch/arm/configs/multi_v7_defconfig > @@ -569,6 +569,7 @@ CONFIG_VIDEO_SAMSUNG_S5P_MFC=m > CONFIG_VIDEO_SAMSUNG_EXYNOS_GSC=m > CONFIG_VIDEO_STI_BDISP=m > CONFIG_VIDEO_STI_HVA=m > +CONFIG_VIDEO_STI_DELTA=m > CONFIG_VIDEO_RENESAS_JPU=m > CONFIG_VIDEO_RENESAS_VSP1=m > CONFIG_V4L_TEST_DRIVERS=y -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [STLinux Kernel] [PATCH v6 04/10] [media] MAINTAINERS: add st-delta driver
Hi Hugues, On Wed, 01 Feb 2017, Hugues Fruchet wrote: > Add entry for the STMicroelectronics DELTA driver. > > Signed-off-by: Hugues Fruchet > --- > MAINTAINERS | 8 > 1 file changed, 8 insertions(+) > > diff --git a/MAINTAINERS b/MAINTAINERS > index cfff2c9..38cc652 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -2429,6 +2429,14 @@ W: https://linuxtv.org > S: Supported > F: drivers/media/platform/sti/bdisp > > +DELTA ST MEDIA DRIVER > +M: Hugues Fruchet > +L: linux-media@vger.kernel.org Would be useful to also include ker...@stlinux.com mailing list. Apart from that: Acked-by: Peter Griffin > +T: git git://linuxtv.org/media_tree.git > +W: https://linuxtv.org > +S: Supported > +F: drivers/media/platform/sti/delta > + > BEFS FILE SYSTEM > M: Luis de Bethencourt > M: Salah Triki -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [STLinux Kernel] [PATCH v6 05/10] [media] st-delta: STiH4xx multi-format video decoder v4l2 driver
On Wed, 01 Feb 2017, Hugues Fruchet wrote: > This V4L2 driver enables DELTA multi-format video decoder > of STMicroelectronics STiH4xx SoC series. > > Signed-off-by: Hugues Fruchet Acked-by: Peter Griffin > --- > drivers/media/platform/Kconfig| 28 + > drivers/media/platform/Makefile |2 + > drivers/media/platform/sti/delta/Makefile |2 + > drivers/media/platform/sti/delta/delta-cfg.h | 61 + > drivers/media/platform/sti/delta/delta-v4l2.c | 1813 > + > drivers/media/platform/sti/delta/delta.h | 514 +++ > 6 files changed, 2420 insertions(+) > create mode 100644 drivers/media/platform/sti/delta/Makefile > create mode 100644 drivers/media/platform/sti/delta/delta-cfg.h > create mode 100644 drivers/media/platform/sti/delta/delta-v4l2.c > create mode 100644 drivers/media/platform/sti/delta/delta.h > > diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig > index d944421..2247d9d 100644 > --- a/drivers/media/platform/Kconfig > +++ b/drivers/media/platform/Kconfig > @@ -298,6 +298,34 @@ config VIDEO_STI_HVA > To compile this driver as a module, choose M here: > the module will be called st-hva. > > +config VIDEO_STI_DELTA > + tristate "STMicroelectronics DELTA multi-format video decoder V4L2 > driver" > + depends on VIDEO_DEV && VIDEO_V4L2 > + depends on ARCH_STI || COMPILE_TEST > + depends on HAS_DMA > + help > + This V4L2 driver enables DELTA multi-format video decoder > + of STMicroelectronics STiH4xx SoC series allowing hardware > + decoding of various compressed video bitstream format in > + raw uncompressed format. > + > + Use this option to see the decoders available for such > + hardware. > + > + Please notice that the driver will only be built if > + at least one of the DELTA decoder below is selected. > + > +if VIDEO_STI_DELTA > + > +config VIDEO_STI_DELTA_DRIVER > + tristate > + depends on VIDEO_STI_DELTA > + default n > + select VIDEOBUF2_DMA_CONTIG > + select V4L2_MEM2MEM_DEV > + > +endif # VIDEO_STI_DELTA > + > config VIDEO_SH_VEU > tristate "SuperH VEU mem2mem video processing driver" > depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA > diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile > index 5b3cb27..349ddf6 100644 > --- a/drivers/media/platform/Makefile > +++ b/drivers/media/platform/Makefile > @@ -39,6 +39,8 @@ obj-$(CONFIG_VIDEO_STI_BDISP) += sti/bdisp/ > obj-$(CONFIG_VIDEO_STI_HVA) += sti/hva/ > obj-$(CONFIG_DVB_C8SECTPFE) += sti/c8sectpfe/ > > +obj-$(CONFIG_VIDEO_STI_DELTA)+= sti/delta/ > + > obj-$(CONFIG_BLACKFIN) += blackfin/ > > obj-$(CONFIG_ARCH_DAVINCI) += davinci/ > diff --git a/drivers/media/platform/sti/delta/Makefile > b/drivers/media/platform/sti/delta/Makefile > new file mode 100644 > index 000..467519e > --- /dev/null > +++ b/drivers/media/platform/sti/delta/Makefile > @@ -0,0 +1,2 @@ > +obj-$(CONFIG_VIDEO_STI_DELTA_DRIVER) := st-delta.o > +st-delta-y := delta-v4l2.o > diff --git a/drivers/media/platform/sti/delta/delta-cfg.h > b/drivers/media/platform/sti/delta/delta-cfg.h > new file mode 100644 > index 000..f6674f6 > --- /dev/null > +++ b/drivers/media/platform/sti/delta/delta-cfg.h > @@ -0,0 +1,61 @@ > +/* > + * Copyright (C) STMicroelectronics SA 2015 > + * Author: Hugues Fruchet for STMicroelectronics. > + * License terms: GNU General Public License (GPL), version 2 > + */ > + > +#ifndef DELTA_CFG_H > +#define DELTA_CFG_H > + > +#define DELTA_FW_VERSION "21.1-3" > + > +#define DELTA_MIN_WIDTH 32 > +#define DELTA_MAX_WIDTH 4096 > +#define DELTA_MIN_HEIGHT 32 > +#define DELTA_MAX_HEIGHT 2400 > + > +/* DELTA requires a 32x32 pixels alignment for frames */ > +#define DELTA_WIDTH_ALIGNMENT32 > +#define DELTA_HEIGHT_ALIGNMENT 32 > + > +#define DELTA_DEFAULT_WIDTH DELTA_MIN_WIDTH > +#define DELTA_DEFAULT_HEIGHT DELTA_MIN_HEIGHT > +#define DELTA_DEFAULT_FRAMEFORMAT V4L2_PIX_FMT_NV12 > +#define DELTA_DEFAULT_STREAMFORMAT V4L2_PIX_FMT_MJPEG > + > +#define DELTA_MAX_RESO (DELTA_MAX_WIDTH * DELTA_MAX_HEIGHT) > + > +/* guard value for number of access units */ > +#define DELTA_MAX_AUS 10 > + > +/* IP perf dependent, can be tuned */ > +#define DELTA_PEAK_FRAME_SMOOTHING 2 > + > +/* > + * guard output frame count: > + * - at least 1 frame needed for
Re: [STLinux Kernel] [PATCH v6 08/10] [media] st-delta: EOS (End Of Stream) support
On Wed, 01 Feb 2017, Hugues Fruchet wrote: > EOS (End Of Stream) support allows user to get > all the potential decoded frames remaining in decoder > pipeline after having reached the end of video bitstream. > To do so, user calls VIDIOC_DECODER_CMD(V4L2_DEC_CMD_STOP) > which will drain the decoder and get the drained frames > that are then returned to user. > User is informed of EOS completion in two ways: > - dequeue of an empty frame flagged to V4L2_BUF_FLAG_LAST > - reception of a V4L2_EVENT_EOS event. > If, unfortunately, no buffer is available on CAPTURE queue > to return the empty frame, EOS is delayed till user queue > one CAPTURE buffer. > > Signed-off-by: Hugues Fruchet Acked-by: Peter Griffin > --- > drivers/media/platform/sti/delta/delta-v4l2.c | 146 > +- > drivers/media/platform/sti/delta/delta.h | 23 > 2 files changed, 168 insertions(+), 1 deletion(-) > > diff --git a/drivers/media/platform/sti/delta/delta-v4l2.c > b/drivers/media/platform/sti/delta/delta-v4l2.c > index 237a938..c959614 100644 > --- a/drivers/media/platform/sti/delta/delta-v4l2.c > +++ b/drivers/media/platform/sti/delta/delta-v4l2.c > @@ -106,7 +106,8 @@ static void delta_frame_done(struct delta_ctx *ctx, > struct delta_frame *frame, > vbuf->sequence = ctx->frame_num++; > v4l2_m2m_buf_done(vbuf, err ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); > > - ctx->output_frames++; > + if (frame->info.size) /* ignore EOS */ > + ctx->output_frames++; > } > > static void requeue_free_frames(struct delta_ctx *ctx) > @@ -762,6 +763,135 @@ static int delta_g_selection(struct file *file, void > *fh, > return 0; > } > > +static void delta_complete_eos(struct delta_ctx *ctx, > +struct delta_frame *frame) > +{ > + struct delta_dev *delta = ctx->dev; > + const struct v4l2_event ev = {.type = V4L2_EVENT_EOS}; > + > + /* > + * Send EOS to user: > + * - by returning an empty frame flagged to V4L2_BUF_FLAG_LAST > + * - and then send EOS event > + */ > + > + /* empty frame */ > + frame->info.size = 0; > + > + /* set the last buffer flag */ > + frame->flags |= V4L2_BUF_FLAG_LAST; > + > + /* release frame to user */ > + delta_frame_done(ctx, frame, 0); > + > + /* send EOS event */ > + v4l2_event_queue_fh(&ctx->fh, &ev); > + > + dev_dbg(delta->dev, "%s EOS completed\n", ctx->name); > +} > + > +static int delta_try_decoder_cmd(struct file *file, void *fh, > + struct v4l2_decoder_cmd *cmd) > +{ > + if (cmd->cmd != V4L2_DEC_CMD_STOP) > + return -EINVAL; > + > + if (cmd->flags & V4L2_DEC_CMD_STOP_TO_BLACK) > + return -EINVAL; > + > + if (!(cmd->flags & V4L2_DEC_CMD_STOP_IMMEDIATELY) && > + (cmd->stop.pts != 0)) > + return -EINVAL; > + > + return 0; > +} > + > +static int delta_decoder_stop_cmd(struct delta_ctx *ctx, void *fh) > +{ > + const struct delta_dec *dec = ctx->dec; > + struct delta_dev *delta = ctx->dev; > + struct delta_frame *frame = NULL; > + int ret = 0; > + > + dev_dbg(delta->dev, "%s EOS received\n", ctx->name); > + > + if (ctx->state != DELTA_STATE_READY) > + return 0; > + > + /* drain the decoder */ > + call_dec_op(dec, drain, ctx); > + > + /* release to user drained frames */ > + while (1) { > + frame = NULL; > + ret = call_dec_op(dec, get_frame, ctx, &frame); > + if (ret == -ENODATA) { > + /* no more decoded frames */ > + break; > + } > + if (frame) { > + dev_dbg(delta->dev, "%s drain frame[%d]\n", > + ctx->name, frame->index); > + > + /* pop timestamp and mark frame with it */ > + delta_pop_dts(ctx, &frame->dts); > + > + /* release decoded frame to user */ > + delta_frame_done(ctx, frame, 0); > + } > + } > + > + /* try to complete EOS */ > + ret = delta_get_free_frame(ctx, &frame); > + if (ret) > + goto delay_eos; > + > + /* new frame available, EOS can now be completed */ > + delta_complete_eos(ctx, frame); > + > + ctx->state = DELTA_STATE_EOS; > + > + return 0; > + > +delay_eos: > + /* > +
Re: [STLinux Kernel] [PATCH v6 06/10] [media] st-delta: add memory allocator helper functions
On Wed, 01 Feb 2017, Hugues Fruchet wrote: > Helper functions used by decoder back-ends to allocate > physically contiguous memory required by hardware video > decoder. > > Signed-off-by: Hugues Fruchet Acked-by: Peter Griffin > --- > drivers/media/platform/sti/delta/Makefile| 2 +- > drivers/media/platform/sti/delta/delta-mem.c | 51 > > drivers/media/platform/sti/delta/delta-mem.h | 14 > drivers/media/platform/sti/delta/delta.h | 8 + > 4 files changed, 74 insertions(+), 1 deletion(-) > create mode 100644 drivers/media/platform/sti/delta/delta-mem.c > create mode 100644 drivers/media/platform/sti/delta/delta-mem.h > > diff --git a/drivers/media/platform/sti/delta/Makefile > b/drivers/media/platform/sti/delta/Makefile > index 467519e..93a3037 100644 > --- a/drivers/media/platform/sti/delta/Makefile > +++ b/drivers/media/platform/sti/delta/Makefile > @@ -1,2 +1,2 @@ > obj-$(CONFIG_VIDEO_STI_DELTA_DRIVER) := st-delta.o > -st-delta-y := delta-v4l2.o > +st-delta-y := delta-v4l2.o delta-mem.o > diff --git a/drivers/media/platform/sti/delta/delta-mem.c > b/drivers/media/platform/sti/delta/delta-mem.c > new file mode 100644 > index 000..d7b53d3 > --- /dev/null > +++ b/drivers/media/platform/sti/delta/delta-mem.c > @@ -0,0 +1,51 @@ > +/* > + * Copyright (C) STMicroelectronics SA 2015 > + * Author: Hugues Fruchet for STMicroelectronics. > + * License terms: GNU General Public License (GPL), version 2 > + */ > + > +#include "delta.h" > +#include "delta-mem.h" > + > +int hw_alloc(struct delta_ctx *ctx, u32 size, const char *name, > + struct delta_buf *buf) > +{ > + struct delta_dev *delta = ctx->dev; > + dma_addr_t dma_addr; > + void *addr; > + unsigned long attrs = DMA_ATTR_WRITE_COMBINE; > + > + addr = dma_alloc_attrs(delta->dev, size, &dma_addr, > +GFP_KERNEL | __GFP_NOWARN, attrs); > + if (!addr) { > + dev_err(delta->dev, > + "%s hw_alloc:dma_alloc_coherent failed for %s > (size=%d)\n", > + ctx->name, name, size); > + ctx->sys_errors++; > + return -ENOMEM; > + } > + > + buf->size = size; > + buf->paddr = dma_addr; > + buf->vaddr = addr; > + buf->name = name; > + buf->attrs = attrs; > + > + dev_dbg(delta->dev, > + "%s allocate %d bytes of HW memory @(virt=0x%p, phy=0x%pad): > %s\n", > + ctx->name, size, buf->vaddr, &buf->paddr, buf->name); > + > + return 0; > +} > + > +void hw_free(struct delta_ctx *ctx, struct delta_buf *buf) > +{ > + struct delta_dev *delta = ctx->dev; > + > + dev_dbg(delta->dev, > + "%s free %d bytes of HW memory @(virt=0x%p, phy=0x%pad): > %s\n", > + ctx->name, buf->size, buf->vaddr, &buf->paddr, buf->name); > + > + dma_free_attrs(delta->dev, buf->size, > +buf->vaddr, buf->paddr, buf->attrs); > +} > diff --git a/drivers/media/platform/sti/delta/delta-mem.h > b/drivers/media/platform/sti/delta/delta-mem.h > new file mode 100644 > index 000..f8ca109 > --- /dev/null > +++ b/drivers/media/platform/sti/delta/delta-mem.h > @@ -0,0 +1,14 @@ > +/* > + * Copyright (C) STMicroelectronics SA 2015 > + * Author: Hugues Fruchet for STMicroelectronics. > + * License terms: GNU General Public License (GPL), version 2 > + */ > + > +#ifndef DELTA_MEM_H > +#define DELTA_MEM_H > + > +int hw_alloc(struct delta_ctx *ctx, u32 size, const char *name, > + struct delta_buf *buf); > +void hw_free(struct delta_ctx *ctx, struct delta_buf *buf); > + > +#endif /* DELTA_MEM_H */ > diff --git a/drivers/media/platform/sti/delta/delta.h > b/drivers/media/platform/sti/delta/delta.h > index 74a4240..9e26525 100644 > --- a/drivers/media/platform/sti/delta/delta.h > +++ b/drivers/media/platform/sti/delta/delta.h > @@ -191,6 +191,14 @@ struct delta_dts { > u64 val; > }; > > +struct delta_buf { > + u32 size; > + void *vaddr; > + dma_addr_t paddr; > + const char *name; > + unsigned long attrs; > +}; > + > struct delta_ctx; > > /* -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [STLinux Kernel] [PATCH v6 02/10] ARM: dts: STiH410: add DELTA dt node
On Wed, 01 Feb 2017, Hugues Fruchet wrote: > This patch adds DT node for STMicroelectronics > DELTA V4L2 video decoder > > Signed-off-by: Hugues Fruchet > --- > arch/arm/boot/dts/stih410.dtsi | 10 ++ > 1 file changed, 10 insertions(+) > > diff --git a/arch/arm/boot/dts/stih410.dtsi b/arch/arm/boot/dts/stih410.dtsi > index 281a124..42e070c 100644 > --- a/arch/arm/boot/dts/stih410.dtsi > +++ b/arch/arm/boot/dts/stih410.dtsi > @@ -259,5 +259,15 @@ > clocks = <&clk_sysin>; > interrupts = ; > }; > + delta0 { > + compatible = "st,st-delta"; > + clock-names = "delta", > + "delta-st231", > + "delta-flash-promip"; > + clocks = <&clk_s_c0_flexgen CLK_VID_DMU>, > + <&clk_s_c0_flexgen CLK_ST231_DMU>, > + <&clk_s_c0_flexgen CLK_FLASH_PROMIP>; > + }; > + I think this node should be in stih407-family.dtsi file? regards, Peter. -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v1 1/2] ir-ctl: use strndup instead of strndupa (fixes musl compile)
Fixes buildroot musl compile (see [1], [2]): ir-ctl.c:(.text+0xb06): undefined reference to `strndupa' [1] http://autobuild.buildroot.net/results/b8b96c7bbf2147dacac62485cbfdbcfd758271a5 [2] http://lists.busybox.net/pipermail/buildroot/2017-February/184048.html Signed-off-by: Peter Seiderer --- utils/ir-ctl/ir-ctl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/utils/ir-ctl/ir-ctl.c b/utils/ir-ctl/ir-ctl.c index bc58cee0..f938b429 100644 --- a/utils/ir-ctl/ir-ctl.c +++ b/utils/ir-ctl/ir-ctl.c @@ -344,12 +344,14 @@ static struct file *read_scancode(const char *name) return NULL; } - pstr = strndupa(name, p - name); + pstr = strndup(name, p - name); if (!protocol_match(pstr, &proto)) { fprintf(stderr, _("error: protocol '%s' not found\n"), pstr); + free(pstr); return NULL; } + free(pstr); if (!strtoscancode(p + 1, &scancode)) { fprintf(stderr, _("error: invalid scancode '%s'\n"), p + 1); -- 2.11.0
[PATCH v1 2/2] ir-ctl: add optional copy of TEMP_FAILURE_RETRY macro (fix musl compile)
Fixes buildroot musl compile (see [1], [2]): ir-ctl.c:(.text+0xe01): undefined reference to `TEMP_FAILURE_RETRY' [1] http://autobuild.buildroot.net/results/b8b96c7bbf2147dacac62485cbfdbcfd758271a5 [2] http://lists.busybox.net/pipermail/buildroot/2017-February/184048.html Signed-off-by: Peter Seiderer --- utils/ir-ctl/ir-ctl.c | 9 + 1 file changed, 9 insertions(+) diff --git a/utils/ir-ctl/ir-ctl.c b/utils/ir-ctl/ir-ctl.c index f938b429..e9da7778 100644 --- a/utils/ir-ctl/ir-ctl.c +++ b/utils/ir-ctl/ir-ctl.c @@ -44,6 +44,15 @@ # define N_(string) string +/* taken from glibc unistd.h */ +#ifndef TEMP_FAILURE_RETRY +#define TEMP_FAILURE_RETRY(expression) \ + (__extension__ \ +({ long int __result; \ + do __result = (long int) (expression); \ + while (__result == -1L && errno == EINTR); \ + __result; })) +#endif /* See drivers/media/rc/ir-lirc-codec.c line 23 */ #define LIRCBUF_SIZE 512 -- 2.11.0
Re: [PATCH 02/26] rewrite READ_ONCE/WRITE_ONCE
On Fri, Mar 03, 2017 at 03:49:38PM +0100, Peter Zijlstra wrote: > On Fri, Mar 03, 2017 at 09:26:50AM +0100, Christian Borntraeger wrote: > > Right. The main purpose is to read/write _ONCE_. You can assume a somewhat > > atomic access for sizes <= word size. And there are certainly places that > > rely on that. But the *ONCE thing is mostly used for things where we used > > barrier() 10 years ago. > > A lot of code relies on READ/WRITE_ONCE() to generate single > instructions for naturally aligned machined word sized loads/stores > (something GCC used to guarantee, but does no longer IIRC). > > So much so that I would say its a bug if READ/WRITE_ONCE() doesn't > generate a single instruction under those conditions. > > However, every time I've tried to introduce stricter > semantics/primitives to verify things Linus hated it. See here for the last attempt: https://marc.info/?l=linux-virtualization&m=148007765918101&w=2
Re: [PATCH 02/26] rewrite READ_ONCE/WRITE_ONCE
On Fri, Mar 03, 2017 at 09:26:50AM +0100, Christian Borntraeger wrote: > Right. The main purpose is to read/write _ONCE_. You can assume a somewhat > atomic access for sizes <= word size. And there are certainly places that > rely on that. But the *ONCE thing is mostly used for things where we used > barrier() 10 years ago. A lot of code relies on READ/WRITE_ONCE() to generate single instructions for naturally aligned machined word sized loads/stores (something GCC used to guarantee, but does no longer IIRC). So much so that I would say its a bug if READ/WRITE_ONCE() doesn't generate a single instruction under those conditions. However, every time I've tried to introduce stricter semantics/primitives to verify things Linus hated it.
[PATCH v1] qv4l2: fix qv4l2.pro qmake project file
The header files v4l2-tpg.h and v4l2-tpg-colors.h moved from v4l2-ctl to common. Signed-off-by: Peter Seiderer --- utils/qv4l2/qv4l2.pro | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/utils/qv4l2/qv4l2.pro b/utils/qv4l2/qv4l2.pro index 02b03dea..6420fa24 100644 --- a/utils/qv4l2/qv4l2.pro +++ b/utils/qv4l2/qv4l2.pro @@ -13,6 +13,7 @@ greaterThan(QT_MAJOR_VERSION, 4): QT += widgets QT += opengl INCLUDEPATH += $$PWD/../.. +INCLUDEPATH += $$PWD/../common INCLUDEPATH += $$PWD/../v4l2-ctl/ INCLUDEPATH += $$PWD/../v4l2-compliance @@ -25,8 +26,8 @@ HEADERS += general-tab.h HEADERS += qv4l2.h HEADERS += raw2sliced.h HEADERS += vbi-tab.h -HEADERS += ../v4l2-ctl/v4l2-tpg.h -HEADERS += ../v4l2-ctl/v4l2-tpg-colors.h +HEADERS += ../common/v4l2-tpg.h +HEADERS += ../common/v4l2-tpg-colors.h HEADERS += ../../config.h SOURCES += capture-win.cpp -- 2.11.0
Probably a new board ID for em28xx: [1b80:e349]
Hi, I have a small usb-device with s-vhs, composite-video and stereo-audio cabels attached. The shell just says "MAGIX" and "Made in China". It has also a black button to push. I plugged it in to get some video grabbed but no /dev/videoX was created. "lsusb" output: Bus 003 Device 012: ID 1b80:e349 Afatech journalctl -f output: kernel: usb 3-1.1: new high-speed USB device number 12 using ehci-pci kernel: usb 3-1.1: New USB device found, idVendor=1b80, idProduct=e349 kernel: usb 3-1.1: New USB device strings: Mfr=0, Product=1, SerialNumber=0 kernel: usb 3-1.1: Product: USB 2861 Device mtp-probe[7688]: checking bus 3, device 12: "/sys/devices/pci:00/:00:1a.0/usb3/3-1/3-1.1" mtp-probe[7688]: bus: 3, device: 12 was not an MTP device Kernel is 4.1.13-5-default on openSuse 42.1 I opened that thing and found these chips: SAA7113H EM2860 EMP202 I did modprobe em28xx modprobe saa7115 but still got no video-device. (I use an application called "cheese" which works perfect with my other usb videograbbing device.) Is there anything else I can do? CU Peter -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: Probably a new board ID for em28xx: [1b80:e349]
To whom it may concern... Added this device to em28xx-cards.c and used "EM2860_BOARD_HT_VIDBOX_NW03" as ID because it's the only device listed there that has the same chips as my hardware. Recompiled the module and it works! --- /usr/src/linux/drivers/media/usb/em28xx/em28xx-cards.c 2015-11-26 17:35:17.0 +0100 +++ em28xx-cards.c 2015-12-30 14:54:19.0 +0100 @@ -2475,6 +2475,8 @@ struct usb_device_id em28xx_id_table[] = .driver_info = EM2861_BOARD_LEADTEK_VC100 }, { USB_DEVICE(0xeb1a, 0x8179), .driver_info = EM28178_BOARD_TERRATEC_T2_STICK_HD }, + { USB_DEVICE(0x1b80, 0xe349), + .driver_info = EM2860_BOARD_HT_VIDBOX_NW03 }, { }, }; MODULE_DEVICE_TABLE(usb, em28xx_id_table); -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v2 1/8] i2c-mux: add common core data for every mux instance
From: Peter Rosin The initial core mux structure starts off small with only the parent adapter pointer, which all muxes have, and a priv pointer for mux driver private data. Add i2c_mux_alloc function to unify the creation of a mux. Where appropriate, pass around the mux core structure instead of the parent adapter or the driver private data. Remove the parent adapter pointer from the driver private data for all mux drivers. Signed-off-by: Peter Rosin --- drivers/i2c/i2c-mux.c| 28 +- drivers/i2c/muxes/i2c-arb-gpio-challenge.c | 24 +-- drivers/i2c/muxes/i2c-mux-gpio.c | 20 drivers/i2c/muxes/i2c-mux-pca9541.c | 35 ++-- drivers/i2c/muxes/i2c-mux-pca954x.c | 19 ++- drivers/i2c/muxes/i2c-mux-pinctrl.c | 23 +- drivers/i2c/muxes/i2c-mux-reg.c | 23 ++ drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 10 +++- drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h| 1 + drivers/media/dvb-frontends/m88ds3103.c | 10 +++- drivers/media/dvb-frontends/m88ds3103_priv.h | 1 + drivers/media/dvb-frontends/rtl2830.c| 10 +++- drivers/media/dvb-frontends/rtl2830_priv.h | 1 + drivers/media/dvb-frontends/rtl2832.c| 10 +++- drivers/media/dvb-frontends/rtl2832_priv.h | 1 + drivers/media/dvb-frontends/si2168.c | 10 +++- drivers/media/dvb-frontends/si2168_priv.h| 1 + drivers/media/usb/cx231xx/cx231xx-core.c | 3 +++ drivers/media/usb/cx231xx/cx231xx-i2c.c | 13 +-- drivers/media/usb/cx231xx/cx231xx.h | 2 ++ drivers/of/unittest.c| 16 +++-- include/linux/i2c-mux.h | 14 ++- 22 files changed, 187 insertions(+), 88 deletions(-) diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c index 00fc5b1c7b66..c2163f6b51d5 100644 --- a/drivers/i2c/i2c-mux.c +++ b/drivers/i2c/i2c-mux.c @@ -31,8 +31,8 @@ struct i2c_mux_priv { struct i2c_adapter adap; struct i2c_algorithm algo; + struct i2c_mux_core *muxc; - struct i2c_adapter *parent; struct device *mux_dev; void *mux_priv; u32 chan_id; @@ -45,7 +45,8 @@ static int i2c_mux_master_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) { struct i2c_mux_priv *priv = adap->algo_data; - struct i2c_adapter *parent = priv->parent; + struct i2c_mux_core *muxc = priv->muxc; + struct i2c_adapter *parent = muxc->parent; int ret; /* Switch to the right mux port and perform the transfer. */ @@ -65,7 +66,8 @@ static int i2c_mux_smbus_xfer(struct i2c_adapter *adap, int size, union i2c_smbus_data *data) { struct i2c_mux_priv *priv = adap->algo_data; - struct i2c_adapter *parent = priv->parent; + struct i2c_mux_core *muxc = priv->muxc; + struct i2c_adapter *parent = muxc->parent; int ret; /* Select the right mux port and perform the transfer. */ @@ -84,7 +86,7 @@ static int i2c_mux_smbus_xfer(struct i2c_adapter *adap, static u32 i2c_mux_functionality(struct i2c_adapter *adap) { struct i2c_mux_priv *priv = adap->algo_data; - struct i2c_adapter *parent = priv->parent; + struct i2c_adapter *parent = priv->muxc->parent; return parent->algo->functionality(parent); } @@ -102,7 +104,20 @@ static unsigned int i2c_mux_parent_classes(struct i2c_adapter *parent) return class; } -struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, +struct i2c_mux_core *i2c_mux_alloc(struct device *dev, int sizeof_priv) +{ + struct i2c_mux_core *muxc; + + muxc = devm_kzalloc(dev, sizeof(*muxc) + sizeof_priv, GFP_KERNEL); + if (!muxc) + return NULL; + if (sizeof_priv) + muxc->priv = muxc + 1; + return muxc; +} +EXPORT_SYMBOL_GPL(i2c_mux_alloc); + +struct i2c_adapter *i2c_add_mux_adapter(struct i2c_mux_core *muxc, struct device *mux_dev, void *mux_priv, u32 force_nr, u32 chan_id, unsigned int class, @@ -111,6 +126,7 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, int (*deselect) (struct i2c_adapter *, void *, u32)) { + struct i2c_adapter *parent = muxc->parent; struct i2c_mux_priv *priv; char symlink_name[20]; int ret; @@ -120,7 +136,7 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, return NULL; /* Set up private adapter data */ - priv->parent = parent; + priv->muxc = muxc; priv->mux_dev = mux_dev;
[PATCH v2 7/8] i2c: muxes always lock the parent adapter
From: Peter Rosin Instead of checking for i2c parent adapters for every lock/unlock, simply override the locking for muxes to always lock/unlock the parent adapter directly. Signed-off-by: Peter Rosin --- drivers/i2c/i2c-core.c | 21 +++-- drivers/i2c/i2c-mux.c | 27 +++ 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 34a7748b4652..4683777f81ca 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -963,12 +963,7 @@ static int i2c_check_addr_busy(struct i2c_adapter *adapter, int addr) */ static void i2c_adapter_lock_bus(struct i2c_adapter *adapter, int flags) { - struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); - - if (parent) - i2c_lock_adapter(parent); - else - rt_mutex_lock(&adapter->bus_lock); + rt_mutex_lock(&adapter->bus_lock); } /** @@ -977,12 +972,7 @@ static void i2c_adapter_lock_bus(struct i2c_adapter *adapter, int flags) */ static int i2c_adapter_trylock_bus(struct i2c_adapter *adapter, int flags) { - struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); - - if (parent) - return parent->trylock_bus(parent, flags); - else - return rt_mutex_trylock(&adapter->bus_lock); + return rt_mutex_trylock(&adapter->bus_lock); } /** @@ -991,12 +981,7 @@ static int i2c_adapter_trylock_bus(struct i2c_adapter *adapter, int flags) */ static void i2c_adapter_unlock_bus(struct i2c_adapter *adapter, int flags) { - struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); - - if (parent) - i2c_unlock_adapter(parent); - else - rt_mutex_unlock(&adapter->bus_lock); + rt_mutex_unlock(&adapter->bus_lock); } static void i2c_dev_set_name(struct i2c_adapter *adap, diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c index 5c1088079231..dd8a790cb4cc 100644 --- a/drivers/i2c/i2c-mux.c +++ b/drivers/i2c/i2c-mux.c @@ -98,6 +98,30 @@ static unsigned int i2c_mux_parent_classes(struct i2c_adapter *parent) return class; } +static void i2c_parent_lock_bus(struct i2c_adapter *adapter, int flags) +{ + struct i2c_mux_priv *priv = adapter->algo_data; + struct i2c_adapter *parent = priv->muxc->parent; + + parent->lock_bus(parent, flags); +} + +static int i2c_parent_trylock_bus(struct i2c_adapter *adapter, int flags) +{ + struct i2c_mux_priv *priv = adapter->algo_data; + struct i2c_adapter *parent = priv->muxc->parent; + + return parent->trylock_bus(parent, flags); +} + +static void i2c_parent_unlock_bus(struct i2c_adapter *adapter, int flags) +{ + struct i2c_mux_priv *priv = adapter->algo_data; + struct i2c_adapter *parent = priv->muxc->parent; + + parent->unlock_bus(parent, flags); +} + int i2c_mux_reserve_adapters(struct i2c_mux_core *muxc, int adapters) { struct i2c_adapter **adapter; @@ -181,6 +205,9 @@ int i2c_add_mux_adapter(struct i2c_mux_core *muxc, priv->adap.retries = parent->retries; priv->adap.timeout = parent->timeout; priv->adap.quirks = parent->quirks; + priv->adap.lock_bus = i2c_parent_lock_bus; + priv->adap.trylock_bus = i2c_parent_trylock_bus; + priv->adap.unlock_bus = i2c_parent_unlock_bus; /* Sanity check on class */ if (i2c_mux_parent_classes(parent) & class) -- 2.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v2 8/8] i2c-mux: relax locking of the top i2c adapter during i2c controlled muxing
From: Peter Rosin With a i2c topology like the following GPIO ---| -- BAT1 | v / I2C -+--+ MUX | \ EEPROM -- BAT2 there is a locking problem with the GPIO controller since it is a client on the same i2c bus that it muxes. Transfers to the mux clients (e.g. BAT1) will lock the whole i2c bus prior to attempting to switch the mux to the correct i2c segment. In the above case, the GPIO device is an I/O expander with an i2c interface, and since the GPIO subsystem knows nothing (and rightfully so) about the lockless needs of the i2c mux code, this results in a deadlock when the GPIO driver issues i2c transfers to modify the mux. So, observing that while it is needed to have the i2c bus locked during the actual MUX update in order to avoid random garbage on the slave side, it is not strictly a must to have it locked over the whole sequence of a full select-transfer-deselect mux client operation. The mux itself needs to be locked, so transfers to clients behind the mux are serialized, and the mux needs to be stable during all i2c traffic (otherwise individual mux slave segments might see garbage, or worse). Add devive tree properties (bool named i2c-controlled) to i2c-mux-gpio and i2c-mux-pinctrl that asserts that the the gpio/pinctrl is controlled via the same i2c bus that it muxes. Modify the i2c mux locking so that muxes that are "i2c-controlled" locks the mux instead of the whole i2c bus when there is a transfer to the slave side of the mux. This lock serializes transfers to the slave side of the mux. Modify the select-transfer-deselect code for "i2c-controlled" muxes so that each of the select-transfer-deselect ops locks the mux parent adapter individually. Signed-off-by: Peter Rosin --- .../devicetree/bindings/i2c/i2c-mux-gpio.txt | 2 + .../devicetree/bindings/i2c/i2c-mux-pinctrl.txt| 4 + drivers/i2c/i2c-mux.c | 109 +++-- drivers/i2c/muxes/i2c-mux-gpio.c | 3 + drivers/i2c/muxes/i2c-mux-pinctrl.c| 3 + include/linux/i2c-mux-gpio.h | 2 + include/linux/i2c-mux-pinctrl.h| 2 + include/linux/i2c-mux.h| 2 + 8 files changed, 120 insertions(+), 7 deletions(-) diff --git a/Documentation/devicetree/bindings/i2c/i2c-mux-gpio.txt b/Documentation/devicetree/bindings/i2c/i2c-mux-gpio.txt index 66709a825541..17670b997d81 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-mux-gpio.txt +++ b/Documentation/devicetree/bindings/i2c/i2c-mux-gpio.txt @@ -28,6 +28,8 @@ Required properties: Optional properties: - idle-state: value to set the muxer to when idle. When no value is given, it defaults to the last value used. +- i2c-controlled: The muxed I2C bus is also used to control all the gpios + used for muxing. For each i2c child node, an I2C child bus will be created. They will be numbered based on their order in the device tree. diff --git a/Documentation/devicetree/bindings/i2c/i2c-mux-pinctrl.txt b/Documentation/devicetree/bindings/i2c/i2c-mux-pinctrl.txt index ae8af1694e95..8374a1f7a709 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-mux-pinctrl.txt +++ b/Documentation/devicetree/bindings/i2c/i2c-mux-pinctrl.txt @@ -23,6 +23,10 @@ Required properties: - i2c-parent: The phandle of the I2C bus that this multiplexer's master-side port is connected to. +Optional properties: +- i2c-controlled: The muxed I2C bus is also used to control all the pinctrl + pins used for muxing. + Also required are: * Standard pinctrl properties that specify the pin mux state for each child diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c index dd8a790cb4cc..c4d4b14a5399 100644 --- a/drivers/i2c/i2c-mux.c +++ b/drivers/i2c/i2c-mux.c @@ -54,6 +54,25 @@ static int i2c_mux_master_xfer(struct i2c_adapter *adap, return ret; } +static int __i2c_mux_master_xfer(struct i2c_adapter *adap, +struct i2c_msg msgs[], int num) +{ + struct i2c_mux_priv *priv = adap->algo_data; + struct i2c_mux_core *muxc = priv->muxc; + struct i2c_adapter *parent = muxc->parent; + int ret; + + /* Switch to the right mux port and perform the transfer. */ + + ret = muxc->select(muxc, priv->chan_id); + if (ret >= 0) + ret = i2c_transfer(parent, msgs, num); + if (muxc->deselect) + muxc->deselect(muxc, priv->chan_id); + + return ret; +} + static int i2c_mux_smbus_xfer(struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, @@ -76,6 +95,28 @@ static int i2c_mux_smbus_xfer(struct i2c_adapter *adap, return ret; } +stat
[PATCH v2 0/8] i2c mux cleanup and locking update
From: Peter Rosin Hi! I have a pair of boards with this i2c topology: GPIO ---| -- BAT1 | v / I2C -+--B---+ MUX | \ EEPROM -- BAT2 (B denotes the boundary between the boards) The problem with this is that the GPIO controller sits on the same i2c bus that it MUXes. For pca954x devices this is worked around by using unlocked transfers when updating the MUX. I have no such luck as the GPIO is a general purpose IO expander and the MUX is just a random bidirectional MUX, unaware of the fact that it is muxing an i2c bus, and extending unlocked transfers into the GPIO subsystem is too ugly to even think about. But the general hw approach is sane in my opinion, with the number of connections between the two boards minimized. To put is plainly, I need support for it. So, I observe that while it is needed to have the i2c bus locked during the actual MUX update in order to avoid random garbage on the slave side, it is not strictly a must to have it locked over the whole sequence of a full select-transfer-deselect operation. The MUX itself needs to be locked, so transfers to clients behind the mux are serialized, and the MUX needs to be stable during all i2c traffic (otherwise individual mux slave segments might see garbage). This series accomplishes this by adding a dt property to i2c-mux-gpio and i2c-mux-pinctrl that can be used to state that the mux is updated by means of the muxed master bus, and that the select-transfer-deselect operations should be locked individually. When this holds, the i2c bus *is* locked during muxing, since the muxing happens as part of i2c transfers. This is true even if the MUX is updated with several transfers to the GPIO (at least as long as *all* MUX changes are using the i2s master bus). A lock is added to the mux so that transfers through the mux are serialized. Concerns: - The locking is perhaps too complex? - I worry about the priority inheritance aspect of the adapter lock. When the transfers behind the mux are divided into select-transfer-deselect all locked individually, low priority transfers get more chances to interfere with high priority transfers. - When doing an i2c_transfer() in_atomic() context of with irqs_disabled(), there is a higher possibility that the mux is not returned to its idle state after a failed (-EAGAIN) transfer due to trylock. To summarize the series, there's some i2c-mux infrastructure cleanup work first (I think that part stands by itself as desireable regardless), the locking changes are in the last three patches of the series, with the real meat in 8/8. PS. needs a bunch of testing, I do not have access to all the involved hw Changes since v1: - Allocate mux core and (optional) priv in a combined allocation. - Killed dev_err messages triggered by memory allocation failure. - Fix the device specific i2c muxes that I had overlooked. - Rebased on top of v4.4-rc8 (was based on v4.4-rc6 previously). Cheers, Peter Peter Rosin (8): i2c-mux: add common core data for every mux instance i2c-mux: move select and deselect ops to i2c_mux_core i2c-mux: move the slave side adapter management to i2c_mux_core i2c-mux: remove the mux dev pointer from the mux per channel data i2c-mux: pinctrl: get rid of the driver private struct device pointer i2c: allow adapter drivers to override the adapter locking i2c: muxes always lock the parent adapter i2c-mux: relax locking of the top i2c adapter during i2c controlled muxing .../devicetree/bindings/i2c/i2c-mux-gpio.txt | 2 + .../devicetree/bindings/i2c/i2c-mux-pinctrl.txt| 4 + drivers/i2c/i2c-core.c | 59 ++--- drivers/i2c/i2c-mux.c | 272 + drivers/i2c/muxes/i2c-arb-gpio-challenge.c | 46 ++-- drivers/i2c/muxes/i2c-mux-gpio.c | 58 ++--- drivers/i2c/muxes/i2c-mux-pca9541.c| 58 +++-- drivers/i2c/muxes/i2c-mux-pca954x.c| 66 ++--- drivers/i2c/muxes/i2c-mux-pinctrl.c| 89 +++ drivers/i2c/muxes/i2c-mux-reg.c| 63 ++--- drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 33 +-- drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h | 2 +- drivers/media/dvb-frontends/m88ds3103.c| 23 +- drivers/media/dvb-frontends/m88ds3103_priv.h | 2 +- drivers/media/dvb-frontends/rtl2830.c | 24 +- drivers/media/dvb-frontends/rtl2830_priv.h | 2 +- drivers/media/dvb-frontends/rtl2832.c | 30 ++- drivers/media/dvb-frontends/rtl2832_priv.h | 2 +- drivers/media/dvb-frontends/si2168.c | 29 ++- drivers/media/dvb-frontends/si2168_priv.h | 2 +- drivers/media/usb/cx231xx/cx231xx-core.c | 6 +- drivers/media/usb/cx231xx/cx231xx-i2c.c
[PATCH v2 2/8] i2c-mux: move select and deselect ops to i2c_mux_core
From: Peter Rosin The mux select and deselect ops are common to the mux most of the time, so store the ops in the mux core. Change the select and deselect op to work in terms of the mux core instead of the child adapter. No driver uses the child adapter anyway, and if it is needed in a future mux driver it can be worked out from the channel id. i2c-arb-gpio-challenge is special in that it needs the mux device pointer in the select op, so store that device pointer in the mux core as well. This pointer is going to get further use in later commits. i2c-mux-pca954x is special since it does not add its deselect op to all its child adapters, handle this by adding a mask that makes the deselect op a no-operation for child adapters not wishing to deselect the mux. Signed-off-by: Peter Rosin --- drivers/i2c/i2c-mux.c | 31 ++ drivers/i2c/muxes/i2c-arb-gpio-challenge.c | 18 - drivers/i2c/muxes/i2c-mux-gpio.c | 19 +- drivers/i2c/muxes/i2c-mux-pca9541.c| 18 + drivers/i2c/muxes/i2c-mux-pca954x.c| 30 + drivers/i2c/muxes/i2c-mux-pinctrl.c| 20 --- drivers/i2c/muxes/i2c-mux-reg.c| 21 +--- drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 17 +++- drivers/media/dvb-frontends/m88ds3103.c| 8 drivers/media/dvb-frontends/rtl2830.c | 8 drivers/media/dvb-frontends/rtl2832.c | 15 --- drivers/media/dvb-frontends/si2168.c | 13 +++-- drivers/media/usb/cx231xx/cx231xx-i2c.c| 12 drivers/of/unittest.c | 7 +++ include/linux/i2c-mux.h| 15 --- 15 files changed, 118 insertions(+), 134 deletions(-) diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c index c2163f6b51d5..6c5cb9f7649b 100644 --- a/drivers/i2c/i2c-mux.c +++ b/drivers/i2c/i2c-mux.c @@ -32,13 +32,8 @@ struct i2c_mux_priv { struct i2c_adapter adap; struct i2c_algorithm algo; struct i2c_mux_core *muxc; - struct device *mux_dev; - void *mux_priv; u32 chan_id; - - int (*select)(struct i2c_adapter *, void *mux_priv, u32 chan_id); - int (*deselect)(struct i2c_adapter *, void *mux_priv, u32 chan_id); }; static int i2c_mux_master_xfer(struct i2c_adapter *adap, @@ -51,11 +46,11 @@ static int i2c_mux_master_xfer(struct i2c_adapter *adap, /* Switch to the right mux port and perform the transfer. */ - ret = priv->select(parent, priv->mux_priv, priv->chan_id); + ret = muxc->select(muxc, priv->chan_id); if (ret >= 0) ret = __i2c_transfer(parent, msgs, num); - if (priv->deselect) - priv->deselect(parent, priv->mux_priv, priv->chan_id); + if (muxc->deselect) + muxc->deselect(muxc, priv->chan_id); return ret; } @@ -72,12 +67,12 @@ static int i2c_mux_smbus_xfer(struct i2c_adapter *adap, /* Select the right mux port and perform the transfer. */ - ret = priv->select(parent, priv->mux_priv, priv->chan_id); + ret = muxc->select(muxc, priv->chan_id); if (ret >= 0) ret = parent->algo->smbus_xfer(parent, addr, flags, read_write, command, size, data); - if (priv->deselect) - priv->deselect(parent, priv->mux_priv, priv->chan_id); + if (muxc->deselect) + muxc->deselect(muxc, priv->chan_id); return ret; } @@ -113,18 +108,15 @@ struct i2c_mux_core *i2c_mux_alloc(struct device *dev, int sizeof_priv) return NULL; if (sizeof_priv) muxc->priv = muxc + 1; + muxc->dev = dev; return muxc; } EXPORT_SYMBOL_GPL(i2c_mux_alloc); struct i2c_adapter *i2c_add_mux_adapter(struct i2c_mux_core *muxc, - struct device *mux_dev, - void *mux_priv, u32 force_nr, u32 chan_id, - unsigned int class, - int (*select) (struct i2c_adapter *, - void *, u32), - int (*deselect) (struct i2c_adapter *, -void *, u32)) + struct device *mux_dev, + u32 force_nr, u32 chan_id, + unsigned int class) { struct i2c_adapter *parent = muxc->parent; struct i2c_mux_priv *priv; @@ -138,10 +130,7 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_mux_core *muxc, /* Set up private adapter data */ priv->muxc = muxc; priv->mux_dev = mux_dev
[PATCH v2 5/8] i2c-mux: pinctrl: get rid of the driver private struct device pointer
From: Peter Rosin There is a copy of the device pointer in the mux core. Signed-off-by: Peter Rosin --- drivers/i2c/muxes/i2c-mux-pinctrl.c | 23 --- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/drivers/i2c/muxes/i2c-mux-pinctrl.c b/drivers/i2c/muxes/i2c-mux-pinctrl.c index 0dc912898813..38129850cbe4 100644 --- a/drivers/i2c/muxes/i2c-mux-pinctrl.c +++ b/drivers/i2c/muxes/i2c-mux-pinctrl.c @@ -26,7 +26,6 @@ #include struct i2c_mux_pinctrl { - struct device *dev; struct i2c_mux_pinctrl_platform_data *pdata; struct pinctrl *pinctrl; struct pinctrl_state **states; @@ -51,6 +50,7 @@ static int i2c_mux_pinctrl_deselect(struct i2c_mux_core *muxc, u32 chan) static int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux, struct platform_device *pdev) { + struct i2c_mux_core *muxc = platform_get_drvdata(pdev); struct device_node *np = pdev->dev.of_node; int num_names, i, ret; struct device_node *adapter_np; @@ -60,15 +60,12 @@ static int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux, return 0; mux->pdata = devm_kzalloc(&pdev->dev, sizeof(*mux->pdata), GFP_KERNEL); - if (!mux->pdata) { - dev_err(mux->dev, - "Cannot allocate i2c_mux_pinctrl_platform_data\n"); + if (!mux->pdata) return -ENOMEM; - } num_names = of_property_count_strings(np, "pinctrl-names"); if (num_names < 0) { - dev_err(mux->dev, "Cannot parse pinctrl-names: %d\n", + dev_err(muxc->dev, "Cannot parse pinctrl-names: %d\n", num_names); return num_names; } @@ -76,23 +73,21 @@ static int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux, mux->pdata->pinctrl_states = devm_kzalloc(&pdev->dev, sizeof(*mux->pdata->pinctrl_states) * num_names, GFP_KERNEL); - if (!mux->pdata->pinctrl_states) { - dev_err(mux->dev, "Cannot allocate pinctrl_states\n"); + if (!mux->pdata->pinctrl_states) return -ENOMEM; - } for (i = 0; i < num_names; i++) { ret = of_property_read_string_index(np, "pinctrl-names", i, &mux->pdata->pinctrl_states[mux->pdata->bus_count]); if (ret < 0) { - dev_err(mux->dev, "Cannot parse pinctrl-names: %d\n", + dev_err(muxc->dev, "Cannot parse pinctrl-names: %d\n", ret); return ret; } if (!strcmp(mux->pdata->pinctrl_states[mux->pdata->bus_count], "idle")) { if (i != num_names - 1) { - dev_err(mux->dev, "idle state must be last\n"); + dev_err(muxc->dev, "idle state must be last\n"); return -EINVAL; } mux->pdata->pinctrl_state_idle = "idle"; @@ -103,13 +98,13 @@ static int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux, adapter_np = of_parse_phandle(np, "i2c-parent", 0); if (!adapter_np) { - dev_err(mux->dev, "Cannot parse i2c-parent\n"); + dev_err(muxc->dev, "Cannot parse i2c-parent\n"); return -ENODEV; } adapter = of_find_i2c_adapter_by_node(adapter_np); of_node_put(adapter_np); if (!adapter) { - dev_err(mux->dev, "Cannot find parent bus\n"); + dev_err(muxc->dev, "Cannot find parent bus\n"); return -EPROBE_DEFER; } mux->pdata->parent_bus_num = i2c_adapter_id(adapter); @@ -139,8 +134,6 @@ static int i2c_mux_pinctrl_probe(struct platform_device *pdev) mux = i2c_mux_priv(muxc); platform_set_drvdata(pdev, muxc); - mux->dev = &pdev->dev; - mux->pdata = dev_get_platdata(&pdev->dev); if (!mux->pdata) { ret = i2c_mux_pinctrl_parse_dt(mux, pdev); -- 2.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v2 4/8] i2c-mux: remove the mux dev pointer from the mux per channel data
From: Peter Rosin The dev pointer is readily available in the mux core struct, no point in keeping multiple copies around. The patch also fixes a bug in rtl2832, which attached its mux slave adapter to the device owning the mux parent adapter instead of attaching it to its own device. Signed-off-by: Peter Rosin --- drivers/i2c/i2c-mux.c | 24 drivers/i2c/muxes/i2c-arb-gpio-challenge.c | 2 +- drivers/i2c/muxes/i2c-mux-gpio.c | 3 +-- drivers/i2c/muxes/i2c-mux-pca9541.c| 2 +- drivers/i2c/muxes/i2c-mux-pca954x.c| 3 +-- drivers/i2c/muxes/i2c-mux-pinctrl.c| 3 +-- drivers/i2c/muxes/i2c-mux-reg.c| 3 +-- drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 2 +- drivers/media/dvb-frontends/m88ds3103.c| 2 +- drivers/media/dvb-frontends/rtl2830.c | 2 +- drivers/media/dvb-frontends/rtl2832.c | 2 +- drivers/media/dvb-frontends/si2168.c | 2 +- drivers/media/usb/cx231xx/cx231xx-i2c.c| 3 --- drivers/of/unittest.c | 2 +- include/linux/i2c-mux.h| 1 - 15 files changed, 24 insertions(+), 32 deletions(-) diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c index 7ba0308537a8..5c1088079231 100644 --- a/drivers/i2c/i2c-mux.c +++ b/drivers/i2c/i2c-mux.c @@ -32,7 +32,6 @@ struct i2c_mux_priv { struct i2c_adapter adap; struct i2c_algorithm algo; struct i2c_mux_core *muxc; - struct device *mux_dev; u32 chan_id; }; @@ -137,7 +136,6 @@ struct i2c_mux_core *i2c_mux_alloc(struct device *dev, int sizeof_priv) EXPORT_SYMBOL_GPL(i2c_mux_alloc); int i2c_add_mux_adapter(struct i2c_mux_core *muxc, - struct device *mux_dev, u32 force_nr, u32 chan_id, unsigned int class) { @@ -162,7 +160,6 @@ int i2c_add_mux_adapter(struct i2c_mux_core *muxc, /* Set up private adapter data */ priv->muxc = muxc; - priv->mux_dev = mux_dev; priv->chan_id = chan_id; /* Need to do algo dynamically because we don't know ahead @@ -197,11 +194,11 @@ int i2c_add_mux_adapter(struct i2c_mux_core *muxc, * Try to populate the mux adapter's of_node, expands to * nothing if !CONFIG_OF. */ - if (mux_dev->of_node) { + if (muxc->dev->of_node) { struct device_node *child; u32 reg; - for_each_child_of_node(mux_dev->of_node, child) { + for_each_child_of_node(muxc->dev->of_node, child) { ret = of_property_read_u32(child, "reg", ®); if (ret) continue; @@ -215,8 +212,9 @@ int i2c_add_mux_adapter(struct i2c_mux_core *muxc, /* * Associate the mux channel with an ACPI node. */ - if (has_acpi_companion(mux_dev)) - acpi_preset_companion(&priv->adap.dev, ACPI_COMPANION(mux_dev), + if (has_acpi_companion(muxc->dev)) + acpi_preset_companion(&priv->adap.dev, + ACPI_COMPANION(muxc->dev), chan_id); if (force_nr) { @@ -233,12 +231,14 @@ int i2c_add_mux_adapter(struct i2c_mux_core *muxc, return ret; } - WARN(sysfs_create_link(&priv->adap.dev.kobj, &mux_dev->kobj, "mux_device"), - "can't create symlink to mux device\n"); + WARN(sysfs_create_link(&priv->adap.dev.kobj, &muxc->dev->kobj, + "mux_device"), +"can't create symlink to mux device\n"); snprintf(symlink_name, sizeof(symlink_name), "channel-%u", chan_id); - WARN(sysfs_create_link(&mux_dev->kobj, &priv->adap.dev.kobj, symlink_name), - "can't create symlink for channel %u\n", chan_id); + WARN(sysfs_create_link(&muxc->dev->kobj, &priv->adap.dev.kobj, + symlink_name), +"can't create symlink for channel %u\n", chan_id); dev_info(&parent->dev, "Added multiplexed i2c bus %d\n", i2c_adapter_id(&priv->adap)); @@ -259,7 +259,7 @@ void i2c_del_mux_adapters(struct i2c_mux_core *muxc) snprintf(symlink_name, sizeof(symlink_name), "channel-%u", priv->chan_id); - sysfs_remove_link(&priv->mux_dev->kobj, symlink_name); + sysfs_remove_link(&muxc->dev->kobj, symlink_name); sysfs_remove_link(&priv->adap.dev.kobj, "mux_device"); i2c_del_adapter(adap); diff --git a/drivers/i2c/mux
[PATCH v2 6/8] i2c: allow adapter drivers to override the adapter locking
From: Peter Rosin Add i2c_lock_bus() and i2c_unlock_bus(), which call the new lock_bus and unlock_bus ops in the adapter. These funcs/ops take an additional flags argument that indicates for what purpose the adapter is locked. There are two flags, I2C_LOCK_ADAPTER and I2C_LOCK_SEGMENT, but they are both implemented the same. For now. Locking the adapter means that the whole bus is locked, locking the segment means that only the current bus segment is locked (i.e. i2c traffic on the parent side of mux is still allowed even if the child side of the mux is locked. Also support a trylock_bus op (but no function to call it, as it is not expected to be needed outside of the i2c core). Implement i2c_lock_adapter/i2c_unlock_adapter in terms of the new locking scheme (i.e. lock with the I2C_LOCK_ADAPTER flag). Annotate some of the locking with explicit I2C_LOCK_SEGMENT flags. Signed-off-by: Peter Rosin --- drivers/i2c/i2c-core.c | 40 ++-- include/linux/i2c.h| 28 ++-- 2 files changed, 48 insertions(+), 20 deletions(-) diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index ba8eb087f224..34a7748b4652 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -958,10 +958,10 @@ static int i2c_check_addr_busy(struct i2c_adapter *adapter, int addr) } /** - * i2c_lock_adapter - Get exclusive access to an I2C bus segment + * i2c_adapter_lock_bus - Get exclusive access to an I2C bus segment * @adapter: Target I2C bus segment */ -void i2c_lock_adapter(struct i2c_adapter *adapter) +static void i2c_adapter_lock_bus(struct i2c_adapter *adapter, int flags) { struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); @@ -970,27 +970,26 @@ void i2c_lock_adapter(struct i2c_adapter *adapter) else rt_mutex_lock(&adapter->bus_lock); } -EXPORT_SYMBOL_GPL(i2c_lock_adapter); /** - * i2c_trylock_adapter - Try to get exclusive access to an I2C bus segment + * i2c_adapter_trylock_bus - Try to get exclusive access to an I2C bus segment * @adapter: Target I2C bus segment */ -static int i2c_trylock_adapter(struct i2c_adapter *adapter) +static int i2c_adapter_trylock_bus(struct i2c_adapter *adapter, int flags) { struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); if (parent) - return i2c_trylock_adapter(parent); + return parent->trylock_bus(parent, flags); else return rt_mutex_trylock(&adapter->bus_lock); } /** - * i2c_unlock_adapter - Release exclusive access to an I2C bus segment + * i2c_adapter_unlock_bus - Release exclusive access to an I2C bus segment * @adapter: Target I2C bus segment */ -void i2c_unlock_adapter(struct i2c_adapter *adapter) +static void i2c_adapter_unlock_bus(struct i2c_adapter *adapter, int flags) { struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); @@ -999,7 +998,6 @@ void i2c_unlock_adapter(struct i2c_adapter *adapter) else rt_mutex_unlock(&adapter->bus_lock); } -EXPORT_SYMBOL_GPL(i2c_unlock_adapter); static void i2c_dev_set_name(struct i2c_adapter *adap, struct i2c_client *client) @@ -1545,6 +1543,12 @@ static int i2c_register_adapter(struct i2c_adapter *adap) return -EINVAL; } + if (!adap->lock_bus) { + adap->lock_bus = i2c_adapter_lock_bus; + adap->trylock_bus = i2c_adapter_trylock_bus; + adap->unlock_bus = i2c_adapter_unlock_bus; + } + rt_mutex_init(&adap->bus_lock); mutex_init(&adap->userspace_clients_lock); INIT_LIST_HEAD(&adap->userspace_clients); @@ -2254,16 +2258,16 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) #endif if (in_atomic() || irqs_disabled()) { - ret = i2c_trylock_adapter(adap); + ret = adap->trylock_bus(adap, I2C_LOCK_SEGMENT); if (!ret) /* I2C activity is ongoing. */ return -EAGAIN; } else { - i2c_lock_adapter(adap); + i2c_lock_bus(adap, I2C_LOCK_SEGMENT); } ret = __i2c_transfer(adap, msgs, num); - i2c_unlock_adapter(adap); + i2c_unlock_bus(adap, I2C_LOCK_SEGMENT); return ret; } else { @@ -3038,7 +3042,7 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, flags &= I2C_M_TEN | I2C_CLIENT_PEC | I2C_CLIENT_SCCB; if (adapter->algo->smbus_xfer) { - i2c_lock_adapter(adapter); + i2c_lock_bus(adapter, I2C_LOCK_SEGMENT); /* Retry automatically on arbitration loss */ o
[PATCH v2 3/8] i2c-mux: move the slave side adapter management to i2c_mux_core
From: Peter Rosin All muxes have slave side adapters, many have some arbitrary number of them. Handle this in the mux core, so that drivers are simplified. Add i2c_mux_reserve_adapter that can be used when it is known in advance how many child adapters that is to be added. This avoids reallocating memory. Drop i2c_del_mux_adapter and replace it with i2c_del_mux_adapters, since no mux driver is dynamically deleting individual child adapters anyway. Signed-off-by: Peter Rosin --- drivers/i2c/i2c-mux.c| 71 ++-- drivers/i2c/muxes/i2c-arb-gpio-challenge.c | 10 ++-- drivers/i2c/muxes/i2c-mux-gpio.c | 23 +++-- drivers/i2c/muxes/i2c-mux-pca9541.c | 13 ++--- drivers/i2c/muxes/i2c-mux-pca954x.c | 26 -- drivers/i2c/muxes/i2c-mux-pinctrl.c | 27 +++ drivers/i2c/muxes/i2c-mux-reg.c | 28 --- drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 12 ++--- drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h| 1 - drivers/media/dvb-frontends/m88ds3103.c | 11 ++--- drivers/media/dvb-frontends/m88ds3103_priv.h | 1 - drivers/media/dvb-frontends/rtl2830.c| 10 ++-- drivers/media/dvb-frontends/rtl2830_priv.h | 1 - drivers/media/dvb-frontends/rtl2832.c| 11 ++--- drivers/media/dvb-frontends/rtl2832_priv.h | 1 - drivers/media/dvb-frontends/si2168.c | 10 ++-- drivers/media/dvb-frontends/si2168_priv.h| 1 - drivers/media/usb/cx231xx/cx231xx-core.c | 3 +- drivers/media/usb/cx231xx/cx231xx-i2c.c | 26 +- drivers/media/usb/cx231xx/cx231xx.h | 2 +- drivers/of/unittest.c| 28 --- include/linux/i2c-mux.h | 15 -- 22 files changed, 149 insertions(+), 182 deletions(-) diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c index 6c5cb9f7649b..7ba0308537a8 100644 --- a/drivers/i2c/i2c-mux.c +++ b/drivers/i2c/i2c-mux.c @@ -99,6 +99,29 @@ static unsigned int i2c_mux_parent_classes(struct i2c_adapter *parent) return class; } +int i2c_mux_reserve_adapters(struct i2c_mux_core *muxc, int adapters) +{ + struct i2c_adapter **adapter; + + if (adapters <= muxc->max_adapters) + return 0; + + adapter = devm_kmalloc_array(muxc->dev, +adapters, sizeof(*adapter), +GFP_KERNEL); + if (!adapter) + return -ENOMEM; + + memcpy(adapter, muxc->adapter, + muxc->max_adapters * sizeof(*adapter)); + + devm_kfree(muxc->dev, muxc->adapter); + muxc->adapter = adapter; + muxc->max_adapters = adapters; + return 0; +} +EXPORT_SYMBOL_GPL(i2c_mux_reserve_adapters); + struct i2c_mux_core *i2c_mux_alloc(struct device *dev, int sizeof_priv) { struct i2c_mux_core *muxc; @@ -113,19 +136,29 @@ struct i2c_mux_core *i2c_mux_alloc(struct device *dev, int sizeof_priv) } EXPORT_SYMBOL_GPL(i2c_mux_alloc); -struct i2c_adapter *i2c_add_mux_adapter(struct i2c_mux_core *muxc, - struct device *mux_dev, - u32 force_nr, u32 chan_id, - unsigned int class) +int i2c_add_mux_adapter(struct i2c_mux_core *muxc, + struct device *mux_dev, + u32 force_nr, u32 chan_id, + unsigned int class) { struct i2c_adapter *parent = muxc->parent; struct i2c_mux_priv *priv; char symlink_name[20]; int ret; + if (muxc->adapters >= muxc->max_adapters) { + int new_max = 2 * muxc->max_adapters; + + if (!new_max) + new_max = 1; + ret = i2c_mux_reserve_adapters(muxc, new_max); + if (ret) + return ret; + } + priv = kzalloc(sizeof(struct i2c_mux_priv), GFP_KERNEL); if (!priv) - return NULL; + return -ENOMEM; /* Set up private adapter data */ priv->muxc = muxc; @@ -197,7 +230,7 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_mux_core *muxc, "failed to add mux-adapter (error=%d)\n", ret); kfree(priv); - return NULL; + return ret; } WARN(sysfs_create_link(&priv->adap.dev.kobj, &mux_dev->kobj, "mux_device"), @@ -209,23 +242,31 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_mux_core *muxc, dev_info(&parent->dev, "Added multiplexed i2c bus %d\n", i2c_adapter_id(&priv->adap)); - return &priv->adap; + muxc->adapter[muxc->adapters++] = &priv->adap; + return 0; } EXPORT_SYMBOL_GPL(i2c_add_mux_adapter);
Re: [PATCH v2 1/8] i2c-mux: add common core data for every mux instance
Hi Guenter, [BTW, if anyone feels spammed by this series, please drop me a note] On 2016-01-05 17:42, Guenter Roeck wrote: > On 01/05/2016 07:57 AM, Peter Rosin wrote: >> From: Peter Rosin >> >> The initial core mux structure starts off small with only the parent >> adapter pointer, which all muxes have, and a priv pointer for mux >> driver private data. >> >> Add i2c_mux_alloc function to unify the creation of a mux. >> >> Where appropriate, pass around the mux core structure instead of the >> parent adapter or the driver private data. >> >> Remove the parent adapter pointer from the driver private data for all >> mux drivers. >> >> Signed-off-by: Peter Rosin >> --- >> drivers/i2c/i2c-mux.c| 28 +- >> drivers/i2c/muxes/i2c-arb-gpio-challenge.c | 24 +-- >> drivers/i2c/muxes/i2c-mux-gpio.c | 20 >> drivers/i2c/muxes/i2c-mux-pca9541.c | 35 >> ++-- >> drivers/i2c/muxes/i2c-mux-pca954x.c | 19 ++- >> drivers/i2c/muxes/i2c-mux-pinctrl.c | 23 +- >> drivers/i2c/muxes/i2c-mux-reg.c | 23 ++ >> drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 10 +++- >> drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h| 1 + >> drivers/media/dvb-frontends/m88ds3103.c | 10 +++- >> drivers/media/dvb-frontends/m88ds3103_priv.h | 1 + >> drivers/media/dvb-frontends/rtl2830.c| 10 +++- >> drivers/media/dvb-frontends/rtl2830_priv.h | 1 + >> drivers/media/dvb-frontends/rtl2832.c| 10 +++- >> drivers/media/dvb-frontends/rtl2832_priv.h | 1 + >> drivers/media/dvb-frontends/si2168.c | 10 +++- >> drivers/media/dvb-frontends/si2168_priv.h| 1 + >> drivers/media/usb/cx231xx/cx231xx-core.c | 3 +++ >> drivers/media/usb/cx231xx/cx231xx-i2c.c | 13 +-- >> drivers/media/usb/cx231xx/cx231xx.h | 2 ++ >> drivers/of/unittest.c| 16 +++-- >> include/linux/i2c-mux.h | 14 ++- >> 22 files changed, 187 insertions(+), 88 deletions(-) >> >> diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c >> index 00fc5b1c7b66..c2163f6b51d5 100644 >> --- a/drivers/i2c/i2c-mux.c >> +++ b/drivers/i2c/i2c-mux.c >> @@ -31,8 +31,8 @@ >> struct i2c_mux_priv { >> struct i2c_adapter adap; >> struct i2c_algorithm algo; >> +struct i2c_mux_core *muxc; >> >> -struct i2c_adapter *parent; >> struct device *mux_dev; >> void *mux_priv; >> u32 chan_id; >> @@ -45,7 +45,8 @@ static int i2c_mux_master_xfer(struct i2c_adapter *adap, >> struct i2c_msg msgs[], int num) >> { >> struct i2c_mux_priv *priv = adap->algo_data; >> -struct i2c_adapter *parent = priv->parent; >> +struct i2c_mux_core *muxc = priv->muxc; >> +struct i2c_adapter *parent = muxc->parent; >> int ret; >> >> /* Switch to the right mux port and perform the transfer. */ >> @@ -65,7 +66,8 @@ static int i2c_mux_smbus_xfer(struct i2c_adapter *adap, >> int size, union i2c_smbus_data *data) >> { >> struct i2c_mux_priv *priv = adap->algo_data; >> -struct i2c_adapter *parent = priv->parent; >> +struct i2c_mux_core *muxc = priv->muxc; >> +struct i2c_adapter *parent = muxc->parent; >> int ret; >> >> /* Select the right mux port and perform the transfer. */ >> @@ -84,7 +86,7 @@ static int i2c_mux_smbus_xfer(struct i2c_adapter *adap, >> static u32 i2c_mux_functionality(struct i2c_adapter *adap) >> { >> struct i2c_mux_priv *priv = adap->algo_data; >> -struct i2c_adapter *parent = priv->parent; >> +struct i2c_adapter *parent = priv->muxc->parent; >> >> return parent->algo->functionality(parent); >> } >> @@ -102,7 +104,20 @@ static unsigned int i2c_mux_parent_classes(struct >> i2c_adapter *parent) >> return class; >> } >> >> -struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, >> +struct i2c_mux_core *i2c_mux_alloc(struct device *dev, int sizeof_priv) >> +{ >> +struct i2c_mux_core *muxc; >> + >> +muxc = devm_kzalloc(dev, sizeof(*muxc) + sizeof_priv, GFP_KERNEL); >> +if (!muxc) >> +return NULL; >> +if (sizeof_priv) >> +
Re: [PATCH v2 0/8] i2c mux cleanup and locking update
Hi Wolfram, On 2016-01-05 19:48, Wolfram Sang wrote: > Peter, > >> PS. needs a bunch of testing, I do not have access to all the involved hw > > First of all, thanks for diving into this topic and the huge effort you > apparently have put into it. Yeah, I started with dipping just the toes, but now it rather feels like I'm fully submerged at the deep end... > It is obviously a quite intrusive series, so it needs careful review. > TBH, I can't really tell when I have the bandwidth to do that, so I hope > other people will step up. And yes, it needs serious testing. > > To all: Although I appreciate any review support, I'd think the first > thing to be done should be a very high level review - is this series > worth the huge update? Is the path chosen proper? Stuff like this. I'd > appreciate Acks or Revs for that. Stuff like fixing checkpatch warnings > and other minor stuff should come later. Right, I'll hold back on sending updates for trivial stuff until the big picture stuff has been cleared. Cheers, Peter -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v2 3/8] i2c-mux: move the slave side adapter management to i2c_mux_core
Hi, This should fix it (I'm not sending a v3 right away). Cheers, Peter diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c index 1c982a56acd5..5de993deca7e 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c @@ -16,6 +16,7 @@ #include #include +#include #include #include #include "inv_mpu_iio.h" @@ -182,7 +183,7 @@ int inv_mpu_acpi_create_mux_client(struct inv_mpu6050_state *st) } else return 0; /* no secondary addr, which is OK */ } - st->mux_client = i2c_new_device(st->mux_adapter, &info); + st->mux_client = i2c_new_device(st->muxc->adapter[0], &info); if (!st->mux_client) return -ENODEV; -- On 2016-01-05 17:49, kbuild test robot wrote: > Hi Peter, > > [auto build test ERROR on wsa/i2c/for-next] > [also build test ERROR on v4.4-rc8 next-20160105] > [if your patch is applied to the wrong git tree, please drop us a note to > help improving the system] > > url: > https://github.com/0day-ci/linux/commits/Peter-Rosin/i2c-mux-cleanup-and-locking-update/20160106-000205 > base: https://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux i2c/for-next > config: i386-randconfig-s1-201601 (attached as .config) > reproduce: > # save the attached .config to linux build tree > make ARCH=i386 > > All errors (new ones prefixed by >>): > >drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c: In function > 'inv_mpu_acpi_create_mux_client': >>> drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c:185:37: error: 'struct >>> inv_mpu6050_state' has no member named 'mux_adapter' > st->mux_client = i2c_new_device(st->mux_adapter, &info); > ^ > > vim +185 drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c > > a35c5d1a Srinivas Pandruvada 2015-01-30 179 > *name = '\0'; > a35c5d1a Srinivas Pandruvada 2015-01-30 180 > strlcat(info.type, "-client", > a35c5d1a Srinivas Pandruvada 2015-01-30 181 > sizeof(info.type)); > a35c5d1a Srinivas Pandruvada 2015-01-30 182 } else > a35c5d1a Srinivas Pandruvada 2015-01-30 183 > return 0; /* no secondary addr, which is OK */ > a35c5d1a Srinivas Pandruvada 2015-01-30 184 } > a35c5d1a Srinivas Pandruvada 2015-01-30 @185 st->mux_client > = i2c_new_device(st->mux_adapter, &info); > a35c5d1a Srinivas Pandruvada 2015-01-30 186 if > (!st->mux_client) > a35c5d1a Srinivas Pandruvada 2015-01-30 187 return > -ENODEV; > a35c5d1a Srinivas Pandruvada 2015-01-30 188 > > :: The code at line 185 was first introduced by commit > :: a35c5d1aa96aa6cc70e91786cbe9be4db23f8f4a iio: imu: inv_mpu6050: Create > mux clients for ACPI > > :: TO: Srinivas Pandruvada > :: CC: Jonathan Cameron > > --- > 0-DAY kernel test infrastructureOpen Source Technology Center > https://lists.01.org/pipermail/kbuild-all Intel Corporation > -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v2 3/8] i2c-mux: move the slave side adapter management to i2c_mux_core
Ouch, this got lost in the shuffle, don't bother testing without it. It will be included in v3. [the reason is that my test hw relies on vendor patches, and I have to rebase before sending. I.e., I can only compile-test the stuff I'm actually sending out. Inconvenient.] diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c index c4d4b14a5399..c5a5886d8be1 100644 --- a/drivers/i2c/i2c-mux.c +++ b/drivers/i2c/i2c-mux.c @@ -215,10 +215,12 @@ int i2c_mux_reserve_adapters(struct i2c_mux_core *muxc, int adapters) if (!adapter) return -ENOMEM; - memcpy(adapter, muxc->adapter, - muxc->max_adapters * sizeof(*adapter)); + if (muxc->adapter) { + memcpy(adapter, muxc->adapter, + muxc->max_adapters * sizeof(*adapter)); + devm_kfree(muxc->dev, muxc->adapter); + } - devm_kfree(muxc->dev, muxc->adapter); muxc->adapter = adapter; muxc->max_adapters = adapters; return 0; -- On 2016-01-05 16:57, Peter Rosin wrote: > From: Peter Rosin > > All muxes have slave side adapters, many have some arbitrary number of > them. Handle this in the mux core, so that drivers are simplified. > > Add i2c_mux_reserve_adapter that can be used when it is known in advance > how many child adapters that is to be added. This avoids reallocating > memory. > > Drop i2c_del_mux_adapter and replace it with i2c_del_mux_adapters, since > no mux driver is dynamically deleting individual child adapters anyway. > > Signed-off-by: Peter Rosin > --- > drivers/i2c/i2c-mux.c| 71 > ++-- > drivers/i2c/muxes/i2c-arb-gpio-challenge.c | 10 ++-- > drivers/i2c/muxes/i2c-mux-gpio.c | 23 +++-- > drivers/i2c/muxes/i2c-mux-pca9541.c | 13 ++--- > drivers/i2c/muxes/i2c-mux-pca954x.c | 26 -- > drivers/i2c/muxes/i2c-mux-pinctrl.c | 27 +++ > drivers/i2c/muxes/i2c-mux-reg.c | 28 --- > drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 12 ++--- > drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h| 1 - > drivers/media/dvb-frontends/m88ds3103.c | 11 ++--- > drivers/media/dvb-frontends/m88ds3103_priv.h | 1 - > drivers/media/dvb-frontends/rtl2830.c| 10 ++-- > drivers/media/dvb-frontends/rtl2830_priv.h | 1 - > drivers/media/dvb-frontends/rtl2832.c| 11 ++--- > drivers/media/dvb-frontends/rtl2832_priv.h | 1 - > drivers/media/dvb-frontends/si2168.c | 10 ++-- > drivers/media/dvb-frontends/si2168_priv.h| 1 - > drivers/media/usb/cx231xx/cx231xx-core.c | 3 +- > drivers/media/usb/cx231xx/cx231xx-i2c.c | 26 +- > drivers/media/usb/cx231xx/cx231xx.h | 2 +- > drivers/of/unittest.c| 28 --- > include/linux/i2c-mux.h | 15 -- > 22 files changed, 149 insertions(+), 182 deletions(-) > > diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c > index 6c5cb9f7649b..7ba0308537a8 100644 > --- a/drivers/i2c/i2c-mux.c > +++ b/drivers/i2c/i2c-mux.c > @@ -99,6 +99,29 @@ static unsigned int i2c_mux_parent_classes(struct > i2c_adapter *parent) > return class; > } > > +int i2c_mux_reserve_adapters(struct i2c_mux_core *muxc, int adapters) > +{ > + struct i2c_adapter **adapter; > + > + if (adapters <= muxc->max_adapters) > + return 0; > + > + adapter = devm_kmalloc_array(muxc->dev, > + adapters, sizeof(*adapter), > + GFP_KERNEL); > + if (!adapter) > + return -ENOMEM; > + > + memcpy(adapter, muxc->adapter, > +muxc->max_adapters * sizeof(*adapter)); > + > + devm_kfree(muxc->dev, muxc->adapter); > + muxc->adapter = adapter; > + muxc->max_adapters = adapters; > + return 0; > +} > +EXPORT_SYMBOL_GPL(i2c_mux_reserve_adapters); > + > struct i2c_mux_core *i2c_mux_alloc(struct device *dev, int sizeof_priv) > { > struct i2c_mux_core *muxc; > @@ -113,19 +136,29 @@ struct i2c_mux_core *i2c_mux_alloc(struct device *dev, > int sizeof_priv) > } > EXPORT_SYMBOL_GPL(i2c_mux_alloc); > > -struct i2c_adapter *i2c_add_mux_adapter(struct i2c_mux_core *muxc, > - struct device *mux_dev, > - u32 force_nr, u32 chan_id, > - unsigned int class) > +int i2c_add_mux_adapter(struct i2c_mux_core *muxc, > + struct device *mux_dev, > + u32 force_nr, u32 chan_id, > + unsigne
Re: [PATCH] si2168: use i2c controlled mux interface
Hi Antti, On 2016-01-06 06:42, Antti Palosaari wrote: > Recent i2c mux locking update offers support for i2c controlled i2c > muxes. Use it and get the rid of homemade hackish i2c adapter > locking code. That looks good on a first glance, and I'm sure it felt good to get rid of the locking workaround :-) However, is this safe? From looking at the short datasheet of the si2168, it seems that the mux is used to open up the channel to the tuner? But what happens is there are two parallel accesses, one to the tuner and one to the si2168 chip? With your change, it could happen that the access to the si2168 happens while the gate to the tuner is open. Can that break anything? I.e. thread one thread two -- -- open gate access si2168 access tuner close gate If that is safe, then I don't understand why the gate isn't left open at all times? The short datasheet is too short to answer my questions... Also, my series needs some Tested-by (and Reviewed-by for that matter), and I assume that you have tested it? Is it ok to add something like that from you? I understand that you may only be able to test your corner of the series, but that would still be very helpful. Thanks! Cheers, Peter > Cc: Peter Rosin > Cc: Peter Rosin > Signed-off-by: Antti Palosaari > --- > drivers/media/dvb-frontends/si2168.c | 61 > > 1 file changed, 6 insertions(+), 55 deletions(-) > > diff --git a/drivers/media/dvb-frontends/si2168.c > b/drivers/media/dvb-frontends/si2168.c > index ae217b5..d2a5608 100644 > --- a/drivers/media/dvb-frontends/si2168.c > +++ b/drivers/media/dvb-frontends/si2168.c > @@ -18,48 +18,15 @@ > > static const struct dvb_frontend_ops si2168_ops; > > -/* Own I2C adapter locking is needed because of I2C gate logic. */ > -static int si2168_i2c_master_send_unlocked(const struct i2c_client *client, > -const char *buf, int count) > -{ > - int ret; > - struct i2c_msg msg = { > - .addr = client->addr, > - .flags = 0, > - .len = count, > - .buf = (char *)buf, > - }; > - > - ret = __i2c_transfer(client->adapter, &msg, 1); > - return (ret == 1) ? count : ret; > -} > - > -static int si2168_i2c_master_recv_unlocked(const struct i2c_client *client, > -char *buf, int count) > -{ > - int ret; > - struct i2c_msg msg = { > - .addr = client->addr, > - .flags = I2C_M_RD, > - .len = count, > - .buf = buf, > - }; > - > - ret = __i2c_transfer(client->adapter, &msg, 1); > - return (ret == 1) ? count : ret; > -} > - > /* execute firmware command */ > -static int si2168_cmd_execute_unlocked(struct i2c_client *client, > -struct si2168_cmd *cmd) > +static int si2168_cmd_execute(struct i2c_client *client, struct si2168_cmd > *cmd) > { > int ret; > unsigned long timeout; > > if (cmd->wlen) { > /* write cmd and args for firmware */ > - ret = si2168_i2c_master_send_unlocked(client, cmd->args, > - cmd->wlen); > + ret = i2c_master_send(client, cmd->args, cmd->wlen); > if (ret < 0) { > goto err; > } else if (ret != cmd->wlen) { > @@ -73,8 +40,7 @@ static int si2168_cmd_execute_unlocked(struct i2c_client > *client, > #define TIMEOUT 70 > timeout = jiffies + msecs_to_jiffies(TIMEOUT); > while (!time_after(jiffies, timeout)) { > - ret = si2168_i2c_master_recv_unlocked(client, cmd->args, > - cmd->rlen); > + ret = i2c_master_recv(client, cmd->args, cmd->rlen); > if (ret < 0) { > goto err; > } else if (ret != cmd->rlen) { > @@ -109,17 +75,6 @@ err: > return ret; > } > > -static int si2168_cmd_execute(struct i2c_client *client, struct si2168_cmd > *cmd) > -{ > - int ret; > - > - i2c_lock_adapter(client->adapter); > - ret = si2168_cmd_execute_unlocked(client, cmd); > - i2c_unlock_adapter(client->adapter); > - > - return ret; > -} > - > static int si2168_read_status(struct dvb_frontend *fe, enum fe_status > *status) > { > struct i2c_client *client
Re: [PATCH v2 0/8] i2c mux cleanup and locking update
Hi Antti, On 2016-01-06 18:17, Antti Palosaari wrote: > On 01/05/2016 05:57 PM, Peter Rosin wrote: >> From: Peter Rosin >> >> Hi! >> >> I have a pair of boards with this i2c topology: >> >> GPIO ---| -- BAT1 >> | v / >> I2C -+--B---+ MUX >> | \ >> EEPROM -- BAT2 >> >> (B denotes the boundary between the boards) > > Handling of I2C muxes that close channel automatically, after the first I2C > stop (P) is seen? > > For example channel is selected to BAT1 => there is EEPROM write => mux > closes channel BAT1 => access to BAT1 will fail. The proposed locking changes affect gpio- and pinctrl-controlled muxes only, and I can't see one of those actually know anything about the i2c-signals that they mux. Such muxes certainly has to obey the pins that control them, no? That fact that other muxes might piggy-back and also use the new i2c-controlled flag is a different question, and it may indeed not be safe to declare a mux "i2c-controlled" just to avoid the locking, if the locking is in fact required. Maybe the name "i2c-controlled" is poor? > Is it possible to lock whole adapter, but allow only traffic to i2c mux > client? This is basically what is there today, and which does not work for the above i2c topology. So maybe you need to expand on what you meant? There is no neat way for the i2c mux code to dip its fingers into all relevant i2c-accesses to categorize them as "mux updates" or "mux slave side accesses", when they come from generic subsystems such as gpio och pinctrl. The only thing I have been able to think of in that area is to add i2c devices that are used to change the mux to a virtual slave adapter of the mux, so that the mux can identify those accesses and bypass the locking. But that isn't generic enough to cover the case where one device is used to control more than one mux (since it needs to sit on more than one adapter in that case), so I scrapped that idea pretty early. But I also find that I have to scrap it again and again, since everything else I cook up usually ends up being some variant of the virtual slave adapter when I think some more about it. Maybe it *is* possible to have the same device in different places in the adapter tree (if all those places have a common ancestor)? But I don't think so... That does not address your concerns about extra accesses creeping in between the mux select and the mux slave side access for auto- closing muxes (or arbitrators), but I never pretended that there would be protection from that. If there are such requirements you basically have to lock the root adapter to prevent i2c "noise". I can't see recursive locks helping either, because that would need lock ownership to be propagated into gpio/pinctrl, and that is again too ugly. Cheers, Peter > regards > Antti > >> >> The problem with this is that the GPIO controller sits on the same i2c bus >> that it MUXes. For pca954x devices this is worked around by using unlocked >> transfers when updating the MUX. I have no such luck as the GPIO is a general >> purpose IO expander and the MUX is just a random bidirectional MUX, unaware >> of the fact that it is muxing an i2c bus, and extending unlocked transfers >> into the GPIO subsystem is too ugly to even think about. But the general hw >> approach is sane in my opinion, with the number of connections between the >> two boards minimized. To put is plainly, I need support for it. >> >> So, I observe that while it is needed to have the i2c bus locked during the >> actual MUX update in order to avoid random garbage on the slave side, it >> is not strictly a must to have it locked over the whole sequence of a full >> select-transfer-deselect operation. The MUX itself needs to be locked, so >> transfers to clients behind the mux are serialized, and the MUX needs to be >> stable during all i2c traffic (otherwise individual mux slave segments >> might see garbage). >> >> This series accomplishes this by adding a dt property to i2c-mux-gpio and >> i2c-mux-pinctrl that can be used to state that the mux is updated by means >> of the muxed master bus, and that the select-transfer-deselect operations >> should be locked individually. When this holds, the i2c bus *is* locked >> during muxing, since the muxing happens as part of i2c transfers. This >> is true even if the MUX is updated with several transfers to the GPIO (at >> least as long as *all* MUX changes are using the i2s master bus). A lock >> is added to the mux so that transfers through the mux are serialize
Re: [PATCH v2 8/8] i2c-mux: relax locking of the top i2c adapter during i2c controlled muxing
Hi Rob, On 2016-01-06 15:49, Rob Herring wrote: > On Tue, Jan 05, 2016 at 04:57:18PM +0100, Peter Rosin wrote: >> From: Peter Rosin >> >> With a i2c topology like the following >> >>GPIO ---| -- BAT1 >> | v / >>I2C -+--+ MUX >> | \ >>EEPROM -- BAT2 > > Yuck. One would think you would just use an I2C controlled mux in this > case... > >> there is a locking problem with the GPIO controller since it is a client >> on the same i2c bus that it muxes. Transfers to the mux clients (e.g. BAT1) >> will lock the whole i2c bus prior to attempting to switch the mux to the >> correct i2c segment. In the above case, the GPIO device is an I/O expander >> with an i2c interface, and since the GPIO subsystem knows nothing (and >> rightfully so) about the lockless needs of the i2c mux code, this results >> in a deadlock when the GPIO driver issues i2c transfers to modify the >> mux. >> >> So, observing that while it is needed to have the i2c bus locked during the >> actual MUX update in order to avoid random garbage on the slave side, it >> is not strictly a must to have it locked over the whole sequence of a full >> select-transfer-deselect mux client operation. The mux itself needs to be >> locked, so transfers to clients behind the mux are serialized, and the mux >> needs to be stable during all i2c traffic (otherwise individual mux slave >> segments might see garbage, or worse). >> >> Add devive tree properties (bool named i2c-controlled) to i2c-mux-gpio and >> i2c-mux-pinctrl that asserts that the the gpio/pinctrl is controlled via >> the same i2c bus that it muxes. > > Can't you determine this condition by checking the mux parent and gpio > parent are the same i2c controller? Good suggestion, I wrote code that implements this, and will include it in v3. Do not expect v3 to hit the dt crowd though, since no dt changes will be needed then, but I'm sure that is not a problem... > Alternatively, can't you just always do the locking like i2c-controlled > is set when a mux is involved? What is the harm in doing that if the > GPIO is controlled somewhere else? No, that is not possible. If you change a non-i2c-controlled gpio in the middle of some i2c-access, the slave side of the mux might see partial i2c transfers, and that is a recipe for disaster. > I would prefer to see a solution not requiring DT updates to fix and > this change seems like it is working around kernel issues. Right, I'll make it so. Cheers, Peter -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
RE: [PATCH v3 0/8] i2c mux cleanup and locking update
Wolfram Sang wrote: > On Fri, Jan 08, 2016 at 04:04:48PM +0100, Peter Rosin wrote: > > > > Hi! > > > > [doing a v3 even if there is no "big picture" feedback yet, but > > previous versions has bugs that make them harder to test than > > needed, and testing is very much desired] > > > > I have a pair of boards with this i2c topology: > > > >GPIO ---| -- BAT1 > > | v / > >I2C -+--B---+ MUX > > | \ > >EEPROM -- BAT2 > > > > (B denotes the boundary between the boards) > > > > The problem with this is that the GPIO controller sits on the same i2c bus > > that it MUXes. For pca954x devices this is worked around by using unlocked > > transfers when updating the MUX. I have no such luck as the GPIO is a > > general > > purpose IO expander and the MUX is just a random bidirectional MUX, unaware > > of the fact that it is muxing an i2c bus, and extending unlocked transfers > > into the GPIO subsystem is too ugly to even think about. But the general hw > > approach is sane in my opinion, with the number of connections between the > > two boards minimized. To put is plainly, I need support for it. > > > > So, I observe that while it is needed to have the i2c bus locked during the > > actual MUX update in order to avoid random garbage on the slave side, it > > is not strictly a must to have it locked over the whole sequence of a full > > select-transfer-deselect operation. The MUX itself needs to be locked, so > > transfers to clients behind the mux are serialized, and the MUX needs to be > > stable during all i2c traffic (otherwise individual mux slave segments > > might see garbage). > > > > This series accomplishes this by adding code to i2c-mux-gpio and > > i2c-mux-pinctrl that determines if all involved devices used to update the > > mux are controlled by the same root i2c adapter that is muxed. When this > > is the case, the select-transfer-deselect operations should be locked > > individually to avoid the deadlock. The i2c bus *is* still locked > > during muxing, since the muxing happens as part of i2c transfers. This > > is true even if the MUX is updated with several transfers to the GPIO (at > > least as long as *all* MUX changes are using the i2s master bus). A lock > > is added to the mux so that transfers through the mux are serialized. > > > > Concerns: > > - The locking is perhaps too complex? > > - I worry about the priority inheritance aspect of the adapter lock. When > > the transfers behind the mux are divided into select-transfer-deselect all > > locked individually, low priority transfers get more chances to interfere > > with high priority transfers. > > - When doing an i2c_transfer() in_atomic() context or with irqs_disabled(), > > there is a higher possibility that the mux is not returned to its idle > > state after a failed (-EAGAIN) transfer due to trylock. > > - Is the detection of i2c-controlled gpios and pinctrls sane (i.e. the > > usage of the new i2c_root_adapter() function in 8/8)? > > > > To summarize the series, there's some i2c-mux infrastructure cleanup work > > first (I think that part stands by itself as desireable regardless), the > > locking changes are in the last three patches of the series, with the real > > meat in 8/8. > > > > PS. needs a bunch of testing, I do not have access to all the involved hw > > I want to let you know that I am currently thinking about this series. Glad to hear it! > There seems to be a second occasion where it could have helped AFAICT. > http://patchwork.ozlabs.org/patch/584776/ (check my comments there from > yesterday and today) The mpu6050 driver has to set muxc->i2c_controlled before adding any child adapters for anything to behave differently, and it also has to make sure that all accesses in select/deselect are normal i2c accesses (i.e. not unlocked accesses). When doing so, unreleated i2c traffic might interleave with the muxing. So, if the chip is auto-deselecting on the first i2c transfer after select it will never work properly. > First of all, really thank you that you tried to find the proper > solution and went all the way for it. It is easy to do a fire&forget > hack here, but you didn't. Fire&forget often turns out to be just the fire. If you do it properly there is a better chance that you really can forget it... > I hope you understand, though, that I need to make a balance between > features and complexity in my subsystem to
[PATCH v4 06/18] i2c: i2c-mux-pca954x: convert to use an explicit i2c mux core
From: Peter Rosin Allocate an explicit i2c mux core to handle parent and child adapters etc. Update the select/deselect ops to be in terms of the i2c mux core instead of the child adapter. Add a mask to handle the case where not all child adapters should cause a mux deselect to happen, now that there is a common deselect op for all child adapters. Signed-off-by: Peter Rosin --- drivers/i2c/muxes/i2c-mux-pca954x.c | 64 +++-- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c index acfcef3d4068..1693d29c11a4 100644 --- a/drivers/i2c/muxes/i2c-mux-pca954x.c +++ b/drivers/i2c/muxes/i2c-mux-pca954x.c @@ -60,9 +60,10 @@ enum pca_type { struct pca954x { enum pca_type type; - struct i2c_adapter *virt_adaps[PCA954X_MAX_NCHANS]; u8 last_chan; /* last register value */ + u8 deselect; + struct i2c_client *client; }; struct chip_desc { @@ -146,10 +147,10 @@ static int pca954x_reg_write(struct i2c_adapter *adap, return ret; } -static int pca954x_select_chan(struct i2c_adapter *adap, - void *client, u32 chan) +static int pca954x_select_chan(struct i2c_mux_core *muxc, u32 chan) { - struct pca954x *data = i2c_get_clientdata(client); + struct pca954x *data = i2c_mux_priv(muxc); + struct i2c_client *client = data->client; const struct chip_desc *chip = &chips[data->type]; u8 regval; int ret = 0; @@ -162,21 +163,24 @@ static int pca954x_select_chan(struct i2c_adapter *adap, /* Only select the channel if its different from the last channel */ if (data->last_chan != regval) { - ret = pca954x_reg_write(adap, client, regval); + ret = pca954x_reg_write(muxc->parent, client, regval); data->last_chan = regval; } return ret; } -static int pca954x_deselect_mux(struct i2c_adapter *adap, - void *client, u32 chan) +static int pca954x_deselect_mux(struct i2c_mux_core *muxc, u32 chan) { - struct pca954x *data = i2c_get_clientdata(client); + struct pca954x *data = i2c_mux_priv(muxc); + struct i2c_client *client = data->client; + + if (!(data->deselect & (1 << chan))) + return 0; /* Deselect active channel */ data->last_chan = 0; - return pca954x_reg_write(adap, client, data->last_chan); + return pca954x_reg_write(muxc->parent, client, data->last_chan); } /* @@ -191,17 +195,21 @@ static int pca954x_probe(struct i2c_client *client, bool idle_disconnect_dt; struct gpio_desc *gpio; int num, force, class; + struct i2c_mux_core *muxc; struct pca954x *data; int ret; if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE)) return -ENODEV; - data = devm_kzalloc(&client->dev, sizeof(struct pca954x), GFP_KERNEL); - if (!data) + muxc = i2c_mux_alloc(adap, &client->dev, sizeof(*data), 0, +pca954x_select_chan, pca954x_deselect_mux); + if (!muxc) return -ENOMEM; + data = i2c_mux_priv(muxc); - i2c_set_clientdata(client, data); + i2c_set_clientdata(client, muxc); + data->client = client; /* Get the mux out of reset if a reset GPIO is specified. */ gpio = devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_LOW); @@ -220,6 +228,10 @@ static int pca954x_probe(struct i2c_client *client, data->type = id->driver_data; data->last_chan = 0; /* force the first selection */ + ret = i2c_mux_reserve_adapters(muxc, chips[data->type].nchans); + if (ret) + return ret; + idle_disconnect_dt = of_node && of_property_read_bool(of_node, "i2c-mux-idle-disconnect"); @@ -238,16 +250,13 @@ static int pca954x_probe(struct i2c_client *client, /* discard unconfigured channels */ break; idle_disconnect_pd = pdata->modes[num].deselect_on_exit; + data->deselect |= (idle_disconnect_pd + || idle_disconnect_dt) << num; } - data->virt_adaps[num] = - i2c_add_mux_adapter(adap, &client->dev, client, - force, num, class, pca954x_select_chan, - (idle_disconnect_pd || idle_disconnect_dt) - ? pca954x_deselect_mux : NULL); + ret = i2c_mux_add_adapter(muxc, force, num, class); - if (data->v
[PATCH v4 01/18] i2c-mux: add common data for every i2c-mux instance
From: Peter Rosin All i2c-muxes have a parent adapter and one or many child adapters. A mux also has some means of selection. Previously, this was stored per child adapter, but it is only needed to keep track of this per mux. Add an i2c mux core, that keeps track of this consistently. Also add some glue for users of the old interface, which will create one implicit mux core per child adapter. Signed-off-by: Peter Rosin --- drivers/i2c/i2c-mux.c | 236 ++-- include/linux/i2c-mux.h | 47 ++ 2 files changed, 236 insertions(+), 47 deletions(-) diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c index 00fc5b1c7b66..7df323a01661 100644 --- a/drivers/i2c/i2c-mux.c +++ b/drivers/i2c/i2c-mux.c @@ -28,33 +28,34 @@ #include /* multiplexer per channel data */ +struct i2c_mux_priv_old { + void *mux_priv; + int (*select)(struct i2c_adapter *, void *mux_priv, u32 chan_id); + int (*deselect)(struct i2c_adapter *, void *mux_priv, u32 chan_id); +}; + struct i2c_mux_priv { struct i2c_adapter adap; struct i2c_algorithm algo; - - struct i2c_adapter *parent; - struct device *mux_dev; - void *mux_priv; + struct i2c_mux_core *muxc; u32 chan_id; - - int (*select)(struct i2c_adapter *, void *mux_priv, u32 chan_id); - int (*deselect)(struct i2c_adapter *, void *mux_priv, u32 chan_id); }; static int i2c_mux_master_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) { struct i2c_mux_priv *priv = adap->algo_data; - struct i2c_adapter *parent = priv->parent; + struct i2c_mux_core *muxc = priv->muxc; + struct i2c_adapter *parent = muxc->parent; int ret; /* Switch to the right mux port and perform the transfer. */ - ret = priv->select(parent, priv->mux_priv, priv->chan_id); + ret = muxc->select(muxc, priv->chan_id); if (ret >= 0) ret = __i2c_transfer(parent, msgs, num); - if (priv->deselect) - priv->deselect(parent, priv->mux_priv, priv->chan_id); + if (muxc->deselect) + muxc->deselect(muxc, priv->chan_id); return ret; } @@ -65,17 +66,18 @@ static int i2c_mux_smbus_xfer(struct i2c_adapter *adap, int size, union i2c_smbus_data *data) { struct i2c_mux_priv *priv = adap->algo_data; - struct i2c_adapter *parent = priv->parent; + struct i2c_mux_core *muxc = priv->muxc; + struct i2c_adapter *parent = muxc->parent; int ret; /* Select the right mux port and perform the transfer. */ - ret = priv->select(parent, priv->mux_priv, priv->chan_id); + ret = muxc->select(muxc, priv->chan_id); if (ret >= 0) ret = parent->algo->smbus_xfer(parent, addr, flags, read_write, command, size, data); - if (priv->deselect) - priv->deselect(parent, priv->mux_priv, priv->chan_id); + if (muxc->deselect) + muxc->deselect(muxc, priv->chan_id); return ret; } @@ -84,7 +86,7 @@ static int i2c_mux_smbus_xfer(struct i2c_adapter *adap, static u32 i2c_mux_functionality(struct i2c_adapter *adap) { struct i2c_mux_priv *priv = adap->algo_data; - struct i2c_adapter *parent = priv->parent; + struct i2c_adapter *parent = priv->muxc->parent; return parent->algo->functionality(parent); } @@ -102,30 +104,80 @@ static unsigned int i2c_mux_parent_classes(struct i2c_adapter *parent) return class; } -struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, - struct device *mux_dev, - void *mux_priv, u32 force_nr, u32 chan_id, - unsigned int class, - int (*select) (struct i2c_adapter *, - void *, u32), - int (*deselect) (struct i2c_adapter *, -void *, u32)) +int i2c_mux_reserve_adapters(struct i2c_mux_core *muxc, int adapters) +{ + struct i2c_adapter **adapter; + + if (adapters <= muxc->max_adapters) + return 0; + + adapter = devm_kmalloc_array(muxc->dev, +adapters, sizeof(*adapter), +GFP_KERNEL); + if (!adapter) + return -ENOMEM; + + if (muxc->adapter) { + memcpy(adapter, muxc->adapter, + muxc->max_adapters * sizeof(*adapter)); + devm_kfree(muxc->dev, muxc->adapter); + } + + muxc->adapter = adapter; + muxc->
[PATCH v4 17/18] i2c: muxes always lock the parent adapter
From: Peter Rosin Instead of checking for i2c parent adapters for every lock/unlock, simply override the locking for muxes to always lock/unlock the parent adapter directly. Signed-off-by: Peter Rosin --- drivers/i2c/i2c-core.c | 21 +++-- drivers/i2c/i2c-mux.c | 27 +++ 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 946afd8e9f78..6be266c3d39b 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -964,12 +964,7 @@ static int i2c_check_addr_busy(struct i2c_adapter *adapter, int addr) */ static void i2c_adapter_lock_bus(struct i2c_adapter *adapter, int flags) { - struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); - - if (parent) - i2c_lock_adapter(parent); - else - rt_mutex_lock(&adapter->bus_lock); + rt_mutex_lock(&adapter->bus_lock); } /** @@ -978,12 +973,7 @@ static void i2c_adapter_lock_bus(struct i2c_adapter *adapter, int flags) */ static int i2c_adapter_trylock_bus(struct i2c_adapter *adapter, int flags) { - struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); - - if (parent) - return parent->trylock_bus(parent, flags); - else - return rt_mutex_trylock(&adapter->bus_lock); + return rt_mutex_trylock(&adapter->bus_lock); } /** @@ -992,12 +982,7 @@ static int i2c_adapter_trylock_bus(struct i2c_adapter *adapter, int flags) */ static void i2c_adapter_unlock_bus(struct i2c_adapter *adapter, int flags) { - struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); - - if (parent) - i2c_unlock_adapter(parent); - else - rt_mutex_unlock(&adapter->bus_lock); + rt_mutex_unlock(&adapter->bus_lock); } static void i2c_dev_set_name(struct i2c_adapter *adap, diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c index e7ac7a4a7a83..6f5c70c527b8 100644 --- a/drivers/i2c/i2c-mux.c +++ b/drivers/i2c/i2c-mux.c @@ -98,6 +98,30 @@ static unsigned int i2c_mux_parent_classes(struct i2c_adapter *parent) return class; } +static void i2c_parent_lock_bus(struct i2c_adapter *adapter, int flags) +{ + struct i2c_mux_priv *priv = adapter->algo_data; + struct i2c_adapter *parent = priv->muxc->parent; + + parent->lock_bus(parent, flags); +} + +static int i2c_parent_trylock_bus(struct i2c_adapter *adapter, int flags) +{ + struct i2c_mux_priv *priv = adapter->algo_data; + struct i2c_adapter *parent = priv->muxc->parent; + + return parent->trylock_bus(parent, flags); +} + +static void i2c_parent_unlock_bus(struct i2c_adapter *adapter, int flags) +{ + struct i2c_mux_priv *priv = adapter->algo_data; + struct i2c_adapter *parent = priv->muxc->parent; + + parent->unlock_bus(parent, flags); +} + int i2c_mux_reserve_adapters(struct i2c_mux_core *muxc, int adapters) { struct i2c_adapter **adapter; @@ -192,6 +216,9 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc, priv->adap.retries = parent->retries; priv->adap.timeout = parent->timeout; priv->adap.quirks = parent->quirks; + priv->adap.lock_bus = i2c_parent_lock_bus; + priv->adap.trylock_bus = i2c_parent_trylock_bus; + priv->adap.unlock_bus = i2c_parent_unlock_bus; /* Sanity check on class */ if (i2c_mux_parent_classes(parent) & class) -- 2.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v4 18/18] i2c-mux: relax locking of the top i2c adapter during i2c controlled muxing
From: Peter Rosin With a i2c topology like the following GPIO ---| -- BAT1 | v / I2C -+--+ MUX | \ EEPROM -- BAT2 there is a locking problem with the GPIO controller since it is a client on the same i2c bus that it muxes. Transfers to the mux clients (e.g. BAT1) will lock the whole i2c bus prior to attempting to switch the mux to the correct i2c segment. In the above case, the GPIO device is an I/O expander with an i2c interface, and since the GPIO subsystem knows nothing (and rightfully so) about the lockless needs of the i2c mux code, this results in a deadlock when the GPIO driver issues i2c transfers to modify the mux. So, observing that while it is needed to have the i2c bus locked during the actual MUX update in order to avoid random garbage on the slave side, it is not strictly a must to have it locked over the whole sequence of a full select-transfer-deselect mux client operation. The mux itself needs to be locked, so transfers to clients behind the mux are serialized, and the mux needs to be stable during all i2c traffic (otherwise individual mux slave segments might see garbage, or worse). Add code to i2c-mux-gpio and i2c-mux-pinctrl that checks if all involved gpio/pinctrl devices have a parent that is an i2c adapter in the same adapter tree that is muxed. Modify the i2c mux locking so that muxes that are "i2c-controlled" locks the mux instead of the whole i2c bus when there is a transfer to the slave side of the mux. This lock serializes transfers to the slave side of the mux. Modify the select-transfer-deselect code for "i2c-controlled" muxes so that each of the select-transfer-deselect ops locks the mux parent adapter individually. Signed-off-by: Peter Rosin --- drivers/i2c/i2c-mux.c | 136 ++-- drivers/i2c/muxes/i2c-mux-gpio.c| 19 + drivers/i2c/muxes/i2c-mux-pinctrl.c | 39 +++ include/linux/i2c-mux.h | 7 ++ 4 files changed, 194 insertions(+), 7 deletions(-) diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c index 6f5c70c527b8..40a4e0397826 100644 --- a/drivers/i2c/i2c-mux.c +++ b/drivers/i2c/i2c-mux.c @@ -54,6 +54,25 @@ static int i2c_mux_master_xfer(struct i2c_adapter *adap, return ret; } +static int __i2c_mux_master_xfer(struct i2c_adapter *adap, +struct i2c_msg msgs[], int num) +{ + struct i2c_mux_priv *priv = adap->algo_data; + struct i2c_mux_core *muxc = priv->muxc; + struct i2c_adapter *parent = muxc->parent; + int ret; + + /* Switch to the right mux port and perform the transfer. */ + + ret = muxc->select(muxc, priv->chan_id); + if (ret >= 0) + ret = i2c_transfer(parent, msgs, num); + if (muxc->deselect) + muxc->deselect(muxc, priv->chan_id); + + return ret; +} + static int i2c_mux_smbus_xfer(struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, @@ -76,6 +95,28 @@ static int i2c_mux_smbus_xfer(struct i2c_adapter *adap, return ret; } +static int __i2c_mux_smbus_xfer(struct i2c_adapter *adap, + u16 addr, unsigned short flags, + char read_write, u8 command, + int size, union i2c_smbus_data *data) +{ + struct i2c_mux_priv *priv = adap->algo_data; + struct i2c_mux_core *muxc = priv->muxc; + struct i2c_adapter *parent = muxc->parent; + int ret; + + /* Select the right mux port and perform the transfer. */ + + ret = muxc->select(muxc, priv->chan_id); + if (ret >= 0) + ret = i2c_smbus_xfer(parent, addr, flags, +read_write, command, size, data); + if (muxc->deselect) + muxc->deselect(muxc, priv->chan_id); + + return ret; +} + /* Return the parent's functionality */ static u32 i2c_mux_functionality(struct i2c_adapter *adap) { @@ -98,6 +139,45 @@ static unsigned int i2c_mux_parent_classes(struct i2c_adapter *parent) return class; } +static void i2c_mux_lock_bus(struct i2c_adapter *adapter, int flags) +{ + struct i2c_mux_priv *priv = adapter->algo_data; + struct i2c_mux_core *muxc = priv->muxc; + struct i2c_adapter *parent = muxc->parent; + + rt_mutex_lock(&muxc->bus_lock); + if (!(flags & I2C_LOCK_ADAPTER)) + return; + i2c_lock_bus(parent, flags); +} + +static int i2c_mux_trylock_bus(struct i2c_adapter *adapter, int flags) +{ + struct i2c_mux_priv *priv = adapter->algo_data; + struct i2c_mux_core *muxc = priv->muxc; + struct i2c_adapter *pare
[PATCH v4 13/18] [media] cx231xx: convert to use an explicit i2c mux core
From: Peter Rosin Allocate an explicit i2c mux core to handle parent and child adapters etc. Update the select op to be in terms of the i2c mux core instead of the child adapter. Signed-off-by: Peter Rosin --- drivers/media/usb/cx231xx/cx231xx-core.c | 6 ++-- drivers/media/usb/cx231xx/cx231xx-i2c.c | 47 drivers/media/usb/cx231xx/cx231xx.h | 4 ++- 3 files changed, 31 insertions(+), 26 deletions(-) diff --git a/drivers/media/usb/cx231xx/cx231xx-core.c b/drivers/media/usb/cx231xx/cx231xx-core.c index f497888d94bf..f7aac2abd783 100644 --- a/drivers/media/usb/cx231xx/cx231xx-core.c +++ b/drivers/media/usb/cx231xx/cx231xx-core.c @@ -1304,6 +1304,9 @@ int cx231xx_dev_init(struct cx231xx *dev) cx231xx_i2c_register(&dev->i2c_bus[1]); cx231xx_i2c_register(&dev->i2c_bus[2]); + errCode = cx231xx_i2c_mux_create(dev); + if (errCode < 0) + return errCode; cx231xx_i2c_mux_register(dev, 0); cx231xx_i2c_mux_register(dev, 1); @@ -1426,8 +1429,7 @@ EXPORT_SYMBOL_GPL(cx231xx_dev_init); void cx231xx_dev_uninit(struct cx231xx *dev) { /* Un Initialize I2C bus */ - cx231xx_i2c_mux_unregister(dev, 1); - cx231xx_i2c_mux_unregister(dev, 0); + cx231xx_i2c_mux_unregister(dev); cx231xx_i2c_unregister(&dev->i2c_bus[2]); cx231xx_i2c_unregister(&dev->i2c_bus[1]); cx231xx_i2c_unregister(&dev->i2c_bus[0]); diff --git a/drivers/media/usb/cx231xx/cx231xx-i2c.c b/drivers/media/usb/cx231xx/cx231xx-i2c.c index a29c345b027d..eb22e05d4add 100644 --- a/drivers/media/usb/cx231xx/cx231xx-i2c.c +++ b/drivers/media/usb/cx231xx/cx231xx-i2c.c @@ -557,40 +557,41 @@ int cx231xx_i2c_unregister(struct cx231xx_i2c *bus) * cx231xx_i2c_mux_select() * switch i2c master number 1 between port1 and port3 */ -static int cx231xx_i2c_mux_select(struct i2c_adapter *adap, - void *mux_priv, u32 chan_id) +static int cx231xx_i2c_mux_select(struct i2c_mux_core *muxc, u32 chan_id) { - struct cx231xx *dev = mux_priv; + struct cx231xx *dev = i2c_mux_priv(muxc); return cx231xx_enable_i2c_port_3(dev, chan_id); } +int cx231xx_i2c_mux_create(struct cx231xx *dev) +{ + dev->muxc = i2c_mux_alloc(&dev->i2c_bus[1].i2c_adap, dev->dev, 0, 0, + cx231xx_i2c_mux_select, NULL); + if (!dev->muxc) + return -ENOMEM; + dev->muxc->priv = dev; + return 0; +} + int cx231xx_i2c_mux_register(struct cx231xx *dev, int mux_no) { - struct i2c_adapter *i2c_parent = &dev->i2c_bus[1].i2c_adap; - /* what is the correct mux_dev? */ - struct device *mux_dev = dev->dev; - - dev->i2c_mux_adap[mux_no] = i2c_add_mux_adapter(i2c_parent, - mux_dev, - dev /* mux_priv */, - 0, - mux_no /* chan_id */, - 0 /* class */, - &cx231xx_i2c_mux_select, - NULL); - - if (!dev->i2c_mux_adap[mux_no]) + int rc; + + rc = i2c_mux_add_adapter(dev->muxc, +0, +mux_no /* chan_id */, +0 /* class */); + if (rc) dev_warn(dev->dev, "i2c mux %d register FAILED\n", mux_no); - return 0; + return rc; } -void cx231xx_i2c_mux_unregister(struct cx231xx *dev, int mux_no) +void cx231xx_i2c_mux_unregister(struct cx231xx *dev) { - i2c_del_mux_adapter(dev->i2c_mux_adap[mux_no]); - dev->i2c_mux_adap[mux_no] = NULL; + i2c_mux_del_adapters(dev->muxc); } struct i2c_adapter *cx231xx_get_i2c_adap(struct cx231xx *dev, int i2c_port) @@ -603,9 +604,9 @@ struct i2c_adapter *cx231xx_get_i2c_adap(struct cx231xx *dev, int i2c_port) case I2C_2: return &dev->i2c_bus[2].i2c_adap; case I2C_1_MUX_1: - return dev->i2c_mux_adap[0]; + return dev->muxc->adapter[0]; case I2C_1_MUX_3: - return dev->i2c_mux_adap[1]; + return dev->muxc->adapter[1]; default: return NULL; } diff --git a/drivers/media/usb/cx231xx/cx231xx.h b/drivers/media/usb/cx231xx/cx231xx.h index ec6d3f5bc36d..fab222059e51 100644 --- a/drivers/media/usb/cx231xx/cx231xx.h +++ b/drivers/media/usb/cx231xx/cx231xx.h @@ -625,6 +625,7 @@ struct cx231xx { /* I2C adapters: Master 1 & 2 (External) & Master 3 (Internal only) */ struct cx231xx_i2c i2c_bus[3]; + struct i2c_mux_core *muxc; struct i2c_adapter *i2c_mux_adap[2]; unsigned int xc_fw_load_done:1; @@ -759,8 +760,9 @@ int cx231xx_reset_analog_t
[PATCH v4 15/18] i2c-mux: drop old unused i2c-mux api
From: Peter Rosin All i2c mux users are using an explicit i2c mux core, drop support for implicit i2c mux cores. Signed-off-by: Peter Rosin --- drivers/i2c/i2c-mux.c | 59 - include/linux/i2c-mux.h | 15 - 2 files changed, 74 deletions(-) diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c index 7df323a01661..e7ac7a4a7a83 100644 --- a/drivers/i2c/i2c-mux.c +++ b/drivers/i2c/i2c-mux.c @@ -28,12 +28,6 @@ #include /* multiplexer per channel data */ -struct i2c_mux_priv_old { - void *mux_priv; - int (*select)(struct i2c_adapter *, void *mux_priv, u32 chan_id); - int (*deselect)(struct i2c_adapter *, void *mux_priv, u32 chan_id); -}; - struct i2c_mux_priv { struct i2c_adapter adap; struct i2c_algorithm algo; @@ -290,48 +284,6 @@ struct i2c_mux_core *i2c_mux_one_adapter(struct i2c_adapter *parent, } EXPORT_SYMBOL_GPL(i2c_mux_one_adapter); -static int i2c_mux_select(struct i2c_mux_core *muxc, u32 chan) -{ - struct i2c_mux_priv_old *priv = i2c_mux_priv(muxc); - - return priv->select(muxc->parent, priv->mux_priv, chan); -} - -static int i2c_mux_deselect(struct i2c_mux_core *muxc, u32 chan) -{ - struct i2c_mux_priv_old *priv = i2c_mux_priv(muxc); - - return priv->deselect(muxc->parent, priv->mux_priv, chan); -} - -struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, - struct device *mux_dev, void *mux_priv, - u32 force_nr, u32 chan_id, - unsigned int class, - int (*select)(struct i2c_adapter *, - void *, u32), - int (*deselect)(struct i2c_adapter *, - void *, u32)) -{ - struct i2c_mux_core *muxc; - struct i2c_mux_priv_old *priv; - - muxc = i2c_mux_one_adapter(parent, mux_dev, sizeof(*priv), 0, - force_nr, chan_id, class, - i2c_mux_select, - deselect ? i2c_mux_deselect : NULL); - if (IS_ERR(muxc)) - return NULL; - - priv = i2c_mux_priv(muxc); - priv->select = select; - priv->deselect = deselect; - priv->mux_priv = mux_priv; - - return muxc->adapter[0]; -} -EXPORT_SYMBOL_GPL(i2c_add_mux_adapter); - void i2c_mux_del_adapters(struct i2c_mux_core *muxc) { char symlink_name[20]; @@ -353,17 +305,6 @@ void i2c_mux_del_adapters(struct i2c_mux_core *muxc) } EXPORT_SYMBOL_GPL(i2c_mux_del_adapters); -void i2c_del_mux_adapter(struct i2c_adapter *adap) -{ - struct i2c_mux_priv *priv = adap->algo_data; - struct i2c_mux_core *muxc = priv->muxc; - - i2c_mux_del_adapters(muxc); - devm_kfree(muxc->dev, muxc->adapter); - devm_kfree(muxc->dev, muxc); -} -EXPORT_SYMBOL_GPL(i2c_del_mux_adapter); - MODULE_AUTHOR("Rodolfo Giometti "); MODULE_DESCRIPTION("I2C driver for multiplexed I2C busses"); MODULE_LICENSE("GPL v2"); diff --git a/include/linux/i2c-mux.h b/include/linux/i2c-mux.h index 0d97d7a3f03c..25c88ccf9c38 100644 --- a/include/linux/i2c-mux.h +++ b/include/linux/i2c-mux.h @@ -55,20 +55,6 @@ int i2c_mux_reserve_adapters(struct i2c_mux_core *muxc, int adapters); /* * Called to create a i2c bus on a multiplexed bus segment. - * The mux_dev and chan_id parameters are passed to the select - * and deselect callback functions to perform hardware-specific - * mux control. - */ -struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, - struct device *mux_dev, - void *mux_priv, u32 force_nr, u32 chan_id, - unsigned int class, - int (*select) (struct i2c_adapter *, - void *mux_dev, u32 chan_id), - int (*deselect) (struct i2c_adapter *, -void *mux_dev, u32 chan_id)); -/* - * Called to create a i2c bus on a multiplexed bus segment. * The chan_id parameter is passed to the select and deselect * callback functions to perform hardware-specific mux control. */ @@ -88,7 +74,6 @@ struct i2c_mux_core *i2c_mux_one_adapter(struct i2c_adapter *parent, int (*deselect)(struct i2c_mux_core *, u32)); -void i2c_del_mux_adapter(struct i2c_adapter *adap); void i2c_mux_del_adapters(struct i2c_mux_core *muxc); #endif /* __KERNEL__ */ -- 2.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v4 14/18] of/unittest: convert to use an explicit i2c mux core
From: Peter Rosin Allocate an explicit i2c mux core to handle parent and child adapters etc. Update the select op to be in terms of the i2c mux core instead of the child adapter. Signed-off-by: Peter Rosin --- drivers/of/unittest.c | 40 +++- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c index 979b6e415cea..a6dc4b18047e 100644 --- a/drivers/of/unittest.c +++ b/drivers/of/unittest.c @@ -1687,13 +1687,7 @@ static struct i2c_driver unittest_i2c_dev_driver = { #if IS_BUILTIN(CONFIG_I2C_MUX) -struct unittest_i2c_mux_data { - int nchans; - struct i2c_adapter *adap[]; -}; - -static int unittest_i2c_mux_select_chan(struct i2c_adapter *adap, - void *client, u32 chan) +static int unittest_i2c_mux_select_chan(struct i2c_mux_core *muxc, u32 chan) { return 0; } @@ -1701,11 +1695,11 @@ static int unittest_i2c_mux_select_chan(struct i2c_adapter *adap, static int unittest_i2c_mux_probe(struct i2c_client *client, const struct i2c_device_id *id) { - int ret, i, nchans, size; + int ret, i, nchans; struct device *dev = &client->dev; struct i2c_adapter *adap = to_i2c_adapter(dev->parent); struct device_node *np = client->dev.of_node, *child; - struct unittest_i2c_mux_data *stm; + struct i2c_mux_core *muxc; u32 reg, max_reg; dev_dbg(dev, "%s for node @%s\n", __func__, np->full_name); @@ -1729,25 +1723,23 @@ static int unittest_i2c_mux_probe(struct i2c_client *client, return -EINVAL; } - size = offsetof(struct unittest_i2c_mux_data, adap[nchans]); - stm = devm_kzalloc(dev, size, GFP_KERNEL); - if (!stm) { - dev_err(dev, "Out of memory\n"); + muxc = i2c_mux_alloc(adap, dev, 0, 0, +unittest_i2c_mux_select_chan, NULL); + if (!muxc) return -ENOMEM; - } - stm->nchans = nchans; + ret = i2c_mux_reserve_adapters(muxc, nchans); + if (ret) + return ret; for (i = 0; i < nchans; i++) { - stm->adap[i] = i2c_add_mux_adapter(adap, dev, client, - 0, i, 0, unittest_i2c_mux_select_chan, NULL); - if (!stm->adap[i]) { + ret = i2c_mux_add_adapter(muxc, 0, i, 0); + if (ret) { dev_err(dev, "Failed to register mux #%d\n", i); - for (i--; i >= 0; i--) - i2c_del_mux_adapter(stm->adap[i]); + i2c_mux_del_adapters(muxc); return -ENODEV; } } - i2c_set_clientdata(client, stm); + i2c_set_clientdata(client, muxc); return 0; }; @@ -1756,12 +1748,10 @@ static int unittest_i2c_mux_remove(struct i2c_client *client) { struct device *dev = &client->dev; struct device_node *np = client->dev.of_node; - struct unittest_i2c_mux_data *stm = i2c_get_clientdata(client); - int i; + struct i2c_mux_core *muxc = i2c_get_clientdata(client); dev_dbg(dev, "%s for node @%s\n", __func__, np->full_name); - for (i = stm->nchans - 1; i >= 0; i--) - i2c_del_mux_adapter(stm->adap[i]); + i2c_mux_del_adapters(muxc); return 0; } -- 2.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v4 16/18] i2c: allow adapter drivers to override the adapter locking
From: Peter Rosin Add i2c_lock_bus() and i2c_unlock_bus(), which call the new lock_bus and unlock_bus ops in the adapter. These funcs/ops take an additional flags argument that indicates for what purpose the adapter is locked. There are two flags, I2C_LOCK_ADAPTER and I2C_LOCK_SEGMENT, but they are both implemented the same. For now. Locking the adapter means that the whole bus is locked, locking the segment means that only the current bus segment is locked (i.e. i2c traffic on the parent side of mux is still allowed even if the child side of the mux is locked. Also support a trylock_bus op (but no function to call it, as it is not expected to be needed outside of the i2c core). Implement i2c_lock_adapter/i2c_unlock_adapter in terms of the new locking scheme (i.e. lock with the I2C_LOCK_ADAPTER flag). Annotate some of the locking with explicit I2C_LOCK_SEGMENT flags. Signed-off-by: Peter Rosin --- drivers/i2c/i2c-core.c | 40 ++-- include/linux/i2c.h| 28 ++-- 2 files changed, 48 insertions(+), 20 deletions(-) diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index ffe715d346d8..946afd8e9f78 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -959,10 +959,10 @@ static int i2c_check_addr_busy(struct i2c_adapter *adapter, int addr) } /** - * i2c_lock_adapter - Get exclusive access to an I2C bus segment + * i2c_adapter_lock_bus - Get exclusive access to an I2C bus segment * @adapter: Target I2C bus segment */ -void i2c_lock_adapter(struct i2c_adapter *adapter) +static void i2c_adapter_lock_bus(struct i2c_adapter *adapter, int flags) { struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); @@ -971,27 +971,26 @@ void i2c_lock_adapter(struct i2c_adapter *adapter) else rt_mutex_lock(&adapter->bus_lock); } -EXPORT_SYMBOL_GPL(i2c_lock_adapter); /** - * i2c_trylock_adapter - Try to get exclusive access to an I2C bus segment + * i2c_adapter_trylock_bus - Try to get exclusive access to an I2C bus segment * @adapter: Target I2C bus segment */ -static int i2c_trylock_adapter(struct i2c_adapter *adapter) +static int i2c_adapter_trylock_bus(struct i2c_adapter *adapter, int flags) { struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); if (parent) - return i2c_trylock_adapter(parent); + return parent->trylock_bus(parent, flags); else return rt_mutex_trylock(&adapter->bus_lock); } /** - * i2c_unlock_adapter - Release exclusive access to an I2C bus segment + * i2c_adapter_unlock_bus - Release exclusive access to an I2C bus segment * @adapter: Target I2C bus segment */ -void i2c_unlock_adapter(struct i2c_adapter *adapter) +static void i2c_adapter_unlock_bus(struct i2c_adapter *adapter, int flags) { struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); @@ -1000,7 +999,6 @@ void i2c_unlock_adapter(struct i2c_adapter *adapter) else rt_mutex_unlock(&adapter->bus_lock); } -EXPORT_SYMBOL_GPL(i2c_unlock_adapter); static void i2c_dev_set_name(struct i2c_adapter *adap, struct i2c_client *client) @@ -1546,6 +1544,12 @@ static int i2c_register_adapter(struct i2c_adapter *adap) return -EINVAL; } + if (!adap->lock_bus) { + adap->lock_bus = i2c_adapter_lock_bus; + adap->trylock_bus = i2c_adapter_trylock_bus; + adap->unlock_bus = i2c_adapter_unlock_bus; + } + rt_mutex_init(&adap->bus_lock); mutex_init(&adap->userspace_clients_lock); INIT_LIST_HEAD(&adap->userspace_clients); @@ -2310,16 +2314,16 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) #endif if (in_atomic() || irqs_disabled()) { - ret = i2c_trylock_adapter(adap); + ret = adap->trylock_bus(adap, I2C_LOCK_SEGMENT); if (!ret) /* I2C activity is ongoing. */ return -EAGAIN; } else { - i2c_lock_adapter(adap); + i2c_lock_bus(adap, I2C_LOCK_SEGMENT); } ret = __i2c_transfer(adap, msgs, num); - i2c_unlock_adapter(adap); + i2c_unlock_bus(adap, I2C_LOCK_SEGMENT); return ret; } else { @@ -3094,7 +3098,7 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, flags &= I2C_M_TEN | I2C_CLIENT_PEC | I2C_CLIENT_SCCB; if (adapter->algo->smbus_xfer) { - i2c_lock_adapter(adapter); + i2c_lock_bus(adapter, I2C_LOCK_SEGMENT); /* Retry automatically on arbitration loss */ o
[PATCH v4 12/18] [media] si2168: convert to use an explicit i2c mux core
From: Peter Rosin Allocate an explicit i2c mux core to handle parent and child adapters etc. Update the select/deselect ops to be in terms of the i2c mux core instead of the child adapter. Signed-off-by: Peter Rosin --- drivers/media/dvb-frontends/si2168.c | 22 -- drivers/media/dvb-frontends/si2168_priv.h | 2 +- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c index 821a8f481507..ca455d01c71d 100644 --- a/drivers/media/dvb-frontends/si2168.c +++ b/drivers/media/dvb-frontends/si2168.c @@ -615,9 +615,9 @@ static int si2168_get_tune_settings(struct dvb_frontend *fe, * We must use unlocked I2C I/O because I2C adapter lock is already taken * by the caller (usually tuner driver). */ -static int si2168_select(struct i2c_adapter *adap, void *mux_priv, u32 chan) +static int si2168_select(struct i2c_mux_core *muxc, u32 chan) { - struct i2c_client *client = mux_priv; + struct i2c_client *client = i2c_mux_priv(muxc); int ret; struct si2168_cmd cmd; @@ -635,9 +635,9 @@ err: return ret; } -static int si2168_deselect(struct i2c_adapter *adap, void *mux_priv, u32 chan) +static int si2168_deselect(struct i2c_mux_core *muxc, u32 chan) { - struct i2c_client *client = mux_priv; + struct i2c_client *client = i2c_mux_priv(muxc); int ret; struct si2168_cmd cmd; @@ -709,17 +709,19 @@ static int si2168_probe(struct i2c_client *client, } /* create mux i2c adapter for tuner */ - dev->adapter = i2c_add_mux_adapter(client->adapter, &client->dev, - client, 0, 0, 0, si2168_select, si2168_deselect); - if (dev->adapter == NULL) { - ret = -ENODEV; + dev->muxc = i2c_mux_one_adapter(client->adapter, &client->dev, 0, 0, + 0, 0, 0, + si2168_select, si2168_deselect); + if (IS_ERR(dev->muxc)) { + ret = PTR_ERR(dev->muxc); goto err_kfree; } + dev->muxc->priv = client; /* create dvb_frontend */ memcpy(&dev->fe.ops, &si2168_ops, sizeof(struct dvb_frontend_ops)); dev->fe.demodulator_priv = client; - *config->i2c_adapter = dev->adapter; + *config->i2c_adapter = dev->muxc->adapter[0]; *config->fe = &dev->fe; dev->ts_mode = config->ts_mode; dev->ts_clock_inv = config->ts_clock_inv; @@ -743,7 +745,7 @@ static int si2168_remove(struct i2c_client *client) dev_dbg(&client->dev, "\n"); - i2c_del_mux_adapter(dev->adapter); + i2c_mux_del_adapters(dev->muxc); dev->fe.ops.release = NULL; dev->fe.demodulator_priv = NULL; diff --git a/drivers/media/dvb-frontends/si2168_priv.h b/drivers/media/dvb-frontends/si2168_priv.h index c07e6fe2cb10..165bf1412063 100644 --- a/drivers/media/dvb-frontends/si2168_priv.h +++ b/drivers/media/dvb-frontends/si2168_priv.h @@ -29,7 +29,7 @@ /* state struct */ struct si2168_dev { - struct i2c_adapter *adapter; + struct i2c_mux_core *muxc; struct dvb_frontend fe; enum fe_delivery_system delivery_system; enum fe_status fe_status; -- 2.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v4 08/18] iio: imu: inv_mpu6050: convert to use an explicit i2c mux core
From: Peter Rosin Allocate an explicit i2c mux core to handle parent and child adapters etc. Update the select/deselect ops to be in terms of the i2c mux core instead of the child adapter. Signed-off-by: Peter Rosin --- drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c | 2 +- drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 30 +- drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h | 3 ++- 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c index 1c982a56acd5..d433e7b64011 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c @@ -182,7 +182,7 @@ int inv_mpu_acpi_create_mux_client(struct inv_mpu6050_state *st) } else return 0; /* no secondary addr, which is OK */ } - st->mux_client = i2c_new_device(st->mux_adapter, &info); + st->mux_client = i2c_new_device(st->muxc->adapter[0], &info); if (!st->mux_client) return -ENODEV; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index f0e06093b5e8..642f22013d17 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include "inv_mpu_iio.h" @@ -109,10 +108,9 @@ static int inv_mpu6050_write_reg_unlocked(struct inv_mpu6050_state *st, return 0; } -static int inv_mpu6050_select_bypass(struct i2c_adapter *adap, void *mux_priv, -u32 chan_id) +static int inv_mpu6050_select_bypass(struct i2c_mux_core *muxc, u32 chan_id) { - struct iio_dev *indio_dev = mux_priv; + struct iio_dev *indio_dev = i2c_mux_priv(muxc); struct inv_mpu6050_state *st = iio_priv(indio_dev); int ret = 0; @@ -138,10 +136,9 @@ write_error: return ret; } -static int inv_mpu6050_deselect_bypass(struct i2c_adapter *adap, - void *mux_priv, u32 chan_id) +static int inv_mpu6050_deselect_bypass(struct i2c_mux_core *muxc, u32 chan_id) { - struct iio_dev *indio_dev = mux_priv; + struct iio_dev *indio_dev = i2c_mux_priv(muxc); struct inv_mpu6050_state *st = iio_priv(indio_dev); mutex_lock(&indio_dev->mlock); @@ -842,16 +839,15 @@ static int inv_mpu_probe(struct i2c_client *client, goto out_remove_trigger; } - st->mux_adapter = i2c_add_mux_adapter(client->adapter, - &client->dev, - indio_dev, - 0, 0, 0, - inv_mpu6050_select_bypass, - inv_mpu6050_deselect_bypass); - if (!st->mux_adapter) { - result = -ENODEV; + st->muxc = i2c_mux_one_adapter(client->adapter, &client->dev, 0, 0, + 0, 0, 0, + inv_mpu6050_select_bypass, + inv_mpu6050_deselect_bypass); + if (IS_ERR(st->muxc)) { + result = PTR_ERR(st->muxc); goto out_unreg_device; } + st->muxc->priv = indio_dev; result = inv_mpu_acpi_create_mux_client(st); if (result) @@ -860,7 +856,7 @@ static int inv_mpu_probe(struct i2c_client *client, return 0; out_del_mux: - i2c_del_mux_adapter(st->mux_adapter); + i2c_mux_del_adapters(st->muxc); out_unreg_device: iio_device_unregister(indio_dev); out_remove_trigger: @@ -876,7 +872,7 @@ static int inv_mpu_remove(struct i2c_client *client) struct inv_mpu6050_state *st = iio_priv(indio_dev); inv_mpu_acpi_delete_mux_client(st); - i2c_del_mux_adapter(st->mux_adapter); + i2c_mux_del_adapters(st->muxc); iio_device_unregister(indio_dev); inv_mpu6050_remove_trigger(st); iio_triggered_buffer_cleanup(indio_dev); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h index db0a4a2758ab..61a3a04b84b8 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -11,6 +11,7 @@ * GNU General Public License for more details. */ #include +#include #include #include #include @@ -120,7 +121,7 @@ struct inv_mpu6050_state { enum inv_devices chip_type; spinlock_t time_stamp_lock; struct i2c_client *client; - struct i2c_adapter *mux_adapter; + struct i2c_mux_core *muxc; struct i2c_client *mux_client; unsigned int powerup_count; s
[PATCH v4 10/18] [media] rtl2830: convert to use an explicit i2c mux core
From: Peter Rosin Allocate an explicit i2c mux core to handle parent and child adapters etc. Update the select op to be in terms of the i2c mux core instead of the child adapter. Signed-off-by: Peter Rosin --- drivers/media/dvb-frontends/rtl2830.c | 17 + drivers/media/dvb-frontends/rtl2830_priv.h | 2 +- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/media/dvb-frontends/rtl2830.c b/drivers/media/dvb-frontends/rtl2830.c index b792f305cf15..0fa60fe09b81 100644 --- a/drivers/media/dvb-frontends/rtl2830.c +++ b/drivers/media/dvb-frontends/rtl2830.c @@ -677,9 +677,9 @@ err: * adapter lock is already taken by tuner driver. * Gate is closed automatically after single I2C transfer. */ -static int rtl2830_select(struct i2c_adapter *adap, void *mux_priv, u32 chan_id) +static int rtl2830_select(struct i2c_mux_core *muxc, u32 chan_id) { - struct i2c_client *client = mux_priv; + struct i2c_client *client = i2c_mux_priv(muxc); struct rtl2830_dev *dev = i2c_get_clientdata(client); int ret; @@ -712,7 +712,7 @@ static struct i2c_adapter *rtl2830_get_i2c_adapter(struct i2c_client *client) dev_dbg(&client->dev, "\n"); - return dev->adapter; + return dev->muxc->adapter[0]; } /* @@ -865,12 +865,13 @@ static int rtl2830_probe(struct i2c_client *client, goto err_regmap_exit; /* create muxed i2c adapter for tuner */ - dev->adapter = i2c_add_mux_adapter(client->adapter, &client->dev, - client, 0, 0, 0, rtl2830_select, NULL); - if (dev->adapter == NULL) { - ret = -ENODEV; + dev->muxc = i2c_mux_one_adapter(client->adapter, &client->dev, 0, 0, + 0, 0, 0, rtl2830_select, NULL); + if (IS_ERR(dev->muxc)) { + ret = PTR_ERR(dev->muxc); goto err_regmap_exit; } + dev->muxc->priv = client; /* create dvb frontend */ memcpy(&dev->fe.ops, &rtl2830_ops, sizeof(dev->fe.ops)); @@ -900,7 +901,7 @@ static int rtl2830_remove(struct i2c_client *client) dev_dbg(&client->dev, "\n"); - i2c_del_mux_adapter(dev->adapter); + i2c_mux_del_adapters(dev->muxc); regmap_exit(dev->regmap); kfree(dev); diff --git a/drivers/media/dvb-frontends/rtl2830_priv.h b/drivers/media/dvb-frontends/rtl2830_priv.h index cf793f39a09b..da4909543da2 100644 --- a/drivers/media/dvb-frontends/rtl2830_priv.h +++ b/drivers/media/dvb-frontends/rtl2830_priv.h @@ -29,7 +29,7 @@ struct rtl2830_dev { struct rtl2830_platform_data *pdata; struct i2c_client *client; struct regmap *regmap; - struct i2c_adapter *adapter; + struct i2c_mux_core *muxc; struct dvb_frontend fe; bool sleeping; unsigned long filters; -- 2.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v4 11/18] [media] rtl2832: convert to use an explicit i2c mux core
From: Peter Rosin Allocate an explicit i2c mux core to handle parent and child adapters etc. Update the select/deselect ops to be in terms of the i2c mux core instead of the child adapter. Signed-off-by: Peter Rosin --- drivers/media/dvb-frontends/rtl2832.c | 22 +++--- drivers/media/dvb-frontends/rtl2832_priv.h | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c index 10f2119935da..775444898599 100644 --- a/drivers/media/dvb-frontends/rtl2832.c +++ b/drivers/media/dvb-frontends/rtl2832.c @@ -866,9 +866,9 @@ err: dev_dbg(&client->dev, "failed=%d\n", ret); } -static int rtl2832_select(struct i2c_adapter *adap, void *mux_priv, u32 chan_id) +static int rtl2832_select(struct i2c_mux_core *muxc, u32 chan_id) { - struct rtl2832_dev *dev = mux_priv; + struct rtl2832_dev *dev = i2c_mux_priv(muxc); struct i2c_client *client = dev->client; int ret; @@ -889,10 +889,9 @@ err: return ret; } -static int rtl2832_deselect(struct i2c_adapter *adap, void *mux_priv, - u32 chan_id) +static int rtl2832_deselect(struct i2c_mux_core *muxc, u32 chan_id) { - struct rtl2832_dev *dev = mux_priv; + struct rtl2832_dev *dev = i2c_mux_priv(muxc); schedule_delayed_work(&dev->i2c_gate_work, usecs_to_jiffies(100)); return 0; @@ -1078,7 +1077,7 @@ static struct i2c_adapter *rtl2832_get_i2c_adapter(struct i2c_client *client) struct rtl2832_dev *dev = i2c_get_clientdata(client); dev_dbg(&client->dev, "\n"); - return dev->i2c_adapter_tuner; + return dev->muxc->adapter[0]; } static int rtl2832_enable_slave_ts(struct i2c_client *client) @@ -1253,12 +1252,13 @@ static int rtl2832_probe(struct i2c_client *client, goto err_regmap_exit; /* create muxed i2c adapter for demod tuner bus */ - dev->i2c_adapter_tuner = i2c_add_mux_adapter(i2c, &i2c->dev, dev, - 0, 0, 0, rtl2832_select, rtl2832_deselect); - if (dev->i2c_adapter_tuner == NULL) { - ret = -ENODEV; + dev->muxc = i2c_mux_one_adapter(i2c, &i2c->dev, 0, 0, 0, 0, 0, + rtl2832_select, rtl2832_deselect); + if (IS_ERR(dev->muxc)) { + ret = PTR_ERR(dev->muxc); goto err_regmap_exit; } + dev->muxc->priv = dev; /* create dvb_frontend */ memcpy(&dev->fe.ops, &rtl2832_ops, sizeof(struct dvb_frontend_ops)); @@ -1293,7 +1293,7 @@ static int rtl2832_remove(struct i2c_client *client) cancel_delayed_work_sync(&dev->i2c_gate_work); - i2c_del_mux_adapter(dev->i2c_adapter_tuner); + i2c_mux_del_adapters(dev->muxc); regmap_exit(dev->regmap); diff --git a/drivers/media/dvb-frontends/rtl2832_priv.h b/drivers/media/dvb-frontends/rtl2832_priv.h index 5dcd3a41d23f..6b3cd23a2c26 100644 --- a/drivers/media/dvb-frontends/rtl2832_priv.h +++ b/drivers/media/dvb-frontends/rtl2832_priv.h @@ -36,7 +36,7 @@ struct rtl2832_dev { struct mutex regmap_mutex; struct regmap_config regmap_config; struct regmap *regmap; - struct i2c_adapter *i2c_adapter_tuner; + struct i2c_mux_core *muxc; struct dvb_frontend fe; struct delayed_work stat_work; enum fe_status fe_status; -- 2.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v4 03/18] i2c: i2c-mux-pinctrl: convert to use an explicit i2c mux core
From: Peter Rosin Allocate an explicit i2c mux core to handle parent and child adapters etc. Update the select/deselect ops to be in terms of the i2c mux core instead of the child adapter. Signed-off-by: Peter Rosin --- drivers/i2c/muxes/i2c-mux-pinctrl.c | 86 + 1 file changed, 30 insertions(+), 56 deletions(-) diff --git a/drivers/i2c/muxes/i2c-mux-pinctrl.c b/drivers/i2c/muxes/i2c-mux-pinctrl.c index b5a982ba8898..bbfabf4f52be 100644 --- a/drivers/i2c/muxes/i2c-mux-pinctrl.c +++ b/drivers/i2c/muxes/i2c-mux-pinctrl.c @@ -26,27 +26,22 @@ #include struct i2c_mux_pinctrl { - struct device *dev; struct i2c_mux_pinctrl_platform_data *pdata; struct pinctrl *pinctrl; struct pinctrl_state **states; struct pinctrl_state *state_idle; - struct i2c_adapter *parent; - struct i2c_adapter **busses; }; -static int i2c_mux_pinctrl_select(struct i2c_adapter *adap, void *data, - u32 chan) +static int i2c_mux_pinctrl_select(struct i2c_mux_core *muxc, u32 chan) { - struct i2c_mux_pinctrl *mux = data; + struct i2c_mux_pinctrl *mux = i2c_mux_priv(muxc); return pinctrl_select_state(mux->pinctrl, mux->states[chan]); } -static int i2c_mux_pinctrl_deselect(struct i2c_adapter *adap, void *data, - u32 chan) +static int i2c_mux_pinctrl_deselect(struct i2c_mux_core *muxc, u32 chan) { - struct i2c_mux_pinctrl *mux = data; + struct i2c_mux_pinctrl *mux = i2c_mux_priv(muxc); return pinctrl_select_state(mux->pinctrl, mux->state_idle); } @@ -55,6 +50,7 @@ static int i2c_mux_pinctrl_deselect(struct i2c_adapter *adap, void *data, static int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux, struct platform_device *pdev) { + struct i2c_mux_core *muxc = platform_get_drvdata(pdev); struct device_node *np = pdev->dev.of_node; int num_names, i, ret; struct device_node *adapter_np; @@ -64,15 +60,12 @@ static int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux, return 0; mux->pdata = devm_kzalloc(&pdev->dev, sizeof(*mux->pdata), GFP_KERNEL); - if (!mux->pdata) { - dev_err(mux->dev, - "Cannot allocate i2c_mux_pinctrl_platform_data\n"); + if (!mux->pdata) return -ENOMEM; - } num_names = of_property_count_strings(np, "pinctrl-names"); if (num_names < 0) { - dev_err(mux->dev, "Cannot parse pinctrl-names: %d\n", + dev_err(muxc->dev, "Cannot parse pinctrl-names: %d\n", num_names); return num_names; } @@ -80,23 +73,21 @@ static int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux, mux->pdata->pinctrl_states = devm_kzalloc(&pdev->dev, sizeof(*mux->pdata->pinctrl_states) * num_names, GFP_KERNEL); - if (!mux->pdata->pinctrl_states) { - dev_err(mux->dev, "Cannot allocate pinctrl_states\n"); + if (!mux->pdata->pinctrl_states) return -ENOMEM; - } for (i = 0; i < num_names; i++) { ret = of_property_read_string_index(np, "pinctrl-names", i, &mux->pdata->pinctrl_states[mux->pdata->bus_count]); if (ret < 0) { - dev_err(mux->dev, "Cannot parse pinctrl-names: %d\n", + dev_err(muxc->dev, "Cannot parse pinctrl-names: %d\n", ret); return ret; } if (!strcmp(mux->pdata->pinctrl_states[mux->pdata->bus_count], "idle")) { if (i != num_names - 1) { - dev_err(mux->dev, "idle state must be last\n"); + dev_err(muxc->dev, "idle state must be last\n"); return -EINVAL; } mux->pdata->pinctrl_state_idle = "idle"; @@ -107,13 +98,13 @@ static int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux, adapter_np = of_parse_phandle(np, "i2c-parent", 0); if (!adapter_np) { - dev_err(mux->dev, "Cannot parse i2c-parent\n"); + dev_err(muxc->dev, "Cannot parse i2c-parent\n"); return -ENODEV; } adapter = of_find_i2c_adapter_by_node(adapter_np); of_node_put(adapter_np); if (!adapter) { - dev_err(mux->dev, "Cannot find parent bus\n"); +
[PATCH v4 02/18] i2c: i2c-mux-gpio: convert to use an explicit i2c mux core
From: Peter Rosin Allocate an explicit i2c mux core to handle parent and child adapters etc. Update the select/deselect ops to be in terms of the i2c mux core instead of the child adapter. Signed-off-by: Peter Rosin --- drivers/i2c/muxes/i2c-mux-gpio.c | 54 1 file changed, 21 insertions(+), 33 deletions(-) diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c index b8e11c16d98c..1bcc26737359 100644 --- a/drivers/i2c/muxes/i2c-mux-gpio.c +++ b/drivers/i2c/muxes/i2c-mux-gpio.c @@ -18,8 +18,6 @@ #include struct gpiomux { - struct i2c_adapter *parent; - struct i2c_adapter **adap; /* child busses */ struct i2c_mux_gpio_platform_data data; unsigned gpio_base; }; @@ -33,18 +31,18 @@ static void i2c_mux_gpio_set(const struct gpiomux *mux, unsigned val) val & (1 << i)); } -static int i2c_mux_gpio_select(struct i2c_adapter *adap, void *data, u32 chan) +static int i2c_mux_gpio_select(struct i2c_mux_core *muxc, u32 chan) { - struct gpiomux *mux = data; + struct gpiomux *mux = i2c_mux_priv(muxc); i2c_mux_gpio_set(mux, chan); return 0; } -static int i2c_mux_gpio_deselect(struct i2c_adapter *adap, void *data, u32 chan) +static int i2c_mux_gpio_deselect(struct i2c_mux_core *muxc, u32 chan) { - struct gpiomux *mux = data; + struct gpiomux *mux = i2c_mux_priv(muxc); i2c_mux_gpio_set(mux, mux->data.idle); @@ -136,19 +134,19 @@ static int i2c_mux_gpio_probe_dt(struct gpiomux *mux, static int i2c_mux_gpio_probe(struct platform_device *pdev) { + struct i2c_mux_core *muxc; struct gpiomux *mux; struct i2c_adapter *parent; - int (*deselect) (struct i2c_adapter *, void *, u32); unsigned initial_state, gpio_base; int i, ret; - mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL); - if (!mux) { - dev_err(&pdev->dev, "Cannot allocate gpiomux structure"); + muxc = i2c_mux_alloc(NULL, &pdev->dev, sizeof(*mux), 0, +i2c_mux_gpio_select, NULL); + if (!muxc) return -ENOMEM; - } + mux = i2c_mux_priv(muxc); - platform_set_drvdata(pdev, mux); + platform_set_drvdata(pdev, muxc); if (!dev_get_platdata(&pdev->dev)) { ret = i2c_mux_gpio_probe_dt(mux, pdev); @@ -180,24 +178,18 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev) if (!parent) return -EPROBE_DEFER; - mux->parent = parent; + muxc->parent = parent; mux->gpio_base = gpio_base; - mux->adap = devm_kzalloc(&pdev->dev, -sizeof(*mux->adap) * mux->data.n_values, -GFP_KERNEL); - if (!mux->adap) { - dev_err(&pdev->dev, "Cannot allocate i2c_adapter structure"); - ret = -ENOMEM; + ret = i2c_mux_reserve_adapters(muxc, mux->data.n_values); + if (ret) goto alloc_failed; - } if (mux->data.idle != I2C_MUX_GPIO_NO_IDLE) { initial_state = mux->data.idle; - deselect = i2c_mux_gpio_deselect; + muxc->deselect = i2c_mux_gpio_deselect; } else { initial_state = mux->data.values[0]; - deselect = NULL; } for (i = 0; i < mux->data.n_gpios; i++) { @@ -223,11 +215,8 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev) u32 nr = mux->data.base_nr ? (mux->data.base_nr + i) : 0; unsigned int class = mux->data.classes ? mux->data.classes[i] : 0; - mux->adap[i] = i2c_add_mux_adapter(parent, &pdev->dev, mux, nr, - mux->data.values[i], class, - i2c_mux_gpio_select, deselect); - if (!mux->adap[i]) { - ret = -ENODEV; + ret = i2c_mux_add_adapter(muxc, nr, mux->data.values[i], class); + if (ret) { dev_err(&pdev->dev, "Failed to add adapter %d\n", i); goto add_adapter_failed; } @@ -239,8 +228,7 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev) return 0; add_adapter_failed: - for (; i > 0; i--) - i2c_del_mux_adapter(mux->adap[i - 1]); + i2c_mux_del_adapters(muxc); i = mux->data.n_gpios; err_request_gpio: for (; i > 0; i--) @@ -253,16 +241,16 @@ alloc_failed: static int i2c_mux_gpio_remove(struct platform_device *pdev) { - struct gpiomux *mux = platform_get_drvdata(pdev);
[PATCH v4 05/18] i2c: i2c-mux-pca9541: convert to use an explicit i2c mux core
From: Peter Rosin Allocate an explicit i2c mux core to handle parent and child adapters etc. Update the select/deselect ops to be in terms of the i2c mux core instead of the child adapter. Signed-off-by: Peter Rosin --- drivers/i2c/muxes/i2c-mux-pca9541.c | 55 - 1 file changed, 23 insertions(+), 32 deletions(-) diff --git a/drivers/i2c/muxes/i2c-mux-pca9541.c b/drivers/i2c/muxes/i2c-mux-pca9541.c index d0ba424adebc..93bea073ed13 100644 --- a/drivers/i2c/muxes/i2c-mux-pca9541.c +++ b/drivers/i2c/muxes/i2c-mux-pca9541.c @@ -73,7 +73,7 @@ #define SELECT_DELAY_LONG 1000 struct pca9541 { - struct i2c_adapter *mux_adap; + struct i2c_client *client; unsigned long select_timeout; unsigned long arb_timeout; }; @@ -217,7 +217,8 @@ static const u8 pca9541_control[16] = { */ static int pca9541_arbitrate(struct i2c_client *client) { - struct pca9541 *data = i2c_get_clientdata(client); + struct i2c_mux_core *muxc = i2c_get_clientdata(client); + struct pca9541 *data = i2c_mux_priv(muxc); int reg; reg = pca9541_reg_read(client, PCA9541_CONTROL); @@ -285,9 +286,10 @@ static int pca9541_arbitrate(struct i2c_client *client) return 0; } -static int pca9541_select_chan(struct i2c_adapter *adap, void *client, u32 chan) +static int pca9541_select_chan(struct i2c_mux_core *muxc, u32 chan) { - struct pca9541 *data = i2c_get_clientdata(client); + struct pca9541 *data = i2c_mux_priv(muxc); + struct i2c_client *client = data->client; int ret; unsigned long timeout = jiffies + ARB2_TIMEOUT; /* give up after this time */ @@ -309,9 +311,11 @@ static int pca9541_select_chan(struct i2c_adapter *adap, void *client, u32 chan) return -ETIMEDOUT; } -static int pca9541_release_chan(struct i2c_adapter *adap, - void *client, u32 chan) +static int pca9541_release_chan(struct i2c_mux_core *muxc, u32 chan) { + struct pca9541 *data = i2c_mux_priv(muxc); + struct i2c_client *client = data->client; + pca9541_release_bus(client); return 0; } @@ -324,20 +328,12 @@ static int pca9541_probe(struct i2c_client *client, { struct i2c_adapter *adap = client->adapter; struct pca954x_platform_data *pdata = dev_get_platdata(&client->dev); + struct i2c_mux_core *muxc; struct pca9541 *data; int force; - int ret = -ENODEV; if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE_DATA)) - goto err; - - data = kzalloc(sizeof(struct pca9541), GFP_KERNEL); - if (!data) { - ret = -ENOMEM; - goto err; - } - - i2c_set_clientdata(client, data); + return -ENODEV; /* * I2C accesses are unprotected here. @@ -352,34 +348,29 @@ static int pca9541_probe(struct i2c_client *client, force = 0; if (pdata) force = pdata->modes[0].adap_id; - data->mux_adap = i2c_add_mux_adapter(adap, &client->dev, client, -force, 0, 0, -pca9541_select_chan, -pca9541_release_chan); - - if (data->mux_adap == NULL) { + muxc = i2c_mux_one_adapter(adap, &client->dev, sizeof(*data), 0, + force, 0, 0, + pca9541_select_chan, pca9541_release_chan); + if (IS_ERR(muxc)) { dev_err(&client->dev, "failed to register master selector\n"); - goto exit_free; + return PTR_ERR(muxc); } + data = i2c_mux_priv(muxc); + data->client = client; + + i2c_set_clientdata(client, muxc); dev_info(&client->dev, "registered master selector for I2C %s\n", client->name); return 0; - -exit_free: - kfree(data); -err: - return ret; } static int pca9541_remove(struct i2c_client *client) { - struct pca9541 *data = i2c_get_clientdata(client); - - i2c_del_mux_adapter(data->mux_adap); + struct i2c_mux_core *muxc = i2c_get_clientdata(client); - kfree(data); + i2c_mux_del_adapters(muxc); return 0; } -- 2.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v4 09/18] [media] m88ds3103: convert to use an explicit i2c mux core
From: Peter Rosin Allocate an explicit i2c mux core to handle parent and child adapters etc. Update the select op to be in terms of the i2c mux core instead of the child adapter. Signed-off-by: Peter Rosin --- drivers/media/dvb-frontends/m88ds3103.c | 18 +- drivers/media/dvb-frontends/m88ds3103_priv.h | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c index ce73a5ec6036..845d206eb70f 100644 --- a/drivers/media/dvb-frontends/m88ds3103.c +++ b/drivers/media/dvb-frontends/m88ds3103.c @@ -1251,9 +1251,9 @@ static void m88ds3103_release(struct dvb_frontend *fe) i2c_unregister_device(client); } -static int m88ds3103_select(struct i2c_adapter *adap, void *mux_priv, u32 chan) +static int m88ds3103_select(struct i2c_mux_core *muxc, u32 chan) { - struct m88ds3103_dev *dev = mux_priv; + struct m88ds3103_dev *dev = i2c_mux_priv(muxc); struct i2c_client *client = dev->client; int ret; struct i2c_msg msg = { @@ -1374,7 +1374,7 @@ static struct i2c_adapter *m88ds3103_get_i2c_adapter(struct i2c_client *client) dev_dbg(&client->dev, "\n"); - return dev->i2c_adapter; + return dev->muxc->adapter[0]; } static int m88ds3103_probe(struct i2c_client *client, @@ -1467,13 +1467,13 @@ static int m88ds3103_probe(struct i2c_client *client, goto err_kfree; /* create mux i2c adapter for tuner */ - dev->i2c_adapter = i2c_add_mux_adapter(client->adapter, &client->dev, - dev, 0, 0, 0, m88ds3103_select, - NULL); - if (dev->i2c_adapter == NULL) { - ret = -ENOMEM; + dev->muxc = i2c_mux_one_adapter(client->adapter, &client->dev, 0, 0, + 0, 0, 0, m88ds3103_select, NULL); + if (IS_ERR(dev->muxc)) { + ret = PTR_ERR(dev->muxc); goto err_kfree; } + dev->muxc->priv = dev; /* create dvb_frontend */ memcpy(&dev->fe.ops, &m88ds3103_ops, sizeof(struct dvb_frontend_ops)); @@ -1502,7 +1502,7 @@ static int m88ds3103_remove(struct i2c_client *client) dev_dbg(&client->dev, "\n"); - i2c_del_mux_adapter(dev->i2c_adapter); + i2c_mux_del_adapters(dev->muxc); kfree(dev); return 0; diff --git a/drivers/media/dvb-frontends/m88ds3103_priv.h b/drivers/media/dvb-frontends/m88ds3103_priv.h index eee8c22c51ec..c5b4e177c6ea 100644 --- a/drivers/media/dvb-frontends/m88ds3103_priv.h +++ b/drivers/media/dvb-frontends/m88ds3103_priv.h @@ -42,7 +42,7 @@ struct m88ds3103_dev { enum fe_status fe_status; u32 dvbv3_ber; /* for old DVBv3 API read_ber */ bool warm; /* FW running */ - struct i2c_adapter *i2c_adapter; + struct i2c_mux_core *muxc; /* auto detect chip id to do different config */ u8 chip_id; /* main mclk is calculated for M88RS6000 dynamically */ -- 2.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v4 04/18] i2c: i2c-arb-gpio-challenge: convert to use an explicit i2c mux core
From: Peter Rosin Allocate an explicit i2c mux core to handle parent and child adapters etc. Update the select/deselect ops to be in terms of the i2c mux core instead of the child adapter. Signed-off-by: Peter Rosin --- drivers/i2c/muxes/i2c-arb-gpio-challenge.c | 47 +- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/drivers/i2c/muxes/i2c-arb-gpio-challenge.c b/drivers/i2c/muxes/i2c-arb-gpio-challenge.c index 402e3a6c671a..a42827b3c672 100644 --- a/drivers/i2c/muxes/i2c-arb-gpio-challenge.c +++ b/drivers/i2c/muxes/i2c-arb-gpio-challenge.c @@ -28,8 +28,6 @@ /** * struct i2c_arbitrator_data - Driver data for I2C arbitrator * - * @parent: Parent adapter - * @child: Child bus * @our_gpio: GPIO we'll use to claim. * @our_gpio_release: 0 if active high; 1 if active low; AKA if the GPIO == * this then consider it released. @@ -42,8 +40,6 @@ */ struct i2c_arbitrator_data { - struct i2c_adapter *parent; - struct i2c_adapter *child; int our_gpio; int our_gpio_release; int their_gpio; @@ -59,9 +55,9 @@ struct i2c_arbitrator_data { * * Use the GPIO-based signalling protocol; return -EBUSY if we fail. */ -static int i2c_arbitrator_select(struct i2c_adapter *adap, void *data, u32 chan) +static int i2c_arbitrator_select(struct i2c_mux_core *muxc, u32 chan) { - const struct i2c_arbitrator_data *arb = data; + const struct i2c_arbitrator_data *arb = i2c_mux_priv(muxc); unsigned long stop_retry, stop_time; /* Start a round of trying to claim the bus */ @@ -93,7 +89,7 @@ static int i2c_arbitrator_select(struct i2c_adapter *adap, void *data, u32 chan) /* Give up, release our claim */ gpio_set_value(arb->our_gpio, arb->our_gpio_release); udelay(arb->slew_delay_us); - dev_err(&adap->dev, "Could not claim bus, timeout\n"); + dev_err(muxc->dev, "Could not claim bus, timeout\n"); return -EBUSY; } @@ -102,10 +98,9 @@ static int i2c_arbitrator_select(struct i2c_adapter *adap, void *data, u32 chan) * * Release the I2C bus using the GPIO-based signalling protocol. */ -static int i2c_arbitrator_deselect(struct i2c_adapter *adap, void *data, - u32 chan) +static int i2c_arbitrator_deselect(struct i2c_mux_core *muxc, u32 chan) { - const struct i2c_arbitrator_data *arb = data; + const struct i2c_arbitrator_data *arb = i2c_mux_priv(muxc); /* Release the bus and wait for the other master to notice */ gpio_set_value(arb->our_gpio, arb->our_gpio_release); @@ -119,6 +114,7 @@ static int i2c_arbitrator_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct device_node *parent_np; + struct i2c_mux_core *muxc; struct i2c_arbitrator_data *arb; enum of_gpio_flags gpio_flags; unsigned long out_init; @@ -134,12 +130,13 @@ static int i2c_arbitrator_probe(struct platform_device *pdev) return -EINVAL; } - arb = devm_kzalloc(dev, sizeof(*arb), GFP_KERNEL); - if (!arb) { - dev_err(dev, "Cannot allocate i2c_arbitrator_data\n"); + muxc = i2c_mux_alloc(NULL, dev, sizeof(*arb), 0, +i2c_arbitrator_select, i2c_arbitrator_deselect); + if (!muxc) return -ENOMEM; - } - platform_set_drvdata(pdev, arb); + arb = i2c_mux_priv(muxc); + + platform_set_drvdata(pdev, muxc); /* Request GPIOs */ ret = of_get_named_gpio_flags(np, "our-claim-gpio", 0, &gpio_flags); @@ -196,21 +193,18 @@ static int i2c_arbitrator_probe(struct platform_device *pdev) dev_err(dev, "Cannot parse i2c-parent\n"); return -EINVAL; } - arb->parent = of_get_i2c_adapter_by_node(parent_np); + muxc->parent = of_get_i2c_adapter_by_node(parent_np); of_node_put(parent_np); - if (!arb->parent) { + if (!muxc->parent) { dev_err(dev, "Cannot find parent bus\n"); return -EPROBE_DEFER; } /* Actually add the mux adapter */ - arb->child = i2c_add_mux_adapter(arb->parent, dev, arb, 0, 0, 0, -i2c_arbitrator_select, -i2c_arbitrator_deselect); - if (!arb->child) { + ret = i2c_mux_add_adapter(muxc, 0, 0, 0); + if (ret) { dev_err(dev, "Failed to add adapter\n"); - ret = -ENODEV; - i2c_put_adapter(arb->parent); + i2c_put_adapter(muxc->parent); } return ret; @@ -218,11 +212,10 @@ static int i2c_arbitrator_probe(struct platform_device *pdev) static int i2c_arbitrator_re
[PATCH v4 00/18] i2c mux cleanup and locking update
From: Peter Rosin Hi! I have a pair of boards with this i2c topology: GPIO ---| -- BAT1 | v / I2C -+--B---+ MUX | \ EEPROM -- BAT2 (B denotes the boundary between the boards) The problem with this is that the GPIO controller sits on the same i2c bus that it MUXes. For pca954x devices this is worked around by using unlocked transfers when updating the MUX. I have no such luck as the GPIO is a general purpose IO expander and the MUX is just a random bidirectional MUX, unaware of the fact that it is muxing an i2c bus. Extending unlocked transfers into the GPIO subsystem is too ugly to even think about. But the general hw approach is sane in my opinion, with the number of connections between the two boards minimized. To put is plainly, I need support for it. So, I observe that while it is needed to have the i2c bus locked during the actual MUX update in order to avoid random garbage on the slave side, it is not strictly a must to have it locked over the whole sequence of a full select-transfer-deselect operation. The MUX itself needs to be locked, so transfers to clients behind the mux are serialized, and the MUX needs to be stable during all i2c traffic (otherwise individual mux slave segments might see garbage). This series accomplishes this by adding code to i2c-mux-gpio and i2c-mux-pinctrl that determines if all involved devices used to update the mux are controlled by the same root i2c adapter that is muxed. When this is the case, the select-transfer-deselect operations should be locked individually to avoid the deadlock. The i2c bus *is* still locked during muxing, since the muxing happens as part of i2c transfers. This is true even if the MUX is updated with several transfers to the GPIO (at least as long as *all* MUX changes are using the i2c master bus). A lock is added to the mux so that transfers through the mux are serialized. Concerns: - The locking is perhaps too complex? - I worry about the priority inheritance aspect of the adapter lock. When the transfers behind the mux are divided into select-transfer-deselect all locked individually, low priority transfers get more chances to interfere with high priority transfers. But there is no risk for regressions, since it did not work at all previously. - When doing an i2c_transfer() in_atomic() context or with irqs_disabled(), there is a higher possibility that the mux is not returned to its idle state after a failed (-EAGAIN) transfer due to trylock. Again, no risk for regressions. - Is the detection of i2c-controlled gpios and pinctrls sane (i.e. the usage of the new i2c_root_adapter() function in 18/18)? To summarize the series, there's some i2c-mux infrastructure cleanup work first (I think that part stands by itself as desireable regardless), the locking changes are in the last three patches of the series, with the real meat in 18/18. PS. needs a bunch of testing, I do not have access to all the involved hw v4 compared to v3: - Rebase on top of v4.5-rc6. - Update to add new i2c-mux interfaces in 01/18 including glue to implement the old interfaces in terms of the new interfaces, then change the mux users over to the new interfaces one by one (in 02/18 through 14/18), and finally removing the old interfaces in 15/18. I.e. the first 15 patches of v4 replaces the first 5 patches of v3, with the following points describing changes in the end result. Each patch is now touching only one subsystem. - Rename i2c_add_mux_adapter and i2c_del_mux_adapters to i2c_mux_add_adapter and i2c_mux_del_adapters (so that the old functions can live on during the transition). - Make i2c_mux_alloc take a parent and the select/deselect ops as arguments. Also add a flags argument to prevent churn later on. - Add a new interface i2c_mux_one_adapter(). Make use of it in suitable mux users with a single child adapter. - Adjust to a rename in struct gpio_chip. - Update a couple of comments to match the new code. v3 compared to v2: - Fix devm_kfree of a NULL pointer in i2c_mux_reserve_adapters(). - Remove device tree "i2c-controlled" property and determine this by walking the dev tree instead. - Fix compile problems with inv_mpu_acpi.c - Wait with adding the client pointer to patch 2/8 for pca9541 and pca954x. v2 compared to v1: - Allocate mux core and (optional) priv in a combined allocation. - Kill dev_err messages triggered by memory allocation failure. - Fix the device specific i2c muxes that I had overlooked. - Rebase on top of v4.4-rc8 (was based on v4.4-rc6 previously). - Drop the last two patches in the series. Cheers, Peter Peter Rosin (18): i2c-mux: add common data for every i2c-mux instance i2c: i2c-mux-gpio: convert to use an explicit i2c mux core i2c: i2c-mux-pinctrl: convert to use an explicit i2c mux core i2c: i2c-arb-gpio-challenge: convert to
[PATCH v4 07/18] i2c: i2c-mux-reg: convert to use an explicit i2c mux core
From: Peter Rosin Allocate an explicit i2c mux core to handle parent and child adapters etc. Update the select/deselect ops to be in terms of the i2c mux core instead of the child adapter. Signed-off-by: Peter Rosin --- drivers/i2c/muxes/i2c-mux-reg.c | 63 - 1 file changed, 24 insertions(+), 39 deletions(-) diff --git a/drivers/i2c/muxes/i2c-mux-reg.c b/drivers/i2c/muxes/i2c-mux-reg.c index 5fbd5bd0878f..3ac9a2dab111 100644 --- a/drivers/i2c/muxes/i2c-mux-reg.c +++ b/drivers/i2c/muxes/i2c-mux-reg.c @@ -21,8 +21,6 @@ #include struct regmux { - struct i2c_adapter *parent; - struct i2c_adapter **adap; /* child busses */ struct i2c_mux_reg_platform_data data; }; @@ -64,18 +62,16 @@ static int i2c_mux_reg_set(const struct regmux *mux, unsigned int chan_id) return 0; } -static int i2c_mux_reg_select(struct i2c_adapter *adap, void *data, - unsigned int chan) +static int i2c_mux_reg_select(struct i2c_mux_core *muxc, u32 chan) { - struct regmux *mux = data; + struct regmux *mux = i2c_mux_priv(muxc); return i2c_mux_reg_set(mux, chan); } -static int i2c_mux_reg_deselect(struct i2c_adapter *adap, void *data, - unsigned int chan) +static int i2c_mux_reg_deselect(struct i2c_mux_core *muxc, u32 chan) { - struct regmux *mux = data; + struct regmux *mux = i2c_mux_priv(muxc); if (mux->data.idle_in_use) return i2c_mux_reg_set(mux, mux->data.idle); @@ -87,6 +83,7 @@ static int i2c_mux_reg_deselect(struct i2c_adapter *adap, void *data, static int i2c_mux_reg_probe_dt(struct regmux *mux, struct platform_device *pdev) { + struct i2c_mux_core *muxc = platform_get_drvdata(pdev); struct device_node *np = pdev->dev.of_node; struct device_node *adapter_np, *child; struct i2c_adapter *adapter; @@ -107,7 +104,7 @@ static int i2c_mux_reg_probe_dt(struct regmux *mux, if (!adapter) return -EPROBE_DEFER; - mux->parent = adapter; + muxc->parent = adapter; mux->data.parent = i2c_adapter_id(adapter); put_device(&adapter->dev); @@ -169,18 +166,20 @@ static int i2c_mux_reg_probe_dt(struct regmux *mux, static int i2c_mux_reg_probe(struct platform_device *pdev) { + struct i2c_mux_core *muxc; struct regmux *mux; struct i2c_adapter *parent; struct resource *res; - int (*deselect)(struct i2c_adapter *, void *, u32); unsigned int class; int i, ret, nr; - mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL); - if (!mux) + muxc = i2c_mux_alloc(NULL, &pdev->dev, sizeof(*mux), 0, +i2c_mux_reg_select, NULL); + if (!muxc) return -ENOMEM; + mux = i2c_mux_priv(muxc); - platform_set_drvdata(pdev, mux); + platform_set_drvdata(pdev, muxc); if (dev_get_platdata(&pdev->dev)) { memcpy(&mux->data, dev_get_platdata(&pdev->dev), @@ -190,7 +189,7 @@ static int i2c_mux_reg_probe(struct platform_device *pdev) if (!parent) return -EPROBE_DEFER; - mux->parent = parent; + muxc->parent = parent; } else { ret = i2c_mux_reg_probe_dt(mux, pdev); if (ret < 0) { @@ -215,55 +214,41 @@ static int i2c_mux_reg_probe(struct platform_device *pdev) return -EINVAL; } - mux->adap = devm_kzalloc(&pdev->dev, -sizeof(*mux->adap) * mux->data.n_values, -GFP_KERNEL); - if (!mux->adap) { - dev_err(&pdev->dev, "Cannot allocate i2c_adapter structure"); - return -ENOMEM; - } + ret = i2c_mux_reserve_adapters(muxc, mux->data.n_values); + if (ret) + return ret; if (mux->data.idle_in_use) - deselect = i2c_mux_reg_deselect; - else - deselect = NULL; + muxc->deselect = i2c_mux_reg_deselect; for (i = 0; i < mux->data.n_values; i++) { nr = mux->data.base_nr ? (mux->data.base_nr + i) : 0; class = mux->data.classes ? mux->data.classes[i] : 0; - mux->adap[i] = i2c_add_mux_adapter(mux->parent, &pdev->dev, mux, - nr, mux->data.values[i], - class, i2c_mux_reg_select, - deselect); - if (!mux->adap[i]) { - ret = -ENODEV; + ret = i2c_mux_add_adapter(muxc, nr, mux->dat
Re: [PATCH v4 18/18] i2c-mux: relax locking of the top i2c adapter during i2c controlled muxing
Hi! Here's a fixup for a problem found by the test robot. Sorry for the inconvenience. Cheers, Peter diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c index 40a4e0397826..b73c42eddca3 100644 --- a/drivers/i2c/i2c-mux.c +++ b/drivers/i2c/i2c-mux.c @@ -226,6 +226,7 @@ struct i2c_adapter *i2c_root_adapter(struct device *dev) return i2c_root; } +EXPORT_SYMBOL_GPL(i2c_root_adapter); int i2c_mux_reserve_adapters(struct i2c_mux_core *muxc, int adapters) { -- 2.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v4 16/18] i2c: allow adapter drivers to override the adapter locking
Hi! Here's a fixup for a problem found by the test robot. Sorry for the inconvenience. Cheers, Peter diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 6be266c3d39b..5ecc6fc52ce0 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -961,6 +961,8 @@ static int i2c_check_addr_busy(struct i2c_adapter *adapter, int addr) /** * i2c_adapter_lock_bus - Get exclusive access to an I2C bus segment * @adapter: Target I2C bus segment + * @flags: I2C_LOCK_ADAPTER locks the root i2c adapter, I2C_LOCK_SEGMENT + * locks only this branch in the adapter tree */ static void i2c_adapter_lock_bus(struct i2c_adapter *adapter, int flags) { @@ -970,6 +972,8 @@ static void i2c_adapter_lock_bus(struct i2c_adapter *adapter, int flags) /** * i2c_adapter_trylock_bus - Try to get exclusive access to an I2C bus segment * @adapter: Target I2C bus segment + * @flags: I2C_LOCK_ADAPTER trylocks the root i2c adapter, I2C_LOCK_SEGMENT + * trylocks only this branch in the adapter tree */ static int i2c_adapter_trylock_bus(struct i2c_adapter *adapter, int flags) { @@ -979,6 +983,8 @@ static int i2c_adapter_trylock_bus(struct i2c_adapter *adapter, int flags) /** * i2c_adapter_unlock_bus - Release exclusive access to an I2C bus segment * @adapter: Target I2C bus segment + * @flags: I2C_LOCK_ADAPTER unlocks the root i2c adapter, I2C_LOCK_SEGMENT + * unlocks only this branch in the adapter tree */ static void i2c_adapter_unlock_bus(struct i2c_adapter *adapter, int flags) { -- 2.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v4 00/18] i2c mux cleanup and locking update
I wrote: > Concerns: > - The locking is perhaps too complex? Ok, to highlight the benefits of this series, I expect that patches such as [1] and the one inlined below can follow up to clean up ad-hoc i2c locking in drivers. Putting this locking in one place instead of having it spread out in random drivers is a good thing. PS. the inlined patch has not been tested as I have no hw, it's a proof of concept. Maybe the simplifications can be extended into rtl2832_sdr.c as well? Anyway, please do test this patch on top of the v4 series if you have the hw. Cheers, Peter [1] https://lkml.org/lkml/2016/3/3/932 diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c index 775444898599..fd1f05e9e79a 100644 --- a/drivers/media/dvb-frontends/rtl2832.c +++ b/drivers/media/dvb-frontends/rtl2832.c @@ -158,36 +158,24 @@ static int rtl2832_bulk_write(struct i2c_client *client, unsigned int reg, const void *val, size_t val_count) { struct rtl2832_dev *dev = i2c_get_clientdata(client); - int ret; - i2c_lock_adapter(client->adapter); - ret = regmap_bulk_write(dev->regmap, reg, val, val_count); - i2c_unlock_adapter(client->adapter); - return ret; + return regmap_bulk_write(dev->regmap, reg, val, val_count); } static int rtl2832_update_bits(struct i2c_client *client, unsigned int reg, unsigned int mask, unsigned int val) { struct rtl2832_dev *dev = i2c_get_clientdata(client); - int ret; - i2c_lock_adapter(client->adapter); - ret = regmap_update_bits(dev->regmap, reg, mask, val); - i2c_unlock_adapter(client->adapter); - return ret; + return regmap_update_bits(dev->regmap, reg, mask, val); } static int rtl2832_bulk_read(struct i2c_client *client, unsigned int reg, void *val, size_t val_count) { struct rtl2832_dev *dev = i2c_get_clientdata(client); - int ret; - i2c_lock_adapter(client->adapter); - ret = regmap_bulk_read(dev->regmap, reg, val, val_count); - i2c_unlock_adapter(client->adapter); - return ret; + return regmap_bulk_read(dev->regmap, reg, val, val_count); } static int rtl2832_rd_demod_reg(struct rtl2832_dev *dev, int reg, u32 *val) @@ -204,7 +192,7 @@ static int rtl2832_rd_demod_reg(struct rtl2832_dev *dev, int reg, u32 *val) len = (msb >> 3) + 1; mask = REG_MASK(msb - lsb); - ret = rtl2832_bulk_read(client, reg_start_addr, reading, len); + ret = regmap_bulk_read(dev->regmap, reg_start_addr, reading, len); if (ret) goto err; @@ -234,7 +222,7 @@ static int rtl2832_wr_demod_reg(struct rtl2832_dev *dev, int reg, u32 val) len = (msb >> 3) + 1; mask = REG_MASK(msb - lsb); - ret = rtl2832_bulk_read(client, reg_start_addr, reading, len); + ret = regmap_bulk_read(dev->regmap, reg_start_addr, reading, len); if (ret) goto err; @@ -248,7 +236,7 @@ static int rtl2832_wr_demod_reg(struct rtl2832_dev *dev, int reg, u32 val) for (i = 0; i < len; i++) writing[i] = (writing_tmp >> ((len - 1 - i) * 8)) & 0xff; - ret = rtl2832_bulk_write(client, reg_start_addr, writing, len); + ret = regmap_bulk_write(dev->regmap, reg_start_addr, writing, len); if (ret) goto err; @@ -492,7 +480,7 @@ static int rtl2832_set_frontend(struct dvb_frontend *fe) fe->ops.tuner_ops.set_params(fe); /* PIP mode related */ - ret = rtl2832_bulk_write(client, 0x192, "\x00\x0f\xff", 3); + ret = regmap_bulk_write(dev->regmap, 0x192, "\x00\x0f\xff", 3); if (ret) goto err; @@ -530,7 +518,7 @@ static int rtl2832_set_frontend(struct dvb_frontend *fe) } for (j = 0; j < sizeof(bw_params[0]); j++) { - ret = rtl2832_bulk_write(client, 0x11c + j, &bw_params[i][j], 1); + ret = regmap_bulk_write(dev->regmap, 0x11c + j, &bw_params[i][j], 1); if (ret) goto err; } @@ -586,11 +574,11 @@ static int rtl2832_get_frontend(struct dvb_frontend *fe) if (dev->sleeping) return 0; - ret = rtl2832_bulk_read(client, 0x33c, buf, 2); + ret = regmap_bulk_read(dev->regmap, 0x33c, buf, 2); if (ret) goto err; - ret = rtl2832_bulk_read(client, 0x351, &buf[2], 1); + ret = regmap_bulk_read(dev->regmap, 0x351, &buf[2], 1); if (ret) goto err; @@ -757,7 +745,7 @@ static void rtl2832_stat_work(struct work_struct *work) /* signal strength */ if (dev->fe_status & FE_HAS_SIGNAL) { /* read digital AGC */ -
Re: [PATCH v4 00/18] i2c mux cleanup and locking update
I wrote: > I wrote: >> Concerns: >> - The locking is perhaps too complex? > > Ok, to highlight the benefits of this series, I expect that patches such as > [1] and the one inlined below can follow up to clean up ad-hoc i2c locking > in drivers. Putting this locking in one place instead of having it spread > out in random drivers is a good thing. > > PS. the inlined patch has not been tested as I have no hw, it's a proof of > concept. Maybe the simplifications can be extended into rtl2832_sdr.c as well? > Anyway, please do test this patch on top of the v4 series if you have the hw. The untested patch in the previous message indeed has some obvious problems, so I fixed that and folded some more removed code now that rtl2832_sdr does not need to do anything special to access device registers. The result builds for me, that's all the testing I have done. diffstat for the two patches combined: drivers/media/dvb-frontends/rtl2832.c | 216 - drivers/media/dvb-frontends/rtl2832.h | 4 +- drivers/media/dvb-frontends/rtl2832_priv.h | 1 - drivers/media/dvb-frontends/rtl2832_sdr.c | 13 +- drivers/media/dvb-frontends/rtl2832_sdr.h | 5 +- drivers/media/usb/dvb-usb-v2/rtl28xxu.c| 5 +- 6 files changed, 34 insertions(+), 210 deletions(-) Cheers, Peter diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c index fd1f05e9e79a..6ade321503f0 100644 --- a/drivers/media/dvb-frontends/rtl2832.c +++ b/drivers/media/dvb-frontends/rtl2832.c @@ -153,31 +153,6 @@ static const struct rtl2832_reg_entry registers[] = { [DVBT_REG_4MSEL]= {0x013, 0, 0}, }; -/* Our regmap is bypassing I2C adapter lock, thus we do it! */ -static int rtl2832_bulk_write(struct i2c_client *client, unsigned int reg, - const void *val, size_t val_count) -{ - struct rtl2832_dev *dev = i2c_get_clientdata(client); - - return regmap_bulk_write(dev->regmap, reg, val, val_count); -} - -static int rtl2832_update_bits(struct i2c_client *client, unsigned int reg, - unsigned int mask, unsigned int val) -{ - struct rtl2832_dev *dev = i2c_get_clientdata(client); - - return regmap_update_bits(dev->regmap, reg, mask, val); -} - -static int rtl2832_bulk_read(struct i2c_client *client, unsigned int reg, -void *val, size_t val_count) -{ - struct rtl2832_dev *dev = i2c_get_clientdata(client); - - return regmap_bulk_read(dev->regmap, reg, val, val_count); -} - static int rtl2832_rd_demod_reg(struct rtl2832_dev *dev, int reg, u32 *val) { struct i2c_client *client = dev->client; @@ -1091,7 +1066,6 @@ static int rtl2832_probe(struct i2c_client *client, INIT_DELAYED_WORK(&dev->i2c_gate_work, rtl2832_i2c_gate_work); INIT_DELAYED_WORK(&dev->stat_work, rtl2832_stat_work); /* create regmap */ - mutex_init(&dev->regmap_mutex); dev->regmap_config.reg_bits = 8, dev->regmap_config.val_bits = 8, dev->regmap_config.volatile_reg = rtl2832_volatile_reg, @@ -1099,8 +1073,7 @@ static int rtl2832_probe(struct i2c_client *client, dev->regmap_config.ranges = regmap_range_cfg, dev->regmap_config.num_ranges = ARRAY_SIZE(regmap_range_cfg), dev->regmap_config.cache_type = REGCACHE_NONE, - dev->regmap = regmap_init(&client->dev, NULL, client, - &dev->regmap_config); + dev->regmap = regmap_init_i2c(client, &dev->regmap_config); if (IS_ERR(dev->regmap)) { ret = PTR_ERR(dev->regmap); goto err_kfree; @@ -1131,9 +1104,7 @@ static int rtl2832_probe(struct i2c_client *client, pdata->enable_slave_ts = rtl2832_enable_slave_ts; pdata->pid_filter = rtl2832_pid_filter; pdata->pid_filter_ctrl = rtl2832_pid_filter_ctrl; - pdata->bulk_read = rtl2832_bulk_read; - pdata->bulk_write = rtl2832_bulk_write; - pdata->update_bits = rtl2832_update_bits; + pdata->regmap = dev->regmap; dev_info(&client->dev, "Realtek RTL2832 successfully attached\n"); return 0; diff --git a/drivers/media/dvb-frontends/rtl2832.h b/drivers/media/dvb-frontends/rtl2832.h index c29a4c2bf71a..fab2f99672d6 100644 --- a/drivers/media/dvb-frontends/rtl2832.h +++ b/drivers/media/dvb-frontends/rtl2832.h @@ -57,9 +57,7 @@ struct rtl2832_platform_data { int (*pid_filter)(struct dvb_frontend *, u8, u16, int); int (*pid_filter_ctrl)(struct dvb_frontend *, int); /* private: Register access for SDR module use only */ - int (*bulk_read)(struct i2c_client *, unsigned int, void *, size_t); - int (*bulk_write)(struct i2c_client *, unsigned int, const void *, size_t); - int (*up
Re: [PATCH v3 0/8] i2c mux cleanup and locking update
On 2016-03-05 19:29, Wolfram Sang wrote: > >> Perhaps it's one to let sit into at least the next cycle (and get some >> testing >> on those media devices if we can) but, whilst it is fiddly the gains seen in >> individual drivers (like the example Peter put in response to the V4 series) >> make it look worthwhile to me. Also, whilst the invensense part is plain odd >> in many ways, the case Peter had looks rather more normal. >> >> At the end of the day, sometimes fiddly problems need fiddly code. >> (says a guy who doesn't have to maintain it!) >> >> It certainly helps that Peter has done a thorough job, broken the patches >> up cleanly and provided clean descriptions of what he is doing. > > Yes, Peter has done a great job so far and the latest results were very > convincing (fixing the invensense issue and the savings for rtl2832). > > And yes, I am reluctant to maintain this code alone, so my question > would be: > > Peter, are you interested in becoming the i2c-mux maintainer and look > after the code even after it was merged? (From "you reviewing patches and > me picking them up" to "you have your own branch which I pull", we can > discuss the best workflow.) My code wouldn't be worth much if I didn't offer to look after it myself. On the other hand, I am also reluctant to be the go-to person for all things i2c-mux, as I don't have a clear picture of how much work it's going to be. My offer is going to be this, I'll look after any unforeseen future problems caused by this rework, and I can be the i2c-mux maintainer. But if being the i2c-mux maintainer turns out to be a huge time-sink, there is no way I can stay on in the long run. But I guess that is the same for any maintainer (whose job description does not explicitly include being maintainer). > If that would be the case, I have the same idea like Jonathan: Give it > another cycle for more review & test and aim for the 4.7 merge window. Yes, sounds good. One of the reasons for doing things right (or at least trying to) is to have more people look at the work. *I* think it's good, but more eyes can't hurt. > I have to admit that I still haven't done a more thorough review, so I > can't say if I see a show-stopper in this series. Yet, even if so I am > positive it can be sorted out. Oh, and we should call for people with > special experience in locking. > > What do people think? > > Regards, > >Wolfram > > PS: Peter, have you seen my demuxer driver in my for-next branch? I hope > it won't spoil your design? I had a brief look, and I can't see anything in the demux that's affected by the mux update. The main commonality of the demux and the preexisting muxes seems to be that the name includes "mux" and that it is all about i2c. Agreed? In short, I see no problems. Cheers, Peter -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v4 00/18] i2c mux cleanup and locking update
Hi! On 2016-03-03 23:27, Peter Rosin wrote: > From: Peter Rosin > > Hi! > > I have a pair of boards with this i2c topology: > >GPIO ---| -- BAT1 > | v / >I2C -+--B---+ MUX > | \ >EEPROM -- BAT2 > > (B denotes the boundary between the boards) > > The problem with this is that the GPIO controller sits on the same i2c bus > that it MUXes. For pca954x devices this is worked around by using unlocked > transfers when updating the MUX. I have no such luck as the GPIO is a general > purpose IO expander and the MUX is just a random bidirectional MUX, unaware > of the fact that it is muxing an i2c bus. Extending unlocked transfers > into the GPIO subsystem is too ugly to even think about. But the general hw > approach is sane in my opinion, with the number of connections between the > two boards minimized. To put is plainly, I need support for it. etc etc I've made a few changes, but instead of posting another copy of a rather long series, I decided to push it to github and only post a note about it here. The plan is to then rebase (if needed) on top of v4.6-rc1 and at that point post all the patches (this is, after all, at least 4.7 material). At the same time I do not want to hide the latest version from anyone wanting to have a look or test. The series will be posted again for review. This is just a heads up. v5 compared to v4: - Rebase on top of v4.5-rc7. - A new patch making me maintainer of i2c muxes (also sent separately). - A new file Documentation/i2c/i2c-topology that describes various muxing issues. - Rename "i2c-controlled" muxes "self-locked" instead, as it is perfectly reasonable to have i2c-controlled muxes that use the pre-existing locking scheme. The pre-existing locking scheme for i2c muxes is from here on called "parent-locked". - Rename i2c-mux.c:i2c_mux_master_xfer to __i2c_mux_master_xfer since it calls __i2c_transfer, which leaves room for a new i2c_mux_master_xfer that calls i2c_transfer. Similar rename shuffle for i2c_mux_smbus_xfer. - Use sizeof(*priv) instead of sizeof(struct i2c_mux_priv). One instance. - Some follow-up patches that were posted in response to v2-v4 cleaning up and simplifying various i2c muxes outside drivers/i2c/, among those is an unrelated cleanup patch to drivers/media/dvb-frontends/rtl2832.c that I carry here since it conflicts (trivially) with this series. That unrelated patch is (currently) the last patch in the series. The series looks like this now: The following changes since commit f6cede5b49e822ebc41a099fe41ab4989f64e2cb: Linux 4.5-rc7 (2016-03-06 14:48:03 -0800) are available in the git repository at: https://github.com/peda-r/i2c-mux.git mux-core-and-locking-5 for you to fetch changes up to c1ef4a249b0bd45ba97e14f15f6ee89e7fbc0222: [media] rtl2832: regmap is aware of lockdep, drop local locking hack (2016-03-15 10:02:15 +0100) ---- Antti Palosaari (1): [media] si2168: declare that the i2c gate is self-locked Peter Rosin (24): MAINTAINERS: add myself as i2c mux maintainer i2c-mux: add common data for every i2c-mux instance i2c: i2c-mux-gpio: convert to use an explicit i2c mux core i2c: i2c-mux-pinctrl: convert to use an explicit i2c mux core i2c: i2c-arb-gpio-challenge: convert to use an explicit i2c mux core i2c: i2c-mux-pca9541: convert to use an explicit i2c mux core i2c: i2c-mux-pca954x: convert to use an explicit i2c mux core i2c: i2c-mux-reg: convert to use an explicit i2c mux core iio: imu: inv_mpu6050: convert to use an explicit i2c mux core [media] m88ds3103: convert to use an explicit i2c mux core [media] rtl2830: convert to use an explicit i2c mux core [media] rtl2832: convert to use an explicit i2c mux core [media] si2168: convert to use an explicit i2c mux core [media] cx231xx: convert to use an explicit i2c mux core of/unittest: convert to use an explicit i2c mux core i2c-mux: drop old unused i2c-mux api i2c: allow adapter drivers to override the adapter locking i2c: muxes always lock the parent adapter i2c-mux: relax locking of the top i2c adapter during self-locked muxing i2c-mux: document i2c muxes and elaborate on parent-/self-locked muxes iio: imu: inv_mpu6050: declare that the i2c gate is self-locked [media] rtl2832: declare that the i2c gate is self-locked [media] rtl2832_sdr: get rid of empty regmap wrappers [media] rtl2832: regmap is aware of lockdep, drop local locking hack Documentation/i2c/i2c-topology | 312 MAINTAINERS | 11 + drivers
RE: [PATCH] si2168: add lock to cmd execute
Hi Antti! Antti Palosaari wrote: > Mauro do not apply that patch, it is fix for Peter I2C-mux serie! Still true with the below patch :-) > Peter, please meld that fix to main patch: > [media] si2168: declare that the i2c gate is self-locked > > We need lock to make sure only single caller could execute firmware > command at the time. Earlier there was manual I2C locking which did > the same job. Right, the squashed thing I have in the mux series after this is the below patch. I simply put you as author of the whole thing and added your sign-off, since my contribution added up to a trivial one-liner rename or something like that, and the commit message. Holler if this is not what you intended. Thanks, Peter From: Antti Palosaari Date: Tue, 15 Mar 2016 20:20:11 +0100 Subject: [PATCH] [media] si2168: declare that the i2c gate is self-locked When the i2c adapter lock is no longer held by the i2c mux during accesses behind the i2c gate, such accesses need to take that lock just like any other ordinary i2c accesses do. So, declare the i2c gate self-locked, and zap the code that makes the i2c accesses unlocked. But add a mutex so that firmware commands are still serialized. Signed-off-by: Antti Palosaari Signed-off-by: Peter Rosin --- drivers/media/dvb-frontends/si2168.c | 85 --- drivers/media/dvb-frontends/si2168_priv.h | 1 + 2 files changed, 22 insertions(+), 64 deletions(-) diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c index ca455d01c71d..786808f923e5 100644 --- a/drivers/media/dvb-frontends/si2168.c +++ b/drivers/media/dvb-frontends/si2168.c @@ -18,53 +18,23 @@ static const struct dvb_frontend_ops si2168_ops; -/* Own I2C adapter locking is needed because of I2C gate logic. */ -static int si2168_i2c_master_send_unlocked(const struct i2c_client *client, - const char *buf, int count) -{ - int ret; - struct i2c_msg msg = { - .addr = client->addr, - .flags = 0, - .len = count, - .buf = (char *)buf, - }; - - ret = __i2c_transfer(client->adapter, &msg, 1); - return (ret == 1) ? count : ret; -} - -static int si2168_i2c_master_recv_unlocked(const struct i2c_client *client, - char *buf, int count) -{ - int ret; - struct i2c_msg msg = { - .addr = client->addr, - .flags = I2C_M_RD, - .len = count, - .buf = buf, - }; - - ret = __i2c_transfer(client->adapter, &msg, 1); - return (ret == 1) ? count : ret; -} - /* execute firmware command */ -static int si2168_cmd_execute_unlocked(struct i2c_client *client, - struct si2168_cmd *cmd) +static int si2168_cmd_execute(struct i2c_client *client, struct si2168_cmd *cmd) { + struct si2168_dev *dev = i2c_get_clientdata(client); int ret; unsigned long timeout; + mutex_lock(&dev->i2c_mutex); + if (cmd->wlen) { /* write cmd and args for firmware */ - ret = si2168_i2c_master_send_unlocked(client, cmd->args, - cmd->wlen); + ret = i2c_master_send(client, cmd->args, cmd->wlen); if (ret < 0) { - goto err; + goto err_mutex_unlock; } else if (ret != cmd->wlen) { ret = -EREMOTEIO; - goto err; + goto err_mutex_unlock; } } @@ -73,13 +43,12 @@ static int si2168_cmd_execute_unlocked(struct i2c_client *client, #define TIMEOUT 70 timeout = jiffies + msecs_to_jiffies(TIMEOUT); while (!time_after(jiffies, timeout)) { - ret = si2168_i2c_master_recv_unlocked(client, cmd->args, - cmd->rlen); + ret = i2c_master_recv(client, cmd->args, cmd->rlen); if (ret < 0) { - goto err; + goto err_mutex_unlock; } else if (ret != cmd->rlen) { ret = -EREMOTEIO; - goto err; + goto err_mutex_unlock; } /* firmware ready? */ @@ -94,32 +63,23 @@ static int si2168_cmd_execute_unlocked(struct i2c_client *client, /* error bit set? */ if ((cmd->args[0] >> 6) & 0x01) { ret = -EREMOTEIO; - goto err; + goto err_mutex_unlock;
[PATCH] [media] m88ds3103: fix undefined division
From: Peter Rosin s32tmp in the below code may be negative, and dev->mclk_khz is an unsigned type. s32tmp = 0x1 * (tuner_frequency - c->frequency); s32tmp = DIV_ROUND_CLOSEST(s32tmp, dev->mclk_khz); This is undefined, as DIV_ROUND_CLOSEST is undefined for negative dividends when the divisor is of unsigned type. So, change mclk_khz to be signed (s32). Signed-off-by: Peter Rosin --- drivers/media/dvb-frontends/m88ds3103_priv.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) Note, this was found by code inspection, I don't have the hardware, I don't know what the consequences of a garbage result are at this point in the code and the patch has only been build-tested. It looks obvious enough though. It should probably go to stable as well... Cheers, Peter diff --git a/drivers/media/dvb-frontends/m88ds3103_priv.h b/drivers/media/dvb-frontends/m88ds3103_priv.h index eee8c22c51ec..651e005146b2 100644 --- a/drivers/media/dvb-frontends/m88ds3103_priv.h +++ b/drivers/media/dvb-frontends/m88ds3103_priv.h @@ -46,7 +46,7 @@ struct m88ds3103_dev { /* auto detect chip id to do different config */ u8 chip_id; /* main mclk is calculated for M88RS6000 dynamically */ - u32 mclk_khz; + s32 mclk_khz; u64 post_bit_error; u64 post_bit_count; }; -- 2.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH] si2168: use i2c controlled mux interface
On 2016-01-06 06:42, Antti Palosaari wrote: > Recent i2c mux locking update offers support for i2c controlled i2c > muxes. Use it and get the rid of homemade hackish i2c adapter > locking code. [actual patch elided] I had a 2nd look and it seems that the saa7164 driver has support for a HVR2055 card with dual si2168 chips. These two chips appear to sit on the same i2c-bus with different i2c-addresses (0x64 and 0x66) and with gates (implemented as muxes) to two identical tuners with the same i2c-address (0x60). Do I read it right? With the current i2c-mux-locking (parent-locked muxes), this works fine as an access to one of the tuners locks the root i2c adapter and thus the other tuner is also locked out. But with the upcoming i2c-mux-locking for i2c-controlled muxes (self-locked muxes), the root i2c adapter would no longer be locked for the full transaction when one of the tuners is accessed. This means that accesses to the two tuners may interleave and cause all kinds of trouble, should both gates be open at the same time. So, is it really correct and safe to change the si2168 driver to use a self-locked mux? Unless there is some other mechanism that prevents the two tuners from being accessed in parallel, I think not. But maybe there is such a mechanism? Cheers, Peter -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v2 1/8] i2c-mux: add common core data for every mux instance
Hi Vladimir, On 2016-03-24 10:50, Vladimir Zapolskiy wrote: > Hi Peter, > > On 05.01.2016 17:57, Peter Rosin wrote: >> From: Peter Rosin >> >> The initial core mux structure starts off small with only the parent >> adapter pointer, which all muxes have, and a priv pointer for mux >> driver private data. >> >> Add i2c_mux_alloc function to unify the creation of a mux. >> >> Where appropriate, pass around the mux core structure instead of the >> parent adapter or the driver private data. >> >> Remove the parent adapter pointer from the driver private data for all >> mux drivers. >> >> Signed-off-by: Peter Rosin > > is it still under review? If yes, please find one question from me below :) Yes, the series is still under review/testing, with an update planned in a week or so. > [snip] > >> @@ -196,21 +195,21 @@ static int i2c_arbitrator_probe(struct platform_device >> *pdev) >> dev_err(dev, "Cannot parse i2c-parent\n"); >> return -EINVAL; >> } >> -arb->parent = of_get_i2c_adapter_by_node(parent_np); >> +muxc->parent = of_find_i2c_adapter_by_node(parent_np); > > why do you prefer here to use "unlocked" version of API? > > Foe example would it be safe/possible to unload an I2C bus device driver > module or unbind I2C device itself in runtime? I think you ask why I change from of_get_i2c_... to of_find_i2c_..., and that change was not intentional. It was the result of a bad merge during an early rebase. Does that cover it? Cheers, Peter -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 2/3] [media] c8sectpfe: Demote print to dev_dbg
Signed-off-by: Peter Griffin --- drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c index 875d384..acd0767 100644 --- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c +++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c @@ -585,7 +585,7 @@ static int configure_memdma_and_inputblock(struct c8sectpfei *fei, writel(tsin->pid_buffer_busaddr, fei->io + PIDF_BASE(tsin->tsin_id)); - dev_info(fei->dev, "chan=%d PIDF_BASE=0x%x pid_bus_addr=%pad\n", + dev_dbg(fei->dev, "chan=%d PIDF_BASE=0x%x pid_bus_addr=%pad\n", tsin->tsin_id, readl(fei->io + PIDF_BASE(tsin->tsin_id)), &tsin->pid_buffer_busaddr); -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 1/3] [media] c8sectpfe: Fix broken circular buffer wp management
During the review process, a regression was intoduced in the circular buffer write pointer management. This means that wp doesn't get managed properly once the buffer becomes full. Signed-off-by: Peter Griffin --- drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c index 78e3cb9..875d384 100644 --- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c +++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c @@ -130,7 +130,7 @@ static void channel_swdemux_tsklet(unsigned long data) writel(channel->back_buffer_busaddr, channel->irec + DMA_PRDS_BUSRP_TP(0)); else - writel(wp, channel->irec + DMA_PRDS_BUSWP_TP(0)); + writel(wp, channel->irec + DMA_PRDS_BUSRP_TP(0)); } static int c8sectpfe_start_feed(struct dvb_demux_feed *dvbdmxfeed) -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 0/3] [media] c8sectpfe: Various driver fixups
Hi Mauro, This series includes a few fixes for the c8sectpfe Linux dvb demux driver. The first fixes a bug introduced in the review process to the circular buffer WP management. The second re-works the firmware loading code to not rely on the CONFIG_FW_LOADER_USER_HELPER_FALLBACK which was removed in a previous patch. The third patch simly demotes a print statement to dev_dbg. regards, Peter. Peter Griffin (3): [media] c8sectpfe: Fix broken circular buffer wp management [media] c8sectpfe: Demote print to dev_dbg [media] c8sectpfe: Rework firmware loading mechanism .../media/platform/sti/c8sectpfe/c8sectpfe-core.c | 69 -- 1 file changed, 24 insertions(+), 45 deletions(-) -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 3/3] [media] c8sectpfe: Rework firmware loading mechanism
c8sectpfe driver relied on CONFIG_FW_LOADER_USER_HELPER_FALLBACK option for loading its xp70 firmware. A previous commit removed this Kconfig option, as it is apparently harmful, but did not update the driver code which relied on it. This patch reworks the firmware loading into the start_feed callback. At this point we can be sure the rootfs is present, thereby removing the depedency on CONFIG_FW_LOADER_USER_HELPER_FALLBACK. Fixes: 79f5b6ae960d ('[media] c8sectpfe: Remove select on CONFIG_FW_LOADER_USER_HELPER_FALLBACK') Signed-off-by: Peter Griffin --- .../media/platform/sti/c8sectpfe/c8sectpfe-core.c | 65 -- 1 file changed, 22 insertions(+), 43 deletions(-) diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c index acd0767..7dddf77 100644 --- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c +++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c @@ -49,7 +49,7 @@ MODULE_FIRMWARE(FIRMWARE_MEMDMA); #define PID_TABLE_SIZE 1024 #define POLL_MSECS 50 -static int load_c8sectpfe_fw_step1(struct c8sectpfei *fei); +static int load_c8sectpfe_fw(struct c8sectpfei *fei); #define TS_PKT_SIZE 188 #define HEADER_SIZE (4) @@ -141,6 +141,7 @@ static int c8sectpfe_start_feed(struct dvb_demux_feed *dvbdmxfeed) struct channel_info *channel; u32 tmp; unsigned long *bitmap; + int ret; switch (dvbdmxfeed->type) { case DMX_TYPE_TS: @@ -169,8 +170,9 @@ static int c8sectpfe_start_feed(struct dvb_demux_feed *dvbdmxfeed) } if (!atomic_read(&fei->fw_loaded)) { - dev_err(fei->dev, "%s: c8sectpfe fw not loaded\n", __func__); - return -EINVAL; + ret = load_c8sectpfe_fw(fei); + if (ret) + return ret; } mutex_lock(&fei->lock); @@ -265,8 +267,9 @@ static int c8sectpfe_stop_feed(struct dvb_demux_feed *dvbdmxfeed) unsigned long *bitmap; if (!atomic_read(&fei->fw_loaded)) { - dev_err(fei->dev, "%s: c8sectpfe fw not loaded\n", __func__); - return -EINVAL; + ret = load_c8sectpfe_fw(fei); + if (ret) + return ret; } mutex_lock(&fei->lock); @@ -880,13 +883,6 @@ static int c8sectpfe_probe(struct platform_device *pdev) goto err_clk_disable; } - /* ensure all other init has been done before requesting firmware */ - ret = load_c8sectpfe_fw_step1(fei); - if (ret) { - dev_err(dev, "Couldn't load slim core firmware\n"); - goto err_clk_disable; - } - c8sectpfe_debugfs_init(fei); return 0; @@ -1091,15 +1087,14 @@ static void load_dmem_segment(struct c8sectpfei *fei, Elf32_Phdr *phdr, phdr->p_memsz - phdr->p_filesz); } -static int load_slim_core_fw(const struct firmware *fw, void *context) +static int load_slim_core_fw(const struct firmware *fw, struct c8sectpfei *fei) { - struct c8sectpfei *fei = context; Elf32_Ehdr *ehdr; Elf32_Phdr *phdr; u8 __iomem *dst; int err = 0, i; - if (!fw || !context) + if (!fw || !fei) return -EINVAL; ehdr = (Elf32_Ehdr *)fw->data; @@ -1151,29 +1146,35 @@ static int load_slim_core_fw(const struct firmware *fw, void *context) return err; } -static void load_c8sectpfe_fw_cb(const struct firmware *fw, void *context) +static int load_c8sectpfe_fw(struct c8sectpfei *fei) { - struct c8sectpfei *fei = context; + const struct firmware *fw; int err; + dev_info(fei->dev, "Loading firmware: %s\n", FIRMWARE_MEMDMA); + + err = request_firmware(&fw, FIRMWARE_MEMDMA, fei->dev); + if (err) + return err; + err = c8sectpfe_elf_sanity_check(fei, fw); if (err) { dev_err(fei->dev, "c8sectpfe_elf_sanity_check failed err=(%d)\n" , err); - goto err; + return err; } - err = load_slim_core_fw(fw, context); + err = load_slim_core_fw(fw, fei); if (err) { dev_err(fei->dev, "load_slim_core_fw failed err=(%d)\n", err); - goto err; + return err; } /* now the firmware is loaded configure the input blocks */ err = configure_channels(fei); if (err) { dev_err(fei->dev, "configure_channels failed err=(%d)\n", err); - goto err; + return err; } /* @@ -1186,28 +1187,6 @@ static void load_c8sectpfe_fw_cb(const struct firmware *fw, void *context) writel(0x1, fei->io + DMA_CPU_RUN); atom
Re: [PATCH v2 1/8] i2c-mux: add common core data for every mux instance
Hi Vladimir, On 2016-03-24 15:24, Vladimir Zapolskiy wrote: > On 24.03.2016 13:05, Peter Rosin wrote: >> On 2016-03-24 10:50, Vladimir Zapolskiy wrote: >>> On 05.01.2016 17:57, Peter Rosin wrote: >>>> @@ -196,21 +195,21 @@ static int i2c_arbitrator_probe(struct >>>> platform_device *pdev) >>>>dev_err(dev, "Cannot parse i2c-parent\n"); >>>>return -EINVAL; >>>>} >>>> - arb->parent = of_get_i2c_adapter_by_node(parent_np); >>>> + muxc->parent = of_find_i2c_adapter_by_node(parent_np); >>> >>> why do you prefer here to use "unlocked" version of API? >>> >>> Foe example would it be safe/possible to unload an I2C bus device driver >>> module or unbind I2C device itself in runtime? >> >> I think you ask why I change from of_get_i2c_... to of_find_i2c_..., and that >> change was not intentional. It was the result of a bad merge during an early >> rebase. >> >> Does that cover it? >> > > Yep, thank you for clarification, please account this in v3. Oh , v3 is old news, v4 was sent out some weeks ago, and there is a v5 on a github branch. This bad rebase was fixed in v4. > I'll try to find some time to review the whole changeset carefully, > in fact I briefly reviewed it two months ago, but I didn't find > anything obviously wrong that time. Please put that on hold until I have rebased ontop of v4.6-rc1 and changed a couple of other things. I'd hate for you to waste your time on outdated patches. Cheers, Peter -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v6 02/24] i2c: i2c-mux-gpio: convert to use an explicit i2c mux core
From: Peter Rosin Allocate an explicit i2c mux core to handle parent and child adapters etc. Update the select/deselect ops to be in terms of the i2c mux core instead of the child adapter. Signed-off-by: Peter Rosin --- drivers/i2c/muxes/i2c-mux-gpio.c | 54 1 file changed, 21 insertions(+), 33 deletions(-) diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c index b8e11c16d98c..1bcc26737359 100644 --- a/drivers/i2c/muxes/i2c-mux-gpio.c +++ b/drivers/i2c/muxes/i2c-mux-gpio.c @@ -18,8 +18,6 @@ #include struct gpiomux { - struct i2c_adapter *parent; - struct i2c_adapter **adap; /* child busses */ struct i2c_mux_gpio_platform_data data; unsigned gpio_base; }; @@ -33,18 +31,18 @@ static void i2c_mux_gpio_set(const struct gpiomux *mux, unsigned val) val & (1 << i)); } -static int i2c_mux_gpio_select(struct i2c_adapter *adap, void *data, u32 chan) +static int i2c_mux_gpio_select(struct i2c_mux_core *muxc, u32 chan) { - struct gpiomux *mux = data; + struct gpiomux *mux = i2c_mux_priv(muxc); i2c_mux_gpio_set(mux, chan); return 0; } -static int i2c_mux_gpio_deselect(struct i2c_adapter *adap, void *data, u32 chan) +static int i2c_mux_gpio_deselect(struct i2c_mux_core *muxc, u32 chan) { - struct gpiomux *mux = data; + struct gpiomux *mux = i2c_mux_priv(muxc); i2c_mux_gpio_set(mux, mux->data.idle); @@ -136,19 +134,19 @@ static int i2c_mux_gpio_probe_dt(struct gpiomux *mux, static int i2c_mux_gpio_probe(struct platform_device *pdev) { + struct i2c_mux_core *muxc; struct gpiomux *mux; struct i2c_adapter *parent; - int (*deselect) (struct i2c_adapter *, void *, u32); unsigned initial_state, gpio_base; int i, ret; - mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL); - if (!mux) { - dev_err(&pdev->dev, "Cannot allocate gpiomux structure"); + muxc = i2c_mux_alloc(NULL, &pdev->dev, sizeof(*mux), 0, +i2c_mux_gpio_select, NULL); + if (!muxc) return -ENOMEM; - } + mux = i2c_mux_priv(muxc); - platform_set_drvdata(pdev, mux); + platform_set_drvdata(pdev, muxc); if (!dev_get_platdata(&pdev->dev)) { ret = i2c_mux_gpio_probe_dt(mux, pdev); @@ -180,24 +178,18 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev) if (!parent) return -EPROBE_DEFER; - mux->parent = parent; + muxc->parent = parent; mux->gpio_base = gpio_base; - mux->adap = devm_kzalloc(&pdev->dev, -sizeof(*mux->adap) * mux->data.n_values, -GFP_KERNEL); - if (!mux->adap) { - dev_err(&pdev->dev, "Cannot allocate i2c_adapter structure"); - ret = -ENOMEM; + ret = i2c_mux_reserve_adapters(muxc, mux->data.n_values); + if (ret) goto alloc_failed; - } if (mux->data.idle != I2C_MUX_GPIO_NO_IDLE) { initial_state = mux->data.idle; - deselect = i2c_mux_gpio_deselect; + muxc->deselect = i2c_mux_gpio_deselect; } else { initial_state = mux->data.values[0]; - deselect = NULL; } for (i = 0; i < mux->data.n_gpios; i++) { @@ -223,11 +215,8 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev) u32 nr = mux->data.base_nr ? (mux->data.base_nr + i) : 0; unsigned int class = mux->data.classes ? mux->data.classes[i] : 0; - mux->adap[i] = i2c_add_mux_adapter(parent, &pdev->dev, mux, nr, - mux->data.values[i], class, - i2c_mux_gpio_select, deselect); - if (!mux->adap[i]) { - ret = -ENODEV; + ret = i2c_mux_add_adapter(muxc, nr, mux->data.values[i], class); + if (ret) { dev_err(&pdev->dev, "Failed to add adapter %d\n", i); goto add_adapter_failed; } @@ -239,8 +228,7 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev) return 0; add_adapter_failed: - for (; i > 0; i--) - i2c_del_mux_adapter(mux->adap[i - 1]); + i2c_mux_del_adapters(muxc); i = mux->data.n_gpios; err_request_gpio: for (; i > 0; i--) @@ -253,16 +241,16 @@ alloc_failed: static int i2c_mux_gpio_remove(struct platform_device *pdev) { - struct gpiomux *mux = platform_get_drvdata(pdev);
[PATCH v6 06/24] i2c: i2c-mux-pca954x: convert to use an explicit i2c mux core
From: Peter Rosin Allocate an explicit i2c mux core to handle parent and child adapters etc. Update the select/deselect ops to be in terms of the i2c mux core instead of the child adapter. Add a mask to handle the case where not all child adapters should cause a mux deselect to happen, now that there is a common deselect op for all child adapters. Signed-off-by: Peter Rosin --- drivers/i2c/muxes/i2c-mux-pca954x.c | 64 +++-- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c index acfcef3d4068..1693d29c11a4 100644 --- a/drivers/i2c/muxes/i2c-mux-pca954x.c +++ b/drivers/i2c/muxes/i2c-mux-pca954x.c @@ -60,9 +60,10 @@ enum pca_type { struct pca954x { enum pca_type type; - struct i2c_adapter *virt_adaps[PCA954X_MAX_NCHANS]; u8 last_chan; /* last register value */ + u8 deselect; + struct i2c_client *client; }; struct chip_desc { @@ -146,10 +147,10 @@ static int pca954x_reg_write(struct i2c_adapter *adap, return ret; } -static int pca954x_select_chan(struct i2c_adapter *adap, - void *client, u32 chan) +static int pca954x_select_chan(struct i2c_mux_core *muxc, u32 chan) { - struct pca954x *data = i2c_get_clientdata(client); + struct pca954x *data = i2c_mux_priv(muxc); + struct i2c_client *client = data->client; const struct chip_desc *chip = &chips[data->type]; u8 regval; int ret = 0; @@ -162,21 +163,24 @@ static int pca954x_select_chan(struct i2c_adapter *adap, /* Only select the channel if its different from the last channel */ if (data->last_chan != regval) { - ret = pca954x_reg_write(adap, client, regval); + ret = pca954x_reg_write(muxc->parent, client, regval); data->last_chan = regval; } return ret; } -static int pca954x_deselect_mux(struct i2c_adapter *adap, - void *client, u32 chan) +static int pca954x_deselect_mux(struct i2c_mux_core *muxc, u32 chan) { - struct pca954x *data = i2c_get_clientdata(client); + struct pca954x *data = i2c_mux_priv(muxc); + struct i2c_client *client = data->client; + + if (!(data->deselect & (1 << chan))) + return 0; /* Deselect active channel */ data->last_chan = 0; - return pca954x_reg_write(adap, client, data->last_chan); + return pca954x_reg_write(muxc->parent, client, data->last_chan); } /* @@ -191,17 +195,21 @@ static int pca954x_probe(struct i2c_client *client, bool idle_disconnect_dt; struct gpio_desc *gpio; int num, force, class; + struct i2c_mux_core *muxc; struct pca954x *data; int ret; if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE)) return -ENODEV; - data = devm_kzalloc(&client->dev, sizeof(struct pca954x), GFP_KERNEL); - if (!data) + muxc = i2c_mux_alloc(adap, &client->dev, sizeof(*data), 0, +pca954x_select_chan, pca954x_deselect_mux); + if (!muxc) return -ENOMEM; + data = i2c_mux_priv(muxc); - i2c_set_clientdata(client, data); + i2c_set_clientdata(client, muxc); + data->client = client; /* Get the mux out of reset if a reset GPIO is specified. */ gpio = devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_LOW); @@ -220,6 +228,10 @@ static int pca954x_probe(struct i2c_client *client, data->type = id->driver_data; data->last_chan = 0; /* force the first selection */ + ret = i2c_mux_reserve_adapters(muxc, chips[data->type].nchans); + if (ret) + return ret; + idle_disconnect_dt = of_node && of_property_read_bool(of_node, "i2c-mux-idle-disconnect"); @@ -238,16 +250,13 @@ static int pca954x_probe(struct i2c_client *client, /* discard unconfigured channels */ break; idle_disconnect_pd = pdata->modes[num].deselect_on_exit; + data->deselect |= (idle_disconnect_pd + || idle_disconnect_dt) << num; } - data->virt_adaps[num] = - i2c_add_mux_adapter(adap, &client->dev, client, - force, num, class, pca954x_select_chan, - (idle_disconnect_pd || idle_disconnect_dt) - ? pca954x_deselect_mux : NULL); + ret = i2c_mux_add_adapter(muxc, force, num, class); - if (data->v
[PATCH v6 07/24] i2c: i2c-mux-reg: convert to use an explicit i2c mux core
From: Peter Rosin Allocate an explicit i2c mux core to handle parent and child adapters etc. Update the select/deselect ops to be in terms of the i2c mux core instead of the child adapter. Signed-off-by: Peter Rosin --- drivers/i2c/muxes/i2c-mux-reg.c | 63 - 1 file changed, 24 insertions(+), 39 deletions(-) diff --git a/drivers/i2c/muxes/i2c-mux-reg.c b/drivers/i2c/muxes/i2c-mux-reg.c index 5fbd5bd0878f..3ac9a2dab111 100644 --- a/drivers/i2c/muxes/i2c-mux-reg.c +++ b/drivers/i2c/muxes/i2c-mux-reg.c @@ -21,8 +21,6 @@ #include struct regmux { - struct i2c_adapter *parent; - struct i2c_adapter **adap; /* child busses */ struct i2c_mux_reg_platform_data data; }; @@ -64,18 +62,16 @@ static int i2c_mux_reg_set(const struct regmux *mux, unsigned int chan_id) return 0; } -static int i2c_mux_reg_select(struct i2c_adapter *adap, void *data, - unsigned int chan) +static int i2c_mux_reg_select(struct i2c_mux_core *muxc, u32 chan) { - struct regmux *mux = data; + struct regmux *mux = i2c_mux_priv(muxc); return i2c_mux_reg_set(mux, chan); } -static int i2c_mux_reg_deselect(struct i2c_adapter *adap, void *data, - unsigned int chan) +static int i2c_mux_reg_deselect(struct i2c_mux_core *muxc, u32 chan) { - struct regmux *mux = data; + struct regmux *mux = i2c_mux_priv(muxc); if (mux->data.idle_in_use) return i2c_mux_reg_set(mux, mux->data.idle); @@ -87,6 +83,7 @@ static int i2c_mux_reg_deselect(struct i2c_adapter *adap, void *data, static int i2c_mux_reg_probe_dt(struct regmux *mux, struct platform_device *pdev) { + struct i2c_mux_core *muxc = platform_get_drvdata(pdev); struct device_node *np = pdev->dev.of_node; struct device_node *adapter_np, *child; struct i2c_adapter *adapter; @@ -107,7 +104,7 @@ static int i2c_mux_reg_probe_dt(struct regmux *mux, if (!adapter) return -EPROBE_DEFER; - mux->parent = adapter; + muxc->parent = adapter; mux->data.parent = i2c_adapter_id(adapter); put_device(&adapter->dev); @@ -169,18 +166,20 @@ static int i2c_mux_reg_probe_dt(struct regmux *mux, static int i2c_mux_reg_probe(struct platform_device *pdev) { + struct i2c_mux_core *muxc; struct regmux *mux; struct i2c_adapter *parent; struct resource *res; - int (*deselect)(struct i2c_adapter *, void *, u32); unsigned int class; int i, ret, nr; - mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL); - if (!mux) + muxc = i2c_mux_alloc(NULL, &pdev->dev, sizeof(*mux), 0, +i2c_mux_reg_select, NULL); + if (!muxc) return -ENOMEM; + mux = i2c_mux_priv(muxc); - platform_set_drvdata(pdev, mux); + platform_set_drvdata(pdev, muxc); if (dev_get_platdata(&pdev->dev)) { memcpy(&mux->data, dev_get_platdata(&pdev->dev), @@ -190,7 +189,7 @@ static int i2c_mux_reg_probe(struct platform_device *pdev) if (!parent) return -EPROBE_DEFER; - mux->parent = parent; + muxc->parent = parent; } else { ret = i2c_mux_reg_probe_dt(mux, pdev); if (ret < 0) { @@ -215,55 +214,41 @@ static int i2c_mux_reg_probe(struct platform_device *pdev) return -EINVAL; } - mux->adap = devm_kzalloc(&pdev->dev, -sizeof(*mux->adap) * mux->data.n_values, -GFP_KERNEL); - if (!mux->adap) { - dev_err(&pdev->dev, "Cannot allocate i2c_adapter structure"); - return -ENOMEM; - } + ret = i2c_mux_reserve_adapters(muxc, mux->data.n_values); + if (ret) + return ret; if (mux->data.idle_in_use) - deselect = i2c_mux_reg_deselect; - else - deselect = NULL; + muxc->deselect = i2c_mux_reg_deselect; for (i = 0; i < mux->data.n_values; i++) { nr = mux->data.base_nr ? (mux->data.base_nr + i) : 0; class = mux->data.classes ? mux->data.classes[i] : 0; - mux->adap[i] = i2c_add_mux_adapter(mux->parent, &pdev->dev, mux, - nr, mux->data.values[i], - class, i2c_mux_reg_select, - deselect); - if (!mux->adap[i]) { - ret = -ENODEV; + ret = i2c_mux_add_adapter(muxc, nr, mux->dat
[PATCH v6 03/24] i2c: i2c-mux-pinctrl: convert to use an explicit i2c mux core
From: Peter Rosin Allocate an explicit i2c mux core to handle parent and child adapters etc. Update the select/deselect ops to be in terms of the i2c mux core instead of the child adapter. Signed-off-by: Peter Rosin --- drivers/i2c/muxes/i2c-mux-pinctrl.c | 86 + 1 file changed, 30 insertions(+), 56 deletions(-) diff --git a/drivers/i2c/muxes/i2c-mux-pinctrl.c b/drivers/i2c/muxes/i2c-mux-pinctrl.c index b5a982ba8898..bbfabf4f52be 100644 --- a/drivers/i2c/muxes/i2c-mux-pinctrl.c +++ b/drivers/i2c/muxes/i2c-mux-pinctrl.c @@ -26,27 +26,22 @@ #include struct i2c_mux_pinctrl { - struct device *dev; struct i2c_mux_pinctrl_platform_data *pdata; struct pinctrl *pinctrl; struct pinctrl_state **states; struct pinctrl_state *state_idle; - struct i2c_adapter *parent; - struct i2c_adapter **busses; }; -static int i2c_mux_pinctrl_select(struct i2c_adapter *adap, void *data, - u32 chan) +static int i2c_mux_pinctrl_select(struct i2c_mux_core *muxc, u32 chan) { - struct i2c_mux_pinctrl *mux = data; + struct i2c_mux_pinctrl *mux = i2c_mux_priv(muxc); return pinctrl_select_state(mux->pinctrl, mux->states[chan]); } -static int i2c_mux_pinctrl_deselect(struct i2c_adapter *adap, void *data, - u32 chan) +static int i2c_mux_pinctrl_deselect(struct i2c_mux_core *muxc, u32 chan) { - struct i2c_mux_pinctrl *mux = data; + struct i2c_mux_pinctrl *mux = i2c_mux_priv(muxc); return pinctrl_select_state(mux->pinctrl, mux->state_idle); } @@ -55,6 +50,7 @@ static int i2c_mux_pinctrl_deselect(struct i2c_adapter *adap, void *data, static int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux, struct platform_device *pdev) { + struct i2c_mux_core *muxc = platform_get_drvdata(pdev); struct device_node *np = pdev->dev.of_node; int num_names, i, ret; struct device_node *adapter_np; @@ -64,15 +60,12 @@ static int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux, return 0; mux->pdata = devm_kzalloc(&pdev->dev, sizeof(*mux->pdata), GFP_KERNEL); - if (!mux->pdata) { - dev_err(mux->dev, - "Cannot allocate i2c_mux_pinctrl_platform_data\n"); + if (!mux->pdata) return -ENOMEM; - } num_names = of_property_count_strings(np, "pinctrl-names"); if (num_names < 0) { - dev_err(mux->dev, "Cannot parse pinctrl-names: %d\n", + dev_err(muxc->dev, "Cannot parse pinctrl-names: %d\n", num_names); return num_names; } @@ -80,23 +73,21 @@ static int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux, mux->pdata->pinctrl_states = devm_kzalloc(&pdev->dev, sizeof(*mux->pdata->pinctrl_states) * num_names, GFP_KERNEL); - if (!mux->pdata->pinctrl_states) { - dev_err(mux->dev, "Cannot allocate pinctrl_states\n"); + if (!mux->pdata->pinctrl_states) return -ENOMEM; - } for (i = 0; i < num_names; i++) { ret = of_property_read_string_index(np, "pinctrl-names", i, &mux->pdata->pinctrl_states[mux->pdata->bus_count]); if (ret < 0) { - dev_err(mux->dev, "Cannot parse pinctrl-names: %d\n", + dev_err(muxc->dev, "Cannot parse pinctrl-names: %d\n", ret); return ret; } if (!strcmp(mux->pdata->pinctrl_states[mux->pdata->bus_count], "idle")) { if (i != num_names - 1) { - dev_err(mux->dev, "idle state must be last\n"); + dev_err(muxc->dev, "idle state must be last\n"); return -EINVAL; } mux->pdata->pinctrl_state_idle = "idle"; @@ -107,13 +98,13 @@ static int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux, adapter_np = of_parse_phandle(np, "i2c-parent", 0); if (!adapter_np) { - dev_err(mux->dev, "Cannot parse i2c-parent\n"); + dev_err(muxc->dev, "Cannot parse i2c-parent\n"); return -ENODEV; } adapter = of_find_i2c_adapter_by_node(adapter_np); of_node_put(adapter_np); if (!adapter) { - dev_err(mux->dev, "Cannot find parent bus\n"); +
[PATCH v6 08/24] iio: imu: inv_mpu6050: convert to use an explicit i2c mux core
From: Peter Rosin Allocate an explicit i2c mux core to handle parent and child adapters etc. Update the select/deselect ops to be in terms of the i2c mux core instead of the child adapter. Signed-off-by: Peter Rosin --- drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c | 2 +- drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 1 - drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c | 32 +- drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h | 3 ++- 4 files changed, 17 insertions(+), 21 deletions(-) diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c index 2771106fd650..f62b8bd9ad7e 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c @@ -183,7 +183,7 @@ int inv_mpu_acpi_create_mux_client(struct i2c_client *client) } else return 0; /* no secondary addr, which is OK */ } - st->mux_client = i2c_new_device(st->mux_adapter, &info); + st->mux_client = i2c_new_device(st->muxc->adapter[0], &info); if (!st->mux_client) return -ENODEV; } diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index d192953e9a38..0c2bded2b5b7 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include "inv_mpu_iio.h" diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c index f581256d9d4c..0d429d788106 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include "inv_mpu_iio.h" @@ -52,10 +51,9 @@ static int inv_mpu6050_write_reg_unlocked(struct i2c_client *client, return 0; } -static int inv_mpu6050_select_bypass(struct i2c_adapter *adap, void *mux_priv, -u32 chan_id) +static int inv_mpu6050_select_bypass(struct i2c_mux_core *muxc, u32 chan_id) { - struct i2c_client *client = mux_priv; + struct i2c_client *client = i2c_mux_priv(muxc); struct iio_dev *indio_dev = dev_get_drvdata(&client->dev); struct inv_mpu6050_state *st = iio_priv(indio_dev); int ret = 0; @@ -84,10 +82,9 @@ write_error: return ret; } -static int inv_mpu6050_deselect_bypass(struct i2c_adapter *adap, - void *mux_priv, u32 chan_id) +static int inv_mpu6050_deselect_bypass(struct i2c_mux_core *muxc, u32 chan_id) { - struct i2c_client *client = mux_priv; + struct i2c_client *client = i2c_mux_priv(muxc); struct iio_dev *indio_dev = dev_get_drvdata(&client->dev); struct inv_mpu6050_state *st = iio_priv(indio_dev); @@ -136,16 +133,15 @@ static int inv_mpu_probe(struct i2c_client *client, return result; st = iio_priv(dev_get_drvdata(&client->dev)); - st->mux_adapter = i2c_add_mux_adapter(client->adapter, - &client->dev, - client, - 0, 0, 0, - inv_mpu6050_select_bypass, - inv_mpu6050_deselect_bypass); - if (!st->mux_adapter) { - result = -ENODEV; + st->muxc = i2c_mux_one_adapter(client->adapter, &client->dev, 0, 0, + 0, 0, 0, + inv_mpu6050_select_bypass, + inv_mpu6050_deselect_bypass); + if (IS_ERR(st->muxc)) { + result = PTR_ERR(st->muxc); goto out_unreg_device; } + st->muxc->priv = client; result = inv_mpu_acpi_create_mux_client(client); if (result) @@ -154,7 +150,7 @@ static int inv_mpu_probe(struct i2c_client *client, return 0; out_del_mux: - i2c_del_mux_adapter(st->mux_adapter); + i2c_mux_del_adapters(st->muxc); out_unreg_device: inv_mpu_core_remove(&client->dev); return result; @@ -162,11 +158,11 @@ out_unreg_device: static int inv_mpu_remove(struct i2c_client *client) { - struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct iio_dev *indio_dev = dev_get_drvdata(&client->dev); struct inv_mpu6050_state *st = iio_priv(indio_dev); inv_mpu_acpi_delete_mux_client(client); - i2c_del_mux_adapter(st->mux_adapter); + i2c_mux_del_adapters(st->muxc); return inv_mpu_core_remove(&client->dev); } diff --git a/drivers
[PATCH v6 12/24] [media] si2168: convert to use an explicit i2c mux core
From: Peter Rosin Allocate an explicit i2c mux core to handle parent and child adapters etc. Update the select/deselect ops to be in terms of the i2c mux core instead of the child adapter. Reviewed-by: Antti Palosaari Signed-off-by: Peter Rosin --- drivers/media/dvb-frontends/si2168.c | 22 -- drivers/media/dvb-frontends/si2168_priv.h | 2 +- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c index 821a8f481507..ca455d01c71d 100644 --- a/drivers/media/dvb-frontends/si2168.c +++ b/drivers/media/dvb-frontends/si2168.c @@ -615,9 +615,9 @@ static int si2168_get_tune_settings(struct dvb_frontend *fe, * We must use unlocked I2C I/O because I2C adapter lock is already taken * by the caller (usually tuner driver). */ -static int si2168_select(struct i2c_adapter *adap, void *mux_priv, u32 chan) +static int si2168_select(struct i2c_mux_core *muxc, u32 chan) { - struct i2c_client *client = mux_priv; + struct i2c_client *client = i2c_mux_priv(muxc); int ret; struct si2168_cmd cmd; @@ -635,9 +635,9 @@ err: return ret; } -static int si2168_deselect(struct i2c_adapter *adap, void *mux_priv, u32 chan) +static int si2168_deselect(struct i2c_mux_core *muxc, u32 chan) { - struct i2c_client *client = mux_priv; + struct i2c_client *client = i2c_mux_priv(muxc); int ret; struct si2168_cmd cmd; @@ -709,17 +709,19 @@ static int si2168_probe(struct i2c_client *client, } /* create mux i2c adapter for tuner */ - dev->adapter = i2c_add_mux_adapter(client->adapter, &client->dev, - client, 0, 0, 0, si2168_select, si2168_deselect); - if (dev->adapter == NULL) { - ret = -ENODEV; + dev->muxc = i2c_mux_one_adapter(client->adapter, &client->dev, 0, 0, + 0, 0, 0, + si2168_select, si2168_deselect); + if (IS_ERR(dev->muxc)) { + ret = PTR_ERR(dev->muxc); goto err_kfree; } + dev->muxc->priv = client; /* create dvb_frontend */ memcpy(&dev->fe.ops, &si2168_ops, sizeof(struct dvb_frontend_ops)); dev->fe.demodulator_priv = client; - *config->i2c_adapter = dev->adapter; + *config->i2c_adapter = dev->muxc->adapter[0]; *config->fe = &dev->fe; dev->ts_mode = config->ts_mode; dev->ts_clock_inv = config->ts_clock_inv; @@ -743,7 +745,7 @@ static int si2168_remove(struct i2c_client *client) dev_dbg(&client->dev, "\n"); - i2c_del_mux_adapter(dev->adapter); + i2c_mux_del_adapters(dev->muxc); dev->fe.ops.release = NULL; dev->fe.demodulator_priv = NULL; diff --git a/drivers/media/dvb-frontends/si2168_priv.h b/drivers/media/dvb-frontends/si2168_priv.h index c07e6fe2cb10..165bf1412063 100644 --- a/drivers/media/dvb-frontends/si2168_priv.h +++ b/drivers/media/dvb-frontends/si2168_priv.h @@ -29,7 +29,7 @@ /* state struct */ struct si2168_dev { - struct i2c_adapter *adapter; + struct i2c_mux_core *muxc; struct dvb_frontend fe; enum fe_delivery_system delivery_system; enum fe_status fe_status; -- 2.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v6 16/24] i2c: allow adapter drivers to override the adapter locking
From: Peter Rosin Add i2c_lock_bus() and i2c_unlock_bus(), which call the new lock_bus and unlock_bus ops in the adapter. These funcs/ops take an additional flags argument that indicates for what purpose the adapter is locked. There are two flags, I2C_LOCK_ADAPTER and I2C_LOCK_SEGMENT, but they are both implemented the same. For now. Locking the adapter means that the whole bus is locked, locking the segment means that only the current bus segment is locked (i.e. i2c traffic on the parent side of mux is still allowed even if the child side of the mux is locked. Also support a trylock_bus op (but no function to call it, as it is not expected to be needed outside of the i2c core). Implement i2c_lock_adapter/i2c_unlock_adapter in terms of the new locking scheme (i.e. lock with the I2C_LOCK_ADAPTER flag). Annotate some of the locking with explicit I2C_LOCK_SEGMENT flags. Signed-off-by: Peter Rosin --- drivers/i2c/i2c-core.c | 46 -- include/linux/i2c.h| 28 ++-- 2 files changed, 54 insertions(+), 20 deletions(-) diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 0f2f8484e8ec..21f46d011c33 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -960,10 +960,12 @@ static int i2c_check_addr_busy(struct i2c_adapter *adapter, int addr) } /** - * i2c_lock_adapter - Get exclusive access to an I2C bus segment + * i2c_adapter_lock_bus - Get exclusive access to an I2C bus segment * @adapter: Target I2C bus segment + * @flags: I2C_LOCK_ADAPTER locks the root i2c adapter, I2C_LOCK_SEGMENT + * locks only this branch in the adapter tree */ -void i2c_lock_adapter(struct i2c_adapter *adapter) +static void i2c_adapter_lock_bus(struct i2c_adapter *adapter, int flags) { struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); @@ -972,27 +974,30 @@ void i2c_lock_adapter(struct i2c_adapter *adapter) else rt_mutex_lock(&adapter->bus_lock); } -EXPORT_SYMBOL_GPL(i2c_lock_adapter); /** - * i2c_trylock_adapter - Try to get exclusive access to an I2C bus segment + * i2c_adapter_trylock_bus - Try to get exclusive access to an I2C bus segment * @adapter: Target I2C bus segment + * @flags: I2C_LOCK_ADAPTER trylocks the root i2c adapter, I2C_LOCK_SEGMENT + * trylocks only this branch in the adapter tree */ -static int i2c_trylock_adapter(struct i2c_adapter *adapter) +static int i2c_adapter_trylock_bus(struct i2c_adapter *adapter, int flags) { struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); if (parent) - return i2c_trylock_adapter(parent); + return parent->trylock_bus(parent, flags); else return rt_mutex_trylock(&adapter->bus_lock); } /** - * i2c_unlock_adapter - Release exclusive access to an I2C bus segment + * i2c_adapter_unlock_bus - Release exclusive access to an I2C bus segment * @adapter: Target I2C bus segment + * @flags: I2C_LOCK_ADAPTER unlocks the root i2c adapter, I2C_LOCK_SEGMENT + * unlocks only this branch in the adapter tree */ -void i2c_unlock_adapter(struct i2c_adapter *adapter) +static void i2c_adapter_unlock_bus(struct i2c_adapter *adapter, int flags) { struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); @@ -1001,7 +1006,6 @@ void i2c_unlock_adapter(struct i2c_adapter *adapter) else rt_mutex_unlock(&adapter->bus_lock); } -EXPORT_SYMBOL_GPL(i2c_unlock_adapter); static void i2c_dev_set_name(struct i2c_adapter *adap, struct i2c_client *client) @@ -1547,6 +1551,12 @@ static int i2c_register_adapter(struct i2c_adapter *adap) return -EINVAL; } + if (!adap->lock_bus) { + adap->lock_bus = i2c_adapter_lock_bus; + adap->trylock_bus = i2c_adapter_trylock_bus; + adap->unlock_bus = i2c_adapter_unlock_bus; + } + rt_mutex_init(&adap->bus_lock); mutex_init(&adap->userspace_clients_lock); INIT_LIST_HEAD(&adap->userspace_clients); @@ -2315,16 +2325,16 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) #endif if (in_atomic() || irqs_disabled()) { - ret = i2c_trylock_adapter(adap); + ret = adap->trylock_bus(adap, I2C_LOCK_SEGMENT); if (!ret) /* I2C activity is ongoing. */ return -EAGAIN; } else { - i2c_lock_adapter(adap); + i2c_lock_bus(adap, I2C_LOCK_SEGMENT); } ret = __i2c_transfer(adap, msgs, num); - i2c_unlock_adapter(adap); + i2c_unlock_bus(adap, I2C_LOCK_SEGMENT); return ret; } else { @@ -3099,7 +3
[PATCH v6 21/24] [media] si2168: change the i2c gate to be mux-locked
From: Antti Palosaari The root i2c adapter lock is then no longer held by the i2c mux during accesses behind the i2c gate, and such accesses need to take that lock just like any other ordinary i2c accesses do. So, declare the i2c gate mux-locked, and zap the code that makes the i2c accesses unlocked. But add a mutex so that firmware commands are still serialized. Signed-off-by: Antti Palosaari Signed-off-by: Peter Rosin --- Documentation/i2c/i2c-topology| 2 +- drivers/media/dvb-frontends/si2168.c | 85 --- drivers/media/dvb-frontends/si2168_priv.h | 1 + 3 files changed, 23 insertions(+), 65 deletions(-) diff --git a/Documentation/i2c/i2c-topology b/Documentation/i2c/i2c-topology index 346623a80bd1..a9ca97df7661 100644 --- a/Documentation/i2c/i2c-topology +++ b/Documentation/i2c/i2c-topology @@ -56,7 +56,7 @@ In drivers/media/ dvb-frontends/m88ds3103 Parent-locked dvb-frontends/rtl2830 Parent-locked dvb-frontends/rtl2832 Parent-locked -dvb-frontends/si2168 Parent-locked +dvb-frontends/si2168 Mux-locked usb/cx231xx/ Parent-locked diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c index ca455d01c71d..2eecbb2ef48e 100644 --- a/drivers/media/dvb-frontends/si2168.c +++ b/drivers/media/dvb-frontends/si2168.c @@ -18,53 +18,23 @@ static const struct dvb_frontend_ops si2168_ops; -/* Own I2C adapter locking is needed because of I2C gate logic. */ -static int si2168_i2c_master_send_unlocked(const struct i2c_client *client, - const char *buf, int count) -{ - int ret; - struct i2c_msg msg = { - .addr = client->addr, - .flags = 0, - .len = count, - .buf = (char *)buf, - }; - - ret = __i2c_transfer(client->adapter, &msg, 1); - return (ret == 1) ? count : ret; -} - -static int si2168_i2c_master_recv_unlocked(const struct i2c_client *client, - char *buf, int count) -{ - int ret; - struct i2c_msg msg = { - .addr = client->addr, - .flags = I2C_M_RD, - .len = count, - .buf = buf, - }; - - ret = __i2c_transfer(client->adapter, &msg, 1); - return (ret == 1) ? count : ret; -} - /* execute firmware command */ -static int si2168_cmd_execute_unlocked(struct i2c_client *client, - struct si2168_cmd *cmd) +static int si2168_cmd_execute(struct i2c_client *client, struct si2168_cmd *cmd) { + struct si2168_dev *dev = i2c_get_clientdata(client); int ret; unsigned long timeout; + mutex_lock(&dev->i2c_mutex); + if (cmd->wlen) { /* write cmd and args for firmware */ - ret = si2168_i2c_master_send_unlocked(client, cmd->args, - cmd->wlen); + ret = i2c_master_send(client, cmd->args, cmd->wlen); if (ret < 0) { - goto err; + goto err_mutex_unlock; } else if (ret != cmd->wlen) { ret = -EREMOTEIO; - goto err; + goto err_mutex_unlock; } } @@ -73,13 +43,12 @@ static int si2168_cmd_execute_unlocked(struct i2c_client *client, #define TIMEOUT 70 timeout = jiffies + msecs_to_jiffies(TIMEOUT); while (!time_after(jiffies, timeout)) { - ret = si2168_i2c_master_recv_unlocked(client, cmd->args, - cmd->rlen); + ret = i2c_master_recv(client, cmd->args, cmd->rlen); if (ret < 0) { - goto err; + goto err_mutex_unlock; } else if (ret != cmd->rlen) { ret = -EREMOTEIO; - goto err; + goto err_mutex_unlock; } /* firmware ready? */ @@ -94,32 +63,23 @@ static int si2168_cmd_execute_unlocked(struct i2c_client *client, /* error bit set? */ if ((cmd->args[0] >> 6) & 0x01) { ret = -EREMOTEIO; - goto err; + goto err_mutex_unlock; } if (!((cmd->args[0] >> 7) & 0x01)) { ret = -ETIMEDOUT; - goto err; + goto err_mutex_unlock; } } + mutex_unlock(&dev->i2c_mutex); return 0; -err: +err_mutex_unlock: + mutex_unlock(&d
[PATCH v6 20/24] iio: imu: inv_mpu6050: change the i2c gate to be mux-locked
From: Peter Rosin The root i2c adapter lock is then no longer held by the i2c mux during accesses behind the i2c gate, and such accesses need to take that lock just like any other ordinary i2c accesses do. So, declare the i2c gate mux-locked, and zap the code that makes the unlocked i2c accesses and just use ordinary regmap_write accesses. This also happens to fix the deadlock described in http://patchwork.ozlabs.org/patch/584776/ authored by Adriana Reus and submitted by Daniel Baluta --8<-- iio: imu: inv_mpu6050: Fix deadlock between i2c adapter lock and mpu lock This deadlock occurs if the accel/gyro and the sensor on the auxiliary I2C (in my setup it's an ak8975) are working at the same time. Scenario: T1T2 inv_mpu6050_read_fifo aux sensor op (eg. ak8975_read_raw) | | mutex_lock(&indio_dev->mlock) i2c_transfer | | i2c transaction i2c adapter lock | | i2c adapter locki2c_mux_master_xfer | inv_mpu6050_select_bypass | mutex_lock(&indio_dev->mlock) When we operate on an mpu sensor the order of locking is mpu lock followed by the i2c adapter lock. However, when we operate the auxiliary sensor the order of locking is the other way around. ... --8<-- The reason this patch fixes the deadlock is that T2 does not grab the i2c adapter lock until the very end (and grabs the newfangled i2c mux lock where it previously grabbed the i2c adapter lock). Signed-off-by: Peter Rosin --- Documentation/i2c/i2c-topology| 2 +- drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c | 56 +++ 2 files changed, 13 insertions(+), 45 deletions(-) diff --git a/Documentation/i2c/i2c-topology b/Documentation/i2c/i2c-topology index 7a10edd0874f..346623a80bd1 100644 --- a/Documentation/i2c/i2c-topology +++ b/Documentation/i2c/i2c-topology @@ -50,7 +50,7 @@ i2c-mux-pinctrl Normally parent-locked, mux-locked iff i2c-mux-reg Parent-locked In drivers/iio/ -imu/inv_mpu6050/ Parent-locked +imu/inv_mpu6050/ Mux-locked In drivers/media/ dvb-frontends/m88ds3103 Parent-locked diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c index 0d429d788106..71ad31a275c9 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c @@ -24,45 +24,16 @@ static const struct regmap_config inv_mpu_regmap_config = { .val_bits = 8, }; -/* - * The i2c read/write needs to happen in unlocked mode. As the parent - * adapter is common. If we use locked versions, it will fail as - * the mux adapter will lock the parent i2c adapter, while calling - * select/deselect functions. - */ -static int inv_mpu6050_write_reg_unlocked(struct i2c_client *client, - u8 reg, u8 d) -{ - int ret; - u8 buf[2] = {reg, d}; - struct i2c_msg msg[1] = { - { - .addr = client->addr, - .flags = 0, - .len = sizeof(buf), - .buf = buf, - } - }; - - ret = __i2c_transfer(client->adapter, msg, 1); - if (ret != 1) - return ret; - - return 0; -} - static int inv_mpu6050_select_bypass(struct i2c_mux_core *muxc, u32 chan_id) { - struct i2c_client *client = i2c_mux_priv(muxc); - struct iio_dev *indio_dev = dev_get_drvdata(&client->dev); + struct iio_dev *indio_dev = i2c_mux_priv(muxc); struct inv_mpu6050_state *st = iio_priv(indio_dev); int ret = 0; /* Use the same mutex which was used everywhere to protect power-op */ mutex_lock(&indio_dev->mlock); if (!st->powerup_count) { - ret = inv_mpu6050_write_reg_unlocked(client, -st->reg->pwr_mgmt_1, 0); + ret = regmap_write(st->map, st->reg->pwr_mgmt_1, 0); if (ret) goto write_error; @@ -71,10 +42,9 @@ static int inv_mpu6050_select_bypass(struct i2c_mux_core *muxc, u32 chan_id) } if (!ret) { st->powerup_count++; - ret = inv_mpu6050_write_reg_unlocked(client, -st->reg->int_pin_cfg, -INV_MPU6050_INT_PIN_CFG | -IN
[PATCH v6 23/24] [media] rtl2832_sdr: get rid of empty regmap wrappers
From: Peter Rosin Reviewed-by: Antti Palosaari Signed-off-by: Peter Rosin --- drivers/media/dvb-frontends/rtl2832_sdr.c | 302 +- 1 file changed, 132 insertions(+), 170 deletions(-) diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c index 6a6b1debe277..47a480a7d46c 100644 --- a/drivers/media/dvb-frontends/rtl2832_sdr.c +++ b/drivers/media/dvb-frontends/rtl2832_sdr.c @@ -120,6 +120,7 @@ struct rtl2832_sdr_dev { unsigned long flags; struct platform_device *pdev; + struct regmap *regmap; struct video_device vdev; struct v4l2_device v4l2_dev; @@ -164,47 +165,6 @@ struct rtl2832_sdr_dev { unsigned long jiffies_next; }; -/* write multiple registers */ -static int rtl2832_sdr_wr_regs(struct rtl2832_sdr_dev *dev, u16 reg, - const u8 *val, int len) -{ - struct platform_device *pdev = dev->pdev; - struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data; - struct regmap *regmap = pdata->regmap; - - return regmap_bulk_write(regmap, reg, val, len); -} - -#if 0 -/* read multiple registers */ -static int rtl2832_sdr_rd_regs(struct rtl2832_sdr_dev *dev, u16 reg, u8 *val, - int len) -{ - struct platform_device *pdev = dev->pdev; - struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data; - struct regmap *regmap = pdata->regmap; - - return regmap_bulk_read(regmap, reg, val, len); -} -#endif - -/* write single register */ -static int rtl2832_sdr_wr_reg(struct rtl2832_sdr_dev *dev, u16 reg, u8 val) -{ - return rtl2832_sdr_wr_regs(dev, reg, &val, 1); -} - -/* write single register with mask */ -static int rtl2832_sdr_wr_reg_mask(struct rtl2832_sdr_dev *dev, u16 reg, - u8 val, u8 mask) -{ - struct platform_device *pdev = dev->pdev; - struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data; - struct regmap *regmap = pdata->regmap; - - return regmap_update_bits(regmap, reg, mask, val); -} - /* Private functions */ static struct rtl2832_sdr_frame_buf *rtl2832_sdr_get_next_fill_buf( struct rtl2832_sdr_dev *dev) @@ -559,11 +519,11 @@ static int rtl2832_sdr_set_adc(struct rtl2832_sdr_dev *dev) f_sr = dev->f_adc; - ret = rtl2832_sdr_wr_regs(dev, 0x13e, "\x00\x00", 2); + ret = regmap_bulk_write(dev->regmap, 0x13e, "\x00\x00", 2); if (ret) goto err; - ret = rtl2832_sdr_wr_regs(dev, 0x115, "\x00\x00\x00\x00", 4); + ret = regmap_bulk_write(dev->regmap, 0x115, "\x00\x00\x00\x00", 4); if (ret) goto err; @@ -589,7 +549,7 @@ static int rtl2832_sdr_set_adc(struct rtl2832_sdr_dev *dev) buf[1] = (u32tmp >> 8) & 0xff; buf[2] = (u32tmp >> 0) & 0xff; - ret = rtl2832_sdr_wr_regs(dev, 0x119, buf, 3); + ret = regmap_bulk_write(dev->regmap, 0x119, buf, 3); if (ret) goto err; @@ -603,15 +563,15 @@ static int rtl2832_sdr_set_adc(struct rtl2832_sdr_dev *dev) u8tmp2 = 0xcd; /* enable ADC I, ADC Q */ } - ret = rtl2832_sdr_wr_reg(dev, 0x1b1, u8tmp1); + ret = regmap_write(dev->regmap, 0x1b1, u8tmp1); if (ret) goto err; - ret = rtl2832_sdr_wr_reg(dev, 0x008, u8tmp2); + ret = regmap_write(dev->regmap, 0x008, u8tmp2); if (ret) goto err; - ret = rtl2832_sdr_wr_reg(dev, 0x006, 0x80); + ret = regmap_write(dev->regmap, 0x006, 0x80); if (ret) goto err; @@ -622,168 +582,169 @@ static int rtl2832_sdr_set_adc(struct rtl2832_sdr_dev *dev) buf[1] = (u32tmp >> 16) & 0xff; buf[2] = (u32tmp >> 8) & 0xff; buf[3] = (u32tmp >> 0) & 0xff; - ret = rtl2832_sdr_wr_regs(dev, 0x19f, buf, 4); + ret = regmap_bulk_write(dev->regmap, 0x19f, buf, 4); if (ret) goto err; /* low-pass filter */ - ret = rtl2832_sdr_wr_regs(dev, 0x11c, - "\xca\xdc\xd7\xd8\xe0\xf2\x0e\x35\x06\x50\x9c\x0d\x71\x11\x14\x71\x74\x19\x41\xa5", - 20); + ret = regmap_bulk_write(dev->regmap, 0x11c, + "\xca\xdc\xd7\xd8\xe0\xf2\x0e\x35\x06\x50\x9c\x0d\x71\x11\x14\x71\x74\x19\x41\xa5", + 20); if (ret) goto err; - ret = rtl2832_sdr_wr_regs(dev, 0x017, "\x11\x10", 2); + ret = regmap_bulk_write(dev->regmap, 0x017, "\x11\x10", 2); if (ret) goto err; /* mode */ - ret = rtl2832_sdr_wr_regs(dev, 0x019, "\x05", 1); + ret = regmap_write(dev->regmap, 0x019, 0x05); if (ret)
[PATCH v6 24/24] [media] rtl2832: regmap is aware of lockdep, drop local locking hack
From: Peter Rosin Reviewed-by: Antti Palosaari Signed-off-by: Peter Rosin --- drivers/media/dvb-frontends/rtl2832.c | 30 -- drivers/media/dvb-frontends/rtl2832_priv.h | 1 - 2 files changed, 31 deletions(-) diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c index ac5ac5f7a335..23459fed2516 100644 --- a/drivers/media/dvb-frontends/rtl2832.c +++ b/drivers/media/dvb-frontends/rtl2832.c @@ -890,32 +890,6 @@ static bool rtl2832_volatile_reg(struct device *dev, unsigned int reg) return false; } -/* - * FIXME: Hack. Implement own regmap locking in order to silence lockdep - * recursive lock warning. That happens when regmap I2C client calls I2C mux - * adapter, which leads demod I2C repeater enable via demod regmap. Operation - * takes two regmap locks recursively - but those are different regmap instances - * in a two different I2C drivers, so it is not deadlock. Proper fix is to make - * regmap aware of lockdep. - */ -static void rtl2832_regmap_lock(void *__dev) -{ - struct rtl2832_dev *dev = __dev; - struct i2c_client *client = dev->client; - - dev_dbg(&client->dev, "\n"); - mutex_lock(&dev->regmap_mutex); -} - -static void rtl2832_regmap_unlock(void *__dev) -{ - struct rtl2832_dev *dev = __dev; - struct i2c_client *client = dev->client; - - dev_dbg(&client->dev, "\n"); - mutex_unlock(&dev->regmap_mutex); -} - static struct dvb_frontend *rtl2832_get_dvb_frontend(struct i2c_client *client) { struct rtl2832_dev *dev = i2c_get_clientdata(client); @@ -1082,12 +1056,8 @@ static int rtl2832_probe(struct i2c_client *client, dev->sleeping = true; INIT_DELAYED_WORK(&dev->i2c_gate_work, rtl2832_i2c_gate_work); /* create regmap */ - mutex_init(&dev->regmap_mutex); dev->regmap_config.reg_bits = 8, dev->regmap_config.val_bits = 8, - dev->regmap_config.lock = rtl2832_regmap_lock, - dev->regmap_config.unlock = rtl2832_regmap_unlock, - dev->regmap_config.lock_arg = dev, dev->regmap_config.volatile_reg = rtl2832_volatile_reg, dev->regmap_config.max_register = 5 * 0x100, dev->regmap_config.ranges = regmap_range_cfg, diff --git a/drivers/media/dvb-frontends/rtl2832_priv.h b/drivers/media/dvb-frontends/rtl2832_priv.h index d8f97d14f6fd..c1a8a69e9015 100644 --- a/drivers/media/dvb-frontends/rtl2832_priv.h +++ b/drivers/media/dvb-frontends/rtl2832_priv.h @@ -33,7 +33,6 @@ struct rtl2832_dev { struct rtl2832_platform_data *pdata; struct i2c_client *client; - struct mutex regmap_mutex; struct regmap_config regmap_config; struct regmap *regmap; struct i2c_mux_core *muxc; -- 2.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v6 15/24] i2c-mux: drop old unused i2c-mux api
From: Peter Rosin All i2c mux users are using an explicit i2c mux core, drop support for implicit i2c mux cores. Signed-off-by: Peter Rosin --- drivers/i2c/i2c-mux.c | 59 - include/linux/i2c-mux.h | 15 - 2 files changed, 74 deletions(-) diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c index d95eb66e11bf..08d99e776a69 100644 --- a/drivers/i2c/i2c-mux.c +++ b/drivers/i2c/i2c-mux.c @@ -28,12 +28,6 @@ #include /* multiplexer per channel data */ -struct i2c_mux_priv_old { - void *mux_priv; - int (*select)(struct i2c_adapter *, void *mux_priv, u32 chan_id); - int (*deselect)(struct i2c_adapter *, void *mux_priv, u32 chan_id); -}; - struct i2c_mux_priv { struct i2c_adapter adap; struct i2c_algorithm algo; @@ -290,48 +284,6 @@ struct i2c_mux_core *i2c_mux_one_adapter(struct i2c_adapter *parent, } EXPORT_SYMBOL_GPL(i2c_mux_one_adapter); -static int i2c_mux_select(struct i2c_mux_core *muxc, u32 chan) -{ - struct i2c_mux_priv_old *priv = i2c_mux_priv(muxc); - - return priv->select(muxc->parent, priv->mux_priv, chan); -} - -static int i2c_mux_deselect(struct i2c_mux_core *muxc, u32 chan) -{ - struct i2c_mux_priv_old *priv = i2c_mux_priv(muxc); - - return priv->deselect(muxc->parent, priv->mux_priv, chan); -} - -struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, - struct device *mux_dev, void *mux_priv, - u32 force_nr, u32 chan_id, - unsigned int class, - int (*select)(struct i2c_adapter *, - void *, u32), - int (*deselect)(struct i2c_adapter *, - void *, u32)) -{ - struct i2c_mux_core *muxc; - struct i2c_mux_priv_old *priv; - - muxc = i2c_mux_one_adapter(parent, mux_dev, sizeof(*priv), 0, - force_nr, chan_id, class, - i2c_mux_select, - deselect ? i2c_mux_deselect : NULL); - if (IS_ERR(muxc)) - return NULL; - - priv = i2c_mux_priv(muxc); - priv->select = select; - priv->deselect = deselect; - priv->mux_priv = mux_priv; - - return muxc->adapter[0]; -} -EXPORT_SYMBOL_GPL(i2c_add_mux_adapter); - void i2c_mux_del_adapters(struct i2c_mux_core *muxc) { char symlink_name[20]; @@ -353,17 +305,6 @@ void i2c_mux_del_adapters(struct i2c_mux_core *muxc) } EXPORT_SYMBOL_GPL(i2c_mux_del_adapters); -void i2c_del_mux_adapter(struct i2c_adapter *adap) -{ - struct i2c_mux_priv *priv = adap->algo_data; - struct i2c_mux_core *muxc = priv->muxc; - - i2c_mux_del_adapters(muxc); - devm_kfree(muxc->dev, muxc->adapter); - devm_kfree(muxc->dev, muxc); -} -EXPORT_SYMBOL_GPL(i2c_del_mux_adapter); - MODULE_AUTHOR("Rodolfo Giometti "); MODULE_DESCRIPTION("I2C driver for multiplexed I2C busses"); MODULE_LICENSE("GPL v2"); diff --git a/include/linux/i2c-mux.h b/include/linux/i2c-mux.h index 0d97d7a3f03c..25c88ccf9c38 100644 --- a/include/linux/i2c-mux.h +++ b/include/linux/i2c-mux.h @@ -55,20 +55,6 @@ int i2c_mux_reserve_adapters(struct i2c_mux_core *muxc, int adapters); /* * Called to create a i2c bus on a multiplexed bus segment. - * The mux_dev and chan_id parameters are passed to the select - * and deselect callback functions to perform hardware-specific - * mux control. - */ -struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, - struct device *mux_dev, - void *mux_priv, u32 force_nr, u32 chan_id, - unsigned int class, - int (*select) (struct i2c_adapter *, - void *mux_dev, u32 chan_id), - int (*deselect) (struct i2c_adapter *, -void *mux_dev, u32 chan_id)); -/* - * Called to create a i2c bus on a multiplexed bus segment. * The chan_id parameter is passed to the select and deselect * callback functions to perform hardware-specific mux control. */ @@ -88,7 +74,6 @@ struct i2c_mux_core *i2c_mux_one_adapter(struct i2c_adapter *parent, int (*deselect)(struct i2c_mux_core *, u32)); -void i2c_del_mux_adapter(struct i2c_adapter *adap); void i2c_mux_del_adapters(struct i2c_mux_core *muxc); #endif /* __KERNEL__ */ -- 2.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v6 14/24] of/unittest: convert to use an explicit i2c mux core
From: Peter Rosin Allocate an explicit i2c mux core to handle parent and child adapters etc. Update the select op to be in terms of the i2c mux core instead of the child adapter. Signed-off-by: Peter Rosin --- drivers/of/unittest.c | 40 +++- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c index e986e6ee52e0..84a65b711e8c 100644 --- a/drivers/of/unittest.c +++ b/drivers/of/unittest.c @@ -1692,13 +1692,7 @@ static struct i2c_driver unittest_i2c_dev_driver = { #if IS_BUILTIN(CONFIG_I2C_MUX) -struct unittest_i2c_mux_data { - int nchans; - struct i2c_adapter *adap[]; -}; - -static int unittest_i2c_mux_select_chan(struct i2c_adapter *adap, - void *client, u32 chan) +static int unittest_i2c_mux_select_chan(struct i2c_mux_core *muxc, u32 chan) { return 0; } @@ -1706,11 +1700,11 @@ static int unittest_i2c_mux_select_chan(struct i2c_adapter *adap, static int unittest_i2c_mux_probe(struct i2c_client *client, const struct i2c_device_id *id) { - int ret, i, nchans, size; + int ret, i, nchans; struct device *dev = &client->dev; struct i2c_adapter *adap = to_i2c_adapter(dev->parent); struct device_node *np = client->dev.of_node, *child; - struct unittest_i2c_mux_data *stm; + struct i2c_mux_core *muxc; u32 reg, max_reg; dev_dbg(dev, "%s for node @%s\n", __func__, np->full_name); @@ -1734,25 +1728,23 @@ static int unittest_i2c_mux_probe(struct i2c_client *client, return -EINVAL; } - size = offsetof(struct unittest_i2c_mux_data, adap[nchans]); - stm = devm_kzalloc(dev, size, GFP_KERNEL); - if (!stm) { - dev_err(dev, "Out of memory\n"); + muxc = i2c_mux_alloc(adap, dev, 0, 0, +unittest_i2c_mux_select_chan, NULL); + if (!muxc) return -ENOMEM; - } - stm->nchans = nchans; + ret = i2c_mux_reserve_adapters(muxc, nchans); + if (ret) + return ret; for (i = 0; i < nchans; i++) { - stm->adap[i] = i2c_add_mux_adapter(adap, dev, client, - 0, i, 0, unittest_i2c_mux_select_chan, NULL); - if (!stm->adap[i]) { + ret = i2c_mux_add_adapter(muxc, 0, i, 0); + if (ret) { dev_err(dev, "Failed to register mux #%d\n", i); - for (i--; i >= 0; i--) - i2c_del_mux_adapter(stm->adap[i]); + i2c_mux_del_adapters(muxc); return -ENODEV; } } - i2c_set_clientdata(client, stm); + i2c_set_clientdata(client, muxc); return 0; }; @@ -1761,12 +1753,10 @@ static int unittest_i2c_mux_remove(struct i2c_client *client) { struct device *dev = &client->dev; struct device_node *np = client->dev.of_node; - struct unittest_i2c_mux_data *stm = i2c_get_clientdata(client); - int i; + struct i2c_mux_core *muxc = i2c_get_clientdata(client); dev_dbg(dev, "%s for node @%s\n", __func__, np->full_name); - for (i = stm->nchans - 1; i >= 0; i--) - i2c_del_mux_adapter(stm->adap[i]); + i2c_mux_del_adapters(muxc); return 0; } -- 2.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html