[PATCH V2 2/4] exynos4-is: Move firmware request to subdev open()

From: Sylwester Nawrocki
Date: Thu May 08 2014 - 13:36:20 EST


Move the firmware request to the FIMC-IS-ISP subdev open callback
so we can avoid crashed on error paths in probe and deferred probing,
when firmware request was sheduled and is in progress while the
fimc-is device is being unregistered and its driver detached.

Signed-off-by: Sylwester Nawrocki <s.nawrocki@xxxxxxxxxxx>
Acked-by: Kyungmin Park <kyungmin.park@xxxxxxxxxxx>
---
drivers/media/platform/exynos4-is/fimc-is.c | 31 ++++++++------------------
drivers/media/platform/exynos4-is/fimc-is.h | 2 +-
drivers/media/platform/exynos4-is/fimc-isp.c | 10 ++++++++-
3 files changed, 19 insertions(+), 24 deletions(-)

diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c
index 5476dce..6bbb6ca 100644
--- a/drivers/media/platform/exynos4-is/fimc-is.c
+++ b/drivers/media/platform/exynos4-is/fimc-is.c
@@ -374,22 +374,22 @@ static void fimc_is_free_cpu_memory(struct fimc_is *is)
is->memory.paddr);
}

-static void fimc_is_load_firmware(const struct firmware *fw, void *context)
+int fimc_is_request_firmware(struct fimc_is *is, const char *fw_name)
{
- struct fimc_is *is = context;
+ const struct firmware *fw;
struct device *dev = &is->pdev->dev;
void *buf;
int ret;

- if (fw == NULL) {
+ ret = request_firmware(&fw, fw_name, &is->pdev->dev);
+ if (ret < 0) {
dev_err(dev, "firmware request failed\n");
- return;
+ return ret;
}
- mutex_lock(&is->lock);

if (fw->size < FIMC_IS_FW_SIZE_MIN || fw->size > FIMC_IS_FW_SIZE_MAX) {
dev_err(dev, "wrong firmware size: %d\n", fw->size);
- goto done;
+ return -EINVAL;
}

is->fw.size = fw->size;
@@ -397,7 +397,7 @@ static void fimc_is_load_firmware(const struct firmware *fw, void *context)
ret = fimc_is_alloc_cpu_memory(is);
if (ret < 0) {
dev_err(dev, "failed to allocate FIMC-IS CPU memory\n");
- goto done;
+ return -ENOMEM;
}

memcpy(is->memory.vaddr, fw->data, fw->size);
@@ -430,16 +430,9 @@ static void fimc_is_load_firmware(const struct firmware *fw, void *context)
*/
if (is->fw.f_w)
release_firmware(is->fw.f_w);
- is->fw.f_w = fw;
-done:
- mutex_unlock(&is->lock);
-}

-static int fimc_is_request_firmware(struct fimc_is *is, const char *fw_name)
-{
- return request_firmware_nowait(THIS_MODULE,
- FW_ACTION_HOTPLUG, fw_name, &is->pdev->dev,
- GFP_KERNEL, is, fimc_is_load_firmware);
+ is->fw.f_w = fw;
+ return 0;
}

/* General IS interrupt handler */
@@ -795,7 +788,6 @@ static int fimc_is_probe(struct platform_device *pdev)

init_waitqueue_head(&is->irq_queue);
spin_lock_init(&is->slock);
- mutex_init(&is->lock);

ret = of_address_to_resource(dev->of_node, 0, &res);
if (ret < 0)
@@ -859,17 +851,12 @@ static int fimc_is_probe(struct platform_device *pdev)
if (ret < 0)
goto err_sd;

- ret = fimc_is_request_firmware(is, FIMC_IS_FW_FILENAME);
- if (ret < 0)
- goto err_dfs;

pm_runtime_put_sync(dev);

dev_dbg(dev, "FIMC-IS registered successfully\n");
return 0;

-err_dfs:
- fimc_is_debugfs_remove(is);
err_sd:
fimc_is_unregister_subdevs(is);
err_vb:
diff --git a/drivers/media/platform/exynos4-is/fimc-is.h b/drivers/media/platform/exynos4-is/fimc-is.h
index e0be691..16afd9c 100644
--- a/drivers/media/platform/exynos4-is/fimc-is.h
+++ b/drivers/media/platform/exynos4-is/fimc-is.h
@@ -234,7 +234,6 @@ struct chain_config {
* @pctrl: pointer to pinctrl structure for this device
* @v4l2_dev: pointer to top the level v4l2_device
* @alloc_ctx: videobuf2 memory allocator context
- * @lock: mutex serializing video device and the subdev operations
* @slock: spinlock protecting this data structure and the hw registers
* @clocks: FIMC-LITE gate clock
* @regs: MCUCTL mmapped registers region
@@ -336,6 +335,7 @@ static inline u32 pmuisp_read(struct fimc_is *is, unsigned int offset)

int fimc_is_wait_event(struct fimc_is *is, unsigned long bit,
unsigned int state, unsigned int timeout);
+int fimc_is_request_firmware(struct fimc_is *is, const char *fw_name);
int fimc_is_cpu_set_power(struct fimc_is *is, int on);
int fimc_is_start_firmware(struct fimc_is *is);
int fimc_is_hw_initialize(struct fimc_is *is);
diff --git a/drivers/media/platform/exynos4-is/fimc-isp.c b/drivers/media/platform/exynos4-is/fimc-isp.c
index be62d6b..915c46b 100644
--- a/drivers/media/platform/exynos4-is/fimc-isp.c
+++ b/drivers/media/platform/exynos4-is/fimc-isp.c
@@ -366,8 +366,11 @@ static int fimc_isp_subdev_s_power(struct v4l2_subdev *sd, int on)
static int fimc_isp_subdev_open(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh)
{
+ struct fimc_isp *isp = v4l2_get_subdevdata(sd);
+ struct fimc_is *is = fimc_isp_to_is(isp);
struct v4l2_mbus_framefmt fmt;
struct v4l2_mbus_framefmt *format;
+ int ret;

format = v4l2_subdev_get_try_format(fh, FIMC_ISP_SD_PAD_SINK);

@@ -386,7 +389,12 @@ static int fimc_isp_subdev_open(struct v4l2_subdev *sd,
format = v4l2_subdev_get_try_format(fh, FIMC_ISP_SD_PAD_SRC_DMA);
*format = fmt;

- return 0;
+ mutex_lock(&isp->subdev_lock);
+ if (is->fw.f_w == NULL)
+ ret = fimc_is_request_firmware(is, FIMC_IS_FW_FILENAME);
+
+ mutex_unlock(&isp->subdev_lock);
+ return ret;
}

static int fimc_isp_subdev_registered(struct v4l2_subdev *sd)
--
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/