All of lore.kernel.org
 help / color / mirror / Atom feed
From: Archit Taneja <archit@ti.com>
To: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Cc: <linux-media@vger.kernel.org>, <linux-omap@vger.kernel.org>,
	<dagriego@biglakesoftware.com>, <dale@farnsworth.org>,
	<pawel@osciak.com>, <m.szyprowski@samsung.com>,
	<hverkuil@xs4all.nl>, <tomi.valkeinen@ti.com>
Subject: Re: [PATCH 1/6] v4l: ti-vpe: Create a vpdma helper library
Date: Wed, 14 Aug 2013 16:27:57 +0530	[thread overview]
Message-ID: <520B62B5.8080000@ti.com> (raw)
In-Reply-To: <7062944.SGK3kvnN1v@avalon>

On Friday 09 August 2013 03:34 AM, Laurent Pinchart wrote:
> Hi Archit,
>
> Thank you for the patch.
>
> On Friday 02 August 2013 19:33:38 Archit Taneja wrote:
>> The primary function of VPDMA is to move data between external memory and
>> internal processing modules(in our case, VPE) that source or sink data.
>> VPDMA is capable of buffering this data and then delivering the data as
>> demanded to the modules as programmed. The modules that source or sink data
>> are referred to as clients or ports. A channel is setup inside the VPDMA to
>> connect a specific memory buffer to a specific client. The VPDMA
>> centralizes the DMA control functions and buffering required to allow all
>> the clients to minimize the effect of long latency times.
>>
>> Add the following to the VPDMA helper:
>>
>> - A data struct which describe VPDMA channels. For now, these channels are
>> the ones used only by VPE, the list of channels will increase when
>> VIP(Video Input Port) also uses the VPDMA library. This channel information
>> will be used to populate fields required by data descriptors.
>>
>> - Data structs which describe the different data types supported by VPDMA.
>> This data type information will be used to populate fields required by data
>> descriptors and used by the VPE driver to map a V4L2 format to the
>> corresponding VPDMA data type.
>>
>> - Provide VPDMA register offset definitions, functions to read, write and
>> modify VPDMA registers.
>>
>> - Functions to create and submit a VPDMA list. A list is a group of
>> descriptors that makes up a set of DMA transfers that need to be completed.
>> Each descriptor will either perform a DMA transaction to fetch input
>> buffers and write to output buffers(data descriptors), or configure the
>> MMRs of sub blocks of VPE(configuration descriptors), or provide control
>> information to VPDMA (control descriptors).
>>
>> - Functions to allocate, map and unmap buffers needed for the descriptor
>> list, payloads containing MMR values and motion vector buffers. These use
>> the DMA mapping APIs to ensure exclusive access to VPDMA.
>>
>> - Functions to enable VPDMA interrupts. VPDMA can trigger an interrupt on
>> the VPE interrupt line when a descriptor list is parsed completely and the
>> DMA transactions are completed. This requires masking the events in VPDMA
>> registers and configuring some top level VPE interrupt registers.
>>
>> - Enable some VPDMA specific parameters: frame start event(when to start DMA
>> for a client) and line mode(whether each line fetched should be mirrored or
>> not).
>>
>> - Function to load firmware required by VPDMA. VPDMA requires a firmware for
>> it's internal list manager. We add the required request_firmware apis to
>> fetch this firmware from user space.
>>
>> - Function to dump VPDMA registers.
>>
>> - A function to initialize VPDMA, this will be called by the VPE driver with
>> it's platform device pointer, this function will take care of loading VPDMA
>> firmware and returning a handle back to the VPE driver. The VIP driver will
>> also call the same init function to initialize it's own VPDMA instance.
>>
>> Signed-off-by: Archit Taneja <archit@ti.com>
>> ---
>>   drivers/media/platform/ti-vpe/vpdma.c      | 589 ++++++++++++++++++++++++++
>>   drivers/media/platform/ti-vpe/vpdma.h      | 154 ++++++++
>>   drivers/media/platform/ti-vpe/vpdma_priv.h | 119 ++++++
>>   3 files changed, 862 insertions(+)
>>   create mode 100644 drivers/media/platform/ti-vpe/vpdma.c
>>   create mode 100644 drivers/media/platform/ti-vpe/vpdma.h
>>   create mode 100644 drivers/media/platform/ti-vpe/vpdma_priv.h
>>
>> diff --git a/drivers/media/platform/ti-vpe/vpdma.c
>> b/drivers/media/platform/ti-vpe/vpdma.c new file mode 100644
>> index 0000000..b15b3dd
>> --- /dev/null
>> +++ b/drivers/media/platform/ti-vpe/vpdma.c
>> @@ -0,0 +1,589 @@
>
> [snip]
>
>> +static int get_field(u32 value, u32 mask, int shift)
>> +{
>> +	return (value & (mask << shift)) >> shift;
>> +}
>> +
>> +static int get_field_reg(struct vpdma_data *vpdma, int offset,
>> +		u32 mask, int shift)
>
> I would call this read_field_reg().
>
>> +{
>> +	return get_field(read_reg(vpdma, offset), mask, shift);
>> +}
>> +
>> +static void insert_field(u32 *valp, u32 field, u32 mask, int shift)
>> +{
>> +	u32 val = *valp;
>> +
>> +	val &= ~(mask << shift);
>> +	val |= (field & mask) << shift;
>> +	*valp = val;
>> +}
>
> get_field() and insert_field() are used in a single location, you can manually
> inline them.

Thanks, I'll make these modifications.

>
>> +static void insert_field_reg(struct vpdma_data *vpdma, int offset, u32
>> field,
>> +		u32 mask, int shift)
>> +{
>> +	u32 val = read_reg(vpdma, offset);
>> +
>> +	insert_field(&val, field, mask, shift);
>> +
>> +	write_reg(vpdma, offset, val);
>> +}
>
> [snip]
>
>> +/*
>> + * Allocate a DMA buffer
>> + */
>> +int vpdma_buf_alloc(struct vpdma_buf *buf, size_t size)
>> +{
>> +	buf->size = size;
>> +	buf->mapped = 0;
>> +	buf->addr = kzalloc(size, GFP_KERNEL);
>
> You should use the dma allocation API (depending on your needs,
> dma_alloc_coherent for instance) to allocate DMA-able buffers.

I'm not sure about this, dma_map_single() api works fine on kzalloc'd 
buffers. The above function is used to allocate small contiguous 
buffers(never more than 1024 bytes) needed for descriptors for the DMA 
IP. I thought of using DMA pool, but that creates small buffers of the 
same size. So I finally went with kzalloc.

>
>> +	if (!buf->addr)
>> +		return -ENOMEM;
>> +
>> +	WARN_ON((u32) buf->addr & VPDMA_DESC_ALIGN);
>> +
>> +	return 0;
>> +}
>> +
>> +void vpdma_buf_free(struct vpdma_buf *buf)
>> +{
>> +	WARN_ON(buf->mapped != 0);
>> +	kfree(buf->addr);
>> +	buf->addr = NULL;
>> +	buf->size = 0;
>> +}
>> +
>> +/*
>> + * map a DMA buffer, enabling DMA access
>> + */
>> +void vpdma_buf_map(struct vpdma_data *vpdma, struct vpdma_buf *buf)
>> +{
>> +	struct device *dev = &vpdma->pdev->dev;
>> +
>> +	WARN_ON(buf->mapped != 0);
>> +	buf->dma_addr = dma_map_single(dev, buf->addr, buf->size,
>> +				DMA_TO_DEVICE);
>> +	buf->mapped = 1;
>> +	BUG_ON(dma_mapping_error(dev, buf->dma_addr));
>
> BUG_ON() is too harsh, you should return a proper error instead.

Right, I'll fix this.

>
>> +}
>> +
>> +/*
>> + * unmap a DMA buffer, disabling DMA access and
>> + * allowing the main processor to acces the data
>> + */
>> +void vpdma_buf_unmap(struct vpdma_data *vpdma, struct vpdma_buf *buf)
>> +{
>> +	struct device *dev = &vpdma->pdev->dev;
>> +
>> +	if (buf->mapped)
>> +		dma_unmap_single(dev, buf->dma_addr, buf->size, DMA_TO_DEVICE);
>> +
>> +	buf->mapped = 0;
>> +}
>> +
>> +/*
>> + * create a descriptor list, the user of this list will append
>> configuration,
>> + * contorl and data descriptors to this list, this list will be submitted
>
> s/contorl/control/
>
>> to
>> + * VPDMA. VPDMA's list parser will go through each descriptor and perform
>> + * the required DMA operations
>> + */
>> +int vpdma_create_desc_list(struct vpdma_desc_list *list, size_t size, int
>> type)
>> +{
>> +	int r;
>> +
>> +	r = vpdma_buf_alloc(&list->buf, size);
>> +	if (r)
>> +		return r;
>> +
>> +	list->next = list->buf.addr;
>> +
>> +	list->type = type;
>> +
>> +	return 0;
>> +}
>> +
>> +/*
>> + * once a descriptor list is parsed by VPDMA, we reset the list by emptying
>> it,
>> + * to allow new descriptors to be added to the list.
>> + */
>> +void vpdma_reset_desc_list(struct vpdma_desc_list *list)
>> +{
>> +	list->next = list->buf.addr;
>> +}
>> +
>> +/*
>> + * free the buffer allocated fot the VPDMA descriptor list, this should be
>> + * called when the user doesn't want to use VPDMA any more.
>> + */
>> +void vpdma_free_desc_list(struct vpdma_desc_list *list)
>> +{
>> +	vpdma_buf_free(&list->buf);
>> +
>> +	list->next = NULL;
>> +}
>> +
>> +static int vpdma_list_busy(struct vpdma_data *vpdma, int list_num)
>
> Should the function return a bool instead of an int ?

Yes, a bool would be better here.
>
>> +{
>> +	u32 sync_reg = read_reg(vpdma, VPDMA_LIST_STAT_SYNC);
>> +
>> +	return (sync_reg >> (list_num + 16)) & 0x01;
>
> You could shorten that as
>
> 	return read_reg(vpdma, VPDMA_LIST_STAT_SYNC) & BIT(list_num + 16);

yes, that does look better, I'll modify it.

>
>> +}
>> +
>> +/*
>> + * submit a list of DMA descriptors to the VPE VPDMA, do not wait for
>> completion
>> + */
>> +int vpdma_submit_descs(struct vpdma_data *vpdma, struct vpdma_desc_list
>> *list)
>> +{
>> +	/* we always use the first list */
>> +	int list_num = 0;
>> +	int list_size;
>> +
>> +	if (vpdma_list_busy(vpdma, list_num))
>> +		return -EBUSY;
>> +
>> +	/* 16-byte granularity */
>> +	list_size = (list->next - list->buf.addr) >> 4;
>> +
>> +	write_reg(vpdma, VPDMA_LIST_ADDR, (u32) list->buf.dma_addr);
>> +	wmb();
>> +	write_reg(vpdma, VPDMA_LIST_ATTR,
>> +			(list_num << VPDMA_LIST_NUM_SHFT) |
>> +			(list->type << VPDMA_LIST_TYPE_SHFT) |
>> +			list_size);
>> +
>> +	return 0;
>> +}
>> +
>> +/* set or clear the mask for list complete interrupt */
>> +void vpdma_enable_list_complete_irq(struct vpdma_data *vpdma, int list_num,
>> +		bool enable)
>> +{
>> +	u32 val;
>> +
>> +	val = read_reg(vpdma, VPDMA_INT_LIST0_MASK);
>> +	if (enable)
>> +		val |= (1 << (list_num * 2));
>> +	else
>> +		val &= ~(1 << (list_num * 2));
>> +	write_reg(vpdma, VPDMA_INT_LIST0_MASK, val);
>> +}
>> +
>> +/* clear previosuly occured list intterupts in the LIST_STAT register */
>> +void vpdma_clear_list_stat(struct vpdma_data *vpdma)
>> +{
>> +	write_reg(vpdma, VPDMA_INT_LIST0_STAT,
>> +		read_reg(vpdma, VPDMA_INT_LIST0_STAT));
>> +}
>> +
>> +/*
>> + * configures the output mode of the line buffer for the given client, the
>> + * line buffer content can either be mirrored(each line repeated twice) or
>> + * passed to the client as is
>> + */
>> +void vpdma_set_line_mode(struct vpdma_data *vpdma, int line_mode,
>> +		enum vpdma_channel chan)
>> +{
>> +	int client_cstat = chan_info[chan].cstat_offset;
>> +
>> +	insert_field_reg(vpdma, client_cstat, line_mode,
>> +		VPDMA_CSTAT_LINE_MODE_MASK, VPDMA_CSTAT_LINE_MODE_SHIFT);
>> +}
>> +
>> +/*
>> + * configures the event which should trigger VPDMA transfer for the given
>> + * client
>> + */
>> +void vpdma_set_frame_start_event(struct vpdma_data *vpdma,
>> +		enum vpdma_frame_start_event fs_event,
>> +		enum vpdma_channel chan)
>> +{
>> +	int client_cstat = chan_info[chan].cstat_offset;
>> +
>> +	insert_field_reg(vpdma, client_cstat, fs_event,
>> +		VPDMA_CSTAT_FRAME_START_MASK, VPDMA_CSTAT_FRAME_START_SHIFT);
>> +}
>> +
>> +static void vpdma_firmware_cb(const struct firmware *f, void *context)
>> +{
>> +	struct vpdma_data *vpdma = context;
>> +	struct vpdma_buf fw_dma_buf;
>> +	int i, r;
>> +
>> +	dev_dbg(&vpdma->pdev->dev, "firmware callback\n");
>> +
>> +	if (!f || !f->data) {
>> +		dev_err(&vpdma->pdev->dev, "couldn't get firmware\n");
>> +		return;
>> +	}
>> +
>> +	/* already initialized */
>> +	if (get_field_reg(vpdma, VPDMA_LIST_ATTR, VPDMA_LIST_RDY_MASK,
>> +			VPDMA_LIST_RDY_SHFT)) {
>> +		vpdma->ready = true;
>> +		return;
>> +	}
>> +
>> +	r = vpdma_buf_alloc(&fw_dma_buf, f->size);
>> +	if (r) {
>> +		dev_err(&vpdma->pdev->dev,
>> +			"failed to allocate dma buffer for firmware\n");
>> +		goto rel_fw;
>> +	}
>> +
>> +	memcpy(fw_dma_buf.addr, f->data, f->size);
>> +
>> +	vpdma_buf_map(vpdma, &fw_dma_buf);
>> +
>> +	write_reg(vpdma, VPDMA_LIST_ADDR, (u32) fw_dma_buf.dma_addr);
>> +
>> +	for (i = 0; i < 100; i++) {		/* max 1 second */
>> +		msleep_interruptible(10);
>> +
>> +		if (get_field_reg(vpdma, VPDMA_LIST_ATTR, VPDMA_LIST_RDY_MASK,
>> +				VPDMA_LIST_RDY_SHFT))
>> +			break;
>> +	}
>> +
>> +	if (i == 100) {
>> +		dev_err(&vpdma->pdev->dev, "firmware upload failed\n");
>> +		goto free_buf;
>> +	}
>> +
>> +	vpdma->ready = true;
>> +
>> +free_buf:
>> +	vpdma_buf_unmap(vpdma, &fw_dma_buf);
>> +
>> +	vpdma_buf_free(&fw_dma_buf);
>> +rel_fw:
>> +	release_firmware(f);
>> +}
>> +
>> +static int vpdma_load_firmware(struct vpdma_data *vpdma)
>> +{
>> +	int r;
>> +	struct device *dev = &vpdma->pdev->dev;
>> +
>> +	r = request_firmware_nowait(THIS_MODULE, 1,
>> +		(const char *) VPDMA_FIRMWARE, dev, GFP_KERNEL, vpdma,
>> +		vpdma_firmware_cb);
>
> Is there a reason not to use the synchronous interface ? That would simplify
> both this code and the callers, as they won't have to check whether the
> firmware has been correctly loaded.

I'm not clear what you mean by that, the firmware would be stored in the 
filesystem. If the driver is built-in, then the synchronous interface 
wouldn't work unless the firmware is appended to the kernel image. Am I 
missing something here? I'm not very aware of the firmware api.


>> +	if (r) {
>> +		dev_err(dev, "firmware not available %s\n", VPDMA_FIRMWARE);
>> +		return r;
>> +	} else {
>> +		dev_info(dev, "loading firmware %s\n", VPDMA_FIRMWARE);
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +int vpdma_init(struct platform_device *pdev, struct vpdma_data **pvpdma)
>
> As the function allocates the vpdma instance, I would call it vpdma_create()
> and make it turn a struct vpdma_data *. You can then return error codes using
> ERR_PTR().

Yes, that makes quite more sense. I'll use your approach.

>
>> +{
>> +	struct resource *res;
>> +	struct vpdma_data *vpdma;
>> +	int r;
>> +
>> +	dev_dbg(&pdev->dev, "vpdma_init\n");
>> +
>> +	vpdma = devm_kzalloc(&pdev->dev, sizeof(*vpdma), GFP_KERNEL);
>> +	if (!vpdma) {
>> +		dev_err(&pdev->dev, "couldn't alloc vpdma_dev\n");
>> +		return -ENOMEM;
>> +	}
>> +
>> +	vpdma->pdev = pdev;
>> +
>> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpdma");
>> +	if (res == NULL) {
>> +		dev_err(&pdev->dev, "missing platform resources data\n");
>> +		return -ENODEV;
>> +	}
>> +
>> +	vpdma->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
>
> You can use devm_ioremap_resource(). The function checks the res pointer and
> prints error messages, so you can remove the res == NULL check above and the
> dev_err() below.

Ah nice, I'll use that one.

Thanks a lot for the comments!

Archit


WARNING: multiple messages have this Message-ID (diff)
From: Archit Taneja <archit@ti.com>
To: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Cc: linux-media@vger.kernel.org, linux-omap@vger.kernel.org,
	dagriego@biglakesoftware.com, dale@farnsworth.org,
	pawel@osciak.com, m.szyprowski@samsung.com, hverkuil@xs4all.nl,
	tomi.valkeinen@ti.com
Subject: Re: [PATCH 1/6] v4l: ti-vpe: Create a vpdma helper library
Date: Wed, 14 Aug 2013 16:27:57 +0530	[thread overview]
Message-ID: <520B62B5.8080000@ti.com> (raw)
In-Reply-To: <7062944.SGK3kvnN1v@avalon>

On Friday 09 August 2013 03:34 AM, Laurent Pinchart wrote:
> Hi Archit,
>
> Thank you for the patch.
>
> On Friday 02 August 2013 19:33:38 Archit Taneja wrote:
>> The primary function of VPDMA is to move data between external memory and
>> internal processing modules(in our case, VPE) that source or sink data.
>> VPDMA is capable of buffering this data and then delivering the data as
>> demanded to the modules as programmed. The modules that source or sink data
>> are referred to as clients or ports. A channel is setup inside the VPDMA to
>> connect a specific memory buffer to a specific client. The VPDMA
>> centralizes the DMA control functions and buffering required to allow all
>> the clients to minimize the effect of long latency times.
>>
>> Add the following to the VPDMA helper:
>>
>> - A data struct which describe VPDMA channels. For now, these channels are
>> the ones used only by VPE, the list of channels will increase when
>> VIP(Video Input Port) also uses the VPDMA library. This channel information
>> will be used to populate fields required by data descriptors.
>>
>> - Data structs which describe the different data types supported by VPDMA.
>> This data type information will be used to populate fields required by data
>> descriptors and used by the VPE driver to map a V4L2 format to the
>> corresponding VPDMA data type.
>>
>> - Provide VPDMA register offset definitions, functions to read, write and
>> modify VPDMA registers.
>>
>> - Functions to create and submit a VPDMA list. A list is a group of
>> descriptors that makes up a set of DMA transfers that need to be completed.
>> Each descriptor will either perform a DMA transaction to fetch input
>> buffers and write to output buffers(data descriptors), or configure the
>> MMRs of sub blocks of VPE(configuration descriptors), or provide control
>> information to VPDMA (control descriptors).
>>
>> - Functions to allocate, map and unmap buffers needed for the descriptor
>> list, payloads containing MMR values and motion vector buffers. These use
>> the DMA mapping APIs to ensure exclusive access to VPDMA.
>>
>> - Functions to enable VPDMA interrupts. VPDMA can trigger an interrupt on
>> the VPE interrupt line when a descriptor list is parsed completely and the
>> DMA transactions are completed. This requires masking the events in VPDMA
>> registers and configuring some top level VPE interrupt registers.
>>
>> - Enable some VPDMA specific parameters: frame start event(when to start DMA
>> for a client) and line mode(whether each line fetched should be mirrored or
>> not).
>>
>> - Function to load firmware required by VPDMA. VPDMA requires a firmware for
>> it's internal list manager. We add the required request_firmware apis to
>> fetch this firmware from user space.
>>
>> - Function to dump VPDMA registers.
>>
>> - A function to initialize VPDMA, this will be called by the VPE driver with
>> it's platform device pointer, this function will take care of loading VPDMA
>> firmware and returning a handle back to the VPE driver. The VIP driver will
>> also call the same init function to initialize it's own VPDMA instance.
>>
>> Signed-off-by: Archit Taneja <archit@ti.com>
>> ---
>>   drivers/media/platform/ti-vpe/vpdma.c      | 589 ++++++++++++++++++++++++++
>>   drivers/media/platform/ti-vpe/vpdma.h      | 154 ++++++++
>>   drivers/media/platform/ti-vpe/vpdma_priv.h | 119 ++++++
>>   3 files changed, 862 insertions(+)
>>   create mode 100644 drivers/media/platform/ti-vpe/vpdma.c
>>   create mode 100644 drivers/media/platform/ti-vpe/vpdma.h
>>   create mode 100644 drivers/media/platform/ti-vpe/vpdma_priv.h
>>
>> diff --git a/drivers/media/platform/ti-vpe/vpdma.c
>> b/drivers/media/platform/ti-vpe/vpdma.c new file mode 100644
>> index 0000000..b15b3dd
>> --- /dev/null
>> +++ b/drivers/media/platform/ti-vpe/vpdma.c
>> @@ -0,0 +1,589 @@
>
> [snip]
>
>> +static int get_field(u32 value, u32 mask, int shift)
>> +{
>> +	return (value & (mask << shift)) >> shift;
>> +}
>> +
>> +static int get_field_reg(struct vpdma_data *vpdma, int offset,
>> +		u32 mask, int shift)
>
> I would call this read_field_reg().
>
>> +{
>> +	return get_field(read_reg(vpdma, offset), mask, shift);
>> +}
>> +
>> +static void insert_field(u32 *valp, u32 field, u32 mask, int shift)
>> +{
>> +	u32 val = *valp;
>> +
>> +	val &= ~(mask << shift);
>> +	val |= (field & mask) << shift;
>> +	*valp = val;
>> +}
>
> get_field() and insert_field() are used in a single location, you can manually
> inline them.

Thanks, I'll make these modifications.

>
>> +static void insert_field_reg(struct vpdma_data *vpdma, int offset, u32
>> field,
>> +		u32 mask, int shift)
>> +{
>> +	u32 val = read_reg(vpdma, offset);
>> +
>> +	insert_field(&val, field, mask, shift);
>> +
>> +	write_reg(vpdma, offset, val);
>> +}
>
> [snip]
>
>> +/*
>> + * Allocate a DMA buffer
>> + */
>> +int vpdma_buf_alloc(struct vpdma_buf *buf, size_t size)
>> +{
>> +	buf->size = size;
>> +	buf->mapped = 0;
>> +	buf->addr = kzalloc(size, GFP_KERNEL);
>
> You should use the dma allocation API (depending on your needs,
> dma_alloc_coherent for instance) to allocate DMA-able buffers.

I'm not sure about this, dma_map_single() api works fine on kzalloc'd 
buffers. The above function is used to allocate small contiguous 
buffers(never more than 1024 bytes) needed for descriptors for the DMA 
IP. I thought of using DMA pool, but that creates small buffers of the 
same size. So I finally went with kzalloc.

>
>> +	if (!buf->addr)
>> +		return -ENOMEM;
>> +
>> +	WARN_ON((u32) buf->addr & VPDMA_DESC_ALIGN);
>> +
>> +	return 0;
>> +}
>> +
>> +void vpdma_buf_free(struct vpdma_buf *buf)
>> +{
>> +	WARN_ON(buf->mapped != 0);
>> +	kfree(buf->addr);
>> +	buf->addr = NULL;
>> +	buf->size = 0;
>> +}
>> +
>> +/*
>> + * map a DMA buffer, enabling DMA access
>> + */
>> +void vpdma_buf_map(struct vpdma_data *vpdma, struct vpdma_buf *buf)
>> +{
>> +	struct device *dev = &vpdma->pdev->dev;
>> +
>> +	WARN_ON(buf->mapped != 0);
>> +	buf->dma_addr = dma_map_single(dev, buf->addr, buf->size,
>> +				DMA_TO_DEVICE);
>> +	buf->mapped = 1;
>> +	BUG_ON(dma_mapping_error(dev, buf->dma_addr));
>
> BUG_ON() is too harsh, you should return a proper error instead.

Right, I'll fix this.

>
>> +}
>> +
>> +/*
>> + * unmap a DMA buffer, disabling DMA access and
>> + * allowing the main processor to acces the data
>> + */
>> +void vpdma_buf_unmap(struct vpdma_data *vpdma, struct vpdma_buf *buf)
>> +{
>> +	struct device *dev = &vpdma->pdev->dev;
>> +
>> +	if (buf->mapped)
>> +		dma_unmap_single(dev, buf->dma_addr, buf->size, DMA_TO_DEVICE);
>> +
>> +	buf->mapped = 0;
>> +}
>> +
>> +/*
>> + * create a descriptor list, the user of this list will append
>> configuration,
>> + * contorl and data descriptors to this list, this list will be submitted
>
> s/contorl/control/
>
>> to
>> + * VPDMA. VPDMA's list parser will go through each descriptor and perform
>> + * the required DMA operations
>> + */
>> +int vpdma_create_desc_list(struct vpdma_desc_list *list, size_t size, int
>> type)
>> +{
>> +	int r;
>> +
>> +	r = vpdma_buf_alloc(&list->buf, size);
>> +	if (r)
>> +		return r;
>> +
>> +	list->next = list->buf.addr;
>> +
>> +	list->type = type;
>> +
>> +	return 0;
>> +}
>> +
>> +/*
>> + * once a descriptor list is parsed by VPDMA, we reset the list by emptying
>> it,
>> + * to allow new descriptors to be added to the list.
>> + */
>> +void vpdma_reset_desc_list(struct vpdma_desc_list *list)
>> +{
>> +	list->next = list->buf.addr;
>> +}
>> +
>> +/*
>> + * free the buffer allocated fot the VPDMA descriptor list, this should be
>> + * called when the user doesn't want to use VPDMA any more.
>> + */
>> +void vpdma_free_desc_list(struct vpdma_desc_list *list)
>> +{
>> +	vpdma_buf_free(&list->buf);
>> +
>> +	list->next = NULL;
>> +}
>> +
>> +static int vpdma_list_busy(struct vpdma_data *vpdma, int list_num)
>
> Should the function return a bool instead of an int ?

Yes, a bool would be better here.
>
>> +{
>> +	u32 sync_reg = read_reg(vpdma, VPDMA_LIST_STAT_SYNC);
>> +
>> +	return (sync_reg >> (list_num + 16)) & 0x01;
>
> You could shorten that as
>
> 	return read_reg(vpdma, VPDMA_LIST_STAT_SYNC) & BIT(list_num + 16);

yes, that does look better, I'll modify it.

>
>> +}
>> +
>> +/*
>> + * submit a list of DMA descriptors to the VPE VPDMA, do not wait for
>> completion
>> + */
>> +int vpdma_submit_descs(struct vpdma_data *vpdma, struct vpdma_desc_list
>> *list)
>> +{
>> +	/* we always use the first list */
>> +	int list_num = 0;
>> +	int list_size;
>> +
>> +	if (vpdma_list_busy(vpdma, list_num))
>> +		return -EBUSY;
>> +
>> +	/* 16-byte granularity */
>> +	list_size = (list->next - list->buf.addr) >> 4;
>> +
>> +	write_reg(vpdma, VPDMA_LIST_ADDR, (u32) list->buf.dma_addr);
>> +	wmb();
>> +	write_reg(vpdma, VPDMA_LIST_ATTR,
>> +			(list_num << VPDMA_LIST_NUM_SHFT) |
>> +			(list->type << VPDMA_LIST_TYPE_SHFT) |
>> +			list_size);
>> +
>> +	return 0;
>> +}
>> +
>> +/* set or clear the mask for list complete interrupt */
>> +void vpdma_enable_list_complete_irq(struct vpdma_data *vpdma, int list_num,
>> +		bool enable)
>> +{
>> +	u32 val;
>> +
>> +	val = read_reg(vpdma, VPDMA_INT_LIST0_MASK);
>> +	if (enable)
>> +		val |= (1 << (list_num * 2));
>> +	else
>> +		val &= ~(1 << (list_num * 2));
>> +	write_reg(vpdma, VPDMA_INT_LIST0_MASK, val);
>> +}
>> +
>> +/* clear previosuly occured list intterupts in the LIST_STAT register */
>> +void vpdma_clear_list_stat(struct vpdma_data *vpdma)
>> +{
>> +	write_reg(vpdma, VPDMA_INT_LIST0_STAT,
>> +		read_reg(vpdma, VPDMA_INT_LIST0_STAT));
>> +}
>> +
>> +/*
>> + * configures the output mode of the line buffer for the given client, the
>> + * line buffer content can either be mirrored(each line repeated twice) or
>> + * passed to the client as is
>> + */
>> +void vpdma_set_line_mode(struct vpdma_data *vpdma, int line_mode,
>> +		enum vpdma_channel chan)
>> +{
>> +	int client_cstat = chan_info[chan].cstat_offset;
>> +
>> +	insert_field_reg(vpdma, client_cstat, line_mode,
>> +		VPDMA_CSTAT_LINE_MODE_MASK, VPDMA_CSTAT_LINE_MODE_SHIFT);
>> +}
>> +
>> +/*
>> + * configures the event which should trigger VPDMA transfer for the given
>> + * client
>> + */
>> +void vpdma_set_frame_start_event(struct vpdma_data *vpdma,
>> +		enum vpdma_frame_start_event fs_event,
>> +		enum vpdma_channel chan)
>> +{
>> +	int client_cstat = chan_info[chan].cstat_offset;
>> +
>> +	insert_field_reg(vpdma, client_cstat, fs_event,
>> +		VPDMA_CSTAT_FRAME_START_MASK, VPDMA_CSTAT_FRAME_START_SHIFT);
>> +}
>> +
>> +static void vpdma_firmware_cb(const struct firmware *f, void *context)
>> +{
>> +	struct vpdma_data *vpdma = context;
>> +	struct vpdma_buf fw_dma_buf;
>> +	int i, r;
>> +
>> +	dev_dbg(&vpdma->pdev->dev, "firmware callback\n");
>> +
>> +	if (!f || !f->data) {
>> +		dev_err(&vpdma->pdev->dev, "couldn't get firmware\n");
>> +		return;
>> +	}
>> +
>> +	/* already initialized */
>> +	if (get_field_reg(vpdma, VPDMA_LIST_ATTR, VPDMA_LIST_RDY_MASK,
>> +			VPDMA_LIST_RDY_SHFT)) {
>> +		vpdma->ready = true;
>> +		return;
>> +	}
>> +
>> +	r = vpdma_buf_alloc(&fw_dma_buf, f->size);
>> +	if (r) {
>> +		dev_err(&vpdma->pdev->dev,
>> +			"failed to allocate dma buffer for firmware\n");
>> +		goto rel_fw;
>> +	}
>> +
>> +	memcpy(fw_dma_buf.addr, f->data, f->size);
>> +
>> +	vpdma_buf_map(vpdma, &fw_dma_buf);
>> +
>> +	write_reg(vpdma, VPDMA_LIST_ADDR, (u32) fw_dma_buf.dma_addr);
>> +
>> +	for (i = 0; i < 100; i++) {		/* max 1 second */
>> +		msleep_interruptible(10);
>> +
>> +		if (get_field_reg(vpdma, VPDMA_LIST_ATTR, VPDMA_LIST_RDY_MASK,
>> +				VPDMA_LIST_RDY_SHFT))
>> +			break;
>> +	}
>> +
>> +	if (i == 100) {
>> +		dev_err(&vpdma->pdev->dev, "firmware upload failed\n");
>> +		goto free_buf;
>> +	}
>> +
>> +	vpdma->ready = true;
>> +
>> +free_buf:
>> +	vpdma_buf_unmap(vpdma, &fw_dma_buf);
>> +
>> +	vpdma_buf_free(&fw_dma_buf);
>> +rel_fw:
>> +	release_firmware(f);
>> +}
>> +
>> +static int vpdma_load_firmware(struct vpdma_data *vpdma)
>> +{
>> +	int r;
>> +	struct device *dev = &vpdma->pdev->dev;
>> +
>> +	r = request_firmware_nowait(THIS_MODULE, 1,
>> +		(const char *) VPDMA_FIRMWARE, dev, GFP_KERNEL, vpdma,
>> +		vpdma_firmware_cb);
>
> Is there a reason not to use the synchronous interface ? That would simplify
> both this code and the callers, as they won't have to check whether the
> firmware has been correctly loaded.

I'm not clear what you mean by that, the firmware would be stored in the 
filesystem. If the driver is built-in, then the synchronous interface 
wouldn't work unless the firmware is appended to the kernel image. Am I 
missing something here? I'm not very aware of the firmware api.


>> +	if (r) {
>> +		dev_err(dev, "firmware not available %s\n", VPDMA_FIRMWARE);
>> +		return r;
>> +	} else {
>> +		dev_info(dev, "loading firmware %s\n", VPDMA_FIRMWARE);
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +int vpdma_init(struct platform_device *pdev, struct vpdma_data **pvpdma)
>
> As the function allocates the vpdma instance, I would call it vpdma_create()
> and make it turn a struct vpdma_data *. You can then return error codes using
> ERR_PTR().

Yes, that makes quite more sense. I'll use your approach.

>
>> +{
>> +	struct resource *res;
>> +	struct vpdma_data *vpdma;
>> +	int r;
>> +
>> +	dev_dbg(&pdev->dev, "vpdma_init\n");
>> +
>> +	vpdma = devm_kzalloc(&pdev->dev, sizeof(*vpdma), GFP_KERNEL);
>> +	if (!vpdma) {
>> +		dev_err(&pdev->dev, "couldn't alloc vpdma_dev\n");
>> +		return -ENOMEM;
>> +	}
>> +
>> +	vpdma->pdev = pdev;
>> +
>> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpdma");
>> +	if (res == NULL) {
>> +		dev_err(&pdev->dev, "missing platform resources data\n");
>> +		return -ENODEV;
>> +	}
>> +
>> +	vpdma->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
>
> You can use devm_ioremap_resource(). The function checks the res pointer and
> prints error messages, so you can remove the res == NULL check above and the
> dev_err() below.

Ah nice, I'll use that one.

Thanks a lot for the comments!

Archit


  reply	other threads:[~2013-08-14 10:59 UTC|newest]

Thread overview: 138+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-08-02 14:03 [PATCH 0/6] v4l: VPE mem to mem driver Archit Taneja
2013-08-02 14:03 ` Archit Taneja
2013-08-02 14:03 ` [PATCH 1/6] v4l: ti-vpe: Create a vpdma helper library Archit Taneja
2013-08-02 14:03   ` Archit Taneja
2013-08-05  8:13   ` Tomi Valkeinen
2013-08-05  8:13     ` Tomi Valkeinen
2013-08-05 11:26     ` Archit Taneja
2013-08-05 11:26       ` Archit Taneja
2013-08-05 12:26       ` Tomi Valkeinen
2013-08-05 12:26         ` Tomi Valkeinen
2013-08-08 21:35       ` Laurent Pinchart
2013-08-14 10:19         ` Archit Taneja
2013-08-14 10:19           ` Archit Taneja
2013-08-08 22:04   ` Laurent Pinchart
2013-08-14 10:57     ` Archit Taneja [this message]
2013-08-14 10:57       ` Archit Taneja
2013-08-20 11:39       ` Laurent Pinchart
2013-08-20 12:51         ` Archit Taneja
2013-08-20 12:51           ` Archit Taneja
2013-08-20 13:16         ` Archit Taneja
2013-08-20 13:16           ` Archit Taneja
2013-08-20 13:56           ` Laurent Pinchart
2013-08-21  6:47             ` Archit Taneja
2013-08-21  6:47               ` Archit Taneja
2013-08-02 14:03 ` [PATCH 2/6] v4l: ti-vpe: Add helpers for creating VPDMA descriptors Archit Taneja
2013-08-02 14:03   ` Archit Taneja
2013-08-05  9:11   ` Tomi Valkeinen
2013-08-05  9:11     ` Tomi Valkeinen
2013-08-05 12:05     ` Archit Taneja
2013-08-05 12:05       ` Archit Taneja
2013-08-05 13:03       ` Tomi Valkeinen
2013-08-05 13:03         ` Tomi Valkeinen
2013-08-02 14:03 ` [PATCH 3/6] v4l: ti-vpe: Add VPE mem to mem driver Archit Taneja
2013-08-02 14:03   ` Archit Taneja
2013-08-02 14:36   ` Hans Verkuil
2013-08-02 14:55     ` Archit Taneja
2013-08-02 14:55       ` Archit Taneja
2013-08-05  9:18   ` Tomi Valkeinen
2013-08-05  9:18     ` Tomi Valkeinen
2013-08-02 14:03 ` [PATCH 4/6] v4l: ti-vpe: Add de-interlacer support in VPE Archit Taneja
2013-08-02 14:03   ` Archit Taneja
2013-08-02 14:40   ` Hans Verkuil
2013-08-02 14:03 ` [PATCH 5/6] arm: dra7xx: hwmod data: add VPE hwmod data and ocp_if info Archit Taneja
2013-08-02 14:03   ` Archit Taneja
2013-08-02 14:03 ` [PATCH 6/6] experimental: arm: dts: dra7xx: Add a DT node for VPE Archit Taneja
2013-08-02 14:03   ` Archit Taneja
2013-08-08 22:11   ` Laurent Pinchart
2013-10-25 10:35     ` Archit Taneja
2013-10-25 10:35       ` Archit Taneja
2013-12-03 10:08     ` Archit Taneja
2013-12-03 10:08       ` Archit Taneja
2013-08-20 11:00 ` [PATCH v2 0/6] v4l: VPE mem to mem driver Archit Taneja
2013-08-20 11:00   ` Archit Taneja
2013-08-20 11:00   ` [PATCH v2 1/6] v4l: ti-vpe: Create a vpdma helper library Archit Taneja
2013-08-20 11:00     ` Archit Taneja
2013-08-20 11:00   ` [PATCH v2 2/6] v4l: ti-vpe: Add helpers for creating VPDMA descriptors Archit Taneja
2013-08-20 11:00     ` Archit Taneja
2013-08-20 11:00   ` [PATCH v2 3/6] v4l: ti-vpe: Add VPE mem to mem driver Archit Taneja
2013-08-20 11:00     ` Archit Taneja
2013-08-20 11:00   ` [PATCH v2 4/6] v4l: ti-vpe: Add de-interlacer support in VPE Archit Taneja
2013-08-20 11:00     ` Archit Taneja
2013-08-20 11:00   ` [PATCH v2 5/6] arm: dra7xx: hwmod data: add VPE hwmod data and ocp_if info Archit Taneja
2013-08-20 11:00     ` Archit Taneja
2013-08-20 11:00   ` [PATCH v2 6/6] experimental: arm: dts: dra7xx: Add a DT node for VPE Archit Taneja
2013-08-20 11:00     ` Archit Taneja
2013-08-29 12:32   ` [PATCH v3 0/6] v4l: VPE mem to mem driver Archit Taneja
2013-08-29 12:32     ` Archit Taneja
2013-08-29 12:32     ` [PATCH v3 1/6] v4l: ti-vpe: Create a vpdma helper library Archit Taneja
2013-08-29 12:32       ` Archit Taneja
2013-08-29 12:32     ` [PATCH v3 2/6] v4l: ti-vpe: Add helpers for creating VPDMA descriptors Archit Taneja
2013-08-29 12:32       ` Archit Taneja
2013-08-29 12:32     ` [PATCH v3 3/6] v4l: ti-vpe: Add VPE mem to mem driver Archit Taneja
2013-08-29 12:32       ` Archit Taneja
2013-08-29 13:28       ` Hans Verkuil
2013-08-30  6:47         ` Archit Taneja
2013-08-30  6:47           ` Archit Taneja
2013-08-30  7:07           ` Hans Verkuil
2013-08-30 10:05             ` Archit Taneja
2013-08-30 10:05               ` Archit Taneja
2013-08-30 10:44               ` Hans Verkuil
2013-09-05  5:56         ` Archit Taneja
2013-09-05  5:56           ` Archit Taneja
2013-08-29 12:32     ` [PATCH v3 4/6] v4l: ti-vpe: Add de-interlacer support in VPE Archit Taneja
2013-08-29 12:32       ` Archit Taneja
2013-08-29 12:32     ` [PATCH v3 5/6] arm: dra7xx: hwmod data: add VPE hwmod data and ocp_if info Archit Taneja
2013-08-29 12:32       ` Archit Taneja
2013-08-29 12:42       ` Rajendra Nayak
2013-08-29 12:42         ` Rajendra Nayak
2013-08-29 13:42         ` Archit Taneja
2013-08-29 13:42           ` Archit Taneja
2013-08-29 12:32     ` [PATCH v3 6/6] experimental: arm: dts: dra7xx: Add a DT node for VPE Archit Taneja
2013-08-29 12:32       ` Archit Taneja
2013-09-06 10:12   ` [PATCH v4 0/4] v4l: VPE mem to mem driver Archit Taneja
2013-09-06 10:12     ` Archit Taneja
2013-09-06 10:12     ` [PATCH v4 1/4] v4l: ti-vpe: Create a vpdma helper library Archit Taneja
2013-09-06 10:12       ` Archit Taneja
2013-10-07  7:46       ` Hans Verkuil
2013-09-06 10:12     ` [PATCH v4 2/4] v4l: ti-vpe: Add helpers for creating VPDMA descriptors Archit Taneja
2013-09-06 10:12       ` Archit Taneja
2013-10-07  7:46       ` Hans Verkuil
2013-09-06 10:12     ` [PATCH v4 3/4] v4l: ti-vpe: Add VPE mem to mem driver Archit Taneja
2013-09-06 10:12       ` Archit Taneja
2013-10-07  7:55       ` Hans Verkuil
2013-10-07  9:16         ` Archit Taneja
2013-10-07  9:16           ` Archit Taneja
2013-10-07  9:34           ` Hans Verkuil
2013-10-07 10:22             ` Archit Taneja
2013-10-07 10:22               ` Archit Taneja
2013-10-07 14:02               ` Hans Verkuil
2013-10-07 14:34                 ` Archit Taneja
2013-10-07 14:34                   ` Archit Taneja
2013-09-06 10:12     ` [PATCH v4 4/4] v4l: ti-vpe: Add de-interlacer support in VPE Archit Taneja
2013-09-06 10:12       ` Archit Taneja
2013-10-07  7:57       ` Hans Verkuil
2013-09-16  6:59     ` [PATCH v4 0/4] v4l: VPE mem to mem driver Archit Taneja
2013-09-16  6:59       ` Archit Taneja
2013-10-07  6:39       ` Archit Taneja
2013-10-07  6:39         ` Archit Taneja
2013-10-09 14:29     ` [PATCH v5 3/4] v4l: ti-vpe: Add " Archit Taneja
2013-10-09 14:29       ` Archit Taneja
2013-10-11  7:46       ` Hans Verkuil
2013-10-15 13:47         ` Archit Taneja
2013-10-15 13:47           ` Archit Taneja
2013-10-15 13:51           ` Hans Verkuil
2013-10-15 14:13             ` Kamil Debski
2013-10-15 15:54             ` Kamil Debski
2013-10-16  5:08               ` Archit Taneja
2013-10-16  5:08                 ` Archit Taneja
2013-10-16  5:36     ` [PATCH v5 0/4] v4l: " Archit Taneja
2013-10-16  5:36       ` Archit Taneja
2013-10-16  5:36       ` [PATCH v5 1/4] v4l: ti-vpe: Create a vpdma helper library Archit Taneja
2013-10-16  5:36         ` Archit Taneja
2013-10-16  5:36       ` [PATCH v5 2/4] v4l: ti-vpe: Add helpers for creating VPDMA descriptors Archit Taneja
2013-10-16  5:36         ` Archit Taneja
2013-10-16  5:36       ` [PATCH v5 3/4] v4l: ti-vpe: Add VPE mem to mem driver Archit Taneja
2013-10-16  5:36         ` Archit Taneja
2013-10-16  5:36       ` [PATCH v5 4/4] v4l: ti-vpe: Add de-interlacer support in VPE Archit Taneja
2013-10-16  5:36         ` Archit Taneja

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=520B62B5.8080000@ti.com \
    --to=archit@ti.com \
    --cc=dagriego@biglakesoftware.com \
    --cc=dale@farnsworth.org \
    --cc=hverkuil@xs4all.nl \
    --cc=laurent.pinchart@ideasonboard.com \
    --cc=linux-media@vger.kernel.org \
    --cc=linux-omap@vger.kernel.org \
    --cc=m.szyprowski@samsung.com \
    --cc=pawel@osciak.com \
    --cc=tomi.valkeinen@ti.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.