Re: [PATCH v4 3/5] media: meson: vdec: add common HEVC decoder support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 14/02/2020 16:07, Hans Verkuil wrote:
> On 2/6/20 9:41 AM, Neil Armstrong wrote:
>> From: Maxime Jourdan <mjourdan@xxxxxxxxxxxx>
>>
>> Add support for the HEVC & VP9 common decoder support, handling
>> Amlogic GXBB, GXL, G12A and SM1 platforms.
>>
>> This handles the "HEVC" hw decoder used for HEVC and VP9, and will be
>> using in the new H264 multi-instance decoder for G12A & SM1 platforms.
>>
>> Signed-off-by: Maxime Jourdan <mjourdan@xxxxxxxxxxxx>
>> Signed-off-by: Neil Armstrong <narmstrong@xxxxxxxxxxxx>
> 
> I'm getting some checkpatch warnings/checks:
> 
> WARNING: Possible unnecessary 'out of memory' message
> #219: FILE: drivers/staging/media/meson/vdec/codec_hevc_common.c:171:
> +               if (!vaddr) {
> +                       dev_err(dev, "Couldn't allocate FBC buffer %u\n", idx);
> 
> WARNING: Possible unnecessary 'out of memory' message
> #273: FILE: drivers/staging/media/meson/vdec/codec_hevc_common.c:225:
> +               if (!vaddr) {
> +                       dev_err(dev, "Couldn't allocate MMU header %u\n", idx);
> 
> WARNING: Possible unnecessary 'out of memory' message
> #692: FILE: drivers/staging/media/meson/vdec/vdec_hevc.c:52:
> +       if (!mc_addr) {
> +               dev_err(dev, "Failed allocating memory for firmware loading\n");
> 
> CHECK: usleep_range is preferred over udelay; see Documentation/timers/timers-howto.rst
> #819: FILE: drivers/staging/media/meson/vdec/vdec_hevc.c:179:
> +       udelay(10);
> 
> CHECK: usleep_range is preferred over udelay; see Documentation/timers/timers-howto.rst
> #857: FILE: drivers/staging/media/meson/vdec/vdec_hevc.c:217:
> +       udelay(10);
> 
> Can you take a look?

I'll fix all these.

Neil

> 
> Regards,
> 
> 	Hans
> 
>> ---
>>  drivers/staging/media/meson/vdec/Makefile     |   4 +-
>>  .../media/meson/vdec/codec_hevc_common.c      | 286 ++++++++++++++++++
>>  .../media/meson/vdec/codec_hevc_common.h      |  77 +++++
>>  drivers/staging/media/meson/vdec/hevc_regs.h  | 211 +++++++++++++
>>  drivers/staging/media/meson/vdec/vdec_hevc.c  | 231 ++++++++++++++
>>  drivers/staging/media/meson/vdec/vdec_hevc.h  |  13 +
>>  6 files changed, 820 insertions(+), 2 deletions(-)
>>  create mode 100644 drivers/staging/media/meson/vdec/codec_hevc_common.c
>>  create mode 100644 drivers/staging/media/meson/vdec/codec_hevc_common.h
>>  create mode 100644 drivers/staging/media/meson/vdec/hevc_regs.h
>>  create mode 100644 drivers/staging/media/meson/vdec/vdec_hevc.c
>>  create mode 100644 drivers/staging/media/meson/vdec/vdec_hevc.h
>>
>> diff --git a/drivers/staging/media/meson/vdec/Makefile b/drivers/staging/media/meson/vdec/Makefile
>> index 711d990c760e..f55b6e625034 100644
>> --- a/drivers/staging/media/meson/vdec/Makefile
>> +++ b/drivers/staging/media/meson/vdec/Makefile
>> @@ -2,7 +2,7 @@
>>  # Makefile for Amlogic meson video decoder driver
>>  
>>  meson-vdec-objs = esparser.o vdec.o vdec_helpers.o vdec_platform.o
>> -meson-vdec-objs += vdec_1.o
>> -meson-vdec-objs += codec_mpeg12.o codec_h264.o
>> +meson-vdec-objs += vdec_1.o vdec_hevc.o
>> +meson-vdec-objs += codec_mpeg12.o codec_h264.o codec_hevc_common.o
>>  
>>  obj-$(CONFIG_VIDEO_MESON_VDEC) += meson-vdec.o
>> diff --git a/drivers/staging/media/meson/vdec/codec_hevc_common.c b/drivers/staging/media/meson/vdec/codec_hevc_common.c
>> new file mode 100644
>> index 000000000000..335bcba062ac
>> --- /dev/null
>> +++ b/drivers/staging/media/meson/vdec/codec_hevc_common.c
>> @@ -0,0 +1,286 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * Copyright (C) 2018 Maxime Jourdan <mjourdan@xxxxxxxxxxxx>
>> + */
>> +
>> +#include <media/v4l2-mem2mem.h>
>> +#include <media/videobuf2-dma-contig.h>
>> +
>> +#include "codec_hevc_common.h"
>> +#include "vdec_helpers.h"
>> +#include "hevc_regs.h"
>> +
>> +#define MMU_COMPRESS_HEADER_SIZE 0x48000
>> +#define MMU_MAP_SIZE 0x4800
>> +
>> +/* Configure decode head read mode */
>> +void codec_hevc_setup_decode_head(struct amvdec_session *sess, int is_10bit)
>> +{
>> +	struct amvdec_core *core = sess->core;
>> +	u32 body_size = amvdec_am21c_body_size(sess->width, sess->height);
>> +	u32 head_size = amvdec_am21c_head_size(sess->width, sess->height);
>> +
>> +	if (!codec_hevc_use_fbc(sess->pixfmt_cap, is_10bit)) {
>> +		/* Enable 2-plane reference read mode */
>> +		amvdec_write_dos(core, HEVCD_MPP_DECOMP_CTL1, BIT(31));
>> +		return;
>> +	}
>> +
>> +	if (codec_hevc_use_mmu(core->platform->revision,
>> +			       sess->pixfmt_cap, is_10bit))
>> +		amvdec_write_dos(core, HEVCD_MPP_DECOMP_CTL1, BIT(4));
>> +	else
>> +		amvdec_write_dos(core, HEVCD_MPP_DECOMP_CTL1, 0);
>> +
>> +	if (core->platform->revision < VDEC_REVISION_SM1)
>> +		amvdec_write_dos(core, HEVCD_MPP_DECOMP_CTL2, body_size / 32);
>> +	amvdec_write_dos(core, HEVC_CM_BODY_LENGTH, body_size);
>> +	amvdec_write_dos(core, HEVC_CM_HEADER_OFFSET, body_size);
>> +	amvdec_write_dos(core, HEVC_CM_HEADER_LENGTH, head_size);
>> +}
>> +EXPORT_SYMBOL_GPL(codec_hevc_setup_decode_head);
>> +
>> +static void codec_hevc_setup_buffers_gxbb(struct amvdec_session *sess,
>> +					  struct codec_hevc_common *comm,
>> +					  int is_10bit)
>> +{
>> +	struct amvdec_core *core = sess->core;
>> +	struct v4l2_m2m_buffer *buf;
>> +	u32 buf_num = v4l2_m2m_num_dst_bufs_ready(sess->m2m_ctx);
>> +	dma_addr_t buf_y_paddr = 0;
>> +	dma_addr_t buf_uv_paddr = 0;
>> +	u32 idx = 0;
>> +	u32 val;
>> +	int i;
>> +
>> +	amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR, 0);
>> +
>> +	v4l2_m2m_for_each_dst_buf(sess->m2m_ctx, buf) {
>> +		struct vb2_buffer *vb = &buf->vb.vb2_buf;
>> +
>> +		idx = vb->index;
>> +
>> +		if (codec_hevc_use_downsample(sess->pixfmt_cap, is_10bit))
>> +			buf_y_paddr = comm->fbc_buffer_paddr[idx];
>> +		else
>> +			buf_y_paddr = vb2_dma_contig_plane_dma_addr(vb, 0);
>> +
>> +		if (codec_hevc_use_fbc(sess->pixfmt_cap, is_10bit)) {
>> +			val = buf_y_paddr | (idx << 8) | 1;
>> +			amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR,
>> +					 val);
>> +		} else {
>> +			buf_uv_paddr = vb2_dma_contig_plane_dma_addr(vb, 1);
>> +			val = buf_y_paddr | ((idx * 2) << 8) | 1;
>> +			amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR,
>> +					 val);
>> +			val = buf_uv_paddr | ((idx * 2 + 1) << 8) | 1;
>> +			amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR,
>> +					 val);
>> +		}
>> +	}
>> +
>> +	if (codec_hevc_use_fbc(sess->pixfmt_cap, is_10bit))
>> +		val = buf_y_paddr | (idx << 8) | 1;
>> +	else
>> +		val = buf_y_paddr | ((idx * 2) << 8) | 1;
>> +
>> +	/* Fill the remaining unused slots with the last buffer's Y addr */
>> +	for (i = buf_num; i < MAX_REF_PIC_NUM; ++i)
>> +		amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR, val);
>> +
>> +	amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR, 1);
>> +	amvdec_write_dos(core, HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR, 1);
>> +	for (i = 0; i < 32; ++i)
>> +		amvdec_write_dos(core, HEVCD_MPP_ANC_CANVAS_DATA_ADDR, 0);
>> +}
>> +
>> +static void codec_hevc_setup_buffers_gxl(struct amvdec_session *sess,
>> +					 struct codec_hevc_common *comm,
>> +					 int is_10bit)
>> +{
>> +	struct amvdec_core *core = sess->core;
>> +	struct v4l2_m2m_buffer *buf;
>> +	u32 revision = core->platform->revision;
>> +	u32 pixfmt_cap = sess->pixfmt_cap;
>> +	int i;
>> +
>> +	amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR,
>> +			 BIT(2) | BIT(1));
>> +
>> +	v4l2_m2m_for_each_dst_buf(sess->m2m_ctx, buf) {
>> +		struct vb2_buffer *vb = &buf->vb.vb2_buf;
>> +		dma_addr_t buf_y_paddr = 0;
>> +		dma_addr_t buf_uv_paddr = 0;
>> +		u32 idx = vb->index;
>> +
>> +		if (codec_hevc_use_mmu(revision, pixfmt_cap, is_10bit))
>> +			buf_y_paddr = comm->mmu_header_paddr[idx];
>> +		else if (codec_hevc_use_downsample(pixfmt_cap, is_10bit))
>> +			buf_y_paddr = comm->fbc_buffer_paddr[idx];
>> +		else
>> +			buf_y_paddr = vb2_dma_contig_plane_dma_addr(vb, 0);
>> +
>> +		amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_DATA,
>> +				 buf_y_paddr >> 5);
>> +
>> +		if (!codec_hevc_use_fbc(pixfmt_cap, is_10bit)) {
>> +			buf_uv_paddr = vb2_dma_contig_plane_dma_addr(vb, 1);
>> +			amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_DATA,
>> +					 buf_uv_paddr >> 5);
>> +		}
>> +	}
>> +
>> +	amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR, 1);
>> +	amvdec_write_dos(core, HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR, 1);
>> +	for (i = 0; i < 32; ++i)
>> +		amvdec_write_dos(core, HEVCD_MPP_ANC_CANVAS_DATA_ADDR, 0);
>> +}
>> +
>> +void codec_hevc_free_fbc_buffers(struct amvdec_session *sess,
>> +				 struct codec_hevc_common *comm)
>> +{
>> +	struct device *dev = sess->core->dev;
>> +	u32 am21_size = amvdec_am21c_size(sess->width, sess->height);
>> +	int i;
>> +
>> +	for (i = 0; i < MAX_REF_PIC_NUM; ++i) {
>> +		if (comm->fbc_buffer_vaddr[i]) {
>> +			dma_free_coherent(dev, am21_size,
>> +					  comm->fbc_buffer_vaddr[i],
>> +					  comm->fbc_buffer_paddr[i]);
>> +			comm->fbc_buffer_vaddr[i] = NULL;
>> +		}
>> +	}
>> +}
>> +EXPORT_SYMBOL_GPL(codec_hevc_free_fbc_buffers);
>> +
>> +static int codec_hevc_alloc_fbc_buffers(struct amvdec_session *sess,
>> +					struct codec_hevc_common *comm)
>> +{
>> +	struct device *dev = sess->core->dev;
>> +	struct v4l2_m2m_buffer *buf;
>> +	u32 am21_size = amvdec_am21c_size(sess->width, sess->height);
>> +
>> +	v4l2_m2m_for_each_dst_buf(sess->m2m_ctx, buf) {
>> +		u32 idx = buf->vb.vb2_buf.index;
>> +		dma_addr_t paddr;
>> +		void *vaddr = dma_alloc_coherent(dev, am21_size, &paddr,
>> +						 GFP_KERNEL);
>> +		if (!vaddr) {
>> +			dev_err(dev, "Couldn't allocate FBC buffer %u\n", idx);
>> +			codec_hevc_free_fbc_buffers(sess, comm);
>> +			return -ENOMEM;
>> +		}
>> +
>> +		comm->fbc_buffer_vaddr[idx] = vaddr;
>> +		comm->fbc_buffer_paddr[idx] = paddr;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +void codec_hevc_free_mmu_headers(struct amvdec_session *sess,
>> +				 struct codec_hevc_common *comm)
>> +{
>> +	struct device *dev = sess->core->dev;
>> +	int i;
>> +
>> +	for (i = 0; i < MAX_REF_PIC_NUM; ++i) {
>> +		if (comm->mmu_header_vaddr[i]) {
>> +			dma_free_coherent(dev, MMU_COMPRESS_HEADER_SIZE,
>> +					  comm->mmu_header_vaddr[i],
>> +					  comm->mmu_header_paddr[i]);
>> +			comm->mmu_header_vaddr[i] = NULL;
>> +		}
>> +	}
>> +
>> +	if (comm->mmu_map_vaddr) {
>> +		dma_free_coherent(dev, MMU_MAP_SIZE,
>> +				  comm->mmu_map_vaddr,
>> +				  comm->mmu_map_paddr);
>> +		comm->mmu_map_vaddr = NULL;
>> +	}
>> +}
>> +EXPORT_SYMBOL_GPL(codec_hevc_free_mmu_headers);
>> +
>> +static int codec_hevc_alloc_mmu_headers(struct amvdec_session *sess,
>> +					struct codec_hevc_common *comm)
>> +{
>> +	struct device *dev = sess->core->dev;
>> +	struct v4l2_m2m_buffer *buf;
>> +
>> +	comm->mmu_map_vaddr = dma_alloc_coherent(dev, MMU_MAP_SIZE,
>> +						 &comm->mmu_map_paddr,
>> +						 GFP_KERNEL);
>> +	if (!comm->mmu_map_vaddr)
>> +		return -ENOMEM;
>> +
>> +	v4l2_m2m_for_each_dst_buf(sess->m2m_ctx, buf) {
>> +		u32 idx = buf->vb.vb2_buf.index;
>> +		dma_addr_t paddr;
>> +		void *vaddr = dma_alloc_coherent(dev, MMU_COMPRESS_HEADER_SIZE,
>> +						 &paddr, GFP_KERNEL);
>> +		if (!vaddr) {
>> +			dev_err(dev, "Couldn't allocate MMU header %u\n", idx);
>> +			codec_hevc_free_mmu_headers(sess, comm);
>> +			return -ENOMEM;
>> +		}
>> +
>> +		comm->mmu_header_vaddr[idx] = vaddr;
>> +		comm->mmu_header_paddr[idx] = paddr;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +int codec_hevc_setup_buffers(struct amvdec_session *sess,
>> +			     struct codec_hevc_common *comm,
>> +			     int is_10bit)
>> +{
>> +	struct amvdec_core *core = sess->core;
>> +	int ret;
>> +
>> +	if (codec_hevc_use_downsample(sess->pixfmt_cap, is_10bit)) {
>> +		ret = codec_hevc_alloc_fbc_buffers(sess, comm);
>> +		if (ret)
>> +			return ret;
>> +	}
>> +
>> +	if (codec_hevc_use_mmu(core->platform->revision,
>> +			       sess->pixfmt_cap, is_10bit)) {
>> +		ret = codec_hevc_alloc_mmu_headers(sess, comm);
>> +		if (ret) {
>> +			codec_hevc_free_fbc_buffers(sess, comm);
>> +			return ret;
>> +		}
>> +	}
>> +
>> +	if (core->platform->revision == VDEC_REVISION_GXBB)
>> +		codec_hevc_setup_buffers_gxbb(sess, comm, is_10bit);
>> +	else
>> +		codec_hevc_setup_buffers_gxl(sess, comm, is_10bit);
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(codec_hevc_setup_buffers);
>> +
>> +void codec_hevc_fill_mmu_map(struct amvdec_session *sess,
>> +			     struct codec_hevc_common *comm,
>> +			     struct vb2_buffer *vb)
>> +{
>> +	u32 size = amvdec_am21c_size(sess->width, sess->height);
>> +	u32 nb_pages = size / PAGE_SIZE;
>> +	u32 *mmu_map = comm->mmu_map_vaddr;
>> +	u32 first_page;
>> +	u32 i;
>> +
>> +	if (sess->pixfmt_cap == V4L2_PIX_FMT_NV12M)
>> +		first_page = comm->fbc_buffer_paddr[vb->index] >> PAGE_SHIFT;
>> +	else
>> +		first_page = vb2_dma_contig_plane_dma_addr(vb, 0) >> PAGE_SHIFT;
>> +
>> +	for (i = 0; i < nb_pages; ++i)
>> +		mmu_map[i] = first_page + i;
>> +}
>> +EXPORT_SYMBOL_GPL(codec_hevc_fill_mmu_map);
>> diff --git a/drivers/staging/media/meson/vdec/codec_hevc_common.h b/drivers/staging/media/meson/vdec/codec_hevc_common.h
>> new file mode 100644
>> index 000000000000..de16d2e43061
>> --- /dev/null
>> +++ b/drivers/staging/media/meson/vdec/codec_hevc_common.h
>> @@ -0,0 +1,77 @@
>> +/* SPDX-License-Identifier: GPL-2.0+ */
>> +/*
>> + * Copyright (C) 2018 BayLibre, SAS
>> + * Author: Maxime Jourdan <mjourdan@xxxxxxxxxxxx>
>> + */
>> +
>> +#ifndef __MESON_VDEC_HEVC_COMMON_H_
>> +#define __MESON_VDEC_HEVC_COMMON_H_
>> +
>> +#include "vdec.h"
>> +
>> +#define PARSER_CMD_SKIP_CFG_0 0x0000090b
>> +#define PARSER_CMD_SKIP_CFG_1 0x1b14140f
>> +#define PARSER_CMD_SKIP_CFG_2 0x001b1910
>> +static const u16 vdec_hevc_parser_cmd[] = {
>> +	0x0401,	0x8401,	0x0800,	0x0402,
>> +	0x9002,	0x1423,	0x8CC3,	0x1423,
>> +	0x8804,	0x9825,	0x0800,	0x04FE,
>> +	0x8406,	0x8411,	0x1800,	0x8408,
>> +	0x8409,	0x8C2A,	0x9C2B,	0x1C00,
>> +	0x840F,	0x8407,	0x8000,	0x8408,
>> +	0x2000,	0xA800,	0x8410,	0x04DE,
>> +	0x840C,	0x840D,	0xAC00,	0xA000,
>> +	0x08C0,	0x08E0,	0xA40E,	0xFC00,
>> +	0x7C00
>> +};
>> +
>> +#define MAX_REF_PIC_NUM	24
>> +
>> +struct codec_hevc_common {
>> +	void      *fbc_buffer_vaddr[MAX_REF_PIC_NUM];
>> +	dma_addr_t fbc_buffer_paddr[MAX_REF_PIC_NUM];
>> +
>> +	void      *mmu_header_vaddr[MAX_REF_PIC_NUM];
>> +	dma_addr_t mmu_header_paddr[MAX_REF_PIC_NUM];
>> +
>> +	void      *mmu_map_vaddr;
>> +	dma_addr_t mmu_map_paddr;
>> +};
>> +
>> +/* Returns 1 if we must use framebuffer compression */
>> +static inline int codec_hevc_use_fbc(u32 pixfmt, int is_10bit)
>> +{
>> +	/* TOFIX: Handle Amlogic Compressed buffer for 8bit also */
>> +	return is_10bit;
>> +}
>> +
>> +/* Returns 1 if we are decoding 10-bit but outputting 8-bit NV12 */
>> +static inline int codec_hevc_use_downsample(u32 pixfmt, int is_10bit)
>> +{
>> +	return is_10bit;
>> +}
>> +
>> +/* Returns 1 if we are decoding using the IOMMU */
>> +static inline int codec_hevc_use_mmu(u32 revision, u32 pixfmt, int is_10bit)
>> +{
>> +	return revision >= VDEC_REVISION_G12A &&
>> +	       codec_hevc_use_fbc(pixfmt, is_10bit);
>> +}
>> +
>> +/**
>> + * Configure decode head read mode
>> + */
>> +void codec_hevc_setup_decode_head(struct amvdec_session *sess, int is_10bit);
>> +
>> +void codec_hevc_free_fbc_buffers(struct amvdec_session *sess,
>> +				 struct codec_hevc_common *comm);
>> +
>> +int codec_hevc_setup_buffers(struct amvdec_session *sess,
>> +			     struct codec_hevc_common *comm,
>> +			     int is_10bit);
>> +
>> +void codec_hevc_fill_mmu_map(struct amvdec_session *sess,
>> +			     struct codec_hevc_common *comm,
>> +			     struct vb2_buffer *vb);
>> +
>> +#endif
>> diff --git a/drivers/staging/media/meson/vdec/hevc_regs.h b/drivers/staging/media/meson/vdec/hevc_regs.h
>> new file mode 100644
>> index 000000000000..55c1a80b955a
>> --- /dev/null
>> +++ b/drivers/staging/media/meson/vdec/hevc_regs.h
>> @@ -0,0 +1,211 @@
>> +/* SPDX-License-Identifier: GPL-2.0+ */
>> +/*
>> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
>> + */
>> +
>> +#ifndef __MESON_VDEC_HEVC_REGS_H_
>> +#define __MESON_VDEC_HEVC_REGS_H_
>> +
>> +#define HEVC_ASSIST_MMU_MAP_ADDR 0xc024
>> +
>> +#define HEVC_ASSIST_MBOX1_CLR_REG 0xc1d4
>> +#define HEVC_ASSIST_MBOX1_MASK 0xc1d8
>> +
>> +#define HEVC_ASSIST_SCRATCH_0 0xc300
>> +#define HEVC_ASSIST_SCRATCH_1 0xc304
>> +#define HEVC_ASSIST_SCRATCH_2 0xc308
>> +#define HEVC_ASSIST_SCRATCH_3 0xc30c
>> +#define HEVC_ASSIST_SCRATCH_4 0xc310
>> +#define HEVC_ASSIST_SCRATCH_5 0xc314
>> +#define HEVC_ASSIST_SCRATCH_6 0xc318
>> +#define HEVC_ASSIST_SCRATCH_7 0xc31c
>> +#define HEVC_ASSIST_SCRATCH_8 0xc320
>> +#define HEVC_ASSIST_SCRATCH_9 0xc324
>> +#define HEVC_ASSIST_SCRATCH_A 0xc328
>> +#define HEVC_ASSIST_SCRATCH_B 0xc32c
>> +#define HEVC_ASSIST_SCRATCH_C 0xc330
>> +#define HEVC_ASSIST_SCRATCH_D 0xc334
>> +#define HEVC_ASSIST_SCRATCH_E 0xc338
>> +#define HEVC_ASSIST_SCRATCH_F 0xc33c
>> +#define HEVC_ASSIST_SCRATCH_G 0xc340
>> +#define HEVC_ASSIST_SCRATCH_H 0xc344
>> +#define HEVC_ASSIST_SCRATCH_I 0xc348
>> +#define HEVC_ASSIST_SCRATCH_J 0xc34c
>> +#define HEVC_ASSIST_SCRATCH_K 0xc350
>> +#define HEVC_ASSIST_SCRATCH_L 0xc354
>> +#define HEVC_ASSIST_SCRATCH_M 0xc358
>> +#define HEVC_ASSIST_SCRATCH_N 0xc35c
>> +
>> +#define HEVC_PARSER_VERSION 0xc400
>> +#define HEVC_STREAM_CONTROL 0xc404
>> +#define HEVC_STREAM_START_ADDR 0xc408
>> +#define HEVC_STREAM_END_ADDR 0xc40c
>> +#define HEVC_STREAM_WR_PTR 0xc410
>> +#define HEVC_STREAM_RD_PTR 0xc414
>> +#define HEVC_STREAM_LEVEL 0xc418
>> +#define HEVC_STREAM_FIFO_CTL 0xc41c
>> +#define HEVC_SHIFT_CONTROL 0xc420
>> +#define HEVC_SHIFT_STARTCODE 0xc424
>> +#define HEVC_SHIFT_EMULATECODE 0xc428
>> +#define HEVC_SHIFT_STATUS 0xc42c
>> +#define HEVC_SHIFTED_DATA 0xc430
>> +#define HEVC_SHIFT_BYTE_COUNT 0xc434
>> +#define HEVC_SHIFT_COMMAND 0xc438
>> +#define HEVC_ELEMENT_RESULT 0xc43c
>> +#define HEVC_CABAC_CONTROL 0xc440
>> +#define HEVC_PARSER_SLICE_INFO 0xc444
>> +#define HEVC_PARSER_CMD_WRITE 0xc448
>> +#define HEVC_PARSER_CORE_CONTROL 0xc44c
>> +#define HEVC_PARSER_CMD_FETCH 0xc450
>> +#define HEVC_PARSER_CMD_STATUS 0xc454
>> +#define HEVC_PARSER_LCU_INFO 0xc458
>> +#define HEVC_PARSER_HEADER_INFO 0xc45c
>> +#define HEVC_PARSER_INT_CONTROL 0xc480
>> +#define HEVC_PARSER_INT_STATUS 0xc484
>> +#define HEVC_PARSER_IF_CONTROL 0xc488
>> +#define HEVC_PARSER_PICTURE_SIZE 0xc48c
>> +#define HEVC_PARSER_LCU_START 0xc490
>> +#define HEVC_PARSER_HEADER_INFO2 0xc494
>> +#define HEVC_PARSER_QUANT_READ 0xc498
>> +#define HEVC_PARSER_RESERVED_27 0xc49c
>> +#define HEVC_PARSER_CMD_SKIP_0 0xc4a0
>> +#define HEVC_PARSER_CMD_SKIP_1 0xc4a4
>> +#define HEVC_PARSER_CMD_SKIP_2 0xc4a8
>> +#define HEVC_SAO_IF_STATUS 0xc4c0
>> +#define HEVC_SAO_IF_DATA_Y 0xc4c4
>> +#define HEVC_SAO_IF_DATA_U 0xc4c8
>> +#define HEVC_SAO_IF_DATA_V 0xc4cc
>> +#define HEVC_STREAM_SWAP_ADDR 0xc4d0
>> +#define HEVC_STREAM_SWAP_CTRL 0xc4d4
>> +#define HEVC_IQIT_IF_WAIT_CNT 0xc4d8
>> +#define HEVC_MPRED_IF_WAIT_CNT 0xc4dc
>> +#define HEVC_SAO_IF_WAIT_CNT 0xc4e0
>> +
>> +#define HEVC_MPRED_VERSION 0xc800
>> +#define HEVC_MPRED_CTRL0 0xc804
>> +	#define MPRED_CTRL0_NEW_PIC	BIT(2)
>> +	#define MPRED_CTRL0_NEW_TILE	BIT(3)
>> +	#define MPRED_CTRL0_NEW_SLI_SEG	BIT(4)
>> +	#define MPRED_CTRL0_TMVP	BIT(5)
>> +	#define MPRED_CTRL0_LDC		BIT(6)
>> +	#define MPRED_CTRL0_COL_FROM_L0	BIT(7)
>> +	#define MPRED_CTRL0_ABOVE_EN	BIT(9)
>> +	#define MPRED_CTRL0_MV_WR_EN	BIT(10)
>> +	#define MPRED_CTRL0_MV_RD_EN	BIT(11)
>> +	#define MPRED_CTRL0_BUF_LINEAR	BIT(13)
>> +#define HEVC_MPRED_CTRL1 0xc808
>> +#define HEVC_MPRED_INT_EN 0xc80c
>> +#define HEVC_MPRED_INT_STATUS 0xc810
>> +#define HEVC_MPRED_PIC_SIZE 0xc814
>> +#define HEVC_MPRED_PIC_SIZE_LCU 0xc818
>> +#define HEVC_MPRED_TILE_START 0xc81c
>> +#define HEVC_MPRED_TILE_SIZE_LCU 0xc820
>> +#define HEVC_MPRED_REF_NUM 0xc824
>> +#define HEVC_MPRED_REF_EN_L0 0xc830
>> +#define HEVC_MPRED_REF_EN_L1 0xc834
>> +#define HEVC_MPRED_COLREF_EN_L0 0xc838
>> +#define HEVC_MPRED_COLREF_EN_L1 0xc83c
>> +#define HEVC_MPRED_AXI_WCTRL 0xc840
>> +#define HEVC_MPRED_AXI_RCTRL 0xc844
>> +#define HEVC_MPRED_ABV_START_ADDR 0xc848
>> +#define HEVC_MPRED_MV_WR_START_ADDR 0xc84c
>> +#define HEVC_MPRED_MV_RD_START_ADDR 0xc850
>> +#define HEVC_MPRED_MV_WPTR 0xc854
>> +#define HEVC_MPRED_MV_RPTR 0xc858
>> +#define HEVC_MPRED_MV_WR_ROW_JUMP 0xc85c
>> +#define HEVC_MPRED_MV_RD_ROW_JUMP 0xc860
>> +#define HEVC_MPRED_CURR_LCU 0xc864
>> +#define HEVC_MPRED_ABV_WPTR 0xc868
>> +#define HEVC_MPRED_ABV_RPTR 0xc86c
>> +#define HEVC_MPRED_CTRL2 0xc870
>> +#define HEVC_MPRED_CTRL3 0xc874
>> +#define HEVC_MPRED_L0_REF00_POC 0xc880
>> +#define HEVC_MPRED_L1_REF00_POC 0xc8c0
>> +
>> +#define HEVC_MPRED_CUR_POC 0xc980
>> +#define HEVC_MPRED_COL_POC 0xc984
>> +#define HEVC_MPRED_MV_RD_END_ADDR 0xc988
>> +
>> +#define HEVC_MSP 0xcc00
>> +#define HEVC_MPSR 0xcc04
>> +#define HEVC_MCPU_INTR_MSK 0xcc10
>> +#define HEVC_MCPU_INTR_REQ 0xcc14
>> +#define HEVC_CPSR 0xcc84
>> +
>> +#define HEVC_IMEM_DMA_CTRL 0xcd00
>> +#define HEVC_IMEM_DMA_ADR 0xcd04
>> +#define HEVC_IMEM_DMA_COUNT 0xcd08
>> +
>> +#define HEVCD_IPP_TOP_CNTL 0xd000
>> +#define HEVCD_IPP_LINEBUFF_BASE 0xd024
>> +#define HEVCD_IPP_AXIIF_CONFIG 0xd02c
>> +
>> +#define HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR 0xd180
>> +#define HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR 0xd184
>> +#define HEVCD_MPP_ANC2AXI_TBL_DATA 0xd190
>> +
>> +#define HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR 0xd300
>> +#define HEVCD_MPP_ANC_CANVAS_DATA_ADDR 0xd304
>> +#define HEVCD_MPP_DECOMP_CTL1 0xd308
>> +#define HEVCD_MPP_DECOMP_CTL2 0xd30c
>> +#define HEVCD_MCRCC_CTL1 0xd3c0
>> +#define HEVCD_MCRCC_CTL2 0xd3c4
>> +#define HEVCD_MCRCC_CTL3 0xd3c8
>> +
>> +#define HEVC_DBLK_CFG0 0xd400
>> +#define HEVC_DBLK_CFG1 0xd404
>> +#define HEVC_DBLK_CFG2 0xd408
>> +#define HEVC_DBLK_CFG3 0xd40c
>> +#define HEVC_DBLK_CFG4 0xd410
>> +#define HEVC_DBLK_CFG5 0xd414
>> +#define HEVC_DBLK_CFG6 0xd418
>> +#define HEVC_DBLK_CFG7 0xd41c
>> +#define HEVC_DBLK_CFG8 0xd420
>> +#define HEVC_DBLK_CFG9 0xd424
>> +#define HEVC_DBLK_CFGA 0xd428
>> +#define HEVC_DBLK_STS0 0xd42c
>> +#define HEVC_DBLK_STS1 0xd430
>> +#define HEVC_DBLK_CFGE 0xd438
>> +
>> +#define HEVC_SAO_VERSION 0xd800
>> +#define HEVC_SAO_CTRL0 0xd804
>> +#define HEVC_SAO_CTRL1 0xd808
>> +#define HEVC_SAO_PIC_SIZE 0xd814
>> +#define HEVC_SAO_PIC_SIZE_LCU 0xd818
>> +#define HEVC_SAO_TILE_START 0xd81c
>> +#define HEVC_SAO_TILE_SIZE_LCU 0xd820
>> +#define HEVC_SAO_Y_START_ADDR 0xd82c
>> +#define HEVC_SAO_Y_LENGTH 0xd830
>> +#define HEVC_SAO_C_START_ADDR 0xd834
>> +#define HEVC_SAO_C_LENGTH 0xd838
>> +#define HEVC_SAO_Y_WPTR 0xd83c
>> +#define HEVC_SAO_C_WPTR 0xd840
>> +#define HEVC_SAO_ABV_START_ADDR 0xd844
>> +#define HEVC_SAO_VB_WR_START_ADDR 0xd848
>> +#define HEVC_SAO_VB_RD_START_ADDR 0xd84c
>> +#define HEVC_SAO_ABV_WPTR 0xd850
>> +#define HEVC_SAO_ABV_RPTR 0xd854
>> +#define HEVC_SAO_VB_WPTR 0xd858
>> +#define HEVC_SAO_VB_RPTR 0xd85c
>> +#define HEVC_SAO_CTRL2 0xd880
>> +#define HEVC_SAO_CTRL3 0xd884
>> +#define HEVC_SAO_CTRL4 0xd888
>> +#define HEVC_SAO_CTRL5 0xd88c
>> +#define HEVC_SAO_CTRL6 0xd890
>> +#define HEVC_SAO_CTRL7 0xd894
>> +#define HEVC_CM_BODY_START_ADDR 0xd898
>> +#define HEVC_CM_BODY_LENGTH 0xd89c
>> +#define HEVC_CM_HEADER_START_ADDR 0xd8a0
>> +#define HEVC_CM_HEADER_LENGTH 0xd8a4
>> +#define HEVC_CM_HEADER_OFFSET 0xd8ac
>> +#define HEVC_SAO_MMU_VH0_ADDR 0xd8e8
>> +#define HEVC_SAO_MMU_VH1_ADDR 0xd8ec
>> +
>> +#define HEVC_IQIT_CLK_RST_CTRL 0xdc00
>> +#define HEVC_IQIT_SCALELUT_WR_ADDR 0xdc08
>> +#define HEVC_IQIT_SCALELUT_RD_ADDR 0xdc0c
>> +#define HEVC_IQIT_SCALELUT_DATA 0xdc10
>> +
>> +#define HEVC_PSCALE_CTRL 0xe444
>> +
>> +#endif
>> diff --git a/drivers/staging/media/meson/vdec/vdec_hevc.c b/drivers/staging/media/meson/vdec/vdec_hevc.c
>> new file mode 100644
>> index 000000000000..af41215e106c
>> --- /dev/null
>> +++ b/drivers/staging/media/meson/vdec/vdec_hevc.c
>> @@ -0,0 +1,231 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * Copyright (C) 2018 Maxime Jourdan <maxi.jourdan@xxxxxxxxxx>
>> + *
>> + * VDEC_HEVC is a video decoding block that allows decoding of
>> + * HEVC, VP9
>> + */
>> +
>> +#include <linux/firmware.h>
>> +#include <linux/clk.h>
>> +
>> +#include "vdec_1.h"
>> +#include "vdec_helpers.h"
>> +#include "hevc_regs.h"
>> +#include "dos_regs.h"
>> +
>> +/* AO Registers */
>> +#define AO_RTI_GEN_PWR_SLEEP0	0xe8
>> +#define AO_RTI_GEN_PWR_ISO0	0xec
>> +	#define GEN_PWR_VDEC_HEVC (BIT(7) | BIT(6))
>> +	#define GEN_PWR_VDEC_HEVC_SM1 (BIT(2))
>> +
>> +#define MC_SIZE	(4096 * 4)
>> +
>> +static int vdec_hevc_load_firmware(struct amvdec_session *sess,
>> +				   const char *fwname)
>> +{
>> +	struct amvdec_core *core = sess->core;
>> +	struct device *dev = core->dev_dec;
>> +	const struct firmware *fw;
>> +	static void *mc_addr;
>> +	static dma_addr_t mc_addr_map;
>> +	int ret;
>> +	u32 i = 100;
>> +
>> +	ret = request_firmware(&fw, fwname, dev);
>> +	if (ret < 0)  {
>> +		dev_err(dev, "Unable to request firmware %s\n", fwname);
>> +		return ret;
>> +	}
>> +
>> +	if (fw->size < MC_SIZE) {
>> +		dev_err(dev, "Firmware size %zu is too small. Expected %u.\n",
>> +			fw->size, MC_SIZE);
>> +		ret = -EINVAL;
>> +		goto release_firmware;
>> +	}
>> +
>> +	mc_addr = dma_alloc_coherent(core->dev, MC_SIZE, &mc_addr_map,
>> +				     GFP_KERNEL);
>> +	if (!mc_addr) {
>> +		dev_err(dev, "Failed allocating memory for firmware loading\n");
>> +		ret = -ENOMEM;
>> +		goto release_firmware;
>> +	}
>> +
>> +	memcpy(mc_addr, fw->data, MC_SIZE);
>> +
>> +	amvdec_write_dos(core, HEVC_MPSR, 0);
>> +	amvdec_write_dos(core, HEVC_CPSR, 0);
>> +
>> +	amvdec_write_dos(core, HEVC_IMEM_DMA_ADR, mc_addr_map);
>> +	amvdec_write_dos(core, HEVC_IMEM_DMA_COUNT, MC_SIZE / 4);
>> +	amvdec_write_dos(core, HEVC_IMEM_DMA_CTRL, (0x8000 | (7 << 16)));
>> +
>> +	while (i && (readl(core->dos_base + HEVC_IMEM_DMA_CTRL) & 0x8000))
>> +		i--;
>> +
>> +	if (i == 0) {
>> +		dev_err(dev, "Firmware load fail (DMA hang?)\n");
>> +		ret = -ENODEV;
>> +	}
>> +
>> +	dma_free_coherent(core->dev, MC_SIZE, mc_addr, mc_addr_map);
>> +release_firmware:
>> +	release_firmware(fw);
>> +	return ret;
>> +}
>> +
>> +static void vdec_hevc_stbuf_init(struct amvdec_session *sess)
>> +{
>> +	struct amvdec_core *core = sess->core;
>> +
>> +	amvdec_write_dos(core, HEVC_STREAM_CONTROL,
>> +			 amvdec_read_dos(core, HEVC_STREAM_CONTROL) & ~1);
>> +	amvdec_write_dos(core, HEVC_STREAM_START_ADDR, sess->vififo_paddr);
>> +	amvdec_write_dos(core, HEVC_STREAM_END_ADDR,
>> +			 sess->vififo_paddr + sess->vififo_size);
>> +	amvdec_write_dos(core, HEVC_STREAM_RD_PTR, sess->vififo_paddr);
>> +	amvdec_write_dos(core, HEVC_STREAM_WR_PTR, sess->vififo_paddr);
>> +}
>> +
>> +/* VDEC_HEVC specific ESPARSER configuration */
>> +static void vdec_hevc_conf_esparser(struct amvdec_session *sess)
>> +{
>> +	struct amvdec_core *core = sess->core;
>> +
>> +	/* set vififo_vbuf_rp_sel=>vdec_hevc */
>> +	amvdec_write_dos(core, DOS_GEN_CTRL0, 3 << 1);
>> +	amvdec_write_dos(core, HEVC_STREAM_CONTROL,
>> +			 amvdec_read_dos(core, HEVC_STREAM_CONTROL) | BIT(3));
>> +	amvdec_write_dos(core, HEVC_STREAM_CONTROL,
>> +			 amvdec_read_dos(core, HEVC_STREAM_CONTROL) | 1);
>> +	amvdec_write_dos(core, HEVC_STREAM_FIFO_CTL,
>> +			 amvdec_read_dos(core, HEVC_STREAM_FIFO_CTL) | BIT(29));
>> +}
>> +
>> +static u32 vdec_hevc_vififo_level(struct amvdec_session *sess)
>> +{
>> +	return readl_relaxed(sess->core->dos_base + HEVC_STREAM_LEVEL);
>> +}
>> +
>> +static int vdec_hevc_stop(struct amvdec_session *sess)
>> +{
>> +	struct amvdec_core *core = sess->core;
>> +	struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops;
>> +
>> +	/* Disable interrupt */
>> +	amvdec_write_dos(core, HEVC_ASSIST_MBOX1_MASK, 0);
>> +	/* Disable firmware processor */
>> +	amvdec_write_dos(core, HEVC_MPSR, 0);
>> +
>> +	if (sess->priv)
>> +		codec_ops->stop(sess);
>> +
>> +	/* Enable VDEC_HEVC Isolation */
>> +	if (core->platform->revision == VDEC_REVISION_SM1)
>> +		regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_ISO0,
>> +				   GEN_PWR_VDEC_HEVC_SM1,
>> +				   GEN_PWR_VDEC_HEVC_SM1);
>> +	else
>> +		regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_ISO0,
>> +				   0xc00, 0xc00);
>> +
>> +	/* VDEC_HEVC Memories */
>> +	amvdec_write_dos(core, DOS_MEM_PD_HEVC, 0xffffffffUL);
>> +
>> +	if (core->platform->revision == VDEC_REVISION_SM1)
>> +		regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
>> +				   GEN_PWR_VDEC_HEVC_SM1,
>> +				   GEN_PWR_VDEC_HEVC_SM1);
>> +	else
>> +		regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
>> +				   GEN_PWR_VDEC_HEVC, GEN_PWR_VDEC_HEVC);
>> +
>> +	clk_disable_unprepare(core->vdec_hevc_clk);
>> +	if (core->platform->revision == VDEC_REVISION_G12A ||
>> +	    core->platform->revision == VDEC_REVISION_SM1)
>> +		clk_disable_unprepare(core->vdec_hevcf_clk);
>> +
>> +	return 0;
>> +}
>> +
>> +static int vdec_hevc_start(struct amvdec_session *sess)
>> +{
>> +	int ret;
>> +	struct amvdec_core *core = sess->core;
>> +	struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops;
>> +
>> +	if (core->platform->revision == VDEC_REVISION_G12A ||
>> +	    core->platform->revision == VDEC_REVISION_SM1) {
>> +		clk_set_rate(core->vdec_hevcf_clk, 666666666);
>> +		ret = clk_prepare_enable(core->vdec_hevcf_clk);
>> +		if (ret)
>> +			return ret;
>> +	}
>> +
>> +	clk_set_rate(core->vdec_hevc_clk, 666666666);
>> +	ret = clk_prepare_enable(core->vdec_hevc_clk);
>> +	if (ret)
>> +		return ret;
>> +
>> +	if (core->platform->revision == VDEC_REVISION_SM1)
>> +		regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
>> +				   GEN_PWR_VDEC_HEVC_SM1, 0);
>> +	else
>> +		regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
>> +				   GEN_PWR_VDEC_HEVC, 0);
>> +	udelay(10);
>> +
>> +	/* Reset VDEC_HEVC*/
>> +	amvdec_write_dos(core, DOS_SW_RESET3, 0xffffffff);
>> +	amvdec_write_dos(core, DOS_SW_RESET3, 0x00000000);
>> +
>> +	amvdec_write_dos(core, DOS_GCLK_EN3, 0xffffffff);
>> +
>> +	/* VDEC_HEVC Memories */
>> +	amvdec_write_dos(core, DOS_MEM_PD_HEVC, 0x00000000);
>> +
>> +	/* Remove VDEC_HEVC Isolation */
>> +	if (core->platform->revision == VDEC_REVISION_SM1)
>> +		regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_ISO0,
>> +				   GEN_PWR_VDEC_HEVC_SM1, 0);
>> +	else
>> +		regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_ISO0,
>> +				   0xc00, 0);
>> +
>> +	amvdec_write_dos(core, DOS_SW_RESET3, 0xffffffff);
>> +	amvdec_write_dos(core, DOS_SW_RESET3, 0x00000000);
>> +
>> +	vdec_hevc_stbuf_init(sess);
>> +
>> +	ret = vdec_hevc_load_firmware(sess, sess->fmt_out->firmware_path);
>> +	if (ret)
>> +		goto stop;
>> +
>> +	ret = codec_ops->start(sess);
>> +	if (ret)
>> +		goto stop;
>> +
>> +	amvdec_write_dos(core, DOS_SW_RESET3, BIT(12) | BIT(11));
>> +	amvdec_write_dos(core, DOS_SW_RESET3, 0);
>> +	amvdec_read_dos(core, DOS_SW_RESET3);
>> +
>> +	amvdec_write_dos(core, HEVC_MPSR, 1);
>> +	/* Let the firmware settle */
>> +	udelay(10);
>> +
>> +	return 0;
>> +
>> +stop:
>> +	vdec_hevc_stop(sess);
>> +	return ret;
>> +}
>> +
>> +struct amvdec_ops vdec_hevc_ops = {
>> +	.start = vdec_hevc_start,
>> +	.stop = vdec_hevc_stop,
>> +	.conf_esparser = vdec_hevc_conf_esparser,
>> +	.vififo_level = vdec_hevc_vififo_level,
>> +};
>> diff --git a/drivers/staging/media/meson/vdec/vdec_hevc.h b/drivers/staging/media/meson/vdec/vdec_hevc.h
>> new file mode 100644
>> index 000000000000..cd576a73a966
>> --- /dev/null
>> +++ b/drivers/staging/media/meson/vdec/vdec_hevc.h
>> @@ -0,0 +1,13 @@
>> +/* SPDX-License-Identifier: GPL-2.0+ */
>> +/*
>> + * Copyright (C) 2018 Maxime Jourdan <maxi.jourdan@xxxxxxxxxx>
>> + */
>> +
>> +#ifndef __MESON_VDEC_VDEC_HEVC_H_
>> +#define __MESON_VDEC_VDEC_HEVC_H_
>> +
>> +#include "vdec.h"
>> +
>> +extern struct amvdec_ops vdec_hevc_ops;
>> +
>> +#endif
>>
> 




[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux