From patchwork Thu Jun 15 16:31:42 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stanimir Varbanov X-Patchwork-Id: 105661 Delivered-To: patch@linaro.org Received: by 10.140.91.77 with SMTP id y71csp888212qgd; Thu, 15 Jun 2017 09:33:20 -0700 (PDT) X-Received: by 10.99.176.1 with SMTP id h1mr6175774pgf.152.1497544400015; Thu, 15 Jun 2017 09:33:20 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1497544400; cv=none; d=google.com; s=arc-20160816; b=P1tHgrKHmLL9+Ue47EEsH3JjA/8QxNjNWgDwIAMdjtmivjoHz90+ZbqviQ3Dg5Qfzy 9NuGJwgTnX+LWVQzAh3g8a2VLZRGnoKOBzuKX4Pca7SBi04Kgz2IiCJ+viZjzWdEmgoy zBcJMcoQR9bwlw/C9+6P9pWM54IZZSYAWZRf9odyypVdIC6ocQhAhkgm9Wy+ipsKulh6 1kxN4+FwIZllY+uji0cxTm1kM6Z9/8GTs0M0jjwJgPlDS1e/H2nA+XaHjrmB4uFf/0l2 93lcuISyb+OrgoZ0vNjoK3s+xDD62lzUchmQ3upeUbwCnUUVwHG0pJcxTREMzJkYbWnJ 6F4w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:arc-authentication-results; bh=OrAlylWhhHEYYyebsMFbln5HOCDCCiwo6pmL/qgrjWk=; b=XBdbu/yiSW7TVarxeErkN58wFIvZSIr1Oor9N3UKZx9I+CRml3vyEL9heFj+H/0lmh 9hGVKRY4cjGr1s0oF21vAaHUmgRyuAv1oy0+76sgc69vA+U8bzN82SJ+IorkSgJP6TRn 8tMp8gIg6AU1+XUad9GcyOuUQlMWEsssrJKhTeFry5tP2s9KDTuyo5afV/QAZCVvlOMY sa0MMd1qlIGuuhHvSLUgPQ326c48T7QZSidu3hK/Q6MIvMJDgFFBwj33MQ9M8zq/H1at zLiV30pqJZazi/E0QUdPUfsZcE7/jArPZmQmSAdb5WW88M9vVHwNt3fuQlaKdKkeHLXK qN6w== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.b=faj3NkLk; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id p5si397449pfl.413.2017.06.15.09.33.19; Thu, 15 Jun 2017 09:33:19 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.b=faj3NkLk; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752108AbdFOQcn (ORCPT + 25 others); Thu, 15 Jun 2017 12:32:43 -0400 Received: from mail-wm0-f47.google.com ([74.125.82.47]:35615 "EHLO mail-wm0-f47.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752051AbdFOQcm (ORCPT ); Thu, 15 Jun 2017 12:32:42 -0400 Received: by mail-wm0-f47.google.com with SMTP id x70so4441137wme.0 for ; Thu, 15 Jun 2017 09:32:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=OrAlylWhhHEYYyebsMFbln5HOCDCCiwo6pmL/qgrjWk=; b=faj3NkLkRoebmM8P4LW7s8JaVr2HzVZ0DEAXze5WNY0QuHfeFjw4Vvy9B/Wvxxi5ld HbHAXrffPUoChUy11JAna8ivNjqDCt5Gz61YogNkam0mV0P3l9H3xaTU/02hjlAVQmJ1 bNbknF1PG45YC0jbd3rFIwxH8UOL+Iz3sDOa0= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=OrAlylWhhHEYYyebsMFbln5HOCDCCiwo6pmL/qgrjWk=; b=hV+SOfC8BIMK/syYP+MSGgwrF+rPAH2/yO26nf0DSPxpUWlZZljJApqiNVbzGwhT8b xm7H0KnbbzPTO1wHuO9fVUzfoaSoO0Ruf5BkYzP08WzIgSRmMKpNqkZK03n1LJR3qqqZ VxbaJTwh0skaQ/YFEhjHZR3pxePMpKKhE/H3mgewmWYsf+FEUufk0g21kbcMXmHMiUow Q8NOzTS3IHXwKVMlc0AdckjtrCnc8qThwehPTuhKuLZah+xCvPPyHehNSaIZYNAaWNze GnSVUtBc9Y9Paf4aHXLg+rnNYED+kx+pzB1ayYVm6pXESz+DIz3YDaVWWRMf0sLr/ytR XqDg== X-Gm-Message-State: AKS2vOw2e68pAB1agxxJ0a8+T/FJEczI04JnLIGT1DetCnkVqknAS5He PxGfSH4kQG/uiniv X-Received: by 10.28.91.209 with SMTP id p200mr4267689wmb.67.1497544359867; Thu, 15 Jun 2017 09:32:39 -0700 (PDT) Received: from mms-0440.qualcomm.mm-sol.com ([37.157.136.206]) by smtp.gmail.com with ESMTPSA id m73sm541890wmi.25.2017.06.15.09.32.38 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 15 Jun 2017 09:32:39 -0700 (PDT) From: Stanimir Varbanov To: Mauro Carvalho Chehab , Hans Verkuil Cc: linux-media@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, Stanimir Varbanov Subject: [PATCH v11 01/19] media: v4l2-mem2mem: extend m2m APIs for more accurate buffer management Date: Thu, 15 Jun 2017 19:31:42 +0300 Message-Id: <1497544320-2269-2-git-send-email-stanimir.varbanov@linaro.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1497544320-2269-1-git-send-email-stanimir.varbanov@linaro.org> References: <1497544320-2269-1-git-send-email-stanimir.varbanov@linaro.org> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org this add functions for: - remove buffers from src/dst queue by index - remove exact buffer from src/dst queue also extends m2m API to iterate over a list of src/dst buffers in safely and non-safely manner. Signed-off-by: Stanimir Varbanov --- drivers/media/v4l2-core/v4l2-mem2mem.c | 37 ++++++++++++++ include/media/v4l2-mem2mem.h | 92 ++++++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+) -- 2.7.4 diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 6bc27e7b2a33..f62e68aa04c4 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -126,6 +126,43 @@ void *v4l2_m2m_buf_remove(struct v4l2_m2m_queue_ctx *q_ctx) } EXPORT_SYMBOL_GPL(v4l2_m2m_buf_remove); +void v4l2_m2m_buf_remove_by_buf(struct v4l2_m2m_queue_ctx *q_ctx, + struct vb2_v4l2_buffer *vbuf) +{ + struct v4l2_m2m_buffer *b; + unsigned long flags; + + spin_lock_irqsave(&q_ctx->rdy_spinlock, flags); + b = container_of(vbuf, struct v4l2_m2m_buffer, vb); + list_del(&b->list); + q_ctx->num_rdy--; + spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags); +} +EXPORT_SYMBOL_GPL(v4l2_m2m_buf_remove_by_buf); + +struct vb2_v4l2_buffer * +v4l2_m2m_buf_remove_by_idx(struct v4l2_m2m_queue_ctx *q_ctx, unsigned int idx) + +{ + struct v4l2_m2m_buffer *b, *tmp; + struct vb2_v4l2_buffer *ret = NULL; + unsigned long flags; + + spin_lock_irqsave(&q_ctx->rdy_spinlock, flags); + list_for_each_entry_safe(b, tmp, &q_ctx->rdy_queue, list) { + if (b->vb.vb2_buf.index == idx) { + list_del(&b->list); + q_ctx->num_rdy--; + ret = &b->vb; + break; + } + } + spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags); + + return ret; +} +EXPORT_SYMBOL_GPL(v4l2_m2m_buf_remove_by_idx); + /* * Scheduling handlers */ diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h index 3ccd01bd245e..e157d5c9b224 100644 --- a/include/media/v4l2-mem2mem.h +++ b/include/media/v4l2-mem2mem.h @@ -437,6 +437,47 @@ static inline void *v4l2_m2m_next_dst_buf(struct v4l2_m2m_ctx *m2m_ctx) } /** + * v4l2_m2m_for_each_dst_buf() - iterate over a list of destination ready + * buffers + * + * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx + * @b: current buffer of type struct v4l2_m2m_buffer + */ +#define v4l2_m2m_for_each_dst_buf(m2m_ctx, b) \ + list_for_each_entry(b, &m2m_ctx->cap_q_ctx.rdy_queue, list) + +/** + * v4l2_m2m_for_each_src_buf() - iterate over a list of source ready buffers + * + * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx + * @b: current buffer of type struct v4l2_m2m_buffer + */ +#define v4l2_m2m_for_each_src_buf(m2m_ctx, b) \ + list_for_each_entry(b, &m2m_ctx->out_q_ctx.rdy_queue, list) + +/** + * v4l2_m2m_for_each_dst_buf_safe() - iterate over a list of destination ready + * buffers safely + * + * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx + * @b: current buffer of type struct v4l2_m2m_buffer + * @n: used as temporary storage + */ +#define v4l2_m2m_for_each_dst_buf_safe(m2m_ctx, b, n) \ + list_for_each_entry_safe(b, n, &m2m_ctx->cap_q_ctx.rdy_queue, list) + +/** + * v4l2_m2m_for_each_src_buf_safe() - iterate over a list of source ready + * buffers safely + * + * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx + * @b: current buffer of type struct v4l2_m2m_buffer + * @n: used as temporary storage + */ +#define v4l2_m2m_for_each_src_buf_safe(m2m_ctx, b, n) \ + list_for_each_entry_safe(b, n, &m2m_ctx->out_q_ctx.rdy_queue, list) + +/** * v4l2_m2m_get_src_vq() - return vb2_queue for source buffers * * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx @@ -488,6 +529,57 @@ static inline void *v4l2_m2m_dst_buf_remove(struct v4l2_m2m_ctx *m2m_ctx) return v4l2_m2m_buf_remove(&m2m_ctx->cap_q_ctx); } +/** + * v4l2_m2m_buf_remove_by_buf() - take off exact buffer from the list of ready + * buffers + * + * @q_ctx: pointer to struct @v4l2_m2m_queue_ctx + * @vbuf: the buffer to be removed + */ +void v4l2_m2m_buf_remove_by_buf(struct v4l2_m2m_queue_ctx *q_ctx, + struct vb2_v4l2_buffer *vbuf); + +/** + * v4l2_m2m_src_buf_remove_by_buf() - take off exact source buffer from the list + * of ready buffers + * + * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx + * @vbuf: the buffer to be removed + */ +static inline void v4l2_m2m_src_buf_remove_by_buf(struct v4l2_m2m_ctx *m2m_ctx, + struct vb2_v4l2_buffer *vbuf) +{ + v4l2_m2m_buf_remove_by_buf(&m2m_ctx->out_q_ctx, vbuf); +} + +/** + * v4l2_m2m_dst_buf_remove_by_buf() - take off exact destination buffer from the + * list of ready buffers + * + * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx + * @vbuf: the buffer to be removed + */ +static inline void v4l2_m2m_dst_buf_remove_by_buf(struct v4l2_m2m_ctx *m2m_ctx, + struct vb2_v4l2_buffer *vbuf) +{ + v4l2_m2m_buf_remove_by_buf(&m2m_ctx->cap_q_ctx, vbuf); +} + +struct vb2_v4l2_buffer * +v4l2_m2m_buf_remove_by_idx(struct v4l2_m2m_queue_ctx *q_ctx, unsigned int idx); + +static inline struct vb2_v4l2_buffer * +v4l2_m2m_src_buf_remove_by_idx(struct v4l2_m2m_ctx *m2m_ctx, unsigned int idx) +{ + return v4l2_m2m_buf_remove_by_idx(&m2m_ctx->out_q_ctx, idx); +} + +static inline struct vb2_v4l2_buffer * +v4l2_m2m_dst_buf_remove_by_idx(struct v4l2_m2m_ctx *m2m_ctx, unsigned int idx) +{ + return v4l2_m2m_buf_remove_by_idx(&m2m_ctx->cap_q_ctx, idx); +} + /* v4l2 ioctl helpers */ int v4l2_m2m_ioctl_reqbufs(struct file *file, void *priv, From patchwork Thu Jun 15 16:31:45 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stanimir Varbanov X-Patchwork-Id: 105664 Delivered-To: patch@linaro.org Received: by 10.140.91.77 with SMTP id y71csp888237qgd; Thu, 15 Jun 2017 09:33:22 -0700 (PDT) X-Received: by 10.98.18.148 with SMTP id 20mr6202528pfs.142.1497544402336; Thu, 15 Jun 2017 09:33:22 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1497544402; cv=none; d=google.com; s=arc-20160816; b=nhtk6rTcjBw/EjKIJU2BOKXwGKUdbWlItpJO7xbGvfMqgk2AM5i6S4mT8VcFsopZIZ 5wtQKexoBr8dhYAEjbePLE2fRPzGYlAa9uJOinHnfQmAFjQtPRX5iw2Vcvrl7yeaNFhN zDQZFL6DMbNlKilzueCiXFT5jxlZWbBgZCe1omWlli5KuzUNJUFUoanHjOQEVnvUqTji pP0Ute+ynx9g/GXaBXfE3SpHQnbcFlulq5dxRFWzhiM5MvlLNNxB44CCOA+byvFCi4NL FwIX/xkwLHVH7Y+ZXqRqYIhbI7X68YiHq2w3r7+9JC8eUvLGbLnVzjvQCu/9VQ2WXMv8 +5wA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:arc-authentication-results; bh=XP8A1k0ihhMBIQG9XUl8Rdo41omx/Ngf0tNYvmy4Lw8=; b=hphPYQTW0qUTjF//I5cbqrdk0u0xv+m8UyDVhMeOS0Trop0gEPUOlk8Jp2pHeou0Gs FxAeciJoR97fasCbk5JgrxKBU4AUzBnlH6QKFif6DeDluIn8zJML0LFfqgcUp848gB0Y 6RxDFuc9zpxdyhABIL3WX2qk431rQuNHYDWtYnFPQAvI3Wiyuf58vGsGWj8QWIc4sT6n qzyElLziqj3BaEfp8Y1/4x3UmIp15r6lcoDS4YrQ0CO3qHvfY8zEzIlAK8n+NTSBQoeH Cm1XNlsbR8vJuA134g7vXfIdSrUyDXmBrhkSnh6Waj+l9eTzD+9qKt2Y0l/HLcLvI8Xt 3TlQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.b=HEGOPDbb; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id p5si397449pfl.413.2017.06.15.09.33.22; Thu, 15 Jun 2017 09:33:22 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.b=HEGOPDbb; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752362AbdFOQdA (ORCPT + 25 others); Thu, 15 Jun 2017 12:33:00 -0400 Received: from mail-wm0-f48.google.com ([74.125.82.48]:38383 "EHLO mail-wm0-f48.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752151AbdFOQcy (ORCPT ); Thu, 15 Jun 2017 12:32:54 -0400 Received: by mail-wm0-f48.google.com with SMTP id n195so4092086wmg.1 for ; Thu, 15 Jun 2017 09:32:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=XP8A1k0ihhMBIQG9XUl8Rdo41omx/Ngf0tNYvmy4Lw8=; b=HEGOPDbbGhZYwCV9zQXtAnWzcqf10cmvcRHMjctI2Oqh52Pfut0pe9mkIJsWxxkpTj ByYCuVU7l2v3TX5M+MjhAruFENPmsijM82s8UCSzTvWRl39peS+f5xZ/Bku/xLYDGtWn j+F2/Gfhi2mOpjUyDNSX9pUSIxYR1GVCpckFI= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=XP8A1k0ihhMBIQG9XUl8Rdo41omx/Ngf0tNYvmy4Lw8=; b=BADIeZegkBAJe7uaO7jJEbYv7rsmdhir3TKoCN14PybMVGT9d50LVW6tDxZLpszP1m aukWfK5wzR0GEtA557dujn/cgJHFfy/N73bTaGzepg1MSS6g5GQBeYQAVKFfDNvB25LF j+ACQtwGOhoJiJRw19+KEcyYDuQ00Q/S23pXZ/NCAgSUbB4OhY6ruf83crGwXAYslu7m mGR76XQKPEMXxUaNTD8h3NgODeXyuiePM1D8DfwQwGZxtMr/vJB6PCg9qVUsfzpvTST2 eh/2CuuHQ8wbUam+9tLYBgqdsAo+T/Gnhsnn4yZn9EILwGQ2BqTGzYzNoimxgGPhHQIv e6zQ== X-Gm-Message-State: AKS2vOwrLYtkqYERIQPtMo13j+NMo9iI2VmK1CQgREwPbQCz8flABZj5 qwF6ZGTii05oKH32 X-Received: by 10.28.167.135 with SMTP id q129mr4241533wme.73.1497544372360; Thu, 15 Jun 2017 09:32:52 -0700 (PDT) Received: from mms-0440.qualcomm.mm-sol.com ([37.157.136.206]) by smtp.gmail.com with ESMTPSA id m73sm541890wmi.25.2017.06.15.09.32.50 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 15 Jun 2017 09:32:51 -0700 (PDT) From: Stanimir Varbanov To: Mauro Carvalho Chehab , Hans Verkuil Cc: linux-media@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, Stanimir Varbanov Subject: [PATCH v11 04/19] media: venus: adding core part and helper functions Date: Thu, 15 Jun 2017 19:31:45 +0300 Message-Id: <1497544320-2269-5-git-send-email-stanimir.varbanov@linaro.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1497544320-2269-1-git-send-email-stanimir.varbanov@linaro.org> References: <1497544320-2269-1-git-send-email-stanimir.varbanov@linaro.org> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org * core.c has implemented the platform driver methods, file operations and v4l2 registration. * helpers.c has implemented common helper functions for: - buffer management - vb2_ops and functions for format propagation, - functions for allocating and freeing buffers for internal usage. The buffer parameters describing internal buffers depends on current format, resolution and codec. - functions for calculation of current load of the hardware. Depending on the count of instances and resolutions it selects the best clock rate for the video core. * firmware loader Signed-off-by: Stanimir Varbanov --- drivers/media/platform/qcom/venus/core.c | 388 ++++++++++++++ drivers/media/platform/qcom/venus/core.h | 323 ++++++++++++ drivers/media/platform/qcom/venus/firmware.c | 109 ++++ drivers/media/platform/qcom/venus/firmware.h | 22 + drivers/media/platform/qcom/venus/helpers.c | 727 +++++++++++++++++++++++++++ drivers/media/platform/qcom/venus/helpers.h | 45 ++ 6 files changed, 1614 insertions(+) create mode 100644 drivers/media/platform/qcom/venus/core.c create mode 100644 drivers/media/platform/qcom/venus/core.h create mode 100644 drivers/media/platform/qcom/venus/firmware.c create mode 100644 drivers/media/platform/qcom/venus/firmware.h create mode 100644 drivers/media/platform/qcom/venus/helpers.c create mode 100644 drivers/media/platform/qcom/venus/helpers.h -- 2.7.4 diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c new file mode 100644 index 000000000000..48391d87d5c3 --- /dev/null +++ b/drivers/media/platform/qcom/venus/core.c @@ -0,0 +1,388 @@ +/* + * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * Copyright (C) 2017 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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 +#include +#include +#include + +#include "core.h" +#include "vdec.h" +#include "venc.h" +#include "firmware.h" + +static void venus_event_notify(struct venus_core *core, u32 event) +{ + struct venus_inst *inst; + + switch (event) { + case EVT_SYS_WATCHDOG_TIMEOUT: + case EVT_SYS_ERROR: + break; + default: + return; + } + + mutex_lock(&core->lock); + core->sys_error = true; + list_for_each_entry(inst, &core->instances, list) + inst->ops->event_notify(inst, EVT_SESSION_ERROR, NULL); + mutex_unlock(&core->lock); + + disable_irq_nosync(core->irq); + + /* + * Delay recovery to ensure venus has completed any pending cache + * operations. Without this sleep, we see device reset when firmware is + * unloaded after a system error. + */ + schedule_delayed_work(&core->work, msecs_to_jiffies(100)); +} + +static const struct hfi_core_ops venus_core_ops = { + .event_notify = venus_event_notify, +}; + +static void venus_sys_error_handler(struct work_struct *work) +{ + struct venus_core *core = + container_of(work, struct venus_core, work.work); + int ret = 0; + + dev_warn(core->dev, "system error has occurred, starting recovery!\n"); + + pm_runtime_get_sync(core->dev); + + hfi_core_deinit(core, true); + hfi_destroy(core); + mutex_lock(&core->lock); + venus_shutdown(&core->dev_fw); + + pm_runtime_put_sync(core->dev); + + ret |= hfi_create(core, &venus_core_ops); + + pm_runtime_get_sync(core->dev); + + ret |= venus_boot(core->dev, &core->dev_fw); + + ret |= hfi_core_resume(core, true); + + enable_irq(core->irq); + + mutex_unlock(&core->lock); + + ret |= hfi_core_init(core); + + pm_runtime_put_sync(core->dev); + + if (ret) { + disable_irq_nosync(core->irq); + dev_warn(core->dev, "recovery failed (%d)\n", ret); + schedule_delayed_work(&core->work, msecs_to_jiffies(10)); + return; + } + + mutex_lock(&core->lock); + core->sys_error = false; + mutex_unlock(&core->lock); +} + +static int venus_clks_get(struct venus_core *core) +{ + const struct venus_resources *res = core->res; + struct device *dev = core->dev; + unsigned int i; + + for (i = 0; i < res->clks_num; i++) { + core->clks[i] = devm_clk_get(dev, res->clks[i]); + if (IS_ERR(core->clks[i])) + return PTR_ERR(core->clks[i]); + } + + return 0; +} + +static int venus_clks_enable(struct venus_core *core) +{ + const struct venus_resources *res = core->res; + unsigned int i; + int ret; + + for (i = 0; i < res->clks_num; i++) { + ret = clk_prepare_enable(core->clks[i]); + if (ret) + goto err; + } + + return 0; +err: + while (--i) + clk_disable_unprepare(core->clks[i]); + + return ret; +} + +static void venus_clks_disable(struct venus_core *core) +{ + const struct venus_resources *res = core->res; + unsigned int i = res->clks_num; + + while (i--) + clk_disable_unprepare(core->clks[i]); +} + +static int venus_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct venus_core *core; + struct resource *r; + int ret; + + core = devm_kzalloc(dev, sizeof(*core), GFP_KERNEL); + if (!core) + return -ENOMEM; + + core->dev = dev; + platform_set_drvdata(pdev, core); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + core->base = devm_ioremap_resource(dev, r); + if (IS_ERR(core->base)) + return PTR_ERR(core->base); + + core->irq = platform_get_irq(pdev, 0); + if (core->irq < 0) + return core->irq; + + core->res = of_device_get_match_data(dev); + if (!core->res) + return -ENODEV; + + ret = venus_clks_get(core); + if (ret) + return ret; + + ret = dma_set_mask_and_coherent(dev, core->res->dma_mask); + if (ret) + return ret; + + INIT_LIST_HEAD(&core->instances); + mutex_init(&core->lock); + INIT_DELAYED_WORK(&core->work, venus_sys_error_handler); + + ret = devm_request_threaded_irq(dev, core->irq, hfi_isr, hfi_isr_thread, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "venus", core); + if (ret) + return ret; + + ret = hfi_create(core, &venus_core_ops); + if (ret) + return ret; + + pm_runtime_enable(dev); + + ret = pm_runtime_get_sync(dev); + if (ret < 0) + goto err_runtime_disable; + + ret = venus_boot(dev, &core->dev_fw); + if (ret) + goto err_runtime_disable; + + ret = hfi_core_resume(core, true); + if (ret) + goto err_venus_shutdown; + + ret = hfi_core_init(core); + if (ret) + goto err_venus_shutdown; + + ret = v4l2_device_register(dev, &core->v4l2_dev); + if (ret) + goto err_core_deinit; + + ret = of_platform_populate(dev->of_node, NULL, NULL, dev); + if (ret) + goto err_dev_unregister; + + ret = pm_runtime_put_sync(dev); + if (ret) + goto err_dev_unregister; + + return 0; + +err_dev_unregister: + v4l2_device_unregister(&core->v4l2_dev); +err_core_deinit: + hfi_core_deinit(core, false); +err_venus_shutdown: + venus_shutdown(&core->dev_fw); +err_runtime_disable: + pm_runtime_set_suspended(dev); + pm_runtime_disable(dev); + hfi_destroy(core); + return ret; +} + +static int venus_remove(struct platform_device *pdev) +{ + struct venus_core *core = platform_get_drvdata(pdev); + struct device *dev = core->dev; + int ret; + + ret = pm_runtime_get_sync(dev); + WARN_ON(ret < 0); + + ret = hfi_core_deinit(core, true); + WARN_ON(ret); + + hfi_destroy(core); + venus_shutdown(&core->dev_fw); + of_platform_depopulate(dev); + + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); + + v4l2_device_unregister(&core->v4l2_dev); + + return ret; +} + +#ifdef CONFIG_PM +static int venus_runtime_suspend(struct device *dev) +{ + struct venus_core *core = dev_get_drvdata(dev); + int ret; + + ret = hfi_core_suspend(core); + + venus_clks_disable(core); + + return ret; +} + +static int venus_runtime_resume(struct device *dev) +{ + struct venus_core *core = dev_get_drvdata(dev); + int ret; + + ret = venus_clks_enable(core); + if (ret) + return ret; + + ret = hfi_core_resume(core, false); + if (ret) + goto err_clks_disable; + + return 0; + +err_clks_disable: + venus_clks_disable(core); + return ret; +} +#endif + +static const struct dev_pm_ops venus_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(venus_runtime_suspend, venus_runtime_resume, NULL) +}; + +static const struct freq_tbl msm8916_freq_table[] = { + { 352800, 228570000 }, /* 1920x1088 @ 30 + 1280x720 @ 30 */ + { 244800, 160000000 }, /* 1920x1088 @ 30 */ + { 108000, 100000000 }, /* 1280x720 @ 30 */ +}; + +static const struct reg_val msm8916_reg_preset[] = { + { 0xe0020, 0x05555556 }, + { 0xe0024, 0x05555556 }, + { 0x80124, 0x00000003 }, +}; + +static const struct venus_resources msm8916_res = { + .freq_tbl = msm8916_freq_table, + .freq_tbl_size = ARRAY_SIZE(msm8916_freq_table), + .reg_tbl = msm8916_reg_preset, + .reg_tbl_size = ARRAY_SIZE(msm8916_reg_preset), + .clks = { "core", "iface", "bus", }, + .clks_num = 3, + .max_load = 352800, /* 720p@30 + 1080p@30 */ + .hfi_version = HFI_VERSION_1XX, + .vmem_id = VIDC_RESOURCE_NONE, + .vmem_size = 0, + .vmem_addr = 0, + .dma_mask = 0xddc00000 - 1, +}; + +static const struct freq_tbl msm8996_freq_table[] = { + { 1944000, 490000000 }, /* 4k UHD @ 60 */ + { 972000, 320000000 }, /* 4k UHD @ 30 */ + { 489600, 150000000 }, /* 1080p @ 60 */ + { 244800, 75000000 }, /* 1080p @ 30 */ +}; + +static const struct reg_val msm8996_reg_preset[] = { + { 0x80010, 0xffffffff }, + { 0x80018, 0x00001556 }, + { 0x8001C, 0x00001556 }, +}; + +static const struct venus_resources msm8996_res = { + .freq_tbl = msm8996_freq_table, + .freq_tbl_size = ARRAY_SIZE(msm8996_freq_table), + .reg_tbl = msm8996_reg_preset, + .reg_tbl_size = ARRAY_SIZE(msm8996_reg_preset), + .clks = {"core", "iface", "bus", "mbus" }, + .clks_num = 4, + .max_load = 2563200, + .hfi_version = HFI_VERSION_3XX, + .vmem_id = VIDC_RESOURCE_NONE, + .vmem_size = 0, + .vmem_addr = 0, + .dma_mask = 0xddc00000 - 1, +}; + +static const struct of_device_id venus_dt_match[] = { + { .compatible = "qcom,msm8916-venus", .data = &msm8916_res, }, + { .compatible = "qcom,msm8996-venus", .data = &msm8996_res, }, + { } +}; +MODULE_DEVICE_TABLE(of, venus_dt_match); + +static struct platform_driver qcom_venus_driver = { + .probe = venus_probe, + .remove = venus_remove, + .driver = { + .name = "qcom-venus", + .of_match_table = venus_dt_match, + .pm = &venus_pm_ops, + }, +}; +module_platform_driver(qcom_venus_driver); + +MODULE_ALIAS("platform:qcom-venus"); +MODULE_DESCRIPTION("Qualcomm Venus video encoder and decoder driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h new file mode 100644 index 000000000000..2f8492090224 --- /dev/null +++ b/drivers/media/platform/qcom/venus/core.h @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * Copyright (C) 2017 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + * + */ + +#ifndef __VENUS_CORE_H_ +#define __VENUS_CORE_H_ + +#include +#include +#include +#include + +#include "hfi.h" + +#define VIDC_CLKS_NUM_MAX 4 + +struct freq_tbl { + unsigned int load; + unsigned long freq; +}; + +struct reg_val { + u32 reg; + u32 value; +}; + +struct venus_resources { + u64 dma_mask; + const struct freq_tbl *freq_tbl; + unsigned int freq_tbl_size; + const struct reg_val *reg_tbl; + unsigned int reg_tbl_size; + const char * const clks[VIDC_CLKS_NUM_MAX]; + unsigned int clks_num; + enum hfi_version hfi_version; + u32 max_load; + unsigned int vmem_id; + u32 vmem_size; + u32 vmem_addr; +}; + +struct venus_format { + u32 pixfmt; + unsigned int num_planes; + u32 type; +}; + +/** + * struct venus_core - holds core parameters valid for all instances + * + * @base: IO memory base address + * @irq: Venus irq + * @clks: an array of struct clk pointers + * @core0_clk: a struct clk pointer for core0 + * @core1_clk: a struct clk pointer for core1 + * @vdev_dec: a reference to video device structure for decoder instances + * @vdev_enc: a reference to video device structure for encoder instances + * @v4l2_dev: a holder for v4l2 device structure + * @res: a reference to venus resources structure + * @dev: convenience struct device pointer + * @dev_dec: convenience struct device pointer for decoder device + * @dev_enc: convenience struct device pointer for encoder device + * @lock: a lock for this strucure + * @instances: a list_head of all instances + * @insts_count: num of instances + * @state: the state of the venus core + * @done: a completion for sync HFI operations + * @error: an error returned during last HFI sync operations + * @sys_error: an error flag that signal system error event + * @core_ops: the core operations + * @enc_codecs: encoders supported by this core + * @dec_codecs: decoders supported by this core + * @max_sessions_supported: holds the maximum number of sessions + * @core_caps: core capabilities + * @priv: a private filed for HFI operations + * @ops: the core HFI operations + * @work: a delayed work for handling system fatal error + */ +struct venus_core { + void __iomem *base; + int irq; + struct clk *clks[VIDC_CLKS_NUM_MAX]; + struct clk *core0_clk; + struct clk *core1_clk; + struct video_device *vdev_dec; + struct video_device *vdev_enc; + struct v4l2_device v4l2_dev; + const struct venus_resources *res; + struct device *dev; + struct device *dev_dec; + struct device *dev_enc; + struct device dev_fw; + struct mutex lock; + struct list_head instances; + atomic_t insts_count; + unsigned int state; + struct completion done; + unsigned int error; + bool sys_error; + const struct hfi_core_ops *core_ops; + u32 enc_codecs; + u32 dec_codecs; + unsigned int max_sessions_supported; +#define ENC_ROTATION_CAPABILITY 0x1 +#define ENC_SCALING_CAPABILITY 0x2 +#define ENC_DEINTERLACE_CAPABILITY 0x4 +#define DEC_MULTI_STREAM_CAPABILITY 0x8 + unsigned int core_caps; + void *priv; + const struct hfi_ops *ops; + struct delayed_work work; +}; + +struct vdec_controls { + u32 post_loop_deb_mode; + u32 profile; + u32 level; +}; + +struct venc_controls { + u16 gop_size; + u32 num_p_frames; + u32 num_b_frames; + u32 bitrate_mode; + u32 bitrate; + u32 bitrate_peak; + + u32 h264_i_period; + u32 h264_entropy_mode; + u32 h264_i_qp; + u32 h264_p_qp; + u32 h264_b_qp; + u32 h264_min_qp; + u32 h264_max_qp; + u32 h264_loop_filter_mode; + u32 h264_loop_filter_alpha; + u32 h264_loop_filter_beta; + + u32 vp8_min_qp; + u32 vp8_max_qp; + + u32 multi_slice_mode; + u32 multi_slice_max_bytes; + u32 multi_slice_max_mb; + + u32 header_mode; + + struct { + u32 mpeg4; + u32 h264; + u32 vpx; + } profile; + struct { + u32 mpeg4; + u32 h264; + } level; +}; + +struct venus_buffer { + struct vb2_v4l2_buffer vb; + struct list_head list; + dma_addr_t dma_addr; + u32 size; + struct list_head reg_list; + u32 flags; + struct list_head ref_list; +}; + +#define to_venus_buffer(ptr) container_of(ptr, struct venus_buffer, vb) + +/** + * struct venus_inst - holds per instance paramerters + * + * @list: used for attach an instance to the core + * @lock: instance lock + * @core: a reference to the core struct + * @internalbufs: a list of internal bufferes + * @registeredbufs: a list of registered capture bufferes + * @delayed_process a list of delayed buffers + * @delayed_process_work: a work_struct for process delayed buffers + * @ctrl_handler: v4l control handler + * @controls: a union of decoder and encoder control parameters + * @fh: a holder of v4l file handle structure + * @streamon_cap: stream on flag for capture queue + * @streamon_out: stream on flag for output queue + * @cmd_stop: a flag to signal encoder/decoder commands + * @width: current capture width + * @height: current capture height + * @out_width: current output width + * @out_height: current output height + * @colorspace: current color space + * @quantization: current quantization + * @xfer_func: current xfer function + * @fps: holds current FPS + * @timeperframe: holds current time per frame structure + * @fmt_out: a reference to output format structure + * @fmt_cap: a reference to capture format structure + * @num_input_bufs: holds number of input buffers + * @num_output_bufs: holds number of output buffers + * @input_buf_size holds input buffer size + * @output_buf_size: holds output buffer size + * @reconfig: a flag raised by decoder when the stream resolution changed + * @reconfig_width: holds the new width + * @reconfig_height: holds the new height + * @sequence_cap: a sequence counter for capture queue + * @sequence_out: a sequence counter for output queue + * @m2m_dev: a reference to m2m device structure + * @m2m_ctx: a reference to m2m context structure + * @state: current state of the instance + * @done: a completion for sync HFI operation + * @error: an error returned during last HFI sync operation + * @session_error: a flag rised by HFI interface in case of session error + * @ops: HFI operations + * @priv: a private for HFI operations callbacks + * @session_type: the type of the session (decoder or encoder) + * @hprop: a union used as a holder by get property + * @cap_width: width capability + * @cap_height: height capability + * @cap_mbs_per_frame: macroblocks per frame capability + * @cap_mbs_per_sec: macroblocks per second capability + * @cap_framerate: framerate capability + * @cap_scale_x: horizontal scaling capability + * @cap_scale_y: vertical scaling capability + * @cap_bitrate: bitrate capability + * @cap_hier_p: hier capability + * @cap_ltr_count: LTR count capability + * @cap_secure_output2_threshold: secure OUTPUT2 threshold capability + * @cap_bufs_mode_static: buffers allocation mode capability + * @cap_bufs_mode_dynamic: buffers allocation mode capability + * @pl_count: count of supported profiles/levels + * @pl: supported profiles/levels + * @bufreq: holds buffer requirements + */ +struct venus_inst { + struct list_head list; + struct mutex lock; + struct venus_core *core; + struct list_head internalbufs; + struct list_head registeredbufs; + struct list_head delayed_process; + struct work_struct delayed_process_work; + + struct v4l2_ctrl_handler ctrl_handler; + union { + struct vdec_controls dec; + struct venc_controls enc; + } controls; + struct v4l2_fh fh; + unsigned int streamon_cap, streamon_out; + bool cmd_stop; + u32 width; + u32 height; + u32 out_width; + u32 out_height; + u32 colorspace; + u8 ycbcr_enc; + u8 quantization; + u8 xfer_func; + u64 fps; + struct v4l2_fract timeperframe; + const struct venus_format *fmt_out; + const struct venus_format *fmt_cap; + unsigned int num_input_bufs; + unsigned int num_output_bufs; + unsigned int input_buf_size; + unsigned int output_buf_size; + bool reconfig; + u32 reconfig_width; + u32 reconfig_height; + u32 sequence_cap; + u32 sequence_out; + struct v4l2_m2m_dev *m2m_dev; + struct v4l2_m2m_ctx *m2m_ctx; + unsigned int state; + struct completion done; + unsigned int error; + bool session_error; + const struct hfi_inst_ops *ops; + u32 session_type; + union hfi_get_property hprop; + struct hfi_capability cap_width; + struct hfi_capability cap_height; + struct hfi_capability cap_mbs_per_frame; + struct hfi_capability cap_mbs_per_sec; + struct hfi_capability cap_framerate; + struct hfi_capability cap_scale_x; + struct hfi_capability cap_scale_y; + struct hfi_capability cap_bitrate; + struct hfi_capability cap_hier_p; + struct hfi_capability cap_ltr_count; + struct hfi_capability cap_secure_output2_threshold; + bool cap_bufs_mode_static; + bool cap_bufs_mode_dynamic; + unsigned int pl_count; + struct hfi_profile_level pl[HFI_MAX_PROFILE_COUNT]; + struct hfi_buffer_requirements bufreq[HFI_BUFFER_TYPE_MAX]; +}; + +#define ctrl_to_inst(ctrl) \ + container_of((ctrl)->handler, struct venus_inst, ctrl_handler) + +static inline struct venus_inst *to_inst(struct file *filp) +{ + return container_of(filp->private_data, struct venus_inst, fh); +} + +static inline void *to_hfi_priv(struct venus_core *core) +{ + return core->priv; +} + +#endif diff --git a/drivers/media/platform/qcom/venus/firmware.c b/drivers/media/platform/qcom/venus/firmware.c new file mode 100644 index 000000000000..365e64010920 --- /dev/null +++ b/drivers/media/platform/qcom/venus/firmware.c @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2017 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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 "firmware.h" + +#define VENUS_FIRMWARE_NAME "venus.mdt" +#define VENUS_PAS_ID 9 +#define VENUS_FW_MEM_SIZE SZ_8M + +static void device_release_dummy(struct device *dev) +{ + of_reserved_mem_device_release(dev); +} + +int venus_boot(struct device *parent, struct device *fw_dev) +{ + const struct firmware *mdt; + phys_addr_t mem_phys; + ssize_t fw_size; + size_t mem_size; + void *mem_va; + int ret; + + if (!qcom_scm_is_available()) + return -EPROBE_DEFER; + + fw_dev->parent = parent; + fw_dev->release = device_release_dummy; + + ret = dev_set_name(fw_dev, "%s:%s", dev_name(parent), "firmware"); + if (ret) + return ret; + + ret = device_register(fw_dev); + if (ret < 0) + return ret; + + ret = of_reserved_mem_device_init_by_idx(fw_dev, parent->of_node, 0); + if (ret) + goto err_unreg_device; + + mem_size = VENUS_FW_MEM_SIZE; + + mem_va = dmam_alloc_coherent(fw_dev, mem_size, &mem_phys, GFP_KERNEL); + if (!mem_va) { + ret = -ENOMEM; + goto err_unreg_device; + } + + ret = request_firmware(&mdt, VENUS_FIRMWARE_NAME, fw_dev); + if (ret < 0) + goto err_unreg_device; + + fw_size = qcom_mdt_get_size(mdt); + if (fw_size < 0) { + ret = fw_size; + release_firmware(mdt); + goto err_unreg_device; + } + + ret = qcom_mdt_load(fw_dev, mdt, VENUS_FIRMWARE_NAME, VENUS_PAS_ID, + mem_va, mem_phys, mem_size); + + release_firmware(mdt); + + if (ret) + goto err_unreg_device; + + ret = qcom_scm_pas_auth_and_reset(VENUS_PAS_ID); + if (ret) + goto err_unreg_device; + + return 0; + +err_unreg_device: + device_unregister(fw_dev); + return ret; +} + +int venus_shutdown(struct device *fw_dev) +{ + int ret; + + ret = qcom_scm_pas_shutdown(VENUS_PAS_ID); + device_unregister(fw_dev); + memset(fw_dev, 0, sizeof(*fw_dev)); + + return ret; +} diff --git a/drivers/media/platform/qcom/venus/firmware.h b/drivers/media/platform/qcom/venus/firmware.h new file mode 100644 index 000000000000..782e64ae291a --- /dev/null +++ b/drivers/media/platform/qcom/venus/firmware.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2017 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + * + */ +#ifndef __VENUS_FIRMWARE_H__ +#define __VENUS_FIRMWARE_H__ + +struct device; + +int venus_boot(struct device *parent, struct device *fw_dev); +int venus_shutdown(struct device *fw_dev); + +#endif diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c new file mode 100644 index 000000000000..f777ef80b941 --- /dev/null +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -0,0 +1,727 @@ +/* + * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * Copyright (C) 2017 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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 "core.h" +#include "helpers.h" +#include "hfi_helper.h" + +struct intbuf { + struct list_head list; + u32 type; + size_t size; + void *va; + dma_addr_t da; + unsigned long attrs; +}; + +static int intbufs_set_buffer(struct venus_inst *inst, u32 type) +{ + struct venus_core *core = inst->core; + struct device *dev = core->dev; + struct hfi_buffer_requirements bufreq; + struct hfi_buffer_desc bd; + struct intbuf *buf; + unsigned int i; + int ret; + + ret = venus_helper_get_bufreq(inst, type, &bufreq); + if (ret) + return 0; + + if (!bufreq.size) + return 0; + + for (i = 0; i < bufreq.count_actual; i++) { + buf = kzalloc(sizeof(*buf), GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto fail; + } + + buf->type = bufreq.type; + buf->size = bufreq.size; + buf->attrs = DMA_ATTR_WRITE_COMBINE | + DMA_ATTR_NO_KERNEL_MAPPING; + buf->va = dma_alloc_attrs(dev, buf->size, &buf->da, GFP_KERNEL, + buf->attrs); + if (!buf->va) { + ret = -ENOMEM; + goto fail; + } + + memset(&bd, 0, sizeof(bd)); + bd.buffer_size = buf->size; + bd.buffer_type = buf->type; + bd.num_buffers = 1; + bd.device_addr = buf->da; + + ret = hfi_session_set_buffers(inst, &bd); + if (ret) { + dev_err(dev, "set session buffers failed\n"); + goto dma_free; + } + + list_add_tail(&buf->list, &inst->internalbufs); + } + + return 0; + +dma_free: + dma_free_attrs(dev, buf->size, buf->va, buf->da, buf->attrs); +fail: + kfree(buf); + return ret; +} + +static int intbufs_unset_buffers(struct venus_inst *inst) +{ + struct hfi_buffer_desc bd = {0}; + struct intbuf *buf, *n; + int ret = 0; + + list_for_each_entry_safe(buf, n, &inst->internalbufs, list) { + bd.buffer_size = buf->size; + bd.buffer_type = buf->type; + bd.num_buffers = 1; + bd.device_addr = buf->da; + bd.response_required = true; + + ret = hfi_session_unset_buffers(inst, &bd); + + list_del_init(&buf->list); + dma_free_attrs(inst->core->dev, buf->size, buf->va, buf->da, + buf->attrs); + kfree(buf); + } + + return ret; +} + +static const unsigned int intbuf_types[] = { + HFI_BUFFER_INTERNAL_SCRATCH, + HFI_BUFFER_INTERNAL_SCRATCH_1, + HFI_BUFFER_INTERNAL_SCRATCH_2, + HFI_BUFFER_INTERNAL_PERSIST, + HFI_BUFFER_INTERNAL_PERSIST_1, +}; + +static int intbufs_alloc(struct venus_inst *inst) +{ + unsigned int i; + int ret; + + for (i = 0; i < ARRAY_SIZE(intbuf_types); i++) { + ret = intbufs_set_buffer(inst, intbuf_types[i]); + if (ret) + goto error; + } + + return 0; + +error: + intbufs_unset_buffers(inst); + return ret; +} + +static int intbufs_free(struct venus_inst *inst) +{ + return intbufs_unset_buffers(inst); +} + +static u32 load_per_instance(struct venus_inst *inst) +{ + u32 w = inst->width; + u32 h = inst->height; + u32 mbs; + + if (!inst || !(inst->state >= INST_INIT && inst->state < INST_STOP)) + return 0; + + mbs = (ALIGN(w, 16) / 16) * (ALIGN(h, 16) / 16); + + return mbs * inst->fps; +} + +static u32 load_per_type(struct venus_core *core, u32 session_type) +{ + struct venus_inst *inst = NULL; + u32 mbs_per_sec = 0; + + mutex_lock(&core->lock); + list_for_each_entry(inst, &core->instances, list) { + if (inst->session_type != session_type) + continue; + + mbs_per_sec += load_per_instance(inst); + } + mutex_unlock(&core->lock); + + return mbs_per_sec; +} + +static int load_scale_clocks(struct venus_core *core) +{ + const struct freq_tbl *table = core->res->freq_tbl; + unsigned int num_rows = core->res->freq_tbl_size; + unsigned long freq = table[0].freq; + struct clk *clk = core->clks[0]; + struct device *dev = core->dev; + u32 mbs_per_sec; + unsigned int i; + int ret; + + mbs_per_sec = load_per_type(core, VIDC_SESSION_TYPE_ENC) + + load_per_type(core, VIDC_SESSION_TYPE_DEC); + + if (mbs_per_sec > core->res->max_load) + dev_warn(dev, "HW is overloaded, needed: %d max: %d\n", + mbs_per_sec, core->res->max_load); + + if (!mbs_per_sec && num_rows > 1) { + freq = table[num_rows - 1].freq; + goto set_freq; + } + + for (i = 0; i < num_rows; i++) { + if (mbs_per_sec > table[i].load) + break; + freq = table[i].freq; + } + +set_freq: + + if (core->res->hfi_version == HFI_VERSION_3XX) { + ret = clk_set_rate(clk, freq); + ret |= clk_set_rate(core->core0_clk, freq); + ret |= clk_set_rate(core->core1_clk, freq); + } else { + ret = clk_set_rate(clk, freq); + } + + if (ret) { + dev_err(dev, "failed to set clock rate %lu (%d)\n", freq, ret); + return ret; + } + + return 0; +} + +static void fill_buffer_desc(const struct venus_buffer *buf, + struct hfi_buffer_desc *bd, bool response) +{ + memset(bd, 0, sizeof(*bd)); + bd->buffer_type = HFI_BUFFER_OUTPUT; + bd->buffer_size = buf->size; + bd->num_buffers = 1; + bd->device_addr = buf->dma_addr; + bd->response_required = response; +} + +static void return_buf_error(struct venus_inst *inst, + struct vb2_v4l2_buffer *vbuf) +{ + struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx; + + if (vbuf->vb2_buf.type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + v4l2_m2m_src_buf_remove_by_buf(m2m_ctx, vbuf); + else + v4l2_m2m_src_buf_remove_by_buf(m2m_ctx, vbuf); + + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR); +} + +static int +session_process_buf(struct venus_inst *inst, struct vb2_v4l2_buffer *vbuf) +{ + struct venus_buffer *buf = to_venus_buffer(vbuf); + struct vb2_buffer *vb = &vbuf->vb2_buf; + unsigned int type = vb->type; + struct hfi_frame_data fdata; + int ret; + + memset(&fdata, 0, sizeof(fdata)); + fdata.alloc_len = buf->size; + fdata.device_addr = buf->dma_addr; + fdata.timestamp = vb->timestamp; + do_div(fdata.timestamp, NSEC_PER_USEC); + fdata.flags = 0; + fdata.clnt_data = vbuf->vb2_buf.index; + + if (!fdata.timestamp) + fdata.flags |= HFI_BUFFERFLAG_TIMESTAMPINVALID; + + if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + fdata.buffer_type = HFI_BUFFER_INPUT; + fdata.filled_len = vb2_get_plane_payload(vb, 0); + fdata.offset = vb->planes[0].data_offset; + + if (vbuf->flags & V4L2_BUF_FLAG_LAST || !fdata.filled_len) + fdata.flags |= HFI_BUFFERFLAG_EOS; + } else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + fdata.buffer_type = HFI_BUFFER_OUTPUT; + fdata.filled_len = 0; + fdata.offset = 0; + } + + ret = hfi_session_process_buf(inst, &fdata); + if (ret) + return ret; + + return 0; +} + +static inline int is_reg_unreg_needed(struct venus_inst *inst) +{ + if (inst->session_type == VIDC_SESSION_TYPE_DEC && + inst->core->res->hfi_version == HFI_VERSION_3XX) + return 0; + + if (inst->session_type == VIDC_SESSION_TYPE_DEC && + inst->cap_bufs_mode_dynamic && + inst->core->res->hfi_version == HFI_VERSION_1XX) + return 0; + + return 1; +} + +static int session_unregister_bufs(struct venus_inst *inst) +{ + struct venus_buffer *buf, *n; + struct hfi_buffer_desc bd; + int ret = 0; + + if (!is_reg_unreg_needed(inst)) + return 0; + + list_for_each_entry_safe(buf, n, &inst->registeredbufs, reg_list) { + fill_buffer_desc(buf, &bd, true); + ret = hfi_session_unset_buffers(inst, &bd); + list_del_init(&buf->reg_list); + } + + return ret; +} + +static int session_register_bufs(struct venus_inst *inst) +{ + struct venus_core *core = inst->core; + struct device *dev = core->dev; + struct hfi_buffer_desc bd; + struct venus_buffer *buf; + int ret = 0; + + if (!is_reg_unreg_needed(inst)) + return 0; + + list_for_each_entry(buf, &inst->registeredbufs, reg_list) { + fill_buffer_desc(buf, &bd, false); + ret = hfi_session_set_buffers(inst, &bd); + if (ret) { + dev_err(dev, "%s: set buffer failed\n", __func__); + break; + } + } + + return ret; +} + +int venus_helper_get_bufreq(struct venus_inst *inst, u32 type, + struct hfi_buffer_requirements *req) +{ + u32 ptype = HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS; + union hfi_get_property hprop; + unsigned int i; + int ret; + + if (req) + memset(req, 0, sizeof(*req)); + + ret = hfi_session_get_property(inst, ptype, &hprop); + if (ret) + return ret; + + ret = -EINVAL; + + for (i = 0; i < HFI_BUFFER_TYPE_MAX; i++) { + if (hprop.bufreq[i].type != type) + continue; + + if (req) + memcpy(req, &hprop.bufreq[i], sizeof(*req)); + ret = 0; + break; + } + + return ret; +} +EXPORT_SYMBOL_GPL(venus_helper_get_bufreq); + +int venus_helper_set_input_resolution(struct venus_inst *inst, + unsigned int width, unsigned int height) +{ + u32 ptype = HFI_PROPERTY_PARAM_FRAME_SIZE; + struct hfi_framesize fs; + + fs.buffer_type = HFI_BUFFER_INPUT; + fs.width = width; + fs.height = height; + + return hfi_session_set_property(inst, ptype, &fs); +} +EXPORT_SYMBOL_GPL(venus_helper_set_input_resolution); + +int venus_helper_set_output_resolution(struct venus_inst *inst, + unsigned int width, unsigned int height) +{ + u32 ptype = HFI_PROPERTY_PARAM_FRAME_SIZE; + struct hfi_framesize fs; + + fs.buffer_type = HFI_BUFFER_OUTPUT; + fs.width = width; + fs.height = height; + + return hfi_session_set_property(inst, ptype, &fs); +} +EXPORT_SYMBOL_GPL(venus_helper_set_output_resolution); + +int venus_helper_set_num_bufs(struct venus_inst *inst, unsigned int input_bufs, + unsigned int output_bufs) +{ + u32 ptype = HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL; + struct hfi_buffer_count_actual buf_count; + int ret; + + buf_count.type = HFI_BUFFER_INPUT; + buf_count.count_actual = input_bufs; + + ret = hfi_session_set_property(inst, ptype, &buf_count); + if (ret) + return ret; + + buf_count.type = HFI_BUFFER_OUTPUT; + buf_count.count_actual = output_bufs; + + return hfi_session_set_property(inst, ptype, &buf_count); +} +EXPORT_SYMBOL_GPL(venus_helper_set_num_bufs); + +int venus_helper_set_color_format(struct venus_inst *inst, u32 pixfmt) +{ + struct hfi_uncompressed_format_select fmt; + u32 ptype = HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT; + int ret; + + if (inst->session_type == VIDC_SESSION_TYPE_DEC) + fmt.buffer_type = HFI_BUFFER_OUTPUT; + else if (inst->session_type == VIDC_SESSION_TYPE_ENC) + fmt.buffer_type = HFI_BUFFER_INPUT; + else + return -EINVAL; + + switch (pixfmt) { + case V4L2_PIX_FMT_NV12: + fmt.format = HFI_COLOR_FORMAT_NV12; + break; + case V4L2_PIX_FMT_NV21: + fmt.format = HFI_COLOR_FORMAT_NV21; + break; + default: + return -EINVAL; + } + + ret = hfi_session_set_property(inst, ptype, &fmt); + if (ret) + return ret; + + return 0; +} +EXPORT_SYMBOL_GPL(venus_helper_set_color_format); + +static void delayed_process_buf_func(struct work_struct *work) +{ + struct venus_buffer *buf, *n; + struct venus_inst *inst; + int ret; + + inst = container_of(work, struct venus_inst, delayed_process_work); + + mutex_lock(&inst->lock); + + if (!(inst->streamon_out & inst->streamon_cap)) + goto unlock; + + list_for_each_entry_safe(buf, n, &inst->delayed_process, ref_list) { + if (buf->flags & HFI_BUFFERFLAG_READONLY) + continue; + + ret = session_process_buf(inst, &buf->vb); + if (ret) + return_buf_error(inst, &buf->vb); + + list_del_init(&buf->ref_list); + } +unlock: + mutex_unlock(&inst->lock); +} + +void venus_helper_release_buf_ref(struct venus_inst *inst, unsigned int idx) +{ + struct venus_buffer *buf; + + list_for_each_entry(buf, &inst->registeredbufs, reg_list) { + if (buf->vb.vb2_buf.index == idx) { + buf->flags &= ~HFI_BUFFERFLAG_READONLY; + schedule_work(&inst->delayed_process_work); + break; + } + } +} +EXPORT_SYMBOL_GPL(venus_helper_release_buf_ref); + +void venus_helper_acquire_buf_ref(struct vb2_v4l2_buffer *vbuf) +{ + struct venus_buffer *buf = to_venus_buffer(vbuf); + + buf->flags |= HFI_BUFFERFLAG_READONLY; +} +EXPORT_SYMBOL_GPL(venus_helper_acquire_buf_ref); + +static int is_buf_refed(struct venus_inst *inst, struct vb2_v4l2_buffer *vbuf) +{ + struct venus_buffer *buf = to_venus_buffer(vbuf); + + if (buf->flags & HFI_BUFFERFLAG_READONLY) { + list_add_tail(&buf->ref_list, &inst->delayed_process); + schedule_work(&inst->delayed_process_work); + return 1; + } + + return 0; +} + +struct vb2_v4l2_buffer * +venus_helper_find_buf(struct venus_inst *inst, unsigned int type, u32 idx) +{ + struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx; + + if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + return v4l2_m2m_src_buf_remove_by_idx(m2m_ctx, idx); + else + return v4l2_m2m_dst_buf_remove_by_idx(m2m_ctx, idx); +} +EXPORT_SYMBOL_GPL(venus_helper_find_buf); + +int venus_helper_vb2_buf_init(struct vb2_buffer *vb) +{ + struct venus_inst *inst = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct venus_buffer *buf = to_venus_buffer(vbuf); + struct sg_table *sgt; + + sgt = vb2_dma_sg_plane_desc(vb, 0); + if (!sgt) + return -EFAULT; + + buf->size = vb2_plane_size(vb, 0); + buf->dma_addr = sg_dma_address(sgt->sgl); + + if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + list_add_tail(&buf->reg_list, &inst->registeredbufs); + + return 0; +} +EXPORT_SYMBOL_GPL(venus_helper_vb2_buf_init); + +int venus_helper_vb2_buf_prepare(struct vb2_buffer *vb) +{ + struct venus_inst *inst = vb2_get_drv_priv(vb->vb2_queue); + + if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && + vb2_plane_size(vb, 0) < inst->output_buf_size) + return -EINVAL; + if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && + vb2_plane_size(vb, 0) < inst->input_buf_size) + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL_GPL(venus_helper_vb2_buf_prepare); + +void venus_helper_vb2_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct venus_inst *inst = vb2_get_drv_priv(vb->vb2_queue); + struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx; + int ret; + + mutex_lock(&inst->lock); + + if (inst->cmd_stop) { + vbuf->flags |= V4L2_BUF_FLAG_LAST; + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE); + inst->cmd_stop = false; + goto unlock; + } + + v4l2_m2m_buf_queue(m2m_ctx, vbuf); + + if (!(inst->streamon_out & inst->streamon_cap)) + goto unlock; + + ret = is_buf_refed(inst, vbuf); + if (ret) + goto unlock; + + ret = session_process_buf(inst, vbuf); + if (ret) + return_buf_error(inst, vbuf); + +unlock: + mutex_unlock(&inst->lock); +} +EXPORT_SYMBOL_GPL(venus_helper_vb2_buf_queue); + +void venus_helper_buffers_done(struct venus_inst *inst, + enum vb2_buffer_state state) +{ + struct vb2_v4l2_buffer *buf; + + while ((buf = v4l2_m2m_src_buf_remove(inst->m2m_ctx))) + v4l2_m2m_buf_done(buf, state); + while ((buf = v4l2_m2m_dst_buf_remove(inst->m2m_ctx))) + v4l2_m2m_buf_done(buf, state); +} +EXPORT_SYMBOL_GPL(venus_helper_buffers_done); + +void venus_helper_vb2_stop_streaming(struct vb2_queue *q) +{ + struct venus_inst *inst = vb2_get_drv_priv(q); + struct venus_core *core = inst->core; + int ret; + + mutex_lock(&inst->lock); + + if (inst->streamon_out & inst->streamon_cap) { + ret = hfi_session_stop(inst); + ret |= hfi_session_unload_res(inst); + ret |= session_unregister_bufs(inst); + ret |= intbufs_free(inst); + ret |= hfi_session_deinit(inst); + + if (inst->session_error || core->sys_error) + ret = -EIO; + + if (ret) + hfi_session_abort(inst); + + load_scale_clocks(core); + } + + venus_helper_buffers_done(inst, VB2_BUF_STATE_ERROR); + + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + inst->streamon_out = 0; + else + inst->streamon_cap = 0; + + mutex_unlock(&inst->lock); +} +EXPORT_SYMBOL_GPL(venus_helper_vb2_stop_streaming); + +int venus_helper_vb2_start_streaming(struct venus_inst *inst) +{ + struct venus_core *core = inst->core; + int ret; + + ret = intbufs_alloc(inst); + if (ret) + return ret; + + ret = session_register_bufs(inst); + if (ret) + goto err_bufs_free; + + load_scale_clocks(core); + + ret = hfi_session_load_res(inst); + if (ret) + goto err_unreg_bufs; + + ret = hfi_session_start(inst); + if (ret) + goto err_unload_res; + + return 0; + +err_unload_res: + hfi_session_unload_res(inst); +err_unreg_bufs: + session_unregister_bufs(inst); +err_bufs_free: + intbufs_free(inst); + return ret; +} +EXPORT_SYMBOL_GPL(venus_helper_vb2_start_streaming); + +void venus_helper_m2m_device_run(void *priv) +{ + struct venus_inst *inst = priv; + struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx; + struct v4l2_m2m_buffer *buf, *n; + int ret; + + mutex_lock(&inst->lock); + + v4l2_m2m_for_each_dst_buf_safe(m2m_ctx, buf, n) { + ret = session_process_buf(inst, &buf->vb); + if (ret) + return_buf_error(inst, &buf->vb); + } + + v4l2_m2m_for_each_src_buf_safe(m2m_ctx, buf, n) { + ret = session_process_buf(inst, &buf->vb); + if (ret) + return_buf_error(inst, &buf->vb); + } + + mutex_unlock(&inst->lock); +} +EXPORT_SYMBOL_GPL(venus_helper_m2m_device_run); + +void venus_helper_m2m_job_abort(void *priv) +{ + struct venus_inst *inst = priv; + + v4l2_m2m_job_finish(inst->m2m_dev, inst->m2m_ctx); +} +EXPORT_SYMBOL_GPL(venus_helper_m2m_job_abort); + +void venus_helper_init_instance(struct venus_inst *inst) +{ + if (inst->session_type == VIDC_SESSION_TYPE_DEC) { + INIT_LIST_HEAD(&inst->delayed_process); + INIT_WORK(&inst->delayed_process_work, + delayed_process_buf_func); + } +} +EXPORT_SYMBOL_GPL(venus_helper_init_instance); diff --git a/drivers/media/platform/qcom/venus/helpers.h b/drivers/media/platform/qcom/venus/helpers.h new file mode 100644 index 000000000000..6a061b417a93 --- /dev/null +++ b/drivers/media/platform/qcom/venus/helpers.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * Copyright (C) 2017 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + * + */ +#ifndef __VENUS_HELPERS_H__ +#define __VENUS_HELPERS_H__ + +#include + +struct venus_inst; + +struct vb2_v4l2_buffer *venus_helper_find_buf(struct venus_inst *inst, + unsigned int type, u32 idx); +void venus_helper_buffers_done(struct venus_inst *inst, + enum vb2_buffer_state state); +int venus_helper_vb2_buf_init(struct vb2_buffer *vb); +int venus_helper_vb2_buf_prepare(struct vb2_buffer *vb); +void venus_helper_vb2_buf_queue(struct vb2_buffer *vb); +void venus_helper_vb2_stop_streaming(struct vb2_queue *q); +int venus_helper_vb2_start_streaming(struct venus_inst *inst); +void venus_helper_m2m_device_run(void *priv); +void venus_helper_m2m_job_abort(void *priv); +int venus_helper_get_bufreq(struct venus_inst *inst, u32 type, + struct hfi_buffer_requirements *req); +int venus_helper_set_input_resolution(struct venus_inst *inst, + unsigned int width, unsigned int height); +int venus_helper_set_output_resolution(struct venus_inst *inst, + unsigned int width, unsigned int height); +int venus_helper_set_num_bufs(struct venus_inst *inst, unsigned int input_bufs, + unsigned int output_bufs); +int venus_helper_set_color_format(struct venus_inst *inst, u32 fmt); +void venus_helper_acquire_buf_ref(struct vb2_v4l2_buffer *vbuf); +void venus_helper_release_buf_ref(struct venus_inst *inst, unsigned int idx); +void venus_helper_init_instance(struct venus_inst *inst); +#endif From patchwork Thu Jun 15 16:31:46 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stanimir Varbanov X-Patchwork-Id: 105673 Delivered-To: patch@linaro.org Received: by 10.140.91.77 with SMTP id y71csp889510qgd; Thu, 15 Jun 2017 09:36:07 -0700 (PDT) X-Received: by 10.99.247.83 with SMTP id f19mr6343095pgk.190.1497544567407; Thu, 15 Jun 2017 09:36:07 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1497544567; cv=none; d=google.com; s=arc-20160816; b=pg1vx9ZmwQHNZAK92daBGB8SS7ixiHiQjdd4szGvaN9TF329kXu0GoTbXZEje9oxPp JYFU++eJRHeYfuAWR3amoWzvMN9YjTsDann2zma2E5+8rWE5tonP7erAS12puFs2BITB zrzugGbeogIVH4zzxb5CG6JSNMV01JwBkTAJaK59AknaDhcEBfiZl10r/neDDGkvYk0E rnznjO81qIhUwJ1e3l2fSucSGfTtLLElYtZ/HM88RIabOy5i99ecpVxrkH0goxvKgAQM IRE1WtznEj2uLjD3uBbSjv69h61ripWBXNOvVpYoIH5tI+/0murjeI1M5VHk0mxzwi0K RvUg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:arc-authentication-results; bh=sXZLHz/FnColZ6R5vvBy/brIm5hcndTnDQsD9B3lIfI=; b=hK5/TPc5+W5C6DavRj+SnRHALY8zZqP6UdBX/wpE0LtrpQYszkAN9NA7EXVkCO+6Tx OMwRQJHXj/uOTQP5IEWBPHvPhWPInDSJxUrB48N/cj3Isq59MlvlXobKIRVTfWyBLZ/D l6uU9bUkbHrAofnT6YYmQOziCg3ODkyavvv4CIoD84XhpX8LcG5FjLQeU+FV89SqDGEV CZLD3PTnho6+TuWOy5VvaCn2pw+cRL78chfewkIfkC/QxRgxkw5oLk9PG720U+VuQThY MTgtO1JK81oWfLMPfYIAQMNPi2BHhaSFfT/Tc25yM4/1d/fMdFnnMdQzfRAs+zonPcH5 Am5w== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.b=IOqXgM9U; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id 31si400112plj.25.2017.06.15.09.36.07; Thu, 15 Jun 2017 09:36:07 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.b=IOqXgM9U; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1750849AbdFOQd1 (ORCPT + 25 others); Thu, 15 Jun 2017 12:33:27 -0400 Received: from mail-wm0-f51.google.com ([74.125.82.51]:36908 "EHLO mail-wm0-f51.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751639AbdFOQdV (ORCPT ); Thu, 15 Jun 2017 12:33:21 -0400 Received: by mail-wm0-f51.google.com with SMTP id d73so4068087wma.0 for ; Thu, 15 Jun 2017 09:33:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=sXZLHz/FnColZ6R5vvBy/brIm5hcndTnDQsD9B3lIfI=; b=IOqXgM9UPrQb8OmZFgaCK02YLCpl1lEuqAxnXLLpcMmAHOYo5RGULoywdhacN52mxZ yEvCbsQqa+vCdtqXBriue7FPE6MubKTL85MU0R4brARAiQouA3EezqQwr9YVb6FEBIoT YH8JRFdaGctYi1sJJe0A7pli8/+BrEQiIgkPY= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=sXZLHz/FnColZ6R5vvBy/brIm5hcndTnDQsD9B3lIfI=; b=FWTYj0DCI/8lIwQ3WrPvyhG+00FZ7yFRHG4XObQK/yD85gYZNrd0bcWkcpoQ1bhZrS HUYfSZGv/aqBblSeXb4YoUbkZkG4mnQgQFfA4+kPKORmvBxVLgusfTj9vV3MYCvOIfhx AuF3jLDW87yvglRPTa/ZIj6nc1dbhB+vQ4+cnLQNnNWzA2o2Ii0Dv1tD6TRFnYN/zRtr qSWB1CUmFn4/651+W4mZivgnNOsLRZZSFXWBmnK4PEGxe4CkfHgfY5k7zY0MAXM4DulE 2QuuqYfnuYQwlSvOofsZxuAZiskeYqN0nWcHSu/NP6SXoNls3piFmRAuX7U4eXn6VxWq /Sbw== X-Gm-Message-State: AKS2vOz5cF8/n3iz+bO8Uk+Gib8tf3/rLTgeWvWR+PYN0IQj5lxtpfP/ UJzUa921hq281dLD X-Received: by 10.28.143.16 with SMTP id r16mr4331412wmd.50.1497544374447; Thu, 15 Jun 2017 09:32:54 -0700 (PDT) Received: from mms-0440.qualcomm.mm-sol.com ([37.157.136.206]) by smtp.gmail.com with ESMTPSA id m73sm541890wmi.25.2017.06.15.09.32.53 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 15 Jun 2017 09:32:53 -0700 (PDT) From: Stanimir Varbanov To: Mauro Carvalho Chehab , Hans Verkuil Cc: linux-media@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, Stanimir Varbanov Subject: [PATCH v11 05/19] media: venus: vdec: add video decoder files Date: Thu, 15 Jun 2017 19:31:46 +0300 Message-Id: <1497544320-2269-6-git-send-email-stanimir.varbanov@linaro.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1497544320-2269-1-git-send-email-stanimir.varbanov@linaro.org> References: <1497544320-2269-1-git-send-email-stanimir.varbanov@linaro.org> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org This consists of video decoder implementation plus decoder controls. Signed-off-by: Stanimir Varbanov --- drivers/media/platform/qcom/venus/vdec.c | 1154 ++++++++++++++++++++++++ drivers/media/platform/qcom/venus/vdec.h | 23 + drivers/media/platform/qcom/venus/vdec_ctrls.c | 150 +++ 3 files changed, 1327 insertions(+) create mode 100644 drivers/media/platform/qcom/venus/vdec.c create mode 100644 drivers/media/platform/qcom/venus/vdec.h create mode 100644 drivers/media/platform/qcom/venus/vdec_ctrls.c -- 2.7.4 diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c new file mode 100644 index 000000000000..96e7e7e71e5f --- /dev/null +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -0,0 +1,1154 @@ +/* + * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * Copyright (C) 2017 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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 + +#include "hfi_venus_io.h" +#include "core.h" +#include "helpers.h" +#include "vdec.h" + +static u32 get_framesize_uncompressed(unsigned int plane, u32 width, u32 height) +{ + u32 y_stride, uv_stride, y_plane; + u32 y_sclines, uv_sclines, uv_plane; + u32 size; + + y_stride = ALIGN(width, 128); + uv_stride = ALIGN(width, 128); + y_sclines = ALIGN(height, 32); + uv_sclines = ALIGN(((height + 1) >> 1), 16); + + y_plane = y_stride * y_sclines; + uv_plane = uv_stride * uv_sclines + SZ_4K; + size = y_plane + uv_plane + SZ_8K; + + return ALIGN(size, SZ_4K); +} + +static u32 get_framesize_compressed(unsigned int width, unsigned int height) +{ + return ((width * height * 3 / 2) / 2) + 128; +} + +/* + * Three resons to keep MPLANE formats (despite that the number of planes + * currently is one): + * - the MPLANE formats allow only one plane to be used + * - the downstream driver use MPLANE formats too + * - future firmware versions could add support for >1 planes + */ +static const struct venus_format vdec_formats[] = { + { + .pixfmt = V4L2_PIX_FMT_NV12, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + }, { + .pixfmt = V4L2_PIX_FMT_MPEG4, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, { + .pixfmt = V4L2_PIX_FMT_MPEG2, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, { + .pixfmt = V4L2_PIX_FMT_H263, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, { + .pixfmt = V4L2_PIX_FMT_VC1_ANNEX_G, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, { + .pixfmt = V4L2_PIX_FMT_VC1_ANNEX_L, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, { + .pixfmt = V4L2_PIX_FMT_H264, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, { + .pixfmt = V4L2_PIX_FMT_VP8, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, { + .pixfmt = V4L2_PIX_FMT_VP9, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, { + .pixfmt = V4L2_PIX_FMT_XVID, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, +}; + +static const struct venus_format *find_format(u32 pixfmt, u32 type) +{ + const struct venus_format *fmt = vdec_formats; + unsigned int size = ARRAY_SIZE(vdec_formats); + unsigned int i; + + for (i = 0; i < size; i++) { + if (fmt[i].pixfmt == pixfmt) + break; + } + + if (i == size || fmt[i].type != type) + return NULL; + + return &fmt[i]; +} + +static const struct venus_format * +find_format_by_index(unsigned int index, u32 type) +{ + const struct venus_format *fmt = vdec_formats; + unsigned int size = ARRAY_SIZE(vdec_formats); + unsigned int i, k = 0; + + if (index > size) + return NULL; + + for (i = 0; i < size; i++) { + if (fmt[i].type != type) + continue; + if (k == index) + break; + k++; + } + + if (i == size) + return NULL; + + return &fmt[i]; +} + +static const struct venus_format * +vdec_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; + struct v4l2_plane_pix_format *pfmt = pixmp->plane_fmt; + const struct venus_format *fmt; + unsigned int p; + + memset(pfmt[0].reserved, 0, sizeof(pfmt[0].reserved)); + memset(pixmp->reserved, 0, sizeof(pixmp->reserved)); + + fmt = find_format(pixmp->pixelformat, f->type); + if (!fmt) { + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + pixmp->pixelformat = V4L2_PIX_FMT_NV12; + else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + pixmp->pixelformat = V4L2_PIX_FMT_H264; + else + return NULL; + fmt = find_format(pixmp->pixelformat, f->type); + pixmp->width = 1280; + pixmp->height = 720; + } + + pixmp->width = clamp(pixmp->width, inst->cap_width.min, + inst->cap_width.max); + pixmp->height = clamp(pixmp->height, inst->cap_height.min, + inst->cap_height.max); + + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + pixmp->height = ALIGN(pixmp->height, 32); + + if (pixmp->field == V4L2_FIELD_ANY) + pixmp->field = V4L2_FIELD_NONE; + pixmp->num_planes = fmt->num_planes; + pixmp->flags = 0; + + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + for (p = 0; p < pixmp->num_planes; p++) { + pfmt[p].sizeimage = + get_framesize_uncompressed(p, pixmp->width, + pixmp->height); + pfmt[p].bytesperline = ALIGN(pixmp->width, 128); + } + } else { + pfmt[0].sizeimage = get_framesize_compressed(pixmp->width, + pixmp->height); + pfmt[0].bytesperline = 0; + } + + return fmt; +} + +static int vdec_try_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct venus_inst *inst = to_inst(file); + + vdec_try_fmt_common(inst, f); + + return 0; +} + +static int vdec_g_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct venus_inst *inst = to_inst(file); + const struct venus_format *fmt = NULL; + struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; + + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + fmt = inst->fmt_cap; + else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + fmt = inst->fmt_out; + + if (inst->reconfig) { + struct v4l2_format format = {}; + + inst->out_width = inst->reconfig_width; + inst->out_height = inst->reconfig_height; + inst->reconfig = false; + + format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + format.fmt.pix_mp.pixelformat = inst->fmt_cap->pixfmt; + format.fmt.pix_mp.width = inst->out_width; + format.fmt.pix_mp.height = inst->out_height; + + vdec_try_fmt_common(inst, &format); + + inst->width = format.fmt.pix_mp.width; + inst->height = format.fmt.pix_mp.height; + } + + pixmp->pixelformat = fmt->pixfmt; + + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + pixmp->width = inst->width; + pixmp->height = inst->height; + pixmp->colorspace = inst->colorspace; + pixmp->ycbcr_enc = inst->ycbcr_enc; + pixmp->quantization = inst->quantization; + pixmp->xfer_func = inst->xfer_func; + } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + pixmp->width = inst->out_width; + pixmp->height = inst->out_height; + } + + vdec_try_fmt_common(inst, f); + + return 0; +} + +static int vdec_s_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct venus_inst *inst = to_inst(file); + struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; + struct v4l2_pix_format_mplane orig_pixmp; + const struct venus_format *fmt; + struct v4l2_format format; + u32 pixfmt_out = 0, pixfmt_cap = 0; + + orig_pixmp = *pixmp; + + fmt = vdec_try_fmt_common(inst, f); + + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + pixfmt_out = pixmp->pixelformat; + pixfmt_cap = inst->fmt_cap->pixfmt; + } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + pixfmt_cap = pixmp->pixelformat; + pixfmt_out = inst->fmt_out->pixfmt; + } + + memset(&format, 0, sizeof(format)); + + format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + format.fmt.pix_mp.pixelformat = pixfmt_out; + format.fmt.pix_mp.width = orig_pixmp.width; + format.fmt.pix_mp.height = orig_pixmp.height; + vdec_try_fmt_common(inst, &format); + + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + inst->out_width = format.fmt.pix_mp.width; + inst->out_height = format.fmt.pix_mp.height; + inst->colorspace = pixmp->colorspace; + inst->ycbcr_enc = pixmp->ycbcr_enc; + inst->quantization = pixmp->quantization; + inst->xfer_func = pixmp->xfer_func; + } + + memset(&format, 0, sizeof(format)); + + format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + format.fmt.pix_mp.pixelformat = pixfmt_cap; + format.fmt.pix_mp.width = orig_pixmp.width; + format.fmt.pix_mp.height = orig_pixmp.height; + vdec_try_fmt_common(inst, &format); + + inst->width = format.fmt.pix_mp.width; + inst->height = format.fmt.pix_mp.height; + + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + inst->fmt_out = fmt; + else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + inst->fmt_cap = fmt; + + return 0; +} + +static int +vdec_g_selection(struct file *file, void *fh, struct v4l2_selection *s) +{ + struct venus_inst *inst = to_inst(file); + + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + switch (s->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP: + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + s->r.width = inst->out_width; + s->r.height = inst->out_height; + break; + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_COMPOSE_PADDED: + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + s->r.width = inst->width; + s->r.height = inst->height; + break; + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + case V4L2_SEL_TGT_COMPOSE: + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + s->r.width = inst->out_width; + s->r.height = inst->out_height; + break; + default: + return -EINVAL; + } + + s->r.top = 0; + s->r.left = 0; + + return 0; +} + +static int +vdec_querycap(struct file *file, void *fh, struct v4l2_capability *cap) +{ + strlcpy(cap->driver, "qcom-venus", sizeof(cap->driver)); + strlcpy(cap->card, "Qualcomm Venus video decoder", sizeof(cap->card)); + strlcpy(cap->bus_info, "platform:qcom-venus", sizeof(cap->bus_info)); + + return 0; +} + +static int vdec_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) +{ + const struct venus_format *fmt; + + memset(f->reserved, 0, sizeof(f->reserved)); + + fmt = find_format_by_index(f->index, f->type); + if (!fmt) + return -EINVAL; + + f->pixelformat = fmt->pixfmt; + + return 0; +} + +static int vdec_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct venus_inst *inst = to_inst(file); + struct v4l2_captureparm *cap = &a->parm.capture; + struct v4l2_fract *timeperframe = &cap->timeperframe; + u64 us_per_frame, fps; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && + a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + return -EINVAL; + + memset(cap->reserved, 0, sizeof(cap->reserved)); + if (!timeperframe->denominator) + timeperframe->denominator = inst->timeperframe.denominator; + if (!timeperframe->numerator) + timeperframe->numerator = inst->timeperframe.numerator; + cap->readbuffers = 0; + cap->extendedmode = 0; + cap->capability = V4L2_CAP_TIMEPERFRAME; + us_per_frame = timeperframe->numerator * (u64)USEC_PER_SEC; + do_div(us_per_frame, timeperframe->denominator); + + if (!us_per_frame) + return -EINVAL; + + fps = (u64)USEC_PER_SEC; + do_div(fps, us_per_frame); + + inst->fps = fps; + inst->timeperframe = *timeperframe; + + return 0; +} + +static int vdec_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct venus_inst *inst = to_inst(file); + const struct venus_format *fmt; + + fmt = find_format(fsize->pixel_format, + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + if (!fmt) { + fmt = find_format(fsize->pixel_format, + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + if (!fmt) + return -EINVAL; + } + + if (fsize->index) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + + fsize->stepwise.min_width = inst->cap_width.min; + fsize->stepwise.max_width = inst->cap_width.max; + fsize->stepwise.step_width = inst->cap_width.step_size; + fsize->stepwise.min_height = inst->cap_height.min; + fsize->stepwise.max_height = inst->cap_height.max; + fsize->stepwise.step_height = inst->cap_height.step_size; + + return 0; +} + +static int vdec_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_EOS: + return v4l2_event_subscribe(fh, sub, 2, NULL); + case V4L2_EVENT_SOURCE_CHANGE: + return v4l2_src_change_event_subscribe(fh, sub); + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subscribe_event(fh, sub); + default: + return -EINVAL; + } +} + +static int +vdec_try_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd) +{ + if (cmd->cmd != V4L2_DEC_CMD_STOP) + return -EINVAL; + + return 0; +} + +static int +vdec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd) +{ + struct venus_inst *inst = to_inst(file); + int ret; + + ret = vdec_try_decoder_cmd(file, fh, cmd); + if (ret) + return ret; + + mutex_lock(&inst->lock); + inst->cmd_stop = true; + mutex_unlock(&inst->lock); + + hfi_session_flush(inst); + + return 0; +} + +static const struct v4l2_ioctl_ops vdec_ioctl_ops = { + .vidioc_querycap = vdec_querycap, + .vidioc_enum_fmt_vid_cap_mplane = vdec_enum_fmt, + .vidioc_enum_fmt_vid_out_mplane = vdec_enum_fmt, + .vidioc_s_fmt_vid_cap_mplane = vdec_s_fmt, + .vidioc_s_fmt_vid_out_mplane = vdec_s_fmt, + .vidioc_g_fmt_vid_cap_mplane = vdec_g_fmt, + .vidioc_g_fmt_vid_out_mplane = vdec_g_fmt, + .vidioc_try_fmt_vid_cap_mplane = vdec_try_fmt, + .vidioc_try_fmt_vid_out_mplane = vdec_try_fmt, + .vidioc_g_selection = vdec_g_selection, + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + .vidioc_s_parm = vdec_s_parm, + .vidioc_enum_framesizes = vdec_enum_framesizes, + .vidioc_subscribe_event = vdec_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_try_decoder_cmd = vdec_try_decoder_cmd, + .vidioc_decoder_cmd = vdec_decoder_cmd, +}; + +static int vdec_set_properties(struct venus_inst *inst) +{ + struct vdec_controls *ctr = &inst->controls.dec; + struct venus_core *core = inst->core; + struct hfi_enable en = { .enable = 1 }; + u32 ptype; + int ret; + + if (core->res->hfi_version == HFI_VERSION_1XX) { + ptype = HFI_PROPERTY_PARAM_VDEC_CONTINUE_DATA_TRANSFER; + ret = hfi_session_set_property(inst, ptype, &en); + if (ret) + return ret; + } + + if (core->res->hfi_version == HFI_VERSION_3XX || + inst->cap_bufs_mode_dynamic) { + struct hfi_buffer_alloc_mode mode; + + ptype = HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE; + mode.type = HFI_BUFFER_OUTPUT; + mode.mode = HFI_BUFFER_MODE_DYNAMIC; + + ret = hfi_session_set_property(inst, ptype, &mode); + if (ret) + return ret; + } + + if (ctr->post_loop_deb_mode) { + ptype = HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER; + en.enable = 1; + ret = hfi_session_set_property(inst, ptype, &en); + if (ret) + return ret; + } + + return 0; +} + +static int vdec_init_session(struct venus_inst *inst) +{ + int ret; + + ret = hfi_session_init(inst, inst->fmt_out->pixfmt); + if (ret) + return ret; + + ret = venus_helper_set_input_resolution(inst, inst->out_width, + inst->out_height); + if (ret) + goto deinit; + + ret = venus_helper_set_color_format(inst, inst->fmt_cap->pixfmt); + if (ret) + goto deinit; + + return 0; +deinit: + hfi_session_deinit(inst); + return ret; +} + +static int vdec_cap_num_buffers(struct venus_inst *inst, unsigned int *num) +{ + struct hfi_buffer_requirements bufreq; + int ret; + + ret = vdec_init_session(inst); + if (ret) + return ret; + + ret = venus_helper_get_bufreq(inst, HFI_BUFFER_OUTPUT, &bufreq); + + *num = bufreq.count_actual; + + hfi_session_deinit(inst); + + return ret; +} + +static int vdec_queue_setup(struct vb2_queue *q, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct venus_inst *inst = vb2_get_drv_priv(q); + unsigned int p, num; + int ret = 0; + + if (*num_planes) { + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && + *num_planes != inst->fmt_out->num_planes) + return -EINVAL; + + if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && + *num_planes != inst->fmt_cap->num_planes) + return -EINVAL; + + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && + sizes[0] < inst->input_buf_size) + return -EINVAL; + + if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && + sizes[0] < inst->output_buf_size) + return -EINVAL; + + return 0; + } + + switch (q->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + *num_planes = inst->fmt_out->num_planes; + sizes[0] = get_framesize_compressed(inst->out_width, + inst->out_height); + inst->input_buf_size = sizes[0]; + inst->num_input_bufs = *num_buffers; + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + *num_planes = inst->fmt_cap->num_planes; + + ret = vdec_cap_num_buffers(inst, &num); + if (ret) + break; + + *num_buffers = max(*num_buffers, num); + + for (p = 0; p < *num_planes; p++) + sizes[p] = get_framesize_uncompressed(p, inst->width, + inst->height); + + inst->num_output_bufs = *num_buffers; + inst->output_buf_size = sizes[0]; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int vdec_verify_conf(struct venus_inst *inst) +{ + struct hfi_buffer_requirements bufreq; + int ret; + + if (!inst->num_input_bufs || !inst->num_output_bufs) + return -EINVAL; + + ret = venus_helper_get_bufreq(inst, HFI_BUFFER_OUTPUT, &bufreq); + if (ret) + return ret; + + if (inst->num_output_bufs < bufreq.count_actual || + inst->num_output_bufs < bufreq.count_min) + return -EINVAL; + + ret = venus_helper_get_bufreq(inst, HFI_BUFFER_INPUT, &bufreq); + if (ret) + return ret; + + if (inst->num_input_bufs < bufreq.count_min) + return -EINVAL; + + return 0; +} + +static int vdec_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct venus_inst *inst = vb2_get_drv_priv(q); + struct venus_core *core = inst->core; + u32 ptype; + int ret; + + mutex_lock(&inst->lock); + + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + inst->streamon_out = 1; + else + inst->streamon_cap = 1; + + if (!(inst->streamon_out & inst->streamon_cap)) { + mutex_unlock(&inst->lock); + return 0; + } + + venus_helper_init_instance(inst); + + inst->reconfig = false; + inst->sequence_cap = 0; + inst->sequence_out = 0; + inst->cmd_stop = false; + + ret = vdec_init_session(inst); + if (ret) + goto bufs_done; + + ret = vdec_set_properties(inst); + if (ret) + goto deinit_sess; + + if (core->res->hfi_version == HFI_VERSION_3XX) { + struct hfi_buffer_size_actual buf_sz; + + ptype = HFI_PROPERTY_PARAM_BUFFER_SIZE_ACTUAL; + buf_sz.type = HFI_BUFFER_OUTPUT; + buf_sz.size = inst->output_buf_size; + + ret = hfi_session_set_property(inst, ptype, &buf_sz); + if (ret) + goto deinit_sess; + } + + ret = vdec_verify_conf(inst); + if (ret) + goto deinit_sess; + + ret = venus_helper_set_num_bufs(inst, inst->num_input_bufs, + VB2_MAX_FRAME); + if (ret) + goto deinit_sess; + + ret = venus_helper_vb2_start_streaming(inst); + if (ret) + goto deinit_sess; + + mutex_unlock(&inst->lock); + + return 0; + +deinit_sess: + hfi_session_deinit(inst); +bufs_done: + venus_helper_buffers_done(inst, VB2_BUF_STATE_QUEUED); + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + inst->streamon_out = 0; + else + inst->streamon_cap = 0; + mutex_unlock(&inst->lock); + return ret; +} + +static const struct vb2_ops vdec_vb2_ops = { + .queue_setup = vdec_queue_setup, + .buf_init = venus_helper_vb2_buf_init, + .buf_prepare = venus_helper_vb2_buf_prepare, + .start_streaming = vdec_start_streaming, + .stop_streaming = venus_helper_vb2_stop_streaming, + .buf_queue = venus_helper_vb2_buf_queue, +}; + +static void vdec_buf_done(struct venus_inst *inst, unsigned int buf_type, + u32 tag, u32 bytesused, u32 data_offset, u32 flags, + u32 hfi_flags, u64 timestamp_us) +{ + enum vb2_buffer_state state = VB2_BUF_STATE_DONE; + struct vb2_v4l2_buffer *vbuf; + struct vb2_buffer *vb; + unsigned int type; + + if (buf_type == HFI_BUFFER_INPUT) + type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + else + type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + + vbuf = venus_helper_find_buf(inst, type, tag); + if (!vbuf) + return; + + vbuf->flags = flags; + vbuf->field = V4L2_FIELD_NONE; + + if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + vb = &vbuf->vb2_buf; + vb->planes[0].bytesused = + max_t(unsigned int, inst->output_buf_size, bytesused); + vb->planes[0].data_offset = data_offset; + vb->timestamp = timestamp_us * NSEC_PER_USEC; + vbuf->sequence = inst->sequence_cap++; + + if (inst->cmd_stop) { + vbuf->flags |= V4L2_BUF_FLAG_LAST; + inst->cmd_stop = false; + } + + if (vbuf->flags & V4L2_BUF_FLAG_LAST) { + const struct v4l2_event ev = { .type = V4L2_EVENT_EOS }; + + v4l2_event_queue_fh(&inst->fh, &ev); + } + } else { + vbuf->sequence = inst->sequence_out++; + } + + if (hfi_flags & HFI_BUFFERFLAG_READONLY) + venus_helper_acquire_buf_ref(vbuf); + + if (hfi_flags & HFI_BUFFERFLAG_DATACORRUPT) + state = VB2_BUF_STATE_ERROR; + + v4l2_m2m_buf_done(vbuf, state); +} + +static void vdec_event_notify(struct venus_inst *inst, u32 event, + struct hfi_event_data *data) +{ + struct venus_core *core = inst->core; + struct device *dev = core->dev_dec; + static const struct v4l2_event ev = { + .type = V4L2_EVENT_SOURCE_CHANGE, + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION }; + + switch (event) { + case EVT_SESSION_ERROR: + inst->session_error = true; + dev_err(dev, "dec: event session error %x\n", inst->error); + break; + case EVT_SYS_EVENT_CHANGE: + switch (data->event_type) { + case HFI_EVENT_DATA_SEQUENCE_CHANGED_SUFFICIENT_BUF_RESOURCES: + hfi_session_continue(inst); + dev_dbg(dev, "event sufficient resources\n"); + break; + case HFI_EVENT_DATA_SEQUENCE_CHANGED_INSUFFICIENT_BUF_RESOURCES: + inst->reconfig_height = data->height; + inst->reconfig_width = data->width; + inst->reconfig = true; + + v4l2_event_queue_fh(&inst->fh, &ev); + + dev_dbg(dev, "event not sufficient resources (%ux%u)\n", + data->width, data->height); + break; + case HFI_EVENT_RELEASE_BUFFER_REFERENCE: + venus_helper_release_buf_ref(inst, data->tag); + break; + default: + break; + } + break; + default: + break; + } +} + +static const struct hfi_inst_ops vdec_hfi_ops = { + .buf_done = vdec_buf_done, + .event_notify = vdec_event_notify, +}; + +static void vdec_inst_init(struct venus_inst *inst) +{ + inst->fmt_out = &vdec_formats[6]; + inst->fmt_cap = &vdec_formats[0]; + inst->width = 1280; + inst->height = ALIGN(720, 32); + inst->out_width = 1280; + inst->out_height = 720; + inst->fps = 30; + inst->timeperframe.numerator = 1; + inst->timeperframe.denominator = 30; + + inst->cap_width.min = 64; + inst->cap_width.max = 1920; + if (inst->core->res->hfi_version == HFI_VERSION_3XX) + inst->cap_width.max = 3840; + inst->cap_width.step_size = 1; + inst->cap_height.min = 64; + inst->cap_height.max = ALIGN(1080, 32); + if (inst->core->res->hfi_version == HFI_VERSION_3XX) + inst->cap_height.max = ALIGN(2160, 32); + inst->cap_height.step_size = 1; + inst->cap_framerate.min = 1; + inst->cap_framerate.max = 30; + inst->cap_framerate.step_size = 1; + inst->cap_mbs_per_frame.min = 16; + inst->cap_mbs_per_frame.max = 8160; +} + +static const struct v4l2_m2m_ops vdec_m2m_ops = { + .device_run = venus_helper_m2m_device_run, + .job_abort = venus_helper_m2m_job_abort, +}; + +static int m2m_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + struct venus_inst *inst = priv; + int ret; + + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->ops = &vdec_vb2_ops; + src_vq->mem_ops = &vb2_dma_sg_memops; + src_vq->drv_priv = inst; + src_vq->buf_struct_size = sizeof(struct venus_buffer); + src_vq->allow_zero_bytesused = 1; + src_vq->min_buffers_needed = 1; + src_vq->dev = inst->core->dev; + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->ops = &vdec_vb2_ops; + dst_vq->mem_ops = &vb2_dma_sg_memops; + dst_vq->drv_priv = inst; + dst_vq->buf_struct_size = sizeof(struct venus_buffer); + dst_vq->allow_zero_bytesused = 1; + dst_vq->min_buffers_needed = 1; + dst_vq->dev = inst->core->dev; + ret = vb2_queue_init(dst_vq); + if (ret) { + vb2_queue_release(src_vq); + return ret; + } + + return 0; +} + +static int vdec_open(struct file *file) +{ + struct venus_core *core = video_drvdata(file); + struct venus_inst *inst; + int ret; + + inst = kzalloc(sizeof(*inst), GFP_KERNEL); + if (!inst) + return -ENOMEM; + + INIT_LIST_HEAD(&inst->registeredbufs); + INIT_LIST_HEAD(&inst->internalbufs); + INIT_LIST_HEAD(&inst->list); + mutex_init(&inst->lock); + + inst->core = core; + inst->session_type = VIDC_SESSION_TYPE_DEC; + + venus_helper_init_instance(inst); + + ret = pm_runtime_get_sync(core->dev_dec); + if (ret < 0) + goto err_free_inst; + + ret = vdec_ctrl_init(inst); + if (ret) + goto err_put_sync; + + ret = hfi_session_create(inst, &vdec_hfi_ops); + if (ret) + goto err_ctrl_deinit; + + vdec_inst_init(inst); + + /* + * create m2m device for every instance, the m2m context scheduling + * is made by firmware side so we do not need to care about. + */ + inst->m2m_dev = v4l2_m2m_init(&vdec_m2m_ops); + if (IS_ERR(inst->m2m_dev)) { + ret = PTR_ERR(inst->m2m_dev); + goto err_session_destroy; + } + + inst->m2m_ctx = v4l2_m2m_ctx_init(inst->m2m_dev, inst, m2m_queue_init); + if (IS_ERR(inst->m2m_ctx)) { + ret = PTR_ERR(inst->m2m_ctx); + goto err_m2m_release; + } + + v4l2_fh_init(&inst->fh, core->vdev_dec); + + inst->fh.ctrl_handler = &inst->ctrl_handler; + v4l2_fh_add(&inst->fh); + inst->fh.m2m_ctx = inst->m2m_ctx; + file->private_data = &inst->fh; + + return 0; + +err_m2m_release: + v4l2_m2m_release(inst->m2m_dev); +err_session_destroy: + hfi_session_destroy(inst); +err_ctrl_deinit: + vdec_ctrl_deinit(inst); +err_put_sync: + pm_runtime_put_sync(core->dev_dec); +err_free_inst: + kfree(inst); + return ret; +} + +static int vdec_close(struct file *file) +{ + struct venus_inst *inst = to_inst(file); + + v4l2_m2m_ctx_release(inst->m2m_ctx); + v4l2_m2m_release(inst->m2m_dev); + vdec_ctrl_deinit(inst); + hfi_session_destroy(inst); + mutex_destroy(&inst->lock); + v4l2_fh_del(&inst->fh); + v4l2_fh_exit(&inst->fh); + kfree(inst); + + pm_runtime_put_sync(inst->core->dev_dec); + return 0; +} + +static const struct v4l2_file_operations vdec_fops = { + .owner = THIS_MODULE, + .open = vdec_open, + .release = vdec_close, + .unlocked_ioctl = video_ioctl2, + .poll = v4l2_m2m_fop_poll, + .mmap = v4l2_m2m_fop_mmap, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = v4l2_compat_ioctl32, +#endif +}; + +static int vdec_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct video_device *vdev; + struct venus_core *core; + int ret; + + if (!dev->parent) + return -EPROBE_DEFER; + + core = dev_get_drvdata(dev->parent); + if (!core) + return -EPROBE_DEFER; + + if (core->res->hfi_version == HFI_VERSION_3XX) { + core->core0_clk = devm_clk_get(dev, "core"); + if (IS_ERR(core->core0_clk)) + return PTR_ERR(core->core0_clk); + } + + platform_set_drvdata(pdev, core); + + vdev = video_device_alloc(); + if (!vdev) + return -ENOMEM; + + vdev->release = video_device_release; + vdev->fops = &vdec_fops; + vdev->ioctl_ops = &vdec_ioctl_ops; + vdev->vfl_dir = VFL_DIR_M2M; + vdev->v4l2_dev = &core->v4l2_dev; + vdev->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; + + ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + if (ret) + goto err_vdev_release; + + core->vdev_dec = vdev; + core->dev_dec = dev; + + video_set_drvdata(vdev, core); + pm_runtime_enable(dev); + + return 0; + +err_vdev_release: + video_device_release(vdev); + return ret; +} + +static int vdec_remove(struct platform_device *pdev) +{ + struct venus_core *core = dev_get_drvdata(pdev->dev.parent); + + video_unregister_device(core->vdev_dec); + pm_runtime_disable(core->dev_dec); + + return 0; +} + +#ifdef CONFIG_PM +static int vdec_runtime_suspend(struct device *dev) +{ + struct venus_core *core = dev_get_drvdata(dev); + + if (core->res->hfi_version == HFI_VERSION_1XX) + return 0; + + writel(0, core->base + WRAPPER_VDEC_VCODEC_POWER_CONTROL); + clk_disable_unprepare(core->core0_clk); + writel(1, core->base + WRAPPER_VDEC_VCODEC_POWER_CONTROL); + + return 0; +} + +static int vdec_runtime_resume(struct device *dev) +{ + struct venus_core *core = dev_get_drvdata(dev); + int ret; + + if (core->res->hfi_version == HFI_VERSION_1XX) + return 0; + + writel(0, core->base + WRAPPER_VDEC_VCODEC_POWER_CONTROL); + ret = clk_prepare_enable(core->core0_clk); + writel(1, core->base + WRAPPER_VDEC_VCODEC_POWER_CONTROL); + + return ret; +} +#endif + +static const struct dev_pm_ops vdec_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(vdec_runtime_suspend, vdec_runtime_resume, NULL) +}; + +static const struct of_device_id vdec_dt_match[] = { + { .compatible = "venus-decoder" }, + { } +}; +MODULE_DEVICE_TABLE(of, vdec_dt_match); + +static struct platform_driver qcom_venus_dec_driver = { + .probe = vdec_probe, + .remove = vdec_remove, + .driver = { + .name = "qcom-venus-decoder", + .of_match_table = vdec_dt_match, + .pm = &vdec_pm_ops, + }, +}; +module_platform_driver(qcom_venus_dec_driver); + +MODULE_ALIAS("platform:qcom-venus-decoder"); +MODULE_DESCRIPTION("Qualcomm Venus video decoder driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/qcom/venus/vdec.h b/drivers/media/platform/qcom/venus/vdec.h new file mode 100644 index 000000000000..84b672c54d02 --- /dev/null +++ b/drivers/media/platform/qcom/venus/vdec.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * Copyright (C) 2017 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + * + */ +#ifndef __VENUS_VDEC_H__ +#define __VENUS_VDEC_H__ + +struct venus_inst; + +int vdec_ctrl_init(struct venus_inst *inst); +void vdec_ctrl_deinit(struct venus_inst *inst); + +#endif diff --git a/drivers/media/platform/qcom/venus/vdec_ctrls.c b/drivers/media/platform/qcom/venus/vdec_ctrls.c new file mode 100644 index 000000000000..1045fc5b4925 --- /dev/null +++ b/drivers/media/platform/qcom/venus/vdec_ctrls.c @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * Copyright (C) 2017 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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 "core.h" +#include "vdec.h" + +static int vdec_op_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct venus_inst *inst = ctrl_to_inst(ctrl); + struct vdec_controls *ctr = &inst->controls.dec; + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER: + ctr->post_loop_deb_mode = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: + case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: + case V4L2_CID_MPEG_VIDEO_VPX_PROFILE: + ctr->profile = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: + ctr->level = ctrl->val; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int vdec_op_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct venus_inst *inst = ctrl_to_inst(ctrl); + struct vdec_controls *ctr = &inst->controls.dec; + union hfi_get_property hprop; + u32 ptype = HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT; + int ret; + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: + case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: + case V4L2_CID_MPEG_VIDEO_VPX_PROFILE: + ret = hfi_session_get_property(inst, ptype, &hprop); + if (!ret) + ctr->profile = hprop.profile_level.profile; + ctrl->val = ctr->profile; + break; + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: + ret = hfi_session_get_property(inst, ptype, &hprop); + if (!ret) + ctr->level = hprop.profile_level.level; + ctrl->val = ctr->level; + break; + case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER: + ctrl->val = ctr->post_loop_deb_mode; + break; + default: + return -EINVAL; + }; + + return 0; +} + +static const struct v4l2_ctrl_ops vdec_ctrl_ops = { + .s_ctrl = vdec_op_s_ctrl, + .g_volatile_ctrl = vdec_op_g_volatile_ctrl, +}; + +int vdec_ctrl_init(struct venus_inst *inst) +{ + struct v4l2_ctrl *ctrl; + int ret; + + ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 6); + if (ret) + return ret; + + ctrl = v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &vdec_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE, + V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY, + ~((1 << V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE) | + (1 << V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE)), + V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; + + ctrl = v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &vdec_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL, + V4L2_MPEG_VIDEO_MPEG4_LEVEL_5, + 0, V4L2_MPEG_VIDEO_MPEG4_LEVEL_0); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; + + ctrl = v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &vdec_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_PROFILE, + V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH, + ~((1 << V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) | + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE) | + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) | + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_HIGH) | + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH) | + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH)), + V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; + + ctrl = v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &vdec_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LEVEL, + V4L2_MPEG_VIDEO_H264_LEVEL_5_1, + 0, V4L2_MPEG_VIDEO_H264_LEVEL_1_0); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; + + ctrl = v4l2_ctrl_new_std(&inst->ctrl_handler, &vdec_ctrl_ops, + V4L2_CID_MPEG_VIDEO_VPX_PROFILE, 0, 3, 1, 0); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; + + v4l2_ctrl_new_std(&inst->ctrl_handler, &vdec_ctrl_ops, + V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER, 0, 1, 1, 0); + + ret = inst->ctrl_handler.error; + if (ret) { + v4l2_ctrl_handler_free(&inst->ctrl_handler); + return ret; + } + + return 0; +} + +void vdec_ctrl_deinit(struct venus_inst *inst) +{ + v4l2_ctrl_handler_free(&inst->ctrl_handler); +} From patchwork Thu Jun 15 16:31:47 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stanimir Varbanov X-Patchwork-Id: 105679 Delivered-To: patch@linaro.org Received: by 10.140.91.77 with SMTP id y71csp890973qgd; Thu, 15 Jun 2017 09:39:32 -0700 (PDT) X-Received: by 10.99.115.16 with SMTP id o16mr6135108pgc.31.1497544772627; Thu, 15 Jun 2017 09:39:32 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1497544772; cv=none; d=google.com; s=arc-20160816; b=VHVtWF5560ooYv2GahCG1CX8rNTk55WX2Im/wCEiX8/KEwi6vZnxmoG5ncNh6pLUwh kF+tomf9he/DAzb8JUUrjdDYmvpVSE89nC/ji/mIF4K2EKftOcuLOWwFnkVSiT0q7tJP pQYjCrNVCljKLcMCDVEmu9+7rfEbBCSotrobPZTi3d0D4pVm16LW3TVbE3VKm0fP3kKu dwiTl9vzSudEVX8d3OpsbCy+5gyUIKw43bny3M84dKBnHHc5BqZCFA6xW1/YnV3NucBJ gfJURMED9wyEQlvpC5O8oe46rv79o9YVmPgLtWZUuuYBqrXk8rqVb7BQzKND0OVlrAYU +SDA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:arc-authentication-results; bh=Mtkbqhh8et1v/Y6aHVWV59D+aC16sUkxNYnB2WlBNOY=; b=v8ehxI7PkY1mx8ZIJv2opOBLDAG+OVVQ4luFW56rEhA4QJ9/xZgAbi0pIuRcJMNakC gUjkTs6YNXFDv5nlH/is2JqiPQclzqls+CJglPqqtUIyPghMboVQgDOj2eaujq3JD2q6 vsR0gJWFSyMknAlo8t46eZeNYvNMsHZFLAfmM2DOsPKYPNIY5/8MBgABNLes8dVfIne5 AXzWluDk3Zgufd7k7AcVoWseCXePV9n+nxpIMwDs3Qm/Kq8aGCbL+REVXXdoV4YzCtMr wpR+LxlAd8FeXMB2st4Fo/NDu2m0sXDypohYBoRdTkAvvVuLismOH3JVAZQtAEs/fr4t aRdA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.b=XPJYgkmN; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id x5si426040pgb.146.2017.06.15.09.39.32; Thu, 15 Jun 2017 09:39:32 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.b=XPJYgkmN; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752699AbdFOQjR (ORCPT + 25 others); Thu, 15 Jun 2017 12:39:17 -0400 Received: from mail-wm0-f41.google.com ([74.125.82.41]:35745 "EHLO mail-wm0-f41.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752426AbdFOQdI (ORCPT ); Thu, 15 Jun 2017 12:33:08 -0400 Received: by mail-wm0-f41.google.com with SMTP id x70so4447345wme.0 for ; Thu, 15 Jun 2017 09:32:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=Mtkbqhh8et1v/Y6aHVWV59D+aC16sUkxNYnB2WlBNOY=; b=XPJYgkmNP4p1TepcD/OS3szII81D0B/TyFeHXZxu/D+LBwCDuC/dr1fNaoz7AN4LfK oiYDix/3ahl/K6qK2WfNvmSHqE5fnw7h3201jpDr9th2AFJps2i5wjchGwxEoRohNR8p lKscIc7WiZqTfFaKA/fbo5e9C03P9sSd03Nkw= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=Mtkbqhh8et1v/Y6aHVWV59D+aC16sUkxNYnB2WlBNOY=; b=hkG/eTOYekZ1jURHrvXRcVt65DG2rj1e6yVxzzRr79BawVggnXYV4kShDd7fSZ6nNl ea5kHREsc4yi6fmqsoTNiF2IGJ7HWsdb3MjhFYIIiDRlr4/N/LwwcjriO4g6T6eC/jVK C7Dq9nmx5ibsxxd3sgJ/M1UW+6/92Wlztg4wA1wNrX4i23oo9w/vQXwkJKkUuceDPlsu Us9omiw0fqekS5Wnjc2aOhrU9WkjzkTR04EnXgkHECGTEYkhowGZiMPmxD8OhsFfui2l r1waoi/RNJXxgaoxEmhwXjs+WU+eCgPYYCJnqkhZEBX7+ffSu+a6mggxA2GD3al6s4Me x2Qw== X-Gm-Message-State: AKS2vOx9X0lRdaJzU4dnWszgtEV3/uT3g8kQnJUIlqjSvMUESY8lJPzc N55JO39HmHxz26iq X-Received: by 10.28.54.13 with SMTP id d13mr4314572wma.124.1497544376288; Thu, 15 Jun 2017 09:32:56 -0700 (PDT) Received: from mms-0440.qualcomm.mm-sol.com ([37.157.136.206]) by smtp.gmail.com with ESMTPSA id m73sm541890wmi.25.2017.06.15.09.32.55 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 15 Jun 2017 09:32:55 -0700 (PDT) From: Stanimir Varbanov To: Mauro Carvalho Chehab , Hans Verkuil Cc: linux-media@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, Stanimir Varbanov Subject: [PATCH v11 06/19] media: venus: venc: add video encoder files Date: Thu, 15 Jun 2017 19:31:47 +0300 Message-Id: <1497544320-2269-7-git-send-email-stanimir.varbanov@linaro.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1497544320-2269-1-git-send-email-stanimir.varbanov@linaro.org> References: <1497544320-2269-1-git-send-email-stanimir.varbanov@linaro.org> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org This adds encoder part of the driver plus encoder controls. Signed-off-by: Stanimir Varbanov --- drivers/media/platform/qcom/venus/venc.c | 1283 ++++++++++++++++++++++++ drivers/media/platform/qcom/venus/venc.h | 23 + drivers/media/platform/qcom/venus/venc_ctrls.c | 270 +++++ 3 files changed, 1576 insertions(+) create mode 100644 drivers/media/platform/qcom/venus/venc.c create mode 100644 drivers/media/platform/qcom/venus/venc.h create mode 100644 drivers/media/platform/qcom/venus/venc_ctrls.c -- 2.7.4 diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c new file mode 100644 index 000000000000..d5b4b5bf10a2 --- /dev/null +++ b/drivers/media/platform/qcom/venus/venc.c @@ -0,0 +1,1283 @@ +/* + * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * Copyright (C) 2017 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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 + +#include "hfi_venus_io.h" +#include "core.h" +#include "helpers.h" +#include "venc.h" + +#define NUM_B_FRAMES_MAX 4 + +static u32 get_framesize_uncompressed(unsigned int plane, u32 width, u32 height) +{ + u32 y_stride, uv_stride, y_plane; + u32 y_sclines, uv_sclines, uv_plane; + u32 size; + + y_stride = ALIGN(width, 128); + uv_stride = ALIGN(width, 128); + y_sclines = ALIGN(height, 32); + uv_sclines = ALIGN(((height + 1) >> 1), 16); + + y_plane = y_stride * y_sclines; + uv_plane = uv_stride * uv_sclines + SZ_4K; + size = y_plane + uv_plane + SZ_8K; + size = ALIGN(size, SZ_4K); + + return size; +} + +static u32 get_framesize_compressed(u32 width, u32 height) +{ + u32 sz = ALIGN(height, 32) * ALIGN(width, 32) * 3 / 2 / 2; + + return ALIGN(sz, SZ_4K); +} + +/* + * Three resons to keep MPLANE formats (despite that the number of planes + * currently is one): + * - the MPLANE formats allow only one plane to be used + * - the downstream driver use MPLANE formats too + * - future firmware versions could add support for >1 planes + */ +static const struct venus_format venc_formats[] = { + { + .pixfmt = V4L2_PIX_FMT_NV12, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, { + .pixfmt = V4L2_PIX_FMT_MPEG4, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + }, { + .pixfmt = V4L2_PIX_FMT_H263, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + }, { + .pixfmt = V4L2_PIX_FMT_H264, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + }, { + .pixfmt = V4L2_PIX_FMT_VP8, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + }, { + .pixfmt = V4L2_PIX_FMT_VP9, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + }, +}; + +static const struct venus_format *find_format(u32 pixfmt, u32 type) +{ + const struct venus_format *fmt = venc_formats; + unsigned int size = ARRAY_SIZE(venc_formats); + unsigned int i; + + for (i = 0; i < size; i++) { + if (fmt[i].pixfmt == pixfmt) + break; + } + + if (i == size || fmt[i].type != type) + return NULL; + + return &fmt[i]; +} + +static const struct venus_format * +find_format_by_index(unsigned int index, u32 type) +{ + const struct venus_format *fmt = venc_formats; + unsigned int size = ARRAY_SIZE(venc_formats); + unsigned int i, k = 0; + + if (index > size) + return NULL; + + for (i = 0; i < size; i++) { + if (fmt[i].type != type) + continue; + if (k == index) + break; + k++; + } + + if (i == size) + return NULL; + + return &fmt[i]; +} + +static int venc_v4l2_to_hfi(int id, int value) +{ + switch (id) { + case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: + switch (value) { + case V4L2_MPEG_VIDEO_MPEG4_LEVEL_0: + default: + return HFI_MPEG4_LEVEL_0; + case V4L2_MPEG_VIDEO_MPEG4_LEVEL_0B: + return HFI_MPEG4_LEVEL_0b; + case V4L2_MPEG_VIDEO_MPEG4_LEVEL_1: + return HFI_MPEG4_LEVEL_1; + case V4L2_MPEG_VIDEO_MPEG4_LEVEL_2: + return HFI_MPEG4_LEVEL_2; + case V4L2_MPEG_VIDEO_MPEG4_LEVEL_3: + return HFI_MPEG4_LEVEL_3; + case V4L2_MPEG_VIDEO_MPEG4_LEVEL_4: + return HFI_MPEG4_LEVEL_4; + case V4L2_MPEG_VIDEO_MPEG4_LEVEL_5: + return HFI_MPEG4_LEVEL_5; + } + case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: + switch (value) { + case V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE: + default: + return HFI_MPEG4_PROFILE_SIMPLE; + case V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE: + return HFI_MPEG4_PROFILE_ADVANCEDSIMPLE; + } + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: + switch (value) { + case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: + return HFI_H264_PROFILE_BASELINE; + case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE: + return HFI_H264_PROFILE_CONSTRAINED_BASE; + case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: + return HFI_H264_PROFILE_MAIN; + case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH: + default: + return HFI_H264_PROFILE_HIGH; + } + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + switch (value) { + case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: + return HFI_H264_LEVEL_1; + case V4L2_MPEG_VIDEO_H264_LEVEL_1B: + return HFI_H264_LEVEL_1b; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: + return HFI_H264_LEVEL_11; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: + return HFI_H264_LEVEL_12; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: + return HFI_H264_LEVEL_13; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: + return HFI_H264_LEVEL_2; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: + return HFI_H264_LEVEL_21; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: + return HFI_H264_LEVEL_22; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: + return HFI_H264_LEVEL_3; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: + return HFI_H264_LEVEL_31; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: + return HFI_H264_LEVEL_32; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: + return HFI_H264_LEVEL_4; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_1: + return HFI_H264_LEVEL_41; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_2: + return HFI_H264_LEVEL_42; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_0: + default: + return HFI_H264_LEVEL_5; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_1: + return HFI_H264_LEVEL_51; + } + case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE: + switch (value) { + case V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC: + default: + return HFI_H264_ENTROPY_CAVLC; + case V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC: + return HFI_H264_ENTROPY_CABAC; + } + case V4L2_CID_MPEG_VIDEO_VPX_PROFILE: + switch (value) { + case 0: + default: + return HFI_VPX_PROFILE_VERSION_0; + case 1: + return HFI_VPX_PROFILE_VERSION_1; + case 2: + return HFI_VPX_PROFILE_VERSION_2; + case 3: + return HFI_VPX_PROFILE_VERSION_3; + } + } + + return 0; +} + +static int +venc_querycap(struct file *file, void *fh, struct v4l2_capability *cap) +{ + strlcpy(cap->driver, "qcom-venus", sizeof(cap->driver)); + strlcpy(cap->card, "Qualcomm Venus video encoder", sizeof(cap->card)); + strlcpy(cap->bus_info, "platform:qcom-venus", sizeof(cap->bus_info)); + + return 0; +} + +static int venc_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) +{ + const struct venus_format *fmt; + + fmt = find_format_by_index(f->index, f->type); + + memset(f->reserved, 0, sizeof(f->reserved)); + + if (!fmt) + return -EINVAL; + + f->pixelformat = fmt->pixfmt; + + return 0; +} + +static const struct venus_format * +venc_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; + struct v4l2_plane_pix_format *pfmt = pixmp->plane_fmt; + const struct venus_format *fmt; + unsigned int p; + + memset(pfmt[0].reserved, 0, sizeof(pfmt[0].reserved)); + memset(pixmp->reserved, 0, sizeof(pixmp->reserved)); + + fmt = find_format(pixmp->pixelformat, f->type); + if (!fmt) { + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + pixmp->pixelformat = V4L2_PIX_FMT_H264; + else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + pixmp->pixelformat = V4L2_PIX_FMT_NV12; + else + return NULL; + fmt = find_format(pixmp->pixelformat, f->type); + pixmp->width = 1280; + pixmp->height = 720; + } + + pixmp->width = clamp(pixmp->width, inst->cap_width.min, + inst->cap_width.max); + pixmp->height = clamp(pixmp->height, inst->cap_height.min, + inst->cap_height.max); + + if (inst->core->res->hfi_version == HFI_VERSION_1XX) + pixmp->height = ALIGN(pixmp->height, 32); + + pixmp->width = ALIGN(pixmp->width, 2); + pixmp->height = ALIGN(pixmp->height, 2); + + if (pixmp->field == V4L2_FIELD_ANY) + pixmp->field = V4L2_FIELD_NONE; + pixmp->num_planes = fmt->num_planes; + pixmp->flags = 0; + + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + for (p = 0; p < pixmp->num_planes; p++) { + pfmt[p].sizeimage = + get_framesize_uncompressed(p, pixmp->width, + pixmp->height); + + pfmt[p].bytesperline = ALIGN(pixmp->width, 128); + } + } else { + pfmt[0].sizeimage = get_framesize_compressed(pixmp->width, + pixmp->height); + pfmt[0].bytesperline = 0; + } + + return fmt; +} + +static int venc_try_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct venus_inst *inst = to_inst(file); + + venc_try_fmt_common(inst, f); + + return 0; +} + +static int venc_s_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct venus_inst *inst = to_inst(file); + struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; + struct v4l2_pix_format_mplane orig_pixmp; + const struct venus_format *fmt; + struct v4l2_format format; + u32 pixfmt_out = 0, pixfmt_cap = 0; + + orig_pixmp = *pixmp; + + fmt = venc_try_fmt_common(inst, f); + if (!fmt) + return -EINVAL; + + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + pixfmt_out = pixmp->pixelformat; + pixfmt_cap = inst->fmt_cap->pixfmt; + } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + pixfmt_cap = pixmp->pixelformat; + pixfmt_out = inst->fmt_out->pixfmt; + } + + memset(&format, 0, sizeof(format)); + + format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + format.fmt.pix_mp.pixelformat = pixfmt_out; + format.fmt.pix_mp.width = orig_pixmp.width; + format.fmt.pix_mp.height = orig_pixmp.height; + venc_try_fmt_common(inst, &format); + + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + inst->out_width = format.fmt.pix_mp.width; + inst->out_height = format.fmt.pix_mp.height; + inst->colorspace = pixmp->colorspace; + inst->ycbcr_enc = pixmp->ycbcr_enc; + inst->quantization = pixmp->quantization; + inst->xfer_func = pixmp->xfer_func; + } + + memset(&format, 0, sizeof(format)); + + format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + format.fmt.pix_mp.pixelformat = pixfmt_cap; + format.fmt.pix_mp.width = orig_pixmp.width; + format.fmt.pix_mp.height = orig_pixmp.height; + venc_try_fmt_common(inst, &format); + + inst->width = format.fmt.pix_mp.width; + inst->height = format.fmt.pix_mp.height; + + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + inst->fmt_out = fmt; + else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + inst->fmt_cap = fmt; + + return 0; +} + +static int venc_g_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; + struct venus_inst *inst = to_inst(file); + const struct venus_format *fmt; + + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + fmt = inst->fmt_cap; + else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + fmt = inst->fmt_out; + else + return -EINVAL; + + pixmp->pixelformat = fmt->pixfmt; + + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + pixmp->width = inst->width; + pixmp->height = inst->height; + pixmp->colorspace = inst->colorspace; + pixmp->ycbcr_enc = inst->ycbcr_enc; + pixmp->quantization = inst->quantization; + pixmp->xfer_func = inst->xfer_func; + } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + pixmp->width = inst->out_width; + pixmp->height = inst->out_height; + } + + venc_try_fmt_common(inst, f); + + return 0; +} + +static int +venc_g_selection(struct file *file, void *fh, struct v4l2_selection *s) +{ + struct venus_inst *inst = to_inst(file); + + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + switch (s->target) { + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + s->r.width = inst->width; + s->r.height = inst->height; + break; + case V4L2_SEL_TGT_CROP: + s->r.width = inst->out_width; + s->r.height = inst->out_height; + break; + default: + return -EINVAL; + } + + s->r.top = 0; + s->r.left = 0; + + return 0; +} + +static int +venc_s_selection(struct file *file, void *fh, struct v4l2_selection *s) +{ + struct venus_inst *inst = to_inst(file); + + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + switch (s->target) { + case V4L2_SEL_TGT_CROP: + if (s->r.width != inst->out_width || + s->r.height != inst->out_height || + s->r.top != 0 || s->r.left != 0) + return -EINVAL; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int venc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct venus_inst *inst = to_inst(file); + struct v4l2_outputparm *out = &a->parm.output; + struct v4l2_fract *timeperframe = &out->timeperframe; + u64 us_per_frame, fps; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && + a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + return -EINVAL; + + memset(out->reserved, 0, sizeof(out->reserved)); + + if (!timeperframe->denominator) + timeperframe->denominator = inst->timeperframe.denominator; + if (!timeperframe->numerator) + timeperframe->numerator = inst->timeperframe.numerator; + + out->capability = V4L2_CAP_TIMEPERFRAME; + + us_per_frame = timeperframe->numerator * (u64)USEC_PER_SEC; + do_div(us_per_frame, timeperframe->denominator); + + if (!us_per_frame) + return -EINVAL; + + fps = (u64)USEC_PER_SEC; + do_div(fps, us_per_frame); + + inst->timeperframe = *timeperframe; + inst->fps = fps; + + return 0; +} + +static int venc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct venus_inst *inst = to_inst(file); + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && + a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + return -EINVAL; + + a->parm.output.capability |= V4L2_CAP_TIMEPERFRAME; + a->parm.output.timeperframe = inst->timeperframe; + + return 0; +} + +static int venc_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct venus_inst *inst = to_inst(file); + const struct venus_format *fmt; + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + + fmt = find_format(fsize->pixel_format, + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + if (!fmt) { + fmt = find_format(fsize->pixel_format, + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + if (!fmt) + return -EINVAL; + } + + if (fsize->index) + return -EINVAL; + + fsize->stepwise.min_width = inst->cap_width.min; + fsize->stepwise.max_width = inst->cap_width.max; + fsize->stepwise.step_width = inst->cap_width.step_size; + fsize->stepwise.min_height = inst->cap_height.min; + fsize->stepwise.max_height = inst->cap_height.max; + fsize->stepwise.step_height = inst->cap_height.step_size; + + return 0; +} + +static int venc_enum_frameintervals(struct file *file, void *fh, + struct v4l2_frmivalenum *fival) +{ + struct venus_inst *inst = to_inst(file); + const struct venus_format *fmt; + + fival->type = V4L2_FRMIVAL_TYPE_STEPWISE; + + fmt = find_format(fival->pixel_format, + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + if (!fmt) { + fmt = find_format(fival->pixel_format, + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + if (!fmt) + return -EINVAL; + } + + if (fival->index) + return -EINVAL; + + if (!fival->width || !fival->height) + return -EINVAL; + + if (fival->width > inst->cap_width.max || + fival->width < inst->cap_width.min || + fival->height > inst->cap_height.max || + fival->height < inst->cap_height.min) + return -EINVAL; + + fival->stepwise.min.numerator = 1; + fival->stepwise.min.denominator = inst->cap_framerate.max; + fival->stepwise.max.numerator = 1; + fival->stepwise.max.denominator = inst->cap_framerate.min; + fival->stepwise.step.numerator = 1; + fival->stepwise.step.denominator = inst->cap_framerate.max; + + return 0; +} + +static const struct v4l2_ioctl_ops venc_ioctl_ops = { + .vidioc_querycap = venc_querycap, + .vidioc_enum_fmt_vid_cap_mplane = venc_enum_fmt, + .vidioc_enum_fmt_vid_out_mplane = venc_enum_fmt, + .vidioc_s_fmt_vid_cap_mplane = venc_s_fmt, + .vidioc_s_fmt_vid_out_mplane = venc_s_fmt, + .vidioc_g_fmt_vid_cap_mplane = venc_g_fmt, + .vidioc_g_fmt_vid_out_mplane = venc_g_fmt, + .vidioc_try_fmt_vid_cap_mplane = venc_try_fmt, + .vidioc_try_fmt_vid_out_mplane = venc_try_fmt, + .vidioc_g_selection = venc_g_selection, + .vidioc_s_selection = venc_s_selection, + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + .vidioc_s_parm = venc_s_parm, + .vidioc_g_parm = venc_g_parm, + .vidioc_enum_framesizes = venc_enum_framesizes, + .vidioc_enum_frameintervals = venc_enum_frameintervals, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static int venc_set_properties(struct venus_inst *inst) +{ + struct venc_controls *ctr = &inst->controls.enc; + struct hfi_intra_period intra_period; + struct hfi_profile_level pl; + struct hfi_framerate frate; + struct hfi_bitrate brate; + struct hfi_idr_period idrp; + u32 ptype, rate_control, bitrate, profile = 0, level = 0; + int ret; + + ptype = HFI_PROPERTY_CONFIG_FRAME_RATE; + frate.buffer_type = HFI_BUFFER_OUTPUT; + frate.framerate = inst->fps * (1 << 16); + + ret = hfi_session_set_property(inst, ptype, &frate); + if (ret) + return ret; + + if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_H264) { + struct hfi_h264_vui_timing_info info; + + ptype = HFI_PROPERTY_PARAM_VENC_H264_VUI_TIMING_INFO; + info.enable = 1; + info.fixed_framerate = 1; + info.time_scale = NSEC_PER_SEC; + + ret = hfi_session_set_property(inst, ptype, &info); + if (ret) + return ret; + } + + ptype = HFI_PROPERTY_CONFIG_VENC_IDR_PERIOD; + idrp.idr_period = ctr->gop_size; + ret = hfi_session_set_property(inst, ptype, &idrp); + if (ret) + return ret; + + if (ctr->num_b_frames) { + u32 max_num_b_frames = NUM_B_FRAMES_MAX; + + ptype = HFI_PROPERTY_PARAM_VENC_MAX_NUM_B_FRAMES; + ret = hfi_session_set_property(inst, ptype, &max_num_b_frames); + if (ret) + return ret; + } + + /* intra_period = pframes + bframes + 1 */ + if (!ctr->num_p_frames) + ctr->num_p_frames = 2 * 15 - 1, + + ptype = HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD; + intra_period.pframes = ctr->num_p_frames; + intra_period.bframes = ctr->num_b_frames; + + ret = hfi_session_set_property(inst, ptype, &intra_period); + if (ret) + return ret; + + if (ctr->bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) + rate_control = HFI_RATE_CONTROL_VBR_CFR; + else + rate_control = HFI_RATE_CONTROL_CBR_CFR; + + ptype = HFI_PROPERTY_PARAM_VENC_RATE_CONTROL; + ret = hfi_session_set_property(inst, ptype, &rate_control); + if (ret) + return ret; + + if (!ctr->bitrate) + bitrate = 64000; + else + bitrate = ctr->bitrate; + + ptype = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE; + brate.bitrate = bitrate; + brate.layer_id = 0; + + ret = hfi_session_set_property(inst, ptype, &brate); + if (ret) + return ret; + + if (!ctr->bitrate_peak) + bitrate *= 2; + else + bitrate = ctr->bitrate_peak; + + ptype = HFI_PROPERTY_CONFIG_VENC_MAX_BITRATE; + brate.bitrate = bitrate; + brate.layer_id = 0; + + ret = hfi_session_set_property(inst, ptype, &brate); + if (ret) + return ret; + + if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_H264) { + profile = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_H264_PROFILE, + ctr->profile.h264); + level = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_H264_LEVEL, + ctr->level.h264); + } else if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_VP8) { + profile = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_VPX_PROFILE, + ctr->profile.vpx); + level = 0; + } else if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_MPEG4) { + profile = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE, + ctr->profile.mpeg4); + level = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL, + ctr->level.mpeg4); + } else if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_H263) { + profile = 0; + level = 0; + } + + ptype = HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT; + pl.profile = profile; + pl.level = level; + + ret = hfi_session_set_property(inst, ptype, &pl); + if (ret) + return ret; + + return 0; +} + +static int venc_init_session(struct venus_inst *inst) +{ + int ret; + + ret = hfi_session_init(inst, inst->fmt_cap->pixfmt); + if (ret) + return ret; + + ret = venus_helper_set_input_resolution(inst, inst->out_width, + inst->out_height); + if (ret) + goto deinit; + + ret = venus_helper_set_output_resolution(inst, inst->width, + inst->height); + if (ret) + goto deinit; + + ret = venus_helper_set_color_format(inst, inst->fmt_out->pixfmt); + if (ret) + goto deinit; + + return 0; +deinit: + hfi_session_deinit(inst); + return ret; +} + +static int venc_out_num_buffers(struct venus_inst *inst, unsigned int *num) +{ + struct hfi_buffer_requirements bufreq; + int ret; + + ret = venc_init_session(inst); + if (ret) + return ret; + + ret = venus_helper_get_bufreq(inst, HFI_BUFFER_INPUT, &bufreq); + + *num = bufreq.count_actual; + + hfi_session_deinit(inst); + + return ret; +} + +static int venc_queue_setup(struct vb2_queue *q, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct venus_inst *inst = vb2_get_drv_priv(q); + unsigned int p, num, min = 4; + int ret = 0; + + if (*num_planes) { + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && + *num_planes != inst->fmt_out->num_planes) + return -EINVAL; + + if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && + *num_planes != inst->fmt_cap->num_planes) + return -EINVAL; + + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && + sizes[0] < inst->input_buf_size) + return -EINVAL; + + if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && + sizes[0] < inst->output_buf_size) + return -EINVAL; + + return 0; + } + + switch (q->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + *num_planes = inst->fmt_out->num_planes; + + ret = venc_out_num_buffers(inst, &num); + if (ret) + break; + + num = max(num, min); + *num_buffers = max(*num_buffers, num); + inst->num_input_bufs = *num_buffers; + + for (p = 0; p < *num_planes; ++p) + sizes[p] = get_framesize_uncompressed(p, inst->width, + inst->height); + inst->input_buf_size = sizes[0]; + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + *num_planes = inst->fmt_cap->num_planes; + *num_buffers = max(*num_buffers, min); + inst->num_output_bufs = *num_buffers; + sizes[0] = get_framesize_compressed(inst->width, inst->height); + inst->output_buf_size = sizes[0]; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int venc_verify_conf(struct venus_inst *inst) +{ + struct hfi_buffer_requirements bufreq; + int ret; + + if (!inst->num_input_bufs || !inst->num_output_bufs) + return -EINVAL; + + ret = venus_helper_get_bufreq(inst, HFI_BUFFER_OUTPUT, &bufreq); + if (ret) + return ret; + + if (inst->num_output_bufs < bufreq.count_actual || + inst->num_output_bufs < bufreq.count_min) + return -EINVAL; + + ret = venus_helper_get_bufreq(inst, HFI_BUFFER_INPUT, &bufreq); + if (ret) + return ret; + + if (inst->num_input_bufs < bufreq.count_actual || + inst->num_input_bufs < bufreq.count_min) + return -EINVAL; + + return 0; +} + +static int venc_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct venus_inst *inst = vb2_get_drv_priv(q); + int ret; + + mutex_lock(&inst->lock); + + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + inst->streamon_out = 1; + else + inst->streamon_cap = 1; + + if (!(inst->streamon_out & inst->streamon_cap)) { + mutex_unlock(&inst->lock); + return 0; + } + + venus_helper_init_instance(inst); + + inst->sequence_cap = 0; + inst->sequence_out = 0; + + ret = venc_init_session(inst); + if (ret) + goto bufs_done; + + ret = venc_set_properties(inst); + if (ret) + goto deinit_sess; + + ret = venc_verify_conf(inst); + if (ret) + goto deinit_sess; + + ret = venus_helper_set_num_bufs(inst, inst->num_input_bufs, + inst->num_output_bufs); + if (ret) + goto deinit_sess; + + ret = venus_helper_vb2_start_streaming(inst); + if (ret) + goto deinit_sess; + + mutex_unlock(&inst->lock); + + return 0; + +deinit_sess: + hfi_session_deinit(inst); +bufs_done: + venus_helper_buffers_done(inst, VB2_BUF_STATE_QUEUED); + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + inst->streamon_out = 0; + else + inst->streamon_cap = 0; + mutex_unlock(&inst->lock); + return ret; +} + +static const struct vb2_ops venc_vb2_ops = { + .queue_setup = venc_queue_setup, + .buf_init = venus_helper_vb2_buf_init, + .buf_prepare = venus_helper_vb2_buf_prepare, + .start_streaming = venc_start_streaming, + .stop_streaming = venus_helper_vb2_stop_streaming, + .buf_queue = venus_helper_vb2_buf_queue, +}; + +static void venc_buf_done(struct venus_inst *inst, unsigned int buf_type, + u32 tag, u32 bytesused, u32 data_offset, u32 flags, + u32 hfi_flags, u64 timestamp_us) +{ + struct vb2_v4l2_buffer *vbuf; + struct vb2_buffer *vb; + unsigned int type; + + if (buf_type == HFI_BUFFER_INPUT) + type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + else + type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + + vbuf = venus_helper_find_buf(inst, type, tag); + if (!vbuf) + return; + + vb = &vbuf->vb2_buf; + vb->planes[0].bytesused = bytesused; + vb->planes[0].data_offset = data_offset; + + vbuf->flags = flags; + + if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + vb->timestamp = timestamp_us * NSEC_PER_USEC; + vbuf->sequence = inst->sequence_cap++; + } else { + vbuf->sequence = inst->sequence_out++; + } + + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE); +} + +static void venc_event_notify(struct venus_inst *inst, u32 event, + struct hfi_event_data *data) +{ + struct device *dev = inst->core->dev_enc; + + if (event == EVT_SESSION_ERROR) { + inst->session_error = true; + dev_err(dev, "enc: event session error %x\n", inst->error); + } +} + +static const struct hfi_inst_ops venc_hfi_ops = { + .buf_done = venc_buf_done, + .event_notify = venc_event_notify, +}; + +static const struct v4l2_m2m_ops venc_m2m_ops = { + .device_run = venus_helper_m2m_device_run, + .job_abort = venus_helper_m2m_job_abort, +}; + +static int m2m_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + struct venus_inst *inst = priv; + int ret; + + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->ops = &venc_vb2_ops; + src_vq->mem_ops = &vb2_dma_sg_memops; + src_vq->drv_priv = inst; + src_vq->buf_struct_size = sizeof(struct venus_buffer); + src_vq->allow_zero_bytesused = 1; + src_vq->min_buffers_needed = 1; + src_vq->dev = inst->core->dev; + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->ops = &venc_vb2_ops; + dst_vq->mem_ops = &vb2_dma_sg_memops; + dst_vq->drv_priv = inst; + dst_vq->buf_struct_size = sizeof(struct venus_buffer); + dst_vq->allow_zero_bytesused = 1; + dst_vq->min_buffers_needed = 1; + dst_vq->dev = inst->core->dev; + ret = vb2_queue_init(dst_vq); + if (ret) { + vb2_queue_release(src_vq); + return ret; + } + + return 0; +} + +static void venc_inst_init(struct venus_inst *inst) +{ + inst->fmt_cap = &venc_formats[2]; + inst->fmt_out = &venc_formats[0]; + inst->width = 1280; + inst->height = ALIGN(720, 32); + inst->out_width = 1280; + inst->out_height = 720; + inst->fps = 15; + inst->timeperframe.numerator = 1; + inst->timeperframe.denominator = 15; + + inst->cap_width.min = 96; + inst->cap_width.max = 1920; + if (inst->core->res->hfi_version == HFI_VERSION_3XX) + inst->cap_width.max = 3840; + inst->cap_width.step_size = 2; + inst->cap_height.min = 64; + inst->cap_height.max = ALIGN(1080, 32); + if (inst->core->res->hfi_version == HFI_VERSION_3XX) + inst->cap_height.max = ALIGN(2160, 32); + inst->cap_height.step_size = 2; + inst->cap_framerate.min = 1; + inst->cap_framerate.max = 30; + inst->cap_framerate.step_size = 1; + inst->cap_mbs_per_frame.min = 24; + inst->cap_mbs_per_frame.max = 8160; +} + +static int venc_open(struct file *file) +{ + struct venus_core *core = video_drvdata(file); + struct venus_inst *inst; + int ret; + + inst = kzalloc(sizeof(*inst), GFP_KERNEL); + if (!inst) + return -ENOMEM; + + INIT_LIST_HEAD(&inst->registeredbufs); + INIT_LIST_HEAD(&inst->internalbufs); + INIT_LIST_HEAD(&inst->list); + mutex_init(&inst->lock); + + inst->core = core; + inst->session_type = VIDC_SESSION_TYPE_ENC; + + venus_helper_init_instance(inst); + + ret = pm_runtime_get_sync(core->dev_enc); + if (ret < 0) + goto err_free_inst; + + ret = venc_ctrl_init(inst); + if (ret) + goto err_put_sync; + + ret = hfi_session_create(inst, &venc_hfi_ops); + if (ret) + goto err_ctrl_deinit; + + venc_inst_init(inst); + + /* + * create m2m device for every instance, the m2m context scheduling + * is made by firmware side so we do not need to care about. + */ + inst->m2m_dev = v4l2_m2m_init(&venc_m2m_ops); + if (IS_ERR(inst->m2m_dev)) { + ret = PTR_ERR(inst->m2m_dev); + goto err_session_destroy; + } + + inst->m2m_ctx = v4l2_m2m_ctx_init(inst->m2m_dev, inst, m2m_queue_init); + if (IS_ERR(inst->m2m_ctx)) { + ret = PTR_ERR(inst->m2m_ctx); + goto err_m2m_release; + } + + v4l2_fh_init(&inst->fh, core->vdev_enc); + + inst->fh.ctrl_handler = &inst->ctrl_handler; + v4l2_fh_add(&inst->fh); + inst->fh.m2m_ctx = inst->m2m_ctx; + file->private_data = &inst->fh; + + return 0; + +err_m2m_release: + v4l2_m2m_release(inst->m2m_dev); +err_session_destroy: + hfi_session_destroy(inst); +err_ctrl_deinit: + venc_ctrl_deinit(inst); +err_put_sync: + pm_runtime_put_sync(core->dev_enc); +err_free_inst: + kfree(inst); + return ret; +} + +static int venc_close(struct file *file) +{ + struct venus_inst *inst = to_inst(file); + + v4l2_m2m_ctx_release(inst->m2m_ctx); + v4l2_m2m_release(inst->m2m_dev); + venc_ctrl_deinit(inst); + hfi_session_destroy(inst); + mutex_destroy(&inst->lock); + v4l2_fh_del(&inst->fh); + v4l2_fh_exit(&inst->fh); + kfree(inst); + + pm_runtime_put_sync(inst->core->dev_enc); + + return 0; +} + +static const struct v4l2_file_operations venc_fops = { + .owner = THIS_MODULE, + .open = venc_open, + .release = venc_close, + .unlocked_ioctl = video_ioctl2, + .poll = v4l2_m2m_fop_poll, + .mmap = v4l2_m2m_fop_mmap, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = v4l2_compat_ioctl32, +#endif +}; + +static int venc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct video_device *vdev; + struct venus_core *core; + int ret; + + if (!dev->parent) + return -EPROBE_DEFER; + + core = dev_get_drvdata(dev->parent); + if (!core) + return -EPROBE_DEFER; + + if (core->res->hfi_version == HFI_VERSION_3XX) { + core->core1_clk = devm_clk_get(dev, "core"); + if (IS_ERR(core->core1_clk)) + return PTR_ERR(core->core1_clk); + } + + platform_set_drvdata(pdev, core); + + vdev = video_device_alloc(); + if (!vdev) + return -ENOMEM; + + vdev->release = video_device_release; + vdev->fops = &venc_fops; + vdev->ioctl_ops = &venc_ioctl_ops; + vdev->vfl_dir = VFL_DIR_M2M; + vdev->v4l2_dev = &core->v4l2_dev; + vdev->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; + + ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + if (ret) + goto err_vdev_release; + + core->vdev_enc = vdev; + core->dev_enc = dev; + + video_set_drvdata(vdev, core); + pm_runtime_enable(dev); + + return 0; + +err_vdev_release: + video_device_release(vdev); + return ret; +} + +static int venc_remove(struct platform_device *pdev) +{ + struct venus_core *core = dev_get_drvdata(pdev->dev.parent); + + video_unregister_device(core->vdev_enc); + pm_runtime_disable(core->dev_enc); + + return 0; +} + +#ifdef CONFIG_PM +static int venc_runtime_suspend(struct device *dev) +{ + struct venus_core *core = dev_get_drvdata(dev); + + if (core->res->hfi_version == HFI_VERSION_1XX) + return 0; + + writel(0, core->base + WRAPPER_VENC_VCODEC_POWER_CONTROL); + clk_disable_unprepare(core->core1_clk); + writel(1, core->base + WRAPPER_VENC_VCODEC_POWER_CONTROL); + + return 0; +} + +static int venc_runtime_resume(struct device *dev) +{ + struct venus_core *core = dev_get_drvdata(dev); + int ret; + + if (core->res->hfi_version == HFI_VERSION_1XX) + return 0; + + writel(0, core->base + WRAPPER_VENC_VCODEC_POWER_CONTROL); + ret = clk_prepare_enable(core->core1_clk); + writel(1, core->base + WRAPPER_VENC_VCODEC_POWER_CONTROL); + + return ret; +} +#endif + +static const struct dev_pm_ops venc_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(venc_runtime_suspend, venc_runtime_resume, NULL) +}; + +static const struct of_device_id venc_dt_match[] = { + { .compatible = "venus-encoder" }, + { } +}; +MODULE_DEVICE_TABLE(of, venc_dt_match); + +static struct platform_driver qcom_venus_enc_driver = { + .probe = venc_probe, + .remove = venc_remove, + .driver = { + .name = "qcom-venus-encoder", + .of_match_table = venc_dt_match, + .pm = &venc_pm_ops, + }, +}; +module_platform_driver(qcom_venus_enc_driver); + +MODULE_ALIAS("platform:qcom-venus-encoder"); +MODULE_DESCRIPTION("Qualcomm Venus video encoder driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/qcom/venus/venc.h b/drivers/media/platform/qcom/venus/venc.h new file mode 100644 index 000000000000..9daca669f307 --- /dev/null +++ b/drivers/media/platform/qcom/venus/venc.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * Copyright (C) 2017 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + * + */ +#ifndef __VENUS_VENC_H__ +#define __VENUS_VENC_H__ + +struct venus_inst; + +int venc_ctrl_init(struct venus_inst *inst); +void venc_ctrl_deinit(struct venus_inst *inst); + +#endif diff --git a/drivers/media/platform/qcom/venus/venc_ctrls.c b/drivers/media/platform/qcom/venus/venc_ctrls.c new file mode 100644 index 000000000000..ab0fe51ff0f7 --- /dev/null +++ b/drivers/media/platform/qcom/venus/venc_ctrls.c @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * Copyright (C) 2017 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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 "core.h" +#include "venc.h" + +#define BITRATE_MIN 32000 +#define BITRATE_MAX 160000000 +#define BITRATE_DEFAULT 1000000 +#define BITRATE_DEFAULT_PEAK (BITRATE_DEFAULT * 2) +#define BITRATE_STEP 100 +#define SLICE_BYTE_SIZE_MAX 1024 +#define SLICE_BYTE_SIZE_MIN 1024 +#define SLICE_MB_SIZE_MAX 300 +#define INTRA_REFRESH_MBS_MAX 300 +#define AT_SLICE_BOUNDARY \ + V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY + +static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct venus_inst *inst = ctrl_to_inst(ctrl); + struct venc_controls *ctr = &inst->controls.enc; + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + ctr->bitrate_mode = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE: + ctr->bitrate = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: + ctr->bitrate_peak = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE: + ctr->h264_entropy_mode = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: + ctr->profile.mpeg4 = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: + ctr->profile.h264 = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_VPX_PROFILE: + ctr->profile.vpx = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: + ctr->level.mpeg4 = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + ctr->level.h264 = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP: + ctr->h264_i_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP: + ctr->h264_p_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP: + ctr->h264_b_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: + ctr->h264_min_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: + ctr->h264_max_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: + ctr->multi_slice_mode = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES: + ctr->multi_slice_max_bytes = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB: + ctr->multi_slice_max_mb = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA: + ctr->h264_loop_filter_alpha = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA: + ctr->h264_loop_filter_beta = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE: + ctr->h264_loop_filter_mode = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEADER_MODE: + ctr->header_mode = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB: + break; + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + ctr->gop_size = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD: + ctr->h264_i_period = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_VPX_MIN_QP: + ctr->vp8_min_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_VPX_MAX_QP: + ctr->vp8_max_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_B_FRAMES: + ctr->num_b_frames = ctrl->val; + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct v4l2_ctrl_ops venc_ctrl_ops = { + .s_ctrl = venc_op_s_ctrl, +}; + +int venc_ctrl_init(struct venus_inst *inst) +{ + int ret; + + ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 27); + if (ret) + return ret; + + v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE_MODE, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, + ~((1 << V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) | + (1 << V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)), + V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); + + v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE, + V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC, + 0, V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC); + + v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE, + V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY, + ~((1 << V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE) | + (1 << V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE)), + V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE); + + v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL, + V4L2_MPEG_VIDEO_MPEG4_LEVEL_5, + 0, V4L2_MPEG_VIDEO_MPEG4_LEVEL_0); + + v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_PROFILE, + V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH, + ~((1 << V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) | + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE) | + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) | + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_HIGH) | + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH) | + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH)), + V4L2_MPEG_VIDEO_H264_PROFILE_HIGH); + + v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LEVEL, + V4L2_MPEG_VIDEO_H264_LEVEL_5_1, + 0, V4L2_MPEG_VIDEO_H264_LEVEL_1_0); + + v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE, + AT_SLICE_BOUNDARY, + 0, V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED); + + v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEADER_MODE, + V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME, + 1 << V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME, + V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE); + + v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE, + V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES, + 0, V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE, BITRATE_MIN, BITRATE_MAX, + BITRATE_STEP, BITRATE_DEFAULT); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, BITRATE_MIN, BITRATE_MAX, + BITRATE_STEP, BITRATE_DEFAULT_PEAK); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_VPX_PROFILE, 0, 3, 1, 0); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, 1, 51, 1, 26); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, 1, 51, 1, 28); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP, 1, 51, 1, 30); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_MIN_QP, 1, 51, 1, 1); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_MAX_QP, 1, 51, 1, 51); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES, SLICE_BYTE_SIZE_MIN, + SLICE_BYTE_SIZE_MAX, 1, SLICE_BYTE_SIZE_MIN); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB, 1, + SLICE_MB_SIZE_MAX, 1, 1); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA, -6, 6, 1, 0); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA, -6, 6, 1, 0); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB, + 0, INTRA_REFRESH_MBS_MAX, 1, 0); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_GOP_SIZE, 0, (1 << 16) - 1, 1, 12); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_VPX_MIN_QP, 1, 128, 1, 1); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_VPX_MAX_QP, 1, 128, 1, 128); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_B_FRAMES, 0, 4, 1, 0); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_I_PERIOD, 0, (1 << 16) - 1, 1, 0); + + ret = inst->ctrl_handler.error; + if (ret) + goto err; + + ret = v4l2_ctrl_handler_setup(&inst->ctrl_handler); + if (ret) + goto err; + + return 0; +err: + v4l2_ctrl_handler_free(&inst->ctrl_handler); + return ret; +} + +void venc_ctrl_deinit(struct venus_inst *inst) +{ + v4l2_ctrl_handler_free(&inst->ctrl_handler); +} From patchwork Thu Jun 15 16:31:49 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stanimir Varbanov X-Patchwork-Id: 105676 Delivered-To: patch@linaro.org Received: by 10.140.91.77 with SMTP id y71csp890124qgd; Thu, 15 Jun 2017 09:37:32 -0700 (PDT) X-Received: by 10.101.91.12 with SMTP id y12mr6224823pgq.44.1497544652049; Thu, 15 Jun 2017 09:37:32 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1497544652; cv=none; d=google.com; s=arc-20160816; b=Q43NP6VD/osMxofi69mKNRhhPFL0hwf/+HOTCG9054LZLMYYOrUW0irH4UyCPzJorn ojcxGo5EnER8tFxhUJfieeQmlzYHwWFHDE6R1lU1Fl90Ck0zMpsQUL6ZUhPTy1oTlMB6 boF09CZODSEnY8Iz9BklOutatEzU3feOjwifih1IkryOcL3fhf/PDJKoTgHqACRJj0Rf Y6Tund82bPi0bld2EMhzM7V5yZozxr8DxUoM1c8nyGDl+xKhL9bKY9JvEZu7Lz9beffx +T8jKXBbtNO6d93WP6+XlQcBVzxgha627puRQEWaTekGzAUm/pUACeMXSv2a4EdWyacW mnpw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:arc-authentication-results; bh=r1lsYKSb5EauUi7C5xPFOBEqB1AGBV1ufUMhOer4fqo=; b=EAlOFWXWMCHSKamS2JPPMzzPRFHEgqxGxfl84Yn+o0/OgOJwDSpqU3XqNhhZEBkjtD s9WWcXYf9JxkAO7skrmye8gt6dPdW+jeslXDp6g1Dyv56Wnu9aambawyuA2A6Rr+b1vx igtlYlJaff/6f2tevQfig4RfNrU16CgKPwMYzMatwv5lDQk/MpulaASZQfANOwxDWX3d TWaXChgGgH9p6pm3G+KBGA/ypufjWG9oAnDAQeBsdL3kngx0xFgo1f5miWvKpexHfZC2 aoqDN7TNjD5mxDUv7jtNPrCgYg1NTCSVD0t8gtzTeLvrC/FPY9WAZ2TkcARHrWx46no7 +fzg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.b=Y79Gw6OZ; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id e12si407721pga.332.2017.06.15.09.37.31; Thu, 15 Jun 2017 09:37:32 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.b=Y79Gw6OZ; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752515AbdFOQdL (ORCPT + 25 others); Thu, 15 Jun 2017 12:33:11 -0400 Received: from mail-wm0-f54.google.com ([74.125.82.54]:36633 "EHLO mail-wm0-f54.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752380AbdFOQdD (ORCPT ); Thu, 15 Jun 2017 12:33:03 -0400 Received: by mail-wm0-f54.google.com with SMTP id m125so4494957wmm.1 for ; Thu, 15 Jun 2017 09:33:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=r1lsYKSb5EauUi7C5xPFOBEqB1AGBV1ufUMhOer4fqo=; b=Y79Gw6OZ2afx689OUfebnuEkxfdMd9NFUMObVFDvMgRwd/DAbstSgQWhgzXTLJkd6G P/IUlCYFgYJY2wv/H40EfjzesVBIsQNhaWJO15wFsQoeZiZTfGKG7m5nzaHBWLYBajgR zm6UVqX4tTI2vAoGeqZDObZebqUhiuOL9h2vU= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=r1lsYKSb5EauUi7C5xPFOBEqB1AGBV1ufUMhOer4fqo=; b=QJz81SGdiMA9dP4t6e1S2yOFVsc4QjlB5ghIZtbQge/bQ6S/15zD+bjxzpbK7lr2h+ aMj/tnfV8CS2sQSnL3MOqHL6nXzkIhFFti7XruHPWjKFk1RK7Zqq/REIT07J3o3++X4k WSK0R0/Kf562Y4dTFTvbxhsGCUrA5WQTgV68nxqrtLSsfAMpDhFjzLkEWx/wqbRwcLdI C32SSK5RDvDa8AhmuUOwUrfA17jsqxfck0EysaI78EH8uvuDxRC/mCEU3uIRenIwOEfT FFPL3IGKZedDwkqj9YMahEF56fzV0AIZwMw/IDwBtK8D9vY2u71BzVdEHqa2BkOnGTxP 84fg== X-Gm-Message-State: AKS2vOzor/25Kg4SpZlhC2l1L/V6lzTfDXMsaEUs7JFiVlX8eUTTLVxN 8BxsaM/DI6d2Kg+QLM0Mfw== X-Received: by 10.28.146.12 with SMTP id u12mr4297158wmd.15.1497544380795; Thu, 15 Jun 2017 09:33:00 -0700 (PDT) Received: from mms-0440.qualcomm.mm-sol.com ([37.157.136.206]) by smtp.gmail.com with ESMTPSA id m73sm541890wmi.25.2017.06.15.09.32.59 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 15 Jun 2017 09:33:00 -0700 (PDT) From: Stanimir Varbanov To: Mauro Carvalho Chehab , Hans Verkuil Cc: linux-media@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, Stanimir Varbanov Subject: [PATCH v11 08/19] media: venus: hfi: add Venus HFI files Date: Thu, 15 Jun 2017 19:31:49 +0300 Message-Id: <1497544320-2269-9-git-send-email-stanimir.varbanov@linaro.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1497544320-2269-1-git-send-email-stanimir.varbanov@linaro.org> References: <1497544320-2269-1-git-send-email-stanimir.varbanov@linaro.org> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Here is the implementation of Venus video accelerator low-level functionality. It contanins code which setup the registers and startup uthe processor, allocate and manipulates with the shared memory used for sending commands and receiving messages. Signed-off-by: Stanimir Varbanov --- drivers/media/platform/qcom/venus/hfi_venus.c | 1571 ++++++++++++++++++++++ drivers/media/platform/qcom/venus/hfi_venus.h | 23 + drivers/media/platform/qcom/venus/hfi_venus_io.h | 113 ++ 3 files changed, 1707 insertions(+) create mode 100644 drivers/media/platform/qcom/venus/hfi_venus.c create mode 100644 drivers/media/platform/qcom/venus/hfi_venus.h create mode 100644 drivers/media/platform/qcom/venus/hfi_venus_io.h -- 2.7.4 diff --git a/drivers/media/platform/qcom/venus/hfi_venus.c b/drivers/media/platform/qcom/venus/hfi_venus.c new file mode 100644 index 000000000000..ab209f3d9498 --- /dev/null +++ b/drivers/media/platform/qcom/venus/hfi_venus.c @@ -0,0 +1,1571 @@ +/* + * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * Copyright (C) 2017 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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 "core.h" +#include "hfi_cmds.h" +#include "hfi_msgs.h" +#include "hfi_venus.h" +#include "hfi_venus_io.h" + +#define HFI_MASK_QHDR_TX_TYPE 0xff000000 +#define HFI_MASK_QHDR_RX_TYPE 0x00ff0000 +#define HFI_MASK_QHDR_PRI_TYPE 0x0000ff00 +#define HFI_MASK_QHDR_ID_TYPE 0x000000ff + +#define HFI_HOST_TO_CTRL_CMD_Q 0 +#define HFI_CTRL_TO_HOST_MSG_Q 1 +#define HFI_CTRL_TO_HOST_DBG_Q 2 +#define HFI_MASK_QHDR_STATUS 0x000000ff + +#define IFACEQ_NUM 3 +#define IFACEQ_CMD_IDX 0 +#define IFACEQ_MSG_IDX 1 +#define IFACEQ_DBG_IDX 2 +#define IFACEQ_MAX_BUF_COUNT 50 +#define IFACEQ_MAX_PARALLEL_CLNTS 16 +#define IFACEQ_DFLT_QHDR 0x01010000 + +#define POLL_INTERVAL_US 50 + +#define IFACEQ_MAX_PKT_SIZE 1024 +#define IFACEQ_MED_PKT_SIZE 768 +#define IFACEQ_MIN_PKT_SIZE 8 +#define IFACEQ_VAR_SMALL_PKT_SIZE 100 +#define IFACEQ_VAR_LARGE_PKT_SIZE 512 +#define IFACEQ_VAR_HUGE_PKT_SIZE (1024 * 12) + +enum tzbsp_video_state { + TZBSP_VIDEO_STATE_SUSPEND = 0, + TZBSP_VIDEO_STATE_RESUME +}; + +struct hfi_queue_table_header { + u32 version; + u32 size; + u32 qhdr0_offset; + u32 qhdr_size; + u32 num_q; + u32 num_active_q; +}; + +struct hfi_queue_header { + u32 status; + u32 start_addr; + u32 type; + u32 q_size; + u32 pkt_size; + u32 pkt_drop_cnt; + u32 rx_wm; + u32 tx_wm; + u32 rx_req; + u32 tx_req; + u32 rx_irq_status; + u32 tx_irq_status; + u32 read_idx; + u32 write_idx; +}; + +#define IFACEQ_TABLE_SIZE \ + (sizeof(struct hfi_queue_table_header) + \ + sizeof(struct hfi_queue_header) * IFACEQ_NUM) + +#define IFACEQ_QUEUE_SIZE (IFACEQ_MAX_PKT_SIZE * \ + IFACEQ_MAX_BUF_COUNT * IFACEQ_MAX_PARALLEL_CLNTS) + +#define IFACEQ_GET_QHDR_START_ADDR(ptr, i) \ + (void *)(((ptr) + sizeof(struct hfi_queue_table_header)) + \ + ((i) * sizeof(struct hfi_queue_header))) + +#define QDSS_SIZE SZ_4K +#define SFR_SIZE SZ_4K +#define QUEUE_SIZE \ + (IFACEQ_TABLE_SIZE + (IFACEQ_QUEUE_SIZE * IFACEQ_NUM)) + +#define ALIGNED_QDSS_SIZE ALIGN(QDSS_SIZE, SZ_4K) +#define ALIGNED_SFR_SIZE ALIGN(SFR_SIZE, SZ_4K) +#define ALIGNED_QUEUE_SIZE ALIGN(QUEUE_SIZE, SZ_4K) +#define SHARED_QSIZE ALIGN(ALIGNED_SFR_SIZE + ALIGNED_QUEUE_SIZE + \ + ALIGNED_QDSS_SIZE, SZ_1M) + +struct mem_desc { + dma_addr_t da; /* device address */ + void *kva; /* kernel virtual address */ + u32 size; + unsigned long attrs; +}; + +struct iface_queue { + struct hfi_queue_header *qhdr; + struct mem_desc qmem; +}; + +enum venus_state { + VENUS_STATE_DEINIT = 1, + VENUS_STATE_INIT, +}; + +struct venus_hfi_device { + struct venus_core *core; + u32 irq_status; + u32 last_packet_type; + bool power_enabled; + bool suspended; + enum venus_state state; + /* serialize read / write to the shared memory */ + struct mutex lock; + struct completion pwr_collapse_prep; + struct completion release_resource; + struct mem_desc ifaceq_table; + struct mem_desc sfr; + struct iface_queue queues[IFACEQ_NUM]; + u8 pkt_buf[IFACEQ_VAR_HUGE_PKT_SIZE]; + u8 dbg_buf[IFACEQ_VAR_HUGE_PKT_SIZE]; +}; + +static bool venus_pkt_debug; +static int venus_fw_debug = HFI_DEBUG_MSG_ERROR | HFI_DEBUG_MSG_FATAL; +static bool venus_sys_idle_indicator; +static bool venus_fw_low_power_mode = true; +static int venus_hw_rsp_timeout = 1000; +static bool venus_fw_coverage; + +static void venus_set_state(struct venus_hfi_device *hdev, + enum venus_state state) +{ + mutex_lock(&hdev->lock); + hdev->state = state; + mutex_unlock(&hdev->lock); +} + +static bool venus_is_valid_state(struct venus_hfi_device *hdev) +{ + return hdev->state != VENUS_STATE_DEINIT; +} + +static void venus_dump_packet(struct venus_hfi_device *hdev, const void *packet) +{ + size_t pkt_size = *(u32 *)packet; + + if (!venus_pkt_debug) + return; + + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 16, 1, packet, + pkt_size, true); +} + +static int venus_write_queue(struct venus_hfi_device *hdev, + struct iface_queue *queue, + void *packet, u32 *rx_req) +{ + struct hfi_queue_header *qhdr; + u32 dwords, new_wr_idx; + u32 empty_space, rd_idx, wr_idx, qsize; + u32 *wr_ptr; + + if (!queue->qmem.kva) + return -EINVAL; + + qhdr = queue->qhdr; + if (!qhdr) + return -EINVAL; + + venus_dump_packet(hdev, packet); + + dwords = (*(u32 *)packet) >> 2; + if (!dwords) + return -EINVAL; + + rd_idx = qhdr->read_idx; + wr_idx = qhdr->write_idx; + qsize = qhdr->q_size; + /* ensure rd/wr indices's are read from memory */ + rmb(); + + if (wr_idx >= rd_idx) + empty_space = qsize - (wr_idx - rd_idx); + else + empty_space = rd_idx - wr_idx; + + if (empty_space <= dwords) { + qhdr->tx_req = 1; + /* ensure tx_req is updated in memory */ + wmb(); + return -ENOSPC; + } + + qhdr->tx_req = 0; + /* ensure tx_req is updated in memory */ + wmb(); + + new_wr_idx = wr_idx + dwords; + wr_ptr = (u32 *)(queue->qmem.kva + (wr_idx << 2)); + if (new_wr_idx < qsize) { + memcpy(wr_ptr, packet, dwords << 2); + } else { + size_t len; + + new_wr_idx -= qsize; + len = (dwords - new_wr_idx) << 2; + memcpy(wr_ptr, packet, len); + memcpy(queue->qmem.kva, packet + len, new_wr_idx << 2); + } + + /* make sure packet is written before updating the write index */ + wmb(); + + qhdr->write_idx = new_wr_idx; + *rx_req = qhdr->rx_req ? 1 : 0; + + /* make sure write index is updated before an interrupt is raised */ + mb(); + + return 0; +} + +static int venus_read_queue(struct venus_hfi_device *hdev, + struct iface_queue *queue, void *pkt, u32 *tx_req) +{ + struct hfi_queue_header *qhdr; + u32 dwords, new_rd_idx; + u32 rd_idx, wr_idx, type, qsize; + u32 *rd_ptr; + u32 recv_request = 0; + int ret = 0; + + if (!queue->qmem.kva) + return -EINVAL; + + qhdr = queue->qhdr; + if (!qhdr) + return -EINVAL; + + type = qhdr->type; + rd_idx = qhdr->read_idx; + wr_idx = qhdr->write_idx; + qsize = qhdr->q_size; + + /* make sure data is valid before using it */ + rmb(); + + /* + * Do not set receive request for debug queue, if set, Venus generates + * interrupt for debug messages even when there is no response message + * available. In general debug queue will not become full as it is being + * emptied out for every interrupt from Venus. Venus will anyway + * generates interrupt if it is full. + */ + if (type & HFI_CTRL_TO_HOST_MSG_Q) + recv_request = 1; + + if (rd_idx == wr_idx) { + qhdr->rx_req = recv_request; + *tx_req = 0; + /* update rx_req field in memory */ + wmb(); + return -ENODATA; + } + + rd_ptr = (u32 *)(queue->qmem.kva + (rd_idx << 2)); + dwords = *rd_ptr >> 2; + if (!dwords) + return -EINVAL; + + new_rd_idx = rd_idx + dwords; + if (((dwords << 2) <= IFACEQ_VAR_HUGE_PKT_SIZE) && rd_idx <= qsize) { + if (new_rd_idx < qsize) { + memcpy(pkt, rd_ptr, dwords << 2); + } else { + size_t len; + + new_rd_idx -= qsize; + len = (dwords - new_rd_idx) << 2; + memcpy(pkt, rd_ptr, len); + memcpy(pkt + len, queue->qmem.kva, new_rd_idx << 2); + } + } else { + /* bad packet received, dropping */ + new_rd_idx = qhdr->write_idx; + ret = -EBADMSG; + } + + /* ensure the packet is read before updating read index */ + rmb(); + + qhdr->read_idx = new_rd_idx; + /* ensure updating read index */ + wmb(); + + rd_idx = qhdr->read_idx; + wr_idx = qhdr->write_idx; + /* ensure rd/wr indices are read from memory */ + rmb(); + + if (rd_idx != wr_idx) + qhdr->rx_req = 0; + else + qhdr->rx_req = recv_request; + + *tx_req = qhdr->tx_req ? 1 : 0; + + /* ensure rx_req is stored to memory and tx_req is loaded from memory */ + mb(); + + venus_dump_packet(hdev, pkt); + + return ret; +} + +static int venus_alloc(struct venus_hfi_device *hdev, struct mem_desc *desc, + u32 size) +{ + struct device *dev = hdev->core->dev; + + desc->attrs = DMA_ATTR_WRITE_COMBINE; + desc->size = ALIGN(size, SZ_4K); + + desc->kva = dma_alloc_attrs(dev, size, &desc->da, GFP_KERNEL, + desc->attrs); + if (!desc->kva) + return -ENOMEM; + + return 0; +} + +static void venus_free(struct venus_hfi_device *hdev, struct mem_desc *mem) +{ + struct device *dev = hdev->core->dev; + + dma_free_attrs(dev, mem->size, mem->kva, mem->da, mem->attrs); +} + +static void venus_writel(struct venus_hfi_device *hdev, u32 reg, u32 value) +{ + writel(value, hdev->core->base + reg); +} + +static u32 venus_readl(struct venus_hfi_device *hdev, u32 reg) +{ + return readl(hdev->core->base + reg); +} + +static void venus_set_registers(struct venus_hfi_device *hdev) +{ + const struct venus_resources *res = hdev->core->res; + const struct reg_val *tbl = res->reg_tbl; + unsigned int count = res->reg_tbl_size; + unsigned int i; + + for (i = 0; i < count; i++) + venus_writel(hdev, tbl[i].reg, tbl[i].value); +} + +static void venus_soft_int(struct venus_hfi_device *hdev) +{ + venus_writel(hdev, CPU_IC_SOFTINT, BIT(CPU_IC_SOFTINT_H2A_SHIFT)); +} + +static int venus_iface_cmdq_write_nolock(struct venus_hfi_device *hdev, + void *pkt) +{ + struct device *dev = hdev->core->dev; + struct hfi_pkt_hdr *cmd_packet; + struct iface_queue *queue; + u32 rx_req; + int ret; + + if (!venus_is_valid_state(hdev)) + return -EINVAL; + + cmd_packet = (struct hfi_pkt_hdr *)pkt; + hdev->last_packet_type = cmd_packet->pkt_type; + + queue = &hdev->queues[IFACEQ_CMD_IDX]; + + ret = venus_write_queue(hdev, queue, pkt, &rx_req); + if (ret) { + dev_err(dev, "write to iface cmd queue failed (%d)\n", ret); + return ret; + } + + if (rx_req) + venus_soft_int(hdev); + + return 0; +} + +static int venus_iface_cmdq_write(struct venus_hfi_device *hdev, void *pkt) +{ + int ret; + + mutex_lock(&hdev->lock); + ret = venus_iface_cmdq_write_nolock(hdev, pkt); + mutex_unlock(&hdev->lock); + + return ret; +} + +static int venus_hfi_core_set_resource(struct venus_core *core, u32 id, + u32 size, u32 addr, void *cookie) +{ + struct venus_hfi_device *hdev = to_hfi_priv(core); + struct hfi_sys_set_resource_pkt *pkt; + u8 packet[IFACEQ_VAR_SMALL_PKT_SIZE]; + int ret; + + if (id == VIDC_RESOURCE_NONE) + return 0; + + pkt = (struct hfi_sys_set_resource_pkt *)packet; + + ret = pkt_sys_set_resource(pkt, id, size, addr, cookie); + if (ret) + return ret; + + ret = venus_iface_cmdq_write(hdev, pkt); + if (ret) + return ret; + + return 0; +} + +static int venus_boot_core(struct venus_hfi_device *hdev) +{ + struct device *dev = hdev->core->dev; + static const unsigned int max_tries = 100; + u32 ctrl_status = 0; + unsigned int count = 0; + int ret = 0; + + venus_writel(hdev, VIDC_CTRL_INIT, BIT(VIDC_CTRL_INIT_CTRL_SHIFT)); + venus_writel(hdev, WRAPPER_INTR_MASK, WRAPPER_INTR_MASK_A2HVCODEC_MASK); + venus_writel(hdev, CPU_CS_SCIACMDARG3, 1); + + while (!ctrl_status && count < max_tries) { + ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0); + if ((ctrl_status & CPU_CS_SCIACMDARG0_ERROR_STATUS_MASK) == 4) { + dev_err(dev, "invalid setting for UC_REGION\n"); + ret = -EINVAL; + break; + } + + usleep_range(500, 1000); + count++; + } + + if (count >= max_tries) + ret = -ETIMEDOUT; + + return ret; +} + +static u32 venus_hwversion(struct venus_hfi_device *hdev) +{ + struct device *dev = hdev->core->dev; + u32 ver = venus_readl(hdev, WRAPPER_HW_VERSION); + u32 major, minor, step; + + major = ver & WRAPPER_HW_VERSION_MAJOR_VERSION_MASK; + major = major >> WRAPPER_HW_VERSION_MAJOR_VERSION_SHIFT; + minor = ver & WRAPPER_HW_VERSION_MINOR_VERSION_MASK; + minor = minor >> WRAPPER_HW_VERSION_MINOR_VERSION_SHIFT; + step = ver & WRAPPER_HW_VERSION_STEP_VERSION_MASK; + + dev_dbg(dev, "venus hw version %x.%x.%x\n", major, minor, step); + + return major; +} + +static int venus_run(struct venus_hfi_device *hdev) +{ + struct device *dev = hdev->core->dev; + int ret; + + /* + * Re-program all of the registers that get reset as a result of + * regulator_disable() and _enable() + */ + venus_set_registers(hdev); + + venus_writel(hdev, UC_REGION_ADDR, hdev->ifaceq_table.da); + venus_writel(hdev, UC_REGION_SIZE, SHARED_QSIZE); + venus_writel(hdev, CPU_CS_SCIACMDARG2, hdev->ifaceq_table.da); + venus_writel(hdev, CPU_CS_SCIACMDARG1, 0x01); + if (hdev->sfr.da) + venus_writel(hdev, SFR_ADDR, hdev->sfr.da); + + ret = venus_boot_core(hdev); + if (ret) { + dev_err(dev, "failed to reset venus core\n"); + return ret; + } + + venus_hwversion(hdev); + + return 0; +} + +static int venus_halt_axi(struct venus_hfi_device *hdev) +{ + void __iomem *base = hdev->core->base; + struct device *dev = hdev->core->dev; + u32 val; + int ret; + + /* Halt AXI and AXI IMEM VBIF Access */ + val = venus_readl(hdev, VBIF_AXI_HALT_CTRL0); + val |= VBIF_AXI_HALT_CTRL0_HALT_REQ; + venus_writel(hdev, VBIF_AXI_HALT_CTRL0, val); + + /* Request for AXI bus port halt */ + ret = readl_poll_timeout(base + VBIF_AXI_HALT_CTRL1, val, + val & VBIF_AXI_HALT_CTRL1_HALT_ACK, + POLL_INTERVAL_US, + VBIF_AXI_HALT_ACK_TIMEOUT_US); + if (ret) { + dev_err(dev, "AXI bus port halt timeout\n"); + return ret; + } + + return 0; +} + +static int venus_power_off(struct venus_hfi_device *hdev) +{ + int ret; + + if (!hdev->power_enabled) + return 0; + + ret = qcom_scm_set_remote_state(TZBSP_VIDEO_STATE_SUSPEND, 0); + if (ret) + return ret; + + ret = venus_halt_axi(hdev); + if (ret) + return ret; + + hdev->power_enabled = false; + + return 0; +} + +static int venus_power_on(struct venus_hfi_device *hdev) +{ + int ret; + + if (hdev->power_enabled) + return 0; + + ret = qcom_scm_set_remote_state(TZBSP_VIDEO_STATE_RESUME, 0); + if (ret) + goto err; + + ret = venus_run(hdev); + if (ret) + goto err_suspend; + + hdev->power_enabled = true; + + return 0; + +err_suspend: + qcom_scm_set_remote_state(TZBSP_VIDEO_STATE_SUSPEND, 0); +err: + hdev->power_enabled = false; + return ret; +} + +static int venus_iface_msgq_read_nolock(struct venus_hfi_device *hdev, + void *pkt) +{ + struct iface_queue *queue; + u32 tx_req; + int ret; + + if (!venus_is_valid_state(hdev)) + return -EINVAL; + + queue = &hdev->queues[IFACEQ_MSG_IDX]; + + ret = venus_read_queue(hdev, queue, pkt, &tx_req); + if (ret) + return ret; + + if (tx_req) + venus_soft_int(hdev); + + return 0; +} + +static int venus_iface_msgq_read(struct venus_hfi_device *hdev, void *pkt) +{ + int ret; + + mutex_lock(&hdev->lock); + ret = venus_iface_msgq_read_nolock(hdev, pkt); + mutex_unlock(&hdev->lock); + + return ret; +} + +static int venus_iface_dbgq_read_nolock(struct venus_hfi_device *hdev, + void *pkt) +{ + struct iface_queue *queue; + u32 tx_req; + int ret; + + ret = venus_is_valid_state(hdev); + if (!ret) + return -EINVAL; + + queue = &hdev->queues[IFACEQ_DBG_IDX]; + + ret = venus_read_queue(hdev, queue, pkt, &tx_req); + if (ret) + return ret; + + if (tx_req) + venus_soft_int(hdev); + + return 0; +} + +static int venus_iface_dbgq_read(struct venus_hfi_device *hdev, void *pkt) +{ + int ret; + + if (!pkt) + return -EINVAL; + + mutex_lock(&hdev->lock); + ret = venus_iface_dbgq_read_nolock(hdev, pkt); + mutex_unlock(&hdev->lock); + + return ret; +} + +static void venus_set_qhdr_defaults(struct hfi_queue_header *qhdr) +{ + qhdr->status = 1; + qhdr->type = IFACEQ_DFLT_QHDR; + qhdr->q_size = IFACEQ_QUEUE_SIZE / 4; + qhdr->pkt_size = 0; + qhdr->rx_wm = 1; + qhdr->tx_wm = 1; + qhdr->rx_req = 1; + qhdr->tx_req = 0; + qhdr->rx_irq_status = 0; + qhdr->tx_irq_status = 0; + qhdr->read_idx = 0; + qhdr->write_idx = 0; +} + +static void venus_interface_queues_release(struct venus_hfi_device *hdev) +{ + mutex_lock(&hdev->lock); + + venus_free(hdev, &hdev->ifaceq_table); + venus_free(hdev, &hdev->sfr); + + memset(hdev->queues, 0, sizeof(hdev->queues)); + memset(&hdev->ifaceq_table, 0, sizeof(hdev->ifaceq_table)); + memset(&hdev->sfr, 0, sizeof(hdev->sfr)); + + mutex_unlock(&hdev->lock); +} + +static int venus_interface_queues_init(struct venus_hfi_device *hdev) +{ + struct hfi_queue_table_header *tbl_hdr; + struct iface_queue *queue; + struct hfi_sfr *sfr; + struct mem_desc desc = {0}; + unsigned int offset; + unsigned int i; + int ret; + + ret = venus_alloc(hdev, &desc, ALIGNED_QUEUE_SIZE); + if (ret) + return ret; + + hdev->ifaceq_table.kva = desc.kva; + hdev->ifaceq_table.da = desc.da; + hdev->ifaceq_table.size = IFACEQ_TABLE_SIZE; + offset = hdev->ifaceq_table.size; + + for (i = 0; i < IFACEQ_NUM; i++) { + queue = &hdev->queues[i]; + queue->qmem.da = desc.da + offset; + queue->qmem.kva = desc.kva + offset; + queue->qmem.size = IFACEQ_QUEUE_SIZE; + offset += queue->qmem.size; + queue->qhdr = + IFACEQ_GET_QHDR_START_ADDR(hdev->ifaceq_table.kva, i); + + venus_set_qhdr_defaults(queue->qhdr); + + queue->qhdr->start_addr = queue->qmem.da; + + if (i == IFACEQ_CMD_IDX) + queue->qhdr->type |= HFI_HOST_TO_CTRL_CMD_Q; + else if (i == IFACEQ_MSG_IDX) + queue->qhdr->type |= HFI_CTRL_TO_HOST_MSG_Q; + else if (i == IFACEQ_DBG_IDX) + queue->qhdr->type |= HFI_CTRL_TO_HOST_DBG_Q; + } + + tbl_hdr = hdev->ifaceq_table.kva; + tbl_hdr->version = 0; + tbl_hdr->size = IFACEQ_TABLE_SIZE; + tbl_hdr->qhdr0_offset = sizeof(struct hfi_queue_table_header); + tbl_hdr->qhdr_size = sizeof(struct hfi_queue_header); + tbl_hdr->num_q = IFACEQ_NUM; + tbl_hdr->num_active_q = IFACEQ_NUM; + + /* + * Set receive request to zero on debug queue as there is no + * need of interrupt from video hardware for debug messages + */ + queue = &hdev->queues[IFACEQ_DBG_IDX]; + queue->qhdr->rx_req = 0; + + ret = venus_alloc(hdev, &desc, ALIGNED_SFR_SIZE); + if (ret) { + hdev->sfr.da = 0; + } else { + hdev->sfr.da = desc.da; + hdev->sfr.kva = desc.kva; + hdev->sfr.size = ALIGNED_SFR_SIZE; + sfr = hdev->sfr.kva; + sfr->buf_size = ALIGNED_SFR_SIZE; + } + + /* ensure table and queue header structs are settled in memory */ + wmb(); + + return 0; +} + +static int venus_sys_set_debug(struct venus_hfi_device *hdev, u32 debug) +{ + struct hfi_sys_set_property_pkt *pkt; + u8 packet[IFACEQ_VAR_SMALL_PKT_SIZE]; + int ret; + + pkt = (struct hfi_sys_set_property_pkt *)packet; + + pkt_sys_debug_config(pkt, HFI_DEBUG_MODE_QUEUE, debug); + + ret = venus_iface_cmdq_write(hdev, pkt); + if (ret) + return ret; + + return 0; +} + +static int venus_sys_set_coverage(struct venus_hfi_device *hdev, u32 mode) +{ + struct hfi_sys_set_property_pkt *pkt; + u8 packet[IFACEQ_VAR_SMALL_PKT_SIZE]; + int ret; + + pkt = (struct hfi_sys_set_property_pkt *)packet; + + pkt_sys_coverage_config(pkt, mode); + + ret = venus_iface_cmdq_write(hdev, pkt); + if (ret) + return ret; + + return 0; +} + +static int venus_sys_set_idle_message(struct venus_hfi_device *hdev, + bool enable) +{ + struct hfi_sys_set_property_pkt *pkt; + u8 packet[IFACEQ_VAR_SMALL_PKT_SIZE]; + int ret; + + if (!enable) + return 0; + + pkt = (struct hfi_sys_set_property_pkt *)packet; + + pkt_sys_idle_indicator(pkt, enable); + + ret = venus_iface_cmdq_write(hdev, pkt); + if (ret) + return ret; + + return 0; +} + +static int venus_sys_set_power_control(struct venus_hfi_device *hdev, + bool enable) +{ + struct hfi_sys_set_property_pkt *pkt; + u8 packet[IFACEQ_VAR_SMALL_PKT_SIZE]; + int ret; + + pkt = (struct hfi_sys_set_property_pkt *)packet; + + pkt_sys_power_control(pkt, enable); + + ret = venus_iface_cmdq_write(hdev, pkt); + if (ret) + return ret; + + return 0; +} + +static int venus_get_queue_size(struct venus_hfi_device *hdev, + unsigned int index) +{ + struct hfi_queue_header *qhdr; + + if (index >= IFACEQ_NUM) + return -EINVAL; + + qhdr = hdev->queues[index].qhdr; + if (!qhdr) + return -EINVAL; + + return abs(qhdr->read_idx - qhdr->write_idx); +} + +static int venus_sys_set_default_properties(struct venus_hfi_device *hdev) +{ + struct device *dev = hdev->core->dev; + int ret; + + ret = venus_sys_set_debug(hdev, venus_fw_debug); + if (ret) + dev_warn(dev, "setting fw debug msg ON failed (%d)\n", ret); + + ret = venus_sys_set_idle_message(hdev, venus_sys_idle_indicator); + if (ret) + dev_warn(dev, "setting idle response ON failed (%d)\n", ret); + + ret = venus_sys_set_power_control(hdev, venus_fw_low_power_mode); + if (ret) + dev_warn(dev, "setting hw power collapse ON failed (%d)\n", + ret); + + return ret; +} + +static int venus_session_cmd(struct venus_inst *inst, u32 pkt_type) +{ + struct venus_hfi_device *hdev = to_hfi_priv(inst->core); + struct hfi_session_pkt pkt; + + pkt_session_cmd(&pkt, pkt_type, inst); + + return venus_iface_cmdq_write(hdev, &pkt); +} + +static void venus_flush_debug_queue(struct venus_hfi_device *hdev) +{ + struct device *dev = hdev->core->dev; + void *packet = hdev->dbg_buf; + + while (!venus_iface_dbgq_read(hdev, packet)) { + struct hfi_msg_sys_coverage_pkt *pkt = packet; + + if (pkt->hdr.pkt_type != HFI_MSG_SYS_COV) { + struct hfi_msg_sys_debug_pkt *pkt = packet; + + dev_dbg(dev, "%s", pkt->msg_data); + } + } +} + +static int venus_prepare_power_collapse(struct venus_hfi_device *hdev, + bool wait) +{ + unsigned long timeout = msecs_to_jiffies(venus_hw_rsp_timeout); + struct hfi_sys_pc_prep_pkt pkt; + int ret; + + init_completion(&hdev->pwr_collapse_prep); + + pkt_sys_pc_prep(&pkt); + + ret = venus_iface_cmdq_write(hdev, &pkt); + if (ret) + return ret; + + if (!wait) + return 0; + + ret = wait_for_completion_timeout(&hdev->pwr_collapse_prep, timeout); + if (!ret) { + venus_flush_debug_queue(hdev); + return -ETIMEDOUT; + } + + return 0; +} + +static int venus_are_queues_empty(struct venus_hfi_device *hdev) +{ + int ret1, ret2; + + ret1 = venus_get_queue_size(hdev, IFACEQ_MSG_IDX); + if (ret1 < 0) + return ret1; + + ret2 = venus_get_queue_size(hdev, IFACEQ_CMD_IDX); + if (ret2 < 0) + return ret2; + + if (!ret1 && !ret2) + return 1; + + return 0; +} + +static void venus_sfr_print(struct venus_hfi_device *hdev) +{ + struct device *dev = hdev->core->dev; + struct hfi_sfr *sfr = hdev->sfr.kva; + void *p; + + if (!sfr) + return; + + p = memchr(sfr->data, '\0', sfr->buf_size); + /* + * SFR isn't guaranteed to be NULL terminated since SYS_ERROR indicates + * that Venus is in the process of crashing. + */ + if (!p) + sfr->data[sfr->buf_size - 1] = '\0'; + + dev_err_ratelimited(dev, "SFR message from FW: %s\n", sfr->data); +} + +static void venus_process_msg_sys_error(struct venus_hfi_device *hdev, + void *packet) +{ + struct hfi_msg_event_notify_pkt *event_pkt = packet; + + if (event_pkt->event_id != HFI_EVENT_SYS_ERROR) + return; + + venus_set_state(hdev, VENUS_STATE_DEINIT); + + /* + * Once SYS_ERROR received from HW, it is safe to halt the AXI. + * With SYS_ERROR, Venus FW may have crashed and HW might be + * active and causing unnecessary transactions. Hence it is + * safe to stop all AXI transactions from venus subsystem. + */ + venus_halt_axi(hdev); + venus_sfr_print(hdev); +} + +static irqreturn_t venus_isr_thread(struct venus_core *core) +{ + struct venus_hfi_device *hdev = to_hfi_priv(core); + const struct venus_resources *res = hdev->core->res; + void *pkt; + u32 msg_ret; + + if (!hdev) + return IRQ_NONE; + + pkt = hdev->pkt_buf; + + if (hdev->irq_status & WRAPPER_INTR_STATUS_A2HWD_MASK) { + venus_sfr_print(hdev); + hfi_process_watchdog_timeout(core); + } + + while (!venus_iface_msgq_read(hdev, pkt)) { + msg_ret = hfi_process_msg_packet(core, pkt); + switch (msg_ret) { + case HFI_MSG_EVENT_NOTIFY: + venus_process_msg_sys_error(hdev, pkt); + break; + case HFI_MSG_SYS_INIT: + venus_hfi_core_set_resource(core, res->vmem_id, + res->vmem_size, + res->vmem_addr, + hdev); + break; + case HFI_MSG_SYS_RELEASE_RESOURCE: + complete(&hdev->release_resource); + break; + case HFI_MSG_SYS_PC_PREP: + complete(&hdev->pwr_collapse_prep); + break; + default: + break; + } + } + + venus_flush_debug_queue(hdev); + + return IRQ_HANDLED; +} + +static irqreturn_t venus_isr(struct venus_core *core) +{ + struct venus_hfi_device *hdev = to_hfi_priv(core); + u32 status; + + if (!hdev) + return IRQ_NONE; + + status = venus_readl(hdev, WRAPPER_INTR_STATUS); + + if (status & WRAPPER_INTR_STATUS_A2H_MASK || + status & WRAPPER_INTR_STATUS_A2HWD_MASK || + status & CPU_CS_SCIACMDARG0_INIT_IDLE_MSG_MASK) + hdev->irq_status = status; + + venus_writel(hdev, CPU_CS_A2HSOFTINTCLR, 1); + venus_writel(hdev, WRAPPER_INTR_CLEAR, status); + + return IRQ_WAKE_THREAD; +} + +static int venus_core_init(struct venus_core *core) +{ + struct venus_hfi_device *hdev = to_hfi_priv(core); + struct device *dev = core->dev; + struct hfi_sys_get_property_pkt version_pkt; + struct hfi_sys_init_pkt pkt; + int ret; + + pkt_sys_init(&pkt, HFI_VIDEO_ARCH_OX); + + venus_set_state(hdev, VENUS_STATE_INIT); + + ret = venus_iface_cmdq_write(hdev, &pkt); + if (ret) + return ret; + + pkt_sys_image_version(&version_pkt); + + ret = venus_iface_cmdq_write(hdev, &version_pkt); + if (ret) + dev_warn(dev, "failed to send image version pkt to fw\n"); + + return 0; +} + +static int venus_core_deinit(struct venus_core *core) +{ + struct venus_hfi_device *hdev = to_hfi_priv(core); + + venus_set_state(hdev, VENUS_STATE_DEINIT); + hdev->suspended = true; + hdev->power_enabled = false; + + return 0; +} + +static int venus_core_ping(struct venus_core *core, u32 cookie) +{ + struct venus_hfi_device *hdev = to_hfi_priv(core); + struct hfi_sys_ping_pkt pkt; + + pkt_sys_ping(&pkt, cookie); + + return venus_iface_cmdq_write(hdev, &pkt); +} + +static int venus_core_trigger_ssr(struct venus_core *core, u32 trigger_type) +{ + struct venus_hfi_device *hdev = to_hfi_priv(core); + struct hfi_sys_test_ssr_pkt pkt; + int ret; + + ret = pkt_sys_ssr_cmd(&pkt, trigger_type); + if (ret) + return ret; + + return venus_iface_cmdq_write(hdev, &pkt); +} + +static int venus_session_init(struct venus_inst *inst, u32 session_type, + u32 codec) +{ + struct venus_hfi_device *hdev = to_hfi_priv(inst->core); + struct hfi_session_init_pkt pkt; + int ret; + + ret = venus_sys_set_default_properties(hdev); + if (ret) + return ret; + + ret = pkt_session_init(&pkt, inst, session_type, codec); + if (ret) + goto err; + + ret = venus_iface_cmdq_write(hdev, &pkt); + if (ret) + goto err; + + return 0; + +err: + venus_flush_debug_queue(hdev); + return ret; +} + +static int venus_session_end(struct venus_inst *inst) +{ + struct venus_hfi_device *hdev = to_hfi_priv(inst->core); + struct device *dev = hdev->core->dev; + + if (venus_fw_coverage) { + if (venus_sys_set_coverage(hdev, venus_fw_coverage)) + dev_warn(dev, "fw coverage msg ON failed\n"); + } + + return venus_session_cmd(inst, HFI_CMD_SYS_SESSION_END); +} + +static int venus_session_abort(struct venus_inst *inst) +{ + struct venus_hfi_device *hdev = to_hfi_priv(inst->core); + + venus_flush_debug_queue(hdev); + + return venus_session_cmd(inst, HFI_CMD_SYS_SESSION_ABORT); +} + +static int venus_session_flush(struct venus_inst *inst, u32 flush_mode) +{ + struct venus_hfi_device *hdev = to_hfi_priv(inst->core); + struct hfi_session_flush_pkt pkt; + int ret; + + ret = pkt_session_flush(&pkt, inst, flush_mode); + if (ret) + return ret; + + return venus_iface_cmdq_write(hdev, &pkt); +} + +static int venus_session_start(struct venus_inst *inst) +{ + return venus_session_cmd(inst, HFI_CMD_SESSION_START); +} + +static int venus_session_stop(struct venus_inst *inst) +{ + return venus_session_cmd(inst, HFI_CMD_SESSION_STOP); +} + +static int venus_session_continue(struct venus_inst *inst) +{ + return venus_session_cmd(inst, HFI_CMD_SESSION_CONTINUE); +} + +static int venus_session_etb(struct venus_inst *inst, + struct hfi_frame_data *in_frame) +{ + struct venus_hfi_device *hdev = to_hfi_priv(inst->core); + u32 session_type = inst->session_type; + int ret; + + if (session_type == VIDC_SESSION_TYPE_DEC) { + struct hfi_session_empty_buffer_compressed_pkt pkt; + + ret = pkt_session_etb_decoder(&pkt, inst, in_frame); + if (ret) + return ret; + + ret = venus_iface_cmdq_write(hdev, &pkt); + } else if (session_type == VIDC_SESSION_TYPE_ENC) { + struct hfi_session_empty_buffer_uncompressed_plane0_pkt pkt; + + ret = pkt_session_etb_encoder(&pkt, inst, in_frame); + if (ret) + return ret; + + ret = venus_iface_cmdq_write(hdev, &pkt); + } else { + ret = -EINVAL; + } + + return ret; +} + +static int venus_session_ftb(struct venus_inst *inst, + struct hfi_frame_data *out_frame) +{ + struct venus_hfi_device *hdev = to_hfi_priv(inst->core); + struct hfi_session_fill_buffer_pkt pkt; + int ret; + + ret = pkt_session_ftb(&pkt, inst, out_frame); + if (ret) + return ret; + + return venus_iface_cmdq_write(hdev, &pkt); +} + +static int venus_session_set_buffers(struct venus_inst *inst, + struct hfi_buffer_desc *bd) +{ + struct venus_hfi_device *hdev = to_hfi_priv(inst->core); + struct hfi_session_set_buffers_pkt *pkt; + u8 packet[IFACEQ_VAR_LARGE_PKT_SIZE]; + int ret; + + if (bd->buffer_type == HFI_BUFFER_INPUT) + return 0; + + pkt = (struct hfi_session_set_buffers_pkt *)packet; + + ret = pkt_session_set_buffers(pkt, inst, bd); + if (ret) + return ret; + + return venus_iface_cmdq_write(hdev, pkt); +} + +static int venus_session_unset_buffers(struct venus_inst *inst, + struct hfi_buffer_desc *bd) +{ + struct venus_hfi_device *hdev = to_hfi_priv(inst->core); + struct hfi_session_release_buffer_pkt *pkt; + u8 packet[IFACEQ_VAR_LARGE_PKT_SIZE]; + int ret; + + if (bd->buffer_type == HFI_BUFFER_INPUT) + return 0; + + pkt = (struct hfi_session_release_buffer_pkt *)packet; + + ret = pkt_session_unset_buffers(pkt, inst, bd); + if (ret) + return ret; + + return venus_iface_cmdq_write(hdev, pkt); +} + +static int venus_session_load_res(struct venus_inst *inst) +{ + return venus_session_cmd(inst, HFI_CMD_SESSION_LOAD_RESOURCES); +} + +static int venus_session_release_res(struct venus_inst *inst) +{ + return venus_session_cmd(inst, HFI_CMD_SESSION_RELEASE_RESOURCES); +} + +static int venus_session_parse_seq_hdr(struct venus_inst *inst, u32 seq_hdr, + u32 seq_hdr_len) +{ + struct venus_hfi_device *hdev = to_hfi_priv(inst->core); + struct hfi_session_parse_sequence_header_pkt *pkt; + u8 packet[IFACEQ_VAR_SMALL_PKT_SIZE]; + int ret; + + pkt = (struct hfi_session_parse_sequence_header_pkt *)packet; + + ret = pkt_session_parse_seq_header(pkt, inst, seq_hdr, seq_hdr_len); + if (ret) + return ret; + + ret = venus_iface_cmdq_write(hdev, pkt); + if (ret) + return ret; + + return 0; +} + +static int venus_session_get_seq_hdr(struct venus_inst *inst, u32 seq_hdr, + u32 seq_hdr_len) +{ + struct venus_hfi_device *hdev = to_hfi_priv(inst->core); + struct hfi_session_get_sequence_header_pkt *pkt; + u8 packet[IFACEQ_VAR_SMALL_PKT_SIZE]; + int ret; + + pkt = (struct hfi_session_get_sequence_header_pkt *)packet; + + ret = pkt_session_get_seq_hdr(pkt, inst, seq_hdr, seq_hdr_len); + if (ret) + return ret; + + return venus_iface_cmdq_write(hdev, pkt); +} + +static int venus_session_set_property(struct venus_inst *inst, u32 ptype, + void *pdata) +{ + struct venus_hfi_device *hdev = to_hfi_priv(inst->core); + struct hfi_session_set_property_pkt *pkt; + u8 packet[IFACEQ_VAR_LARGE_PKT_SIZE]; + int ret; + + pkt = (struct hfi_session_set_property_pkt *)packet; + + ret = pkt_session_set_property(pkt, inst, ptype, pdata); + if (ret) + return ret; + + return venus_iface_cmdq_write(hdev, pkt); +} + +static int venus_session_get_property(struct venus_inst *inst, u32 ptype) +{ + struct venus_hfi_device *hdev = to_hfi_priv(inst->core); + struct hfi_session_get_property_pkt pkt; + int ret; + + ret = pkt_session_get_property(&pkt, inst, ptype); + if (ret) + return ret; + + return venus_iface_cmdq_write(hdev, &pkt); +} + +static int venus_resume(struct venus_core *core) +{ + struct venus_hfi_device *hdev = to_hfi_priv(core); + int ret = 0; + + mutex_lock(&hdev->lock); + + if (!hdev->suspended) + goto unlock; + + ret = venus_power_on(hdev); + +unlock: + if (!ret) + hdev->suspended = false; + + mutex_unlock(&hdev->lock); + + return ret; +} + +static int venus_suspend_1xx(struct venus_core *core) +{ + struct venus_hfi_device *hdev = to_hfi_priv(core); + struct device *dev = core->dev; + u32 ctrl_status; + int ret; + + if (!hdev->power_enabled || hdev->suspended) + return 0; + + mutex_lock(&hdev->lock); + ret = venus_is_valid_state(hdev); + mutex_unlock(&hdev->lock); + + if (!ret) { + dev_err(dev, "bad state, cannot suspend\n"); + return -EINVAL; + } + + ret = venus_prepare_power_collapse(hdev, true); + if (ret) { + dev_err(dev, "prepare for power collapse fail (%d)\n", ret); + return ret; + } + + mutex_lock(&hdev->lock); + + if (hdev->last_packet_type != HFI_CMD_SYS_PC_PREP) { + mutex_unlock(&hdev->lock); + return -EINVAL; + } + + ret = venus_are_queues_empty(hdev); + if (ret < 0 || !ret) { + mutex_unlock(&hdev->lock); + return -EINVAL; + } + + ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0); + if (!(ctrl_status & CPU_CS_SCIACMDARG0_PC_READY)) { + mutex_unlock(&hdev->lock); + return -EINVAL; + } + + ret = venus_power_off(hdev); + if (ret) { + mutex_unlock(&hdev->lock); + return ret; + } + + hdev->suspended = true; + + mutex_unlock(&hdev->lock); + + return 0; +} + +static int venus_suspend_3xx(struct venus_core *core) +{ + struct venus_hfi_device *hdev = to_hfi_priv(core); + struct device *dev = core->dev; + u32 ctrl_status, wfi_status; + int ret; + int cnt = 100; + + if (!hdev->power_enabled || hdev->suspended) + return 0; + + mutex_lock(&hdev->lock); + ret = venus_is_valid_state(hdev); + mutex_unlock(&hdev->lock); + + if (!ret) { + dev_err(dev, "bad state, cannot suspend\n"); + return -EINVAL; + } + + ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0); + if (!(ctrl_status & CPU_CS_SCIACMDARG0_PC_READY)) { + wfi_status = venus_readl(hdev, WRAPPER_CPU_STATUS); + ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0); + + ret = venus_prepare_power_collapse(hdev, false); + if (ret) { + dev_err(dev, "prepare for power collapse fail (%d)\n", + ret); + return ret; + } + + cnt = 100; + while (cnt--) { + wfi_status = venus_readl(hdev, WRAPPER_CPU_STATUS); + ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0); + if (ctrl_status & CPU_CS_SCIACMDARG0_PC_READY && + wfi_status & BIT(0)) + break; + usleep_range(1000, 1500); + } + } + + mutex_lock(&hdev->lock); + + ret = venus_power_off(hdev); + if (ret) { + dev_err(dev, "venus_power_off (%d)\n", ret); + mutex_unlock(&hdev->lock); + return ret; + } + + hdev->suspended = true; + + mutex_unlock(&hdev->lock); + + return 0; +} + +static int venus_suspend(struct venus_core *core) +{ + if (core->res->hfi_version == HFI_VERSION_3XX) + return venus_suspend_3xx(core); + + return venus_suspend_1xx(core); +} + +static const struct hfi_ops venus_hfi_ops = { + .core_init = venus_core_init, + .core_deinit = venus_core_deinit, + .core_ping = venus_core_ping, + .core_trigger_ssr = venus_core_trigger_ssr, + + .session_init = venus_session_init, + .session_end = venus_session_end, + .session_abort = venus_session_abort, + .session_flush = venus_session_flush, + .session_start = venus_session_start, + .session_stop = venus_session_stop, + .session_continue = venus_session_continue, + .session_etb = venus_session_etb, + .session_ftb = venus_session_ftb, + .session_set_buffers = venus_session_set_buffers, + .session_unset_buffers = venus_session_unset_buffers, + .session_load_res = venus_session_load_res, + .session_release_res = venus_session_release_res, + .session_parse_seq_hdr = venus_session_parse_seq_hdr, + .session_get_seq_hdr = venus_session_get_seq_hdr, + .session_set_property = venus_session_set_property, + .session_get_property = venus_session_get_property, + + .resume = venus_resume, + .suspend = venus_suspend, + + .isr = venus_isr, + .isr_thread = venus_isr_thread, +}; + +void venus_hfi_destroy(struct venus_core *core) +{ + struct venus_hfi_device *hdev = to_hfi_priv(core); + + venus_interface_queues_release(hdev); + mutex_destroy(&hdev->lock); + kfree(hdev); + core->priv = NULL; + core->ops = NULL; +} + +int venus_hfi_create(struct venus_core *core) +{ + struct venus_hfi_device *hdev; + int ret; + + hdev = kzalloc(sizeof(*hdev), GFP_KERNEL); + if (!hdev) + return -ENOMEM; + + mutex_init(&hdev->lock); + + hdev->core = core; + hdev->suspended = true; + core->priv = hdev; + core->ops = &venus_hfi_ops; + core->core_caps = ENC_ROTATION_CAPABILITY | ENC_SCALING_CAPABILITY | + ENC_DEINTERLACE_CAPABILITY | + DEC_MULTI_STREAM_CAPABILITY; + + ret = venus_interface_queues_init(hdev); + if (ret) + goto err_kfree; + + return 0; + +err_kfree: + kfree(hdev); + core->priv = NULL; + core->ops = NULL; + return ret; +} diff --git a/drivers/media/platform/qcom/venus/hfi_venus.h b/drivers/media/platform/qcom/venus/hfi_venus.h new file mode 100644 index 000000000000..885923354033 --- /dev/null +++ b/drivers/media/platform/qcom/venus/hfi_venus.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * Copyright (C) 2017 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + * + */ +#ifndef __VENUS_HFI_VENUS_H__ +#define __VENUS_HFI_VENUS_H__ + +struct venus_core; + +void venus_hfi_destroy(struct venus_core *core); +int venus_hfi_create(struct venus_core *core); + +#endif diff --git a/drivers/media/platform/qcom/venus/hfi_venus_io.h b/drivers/media/platform/qcom/venus/hfi_venus_io.h new file mode 100644 index 000000000000..98cc350113ab --- /dev/null +++ b/drivers/media/platform/qcom/venus/hfi_venus_io.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * Copyright (C) 2017 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + * + */ +#ifndef __VENUS_HFI_VENUS_IO_H__ +#define __VENUS_HFI_VENUS_IO_H__ + +#define VBIF_BASE 0x80000 + +#define VBIF_AXI_HALT_CTRL0 (VBIF_BASE + 0x208) +#define VBIF_AXI_HALT_CTRL1 (VBIF_BASE + 0x20c) + +#define VBIF_AXI_HALT_CTRL0_HALT_REQ BIT(0) +#define VBIF_AXI_HALT_CTRL1_HALT_ACK BIT(0) +#define VBIF_AXI_HALT_ACK_TIMEOUT_US 500000 + +#define CPU_BASE 0xc0000 +#define CPU_CS_BASE (CPU_BASE + 0x12000) +#define CPU_IC_BASE (CPU_BASE + 0x1f000) + +#define CPU_CS_A2HSOFTINTCLR (CPU_CS_BASE + 0x1c) + +#define VIDC_CTRL_INIT (CPU_CS_BASE + 0x48) +#define VIDC_CTRL_INIT_RESERVED_BITS31_1_MASK 0xfffffffe +#define VIDC_CTRL_INIT_RESERVED_BITS31_1_SHIFT 1 +#define VIDC_CTRL_INIT_CTRL_MASK 0x1 +#define VIDC_CTRL_INIT_CTRL_SHIFT 0 + +/* HFI control status */ +#define CPU_CS_SCIACMDARG0 (CPU_CS_BASE + 0x4c) +#define CPU_CS_SCIACMDARG0_MASK 0xff +#define CPU_CS_SCIACMDARG0_SHIFT 0x0 +#define CPU_CS_SCIACMDARG0_ERROR_STATUS_MASK 0xfe +#define CPU_CS_SCIACMDARG0_ERROR_STATUS_SHIFT 0x1 +#define CPU_CS_SCIACMDARG0_INIT_STATUS_MASK 0x1 +#define CPU_CS_SCIACMDARG0_INIT_STATUS_SHIFT 0x0 +#define CPU_CS_SCIACMDARG0_PC_READY BIT(8) +#define CPU_CS_SCIACMDARG0_INIT_IDLE_MSG_MASK BIT(30) + +/* HFI queue table info */ +#define CPU_CS_SCIACMDARG1 (CPU_CS_BASE + 0x50) + +/* HFI queue table address */ +#define CPU_CS_SCIACMDARG2 (CPU_CS_BASE + 0x54) + +/* Venus cpu */ +#define CPU_CS_SCIACMDARG3 (CPU_CS_BASE + 0x58) + +#define SFR_ADDR (CPU_CS_BASE + 0x5c) +#define MMAP_ADDR (CPU_CS_BASE + 0x60) +#define UC_REGION_ADDR (CPU_CS_BASE + 0x64) +#define UC_REGION_SIZE (CPU_CS_BASE + 0x68) + +#define CPU_IC_SOFTINT (CPU_IC_BASE + 0x18) +#define CPU_IC_SOFTINT_H2A_MASK 0x8000 +#define CPU_IC_SOFTINT_H2A_SHIFT 0xf + +/* Venus wrapper */ +#define WRAPPER_BASE 0x000e0000 + +#define WRAPPER_HW_VERSION (WRAPPER_BASE + 0x00) +#define WRAPPER_HW_VERSION_MAJOR_VERSION_MASK 0x78000000 +#define WRAPPER_HW_VERSION_MAJOR_VERSION_SHIFT 28 +#define WRAPPER_HW_VERSION_MINOR_VERSION_MASK 0xfff0000 +#define WRAPPER_HW_VERSION_MINOR_VERSION_SHIFT 16 +#define WRAPPER_HW_VERSION_STEP_VERSION_MASK 0xffff + +#define WRAPPER_CLOCK_CONFIG (WRAPPER_BASE + 0x04) + +#define WRAPPER_INTR_STATUS (WRAPPER_BASE + 0x0c) +#define WRAPPER_INTR_STATUS_A2HWD_MASK 0x10 +#define WRAPPER_INTR_STATUS_A2HWD_SHIFT 0x4 +#define WRAPPER_INTR_STATUS_A2H_MASK 0x4 +#define WRAPPER_INTR_STATUS_A2H_SHIFT 0x2 + +#define WRAPPER_INTR_MASK (WRAPPER_BASE + 0x10) +#define WRAPPER_INTR_MASK_A2HWD_BASK 0x10 +#define WRAPPER_INTR_MASK_A2HWD_SHIFT 0x4 +#define WRAPPER_INTR_MASK_A2HVCODEC_MASK 0x8 +#define WRAPPER_INTR_MASK_A2HVCODEC_SHIFT 0x3 +#define WRAPPER_INTR_MASK_A2HCPU_MASK 0x4 +#define WRAPPER_INTR_MASK_A2HCPU_SHIFT 0x2 + +#define WRAPPER_INTR_CLEAR (WRAPPER_BASE + 0x14) +#define WRAPPER_INTR_CLEAR_A2HWD_MASK 0x10 +#define WRAPPER_INTR_CLEAR_A2HWD_SHIFT 0x4 +#define WRAPPER_INTR_CLEAR_A2H_MASK 0x4 +#define WRAPPER_INTR_CLEAR_A2H_SHIFT 0x2 + +#define WRAPPER_POWER_STATUS (WRAPPER_BASE + 0x44) +#define WRAPPER_VDEC_VCODEC_POWER_CONTROL (WRAPPER_BASE + 0x48) +#define WRAPPER_VENC_VCODEC_POWER_CONTROL (WRAPPER_BASE + 0x4c) +#define WRAPPER_VDEC_VENC_AHB_BRIDGE_SYNC_RESET (WRAPPER_BASE + 0x64) + +#define WRAPPER_CPU_CLOCK_CONFIG (WRAPPER_BASE + 0x2000) +#define WRAPPER_CPU_AXI_HALT (WRAPPER_BASE + 0x2008) +#define WRAPPER_CPU_AXI_HALT_STATUS (WRAPPER_BASE + 0x200c) + +#define WRAPPER_CPU_CGC_DIS (WRAPPER_BASE + 0x2010) +#define WRAPPER_CPU_STATUS (WRAPPER_BASE + 0x2014) +#define WRAPPER_SW_RESET (WRAPPER_BASE + 0x3000) + +#endif From patchwork Thu Jun 15 16:31:51 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stanimir Varbanov X-Patchwork-Id: 105677 Delivered-To: patch@linaro.org Received: by 10.140.91.77 with SMTP id y71csp890271qgd; Thu, 15 Jun 2017 09:37:49 -0700 (PDT) X-Received: by 10.84.129.99 with SMTP id 90mr7177534plb.62.1497544669258; Thu, 15 Jun 2017 09:37:49 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1497544669; cv=none; d=google.com; s=arc-20160816; b=UWGIdBouf8hZl1XY0RvVl/r+8ER3tEXQ3eqOwD4CRKX/Y70HUR6olvB7DuOsUfhr4o bDwHwri00qPDwjjirjHDc1umYabhPNQdHnQ222fp65v2dy7wtYC05CvDMPJHKsE5BB8r h30/hPoVdIY5CauupG4TYG9qr2AMHgbeyft/LVGV4tn1FNt4imG+k3XkIIOjOLdpC+Cm Xlf6mOK0TxEtRYOHyaz+TpnuWhvh7/B65bvVYcuC6zjib28GjE5C0J/+YWsDiM1sdT/q ARGdaMl3MGQEBlztkW8jKqHOgG8DA4woQgkseF3WhYkLNYLrhFRWa1jgrfNpsmP7YPld DYSA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:arc-authentication-results; bh=cxypuHuowLRmyRmOB/X98Wse1XczZCK9O7RkdojKQbc=; b=FDHWBpUzcPZeRij90JOucMNdUuO5H7W7v62dH56cth+BfJ0yPXVxopeKSk4pZYc3tG v/xlfU/zqvDsVj49bLFmwHZ3CzxpjnP+WNGeOJgInz1Y+RxvuPyvN5v6su9HjPEFwHJA udBpViqlEGyyosskmy5g0HxEMbbfHlh56ZUoItHBQfGliz3y6anhi5PquMQsoTIA57EG qz/GZtfoFKaRTwYUiD4OJOzRGRejQNKTzd1HCfu6NkUoB1xNSr3uZj+jRuTIFr8BkEKq Cnm+nRv+nsu02/e99ki3gqLpWOdPSUKwmUeGm0hilvxrEvTkzOfxKWsLYadnF+BE7NAv tnNw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.b=j+EixUfw; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id f23si392313pli.265.2017.06.15.09.37.48; Thu, 15 Jun 2017 09:37:49 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.b=j+EixUfw; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752580AbdFOQhQ (ORCPT + 25 others); Thu, 15 Jun 2017 12:37:16 -0400 Received: from mail-wr0-f174.google.com ([209.85.128.174]:35878 "EHLO mail-wr0-f174.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752514AbdFOQdM (ORCPT ); Thu, 15 Jun 2017 12:33:12 -0400 Received: by mail-wr0-f174.google.com with SMTP id 36so25247357wry.3 for ; Thu, 15 Jun 2017 09:33:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=cxypuHuowLRmyRmOB/X98Wse1XczZCK9O7RkdojKQbc=; b=j+EixUfwqKxQ5e4uTbHs8vcwQQDUOLjv8y4KscknCll0+96Zu7jI7dfl3eyRCuYmDg 2IZvH+cSpLB68ciwio6fssLvDRyIkUDlS5xPaA3F4d+BygWEnUnSGCmHUj5U5jx0aBpf h6AsGJzblApOr6uttP9o7pqHTI+mq0ugLelE4= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=cxypuHuowLRmyRmOB/X98Wse1XczZCK9O7RkdojKQbc=; b=Ma9Xb730RsI8GPZSUU6GVVvPTQAy9U8ErQWaFuuWARhADE1QHnPHyL8nTyQbEGBQI+ cE9rZj+0NXUy3PmemChfioi1FLspTj+AepRF0+3SpAmn6Besto5nwkCGJyp+FX03ZYQB VqQoue9AtCrqCqkBUHt6EBgwLCHMafCPxNcohFwX1KTIWf/iwjx/I0alT2IjgBUv9ZK6 6vJHZHIxmFCWace7xLOE1PnRqYtVjdVb4nUgxi8Ik1EKKDgP/JmN3LlVkE3g2fWGfg9c YccbMGsP6M2rq1REm5idpdmvixfOt694hgvWFtg1ljEsvAhWO3ESb5pWItTqEql215Kl XmUg== X-Gm-Message-State: AKS2vOxv3WFg26XMFnzjHi0s1ho1spF6eGed2CbIILyrBhXjdn1esnyn MpIKCDx0j5sAkVEG X-Received: by 10.223.128.65 with SMTP id 59mr4650308wrk.0.1497544385713; Thu, 15 Jun 2017 09:33:05 -0700 (PDT) Received: from mms-0440.qualcomm.mm-sol.com ([37.157.136.206]) by smtp.gmail.com with ESMTPSA id m73sm541890wmi.25.2017.06.15.09.33.04 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 15 Jun 2017 09:33:05 -0700 (PDT) From: Stanimir Varbanov To: Mauro Carvalho Chehab , Hans Verkuil Cc: linux-media@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, Stanimir Varbanov Subject: [PATCH v11 10/19] media: venus: hfi: fix mutex unlock Date: Thu, 15 Jun 2017 19:31:51 +0300 Message-Id: <1497544320-2269-11-git-send-email-stanimir.varbanov@linaro.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1497544320-2269-1-git-send-email-stanimir.varbanov@linaro.org> References: <1497544320-2269-1-git-send-email-stanimir.varbanov@linaro.org> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org This fixed a warning when build driver with gcc7: drivers/media/platform/qcom/venus/hfi.c:171 hfi_core_ping() warn: inconsistent returns 'mutex:&core->lock'. Locked on: line 159 Unlocked on: line 171 Signed-off-by: Stanimir Varbanov --- drivers/media/platform/qcom/venus/hfi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) -- 2.7.4 diff --git a/drivers/media/platform/qcom/venus/hfi.c b/drivers/media/platform/qcom/venus/hfi.c index 20c9205fdbb4..c09490876516 100644 --- a/drivers/media/platform/qcom/venus/hfi.c +++ b/drivers/media/platform/qcom/venus/hfi.c @@ -156,7 +156,7 @@ int hfi_core_ping(struct venus_core *core) ret = core->ops->core_ping(core, 0xbeef); if (ret) - return ret; + goto unlock; ret = wait_for_completion_timeout(&core->done, TIMEOUT); if (!ret) { From patchwork Thu Jun 15 16:31:54 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stanimir Varbanov X-Patchwork-Id: 105672 Delivered-To: patch@linaro.org Received: by 10.140.91.77 with SMTP id y71csp888638qgd; Thu, 15 Jun 2017 09:34:13 -0700 (PDT) X-Received: by 10.98.28.74 with SMTP id c71mr6209864pfc.154.1497544453403; Thu, 15 Jun 2017 09:34:13 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1497544453; cv=none; d=google.com; s=arc-20160816; b=XMlu8goKKKF2hZusrMyPUzjTTMY5KunB3KXlDs2AfOXgnHcYEB+3dK501zkrIkSGr5 xL+Klf6ixVnEB+eJYF58ZuTOxVX+5eVr3H2NGH8r6ID64HtHeF76bsHZMwdfe1JQ3eef NLAlueoycKh7wt+qAn/2RJY1wMacHU9C2CKFPFg54KJe3D5q6cAxPDduhQ3QT0QwxNNT GqKht4PdpIXW4Y2RoPtsHcC3the1wrkRziaiUploIyyfUd/JTOVicAJUx86VYVIWiBjj sfPludQicDz+bDyZGBT+rJ2Yiio7ySNybJYQj1ml45b17vuQJ92a4HjmK6p+cIhOPYuA Fk5w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:arc-authentication-results; bh=rJ57mFkuiS0ZytTOFrfKVE89r/uJoNQNnEvXpV7PPkg=; b=uPcaGgjANioWUEih1p4vaKoW4bUNNlKcJyxGCMBcxNfAJiv0GxOaDpFue+nVoZv1IE LqJwB5VhUZzkH7T5XGGFacIQqwVgJeQ6pHt7BIf6Rp2usUAdsF2wQ0yPdCZzVoWRJRg1 VJt1WVZCpXwg1wv2SbaGxlR/AlsTRDpV+a0IBsTDd3PUZkpsycm5BD6CZD/f6M7lOSfp 7zJUF0tnG8AbF5bkrNu9MbfQtlS4H7XHmeeM3pXJ/SQsEVhGpcyJV03RK7v1O2YBUTmN FROcE5/JnxT+XCJE3U5MQb7OBDf/iqATpXeqjApdWHwVKMCv7oTC3EO7EvXLvy4/wLbE /kjQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.b=IZ8gTNRD; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id e5si405726pgk.85.2017.06.15.09.34.13; Thu, 15 Jun 2017 09:34:13 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.b=IZ8gTNRD; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752762AbdFOQdt (ORCPT + 25 others); Thu, 15 Jun 2017 12:33:49 -0400 Received: from mail-wm0-f41.google.com ([74.125.82.41]:33743 "EHLO mail-wm0-f41.google.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1751829AbdFOQdq (ORCPT ); Thu, 15 Jun 2017 12:33:46 -0400 Received: by mail-wm0-f41.google.com with SMTP id m7so1048443wmg.0 for ; Thu, 15 Jun 2017 09:33:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=rJ57mFkuiS0ZytTOFrfKVE89r/uJoNQNnEvXpV7PPkg=; b=IZ8gTNRDIEw9WloqHFNLpC5DSxfKrK7Ds8auiQOmRL1IYeXwsP4AG0GAyqjszmVNk8 CuQBXb30re4iiD4HqhdsefdaI5aeM9mqZC6SuTh8PPfsuIZYr52ZL+XQoP6lY/PKJ9Hx PvIeOjoBkOxV7fvalow0Egw2sAUo0YX8Q7Yco= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=rJ57mFkuiS0ZytTOFrfKVE89r/uJoNQNnEvXpV7PPkg=; b=Bd9HvShQ+OqpLxwPn450EesFAttArZIKJrLWhwXUHhaotP+Iwb53oEWTS6+kpJIZw+ c/8ovIDEIyQ8qXnpiaK2Pvj2aO0u/yuHQ8AKhZns8F6l34w6zqwNHBXqRoQ9OYHln5Md Tsk5+vQtMHzpPW5EpVi4J1CfIS3039HutQKZVQ4YijZACQbL2qbQ1lgcPrtGCcqfsMc8 xCTiqsVkZXNcObXllue54ZG7qKWlIhDOIXDcU53cnJjnkBksgG/0UnZ3QHpsU1pSZ91D JQWTUeI1TA0E4dwp1AncOdkeCm0hgOG/P6e6AOyx48aRH7M0BXYH2ID7cyAw4rdDv4rt jDGw== X-Gm-Message-State: AKS2vOxVa0kwoyREkUZNi3ne/EU5CrIysTQuzUUWclYmLFa3MFM4yVKg 58BI6k8WnUvHx+3Y X-Received: by 10.28.74.218 with SMTP id n87mr4310657wmi.16.1497544392239; Thu, 15 Jun 2017 09:33:12 -0700 (PDT) Received: from mms-0440.qualcomm.mm-sol.com ([37.157.136.206]) by smtp.gmail.com with ESMTPSA id m73sm541890wmi.25.2017.06.15.09.33.11 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 15 Jun 2017 09:33:11 -0700 (PDT) From: Stanimir Varbanov To: Mauro Carvalho Chehab , Hans Verkuil Cc: linux-media@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, Stanimir Varbanov Subject: [PATCH v11 13/19] media: venus: hfi_venus: fix variable dereferenced before check Date: Thu, 15 Jun 2017 19:31:54 +0300 Message-Id: <1497544320-2269-14-git-send-email-stanimir.varbanov@linaro.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1497544320-2269-1-git-send-email-stanimir.varbanov@linaro.org> References: <1497544320-2269-1-git-send-email-stanimir.varbanov@linaro.org> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org This fixes a warning found when building with gcc7: drivers/media/platform/qcom/venus/hfi_venus.c:998 venus_isr_thread() warn: variable dereferenced before check 'hdev' (see line 994) Signed-off-by: Stanimir Varbanov --- drivers/media/platform/qcom/venus/hfi_venus.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) -- 2.7.4 diff --git a/drivers/media/platform/qcom/venus/hfi_venus.c b/drivers/media/platform/qcom/venus/hfi_venus.c index ab209f3d9498..1caae8feaa36 100644 --- a/drivers/media/platform/qcom/venus/hfi_venus.c +++ b/drivers/media/platform/qcom/venus/hfi_venus.c @@ -991,13 +991,14 @@ static void venus_process_msg_sys_error(struct venus_hfi_device *hdev, static irqreturn_t venus_isr_thread(struct venus_core *core) { struct venus_hfi_device *hdev = to_hfi_priv(core); - const struct venus_resources *res = hdev->core->res; + const struct venus_resources *res; void *pkt; u32 msg_ret; if (!hdev) return IRQ_NONE; + res = hdev->core->res; pkt = hdev->pkt_buf; if (hdev->irq_status & WRAPPER_INTR_STATUS_A2HWD_MASK) { From patchwork Thu Jun 15 16:31:59 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stanimir Varbanov X-Patchwork-Id: 105669 Delivered-To: patch@linaro.org Received: by 10.140.91.77 with SMTP id y71csp888319qgd; Thu, 15 Jun 2017 09:33:32 -0700 (PDT) X-Received: by 10.84.210.105 with SMTP id z96mr7156280plh.219.1497544412339; Thu, 15 Jun 2017 09:33:32 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1497544412; cv=none; d=google.com; s=arc-20160816; b=Z14WAb2nusH3zwPS9LcxM+dgVy42t7S5M6aCMQbgfFhCq+D38jo5bZQG/uxCxKtndI Z9gjBOPy00x31z9YidLgu9632c+Eqhd5+H9XmRHc96ZISirYQ9kDyMC0+TAzPBEBdey0 GUDBSQKELjMg1slSVHkDO1WB2rrfOf0dsxzsjc2plSA3KimzOF4HEz4/eMwvuMU5egjL BW2G2R+KUIUhaSzU0NUbQdvfrZAGhinFAuBNZho41JDj9H9VDrlumFpUHDDY/MxkUbal Cob1Clq8KaeYp2rsL+zGzUjn3kjSLhcBm2YeKLfQAumaTeBWYwJBNXtEwYYMe4rrmSTW 4tFA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:arc-authentication-results; bh=umPh9HDesYKRHBpMw8ASBx6hGWhiuypqV/sX9z1+gxw=; b=s9EyiKI+GvevQVycyQkbA+hBU+42XlZ4yTGWiG7vkch4OOYeUpzWouwTS0fvLpMyrL 7pN/AYGO1qIgD2I+/OewCU+OWXVMywyAlaM6SKY0GUp18lamahNbE+/JHROZ6g+MIqqL vJyDUD467+BRh5FMCr5hYjvCsdKRFgmyahHHhCfGHqiPGMxhprZJAWIM0ygJVwXRQjJV QOL09YhlkS9XkzP+aaw36+JQR2icyyadAi4APEEyxWdjylGljcn1gmWDsxcygzTWqVQ3 4qztXm46TccE9TZoPn3ownvi2dIZMZQx49e9+FMGx5TXk0B1A4WEwamY2VXs0Zr/C/SK w0rw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.b=Ef3+A7Re; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id p5si397449pfl.413.2017.06.15.09.33.32; Thu, 15 Jun 2017 09:33:32 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.b=Ef3+A7Re; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752687AbdFOQda (ORCPT + 25 others); Thu, 15 Jun 2017 12:33:30 -0400 Received: from mail-wr0-f172.google.com ([209.85.128.172]:35153 "EHLO mail-wr0-f172.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751711AbdFOQdX (ORCPT ); Thu, 15 Jun 2017 12:33:23 -0400 Received: by mail-wr0-f172.google.com with SMTP id q97so25311747wrb.2 for ; Thu, 15 Jun 2017 09:33:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=umPh9HDesYKRHBpMw8ASBx6hGWhiuypqV/sX9z1+gxw=; b=Ef3+A7ReoBi/6DtO+CHSrx0WotcdKCsEegFxNqnhJ7qCjN2E1hfY+U5em5OjbhwsFF p816OMjkj8OqqyDwzZIpnGY0Cydwu+Yxql1Bx7L+W+NcL3RVLEhR9dt8HOYYPwOyH7bM ZrgdRGpojtAFyZ50qkgOQPs67JePslb9LbYVE= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=umPh9HDesYKRHBpMw8ASBx6hGWhiuypqV/sX9z1+gxw=; b=M5r4IdcModViJ1MUILLzK4ZLdVKx5GDWYkL84M1n+6PObza6cMrbTcITkXGPDHkM5A 0vB+//A0E/Yb8EWxyrpzTUGkhxZD1XKDxQ0uE5bP2X/XmItRkQ00Wmf5CM93a9MBiB36 ncJvVYmvmzs348W1B7k8YnPtzfJUvYAVPr5yqnln+EekyN4NzQlH+xKOgHTCNPt5TxDb nmoziPm0rPpvh/iAY+ANBDrrcVdw7cqqwQjT03w5yZ5vaEvklUUYNtFqGv7OMtAIQ6lS VV4Iv5CDT4e9UIGO0YICAJ7IQO5YGR0HIsFIKUrg9koQ1iTHC+hAbBlzcdWmYPzZH9+X RufA== X-Gm-Message-State: AKS2vOxOTRcEi1+kDwOHZSu2VAEbj2EqgG0MUE1WMMZVkUZMTQvLI5tJ bNvSPA8pFo4cK2uYTv2KDw== X-Received: by 10.223.133.240 with SMTP id 45mr4391057wru.51.1497544401553; Thu, 15 Jun 2017 09:33:21 -0700 (PDT) Received: from mms-0440.qualcomm.mm-sol.com ([37.157.136.206]) by smtp.gmail.com with ESMTPSA id m73sm541890wmi.25.2017.06.15.09.33.20 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 15 Jun 2017 09:33:21 -0700 (PDT) From: Stanimir Varbanov To: Mauro Carvalho Chehab , Hans Verkuil Cc: linux-media@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, Stanimir Varbanov Subject: [PATCH v11 18/19] media: venus: update firmware path with linux-firmware place Date: Thu, 15 Jun 2017 19:31:59 +0300 Message-Id: <1497544320-2269-19-git-send-email-stanimir.varbanov@linaro.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1497544320-2269-1-git-send-email-stanimir.varbanov@linaro.org> References: <1497544320-2269-1-git-send-email-stanimir.varbanov@linaro.org> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org This makes firmware name and path part of venus_resources structure and initialize it properly depending on the SoC and firmware version. Signed-off-by: Stanimir Varbanov --- drivers/media/platform/qcom/venus/core.c | 6 ++++-- drivers/media/platform/qcom/venus/core.h | 1 + drivers/media/platform/qcom/venus/firmware.c | 9 ++++----- drivers/media/platform/qcom/venus/firmware.h | 3 ++- 4 files changed, 11 insertions(+), 8 deletions(-) -- 2.7.4 diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index 48391d87d5c3..776d2bae6979 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -84,7 +84,7 @@ static void venus_sys_error_handler(struct work_struct *work) pm_runtime_get_sync(core->dev); - ret |= venus_boot(core->dev, &core->dev_fw); + ret |= venus_boot(core->dev, &core->dev_fw, core->res->fwname); ret |= hfi_core_resume(core, true); @@ -207,7 +207,7 @@ static int venus_probe(struct platform_device *pdev) if (ret < 0) goto err_runtime_disable; - ret = venus_boot(dev, &core->dev_fw); + ret = venus_boot(dev, &core->dev_fw, core->res->fwname); if (ret) goto err_runtime_disable; @@ -335,6 +335,7 @@ static const struct venus_resources msm8916_res = { .vmem_size = 0, .vmem_addr = 0, .dma_mask = 0xddc00000 - 1, + .fwname = "qcom/venus-1.8/venus.mdt", }; static const struct freq_tbl msm8996_freq_table[] = { @@ -363,6 +364,7 @@ static const struct venus_resources msm8996_res = { .vmem_size = 0, .vmem_addr = 0, .dma_mask = 0xddc00000 - 1, + .fwname = "qcom/venus-4.2/venus.mdt", }; static const struct of_device_id venus_dt_match[] = { diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 2f8492090224..e542700eee32 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -48,6 +48,7 @@ struct venus_resources { unsigned int vmem_id; u32 vmem_size; u32 vmem_addr; + const char *fwname; }; struct venus_format { diff --git a/drivers/media/platform/qcom/venus/firmware.c b/drivers/media/platform/qcom/venus/firmware.c index 365e64010920..1b1a4f355918 100644 --- a/drivers/media/platform/qcom/venus/firmware.c +++ b/drivers/media/platform/qcom/venus/firmware.c @@ -23,7 +23,6 @@ #include "firmware.h" -#define VENUS_FIRMWARE_NAME "venus.mdt" #define VENUS_PAS_ID 9 #define VENUS_FW_MEM_SIZE SZ_8M @@ -32,7 +31,7 @@ static void device_release_dummy(struct device *dev) of_reserved_mem_device_release(dev); } -int venus_boot(struct device *parent, struct device *fw_dev) +int venus_boot(struct device *parent, struct device *fw_dev, const char *fwname) { const struct firmware *mdt; phys_addr_t mem_phys; @@ -67,7 +66,7 @@ int venus_boot(struct device *parent, struct device *fw_dev) goto err_unreg_device; } - ret = request_firmware(&mdt, VENUS_FIRMWARE_NAME, fw_dev); + ret = request_firmware(&mdt, fwname, fw_dev); if (ret < 0) goto err_unreg_device; @@ -78,8 +77,8 @@ int venus_boot(struct device *parent, struct device *fw_dev) goto err_unreg_device; } - ret = qcom_mdt_load(fw_dev, mdt, VENUS_FIRMWARE_NAME, VENUS_PAS_ID, - mem_va, mem_phys, mem_size); + ret = qcom_mdt_load(fw_dev, mdt, fwname, VENUS_PAS_ID, mem_va, mem_phys, + mem_size); release_firmware(mdt); diff --git a/drivers/media/platform/qcom/venus/firmware.h b/drivers/media/platform/qcom/venus/firmware.h index 782e64ae291a..f81a98979798 100644 --- a/drivers/media/platform/qcom/venus/firmware.h +++ b/drivers/media/platform/qcom/venus/firmware.h @@ -16,7 +16,8 @@ struct device; -int venus_boot(struct device *parent, struct device *fw_dev); +int venus_boot(struct device *parent, struct device *fw_dev, + const char *fwname); int venus_shutdown(struct device *fw_dev); #endif From patchwork Thu Jun 15 16:32:00 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stanimir Varbanov X-Patchwork-Id: 105670 Delivered-To: patch@linaro.org Received: by 10.140.91.77 with SMTP id y71csp888452qgd; Thu, 15 Jun 2017 09:33:50 -0700 (PDT) X-Received: by 10.99.96.132 with SMTP id u126mr6138472pgb.121.1497544430727; Thu, 15 Jun 2017 09:33:50 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1497544430; cv=none; d=google.com; s=arc-20160816; b=UMbOizIzTqzeoMG/0+e6q2Fy7MLj5y1qFflbgFpCuU2ZlwvriUYXHo5N0pv6tA0nk8 prScpblrOErnEEqSyqjXhXBhCRrlWL5i/weuTm/nMGru0AZnpe99qaTZ4LuHPbl0EySB Zpud143gQWUk8sK+vzN9JevgC6irqsRxzIjUgewl4WVKjiFja5j0fBDCFEENRlVYZxyI vWQgUmWrF0KTO3BhLDuYBXhnKcecr0gF/aY7spyNGIS6UgLE1zV2HHRY0WHkUf3QKDzd Tf9qRqAN9cH6oelVpjYzBBhxzZdYwMBv7U86LIWtczGYZJrpUQi24tsLyanQdisK1h7R wmjg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:arc-authentication-results; bh=CAJYQqVielJi7we4ovY+VUI/xvVXkO9WBY1eEx8vXLk=; b=oHTLjpJFLbRxeWDR3uuD85JJ+RaChhnYN9Aut1YYrY70WhAJiSgMWCa5bu4CwniFMv N81tL17LXTSwBVYxlxn46xMJNl+r0IZa9aJEwqZV6FKSk42H5PGEEp3d/zu1u/+kDxMe quMtImJLnSikUBD6dVYgTGBzdiv5ltDEq076OAjyWMXVainzyQyqLX+vNdpEEGUHeUmu eTZSS+x1KwhDasIjdNVq7WUeSU4jX5CpGyAGYvpYpauVrwEzD8Cvzr+YS0Oap4KzP5SO HglJPVkuCc9vC2fKenZOhCpSYsrXEfsadxOBf7ERJynR1TcLsmjnFKlt489sgvqWH/4h xZAQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.b=h2I8uRf2; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id a33si352296pld.507.2017.06.15.09.33.50; Thu, 15 Jun 2017 09:33:50 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.b=h2I8uRf2; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752726AbdFOQdh (ORCPT + 25 others); Thu, 15 Jun 2017 12:33:37 -0400 Received: from mail-wr0-f169.google.com ([209.85.128.169]:35970 "EHLO mail-wr0-f169.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752697AbdFOQde (ORCPT ); Thu, 15 Jun 2017 12:33:34 -0400 Received: by mail-wr0-f169.google.com with SMTP id 36so25251515wry.3 for ; Thu, 15 Jun 2017 09:33:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=CAJYQqVielJi7we4ovY+VUI/xvVXkO9WBY1eEx8vXLk=; b=h2I8uRf2mAAVwfLSUXveWuewDHDrbo6+hmh1v4sYEATdfC7oZOdQ//uvFK4hUDO5eW 5T6mmLwOghK5tBh5uzRZPofrsNwUWciwgY/RnzHJHZ+p8r/Q0I5q7fnvcr7jBLlMHjd8 9rmkBYpa75GkTqhsJyMAIg5J/XLU7J0qNnsyw= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=CAJYQqVielJi7we4ovY+VUI/xvVXkO9WBY1eEx8vXLk=; b=Uh9zR4zfpym4uWShqNKnWZLLcpvjOPZc9KhDxOlJLrRmYf/FGo55srvsQiPDZ+FBMl Lrl0fxgmmkhRniwycrVTrxx3NIRwQBKmEWA1ZPWaQ1FsxZcpA0Xilxrv1c116PZSZF7R hVIgzNJsUSR8YiPdrlTHc6/MQhwTrb63KfSl+5XrpdOIOaf0goRhrDFF/8cNFNl5bJ2C IGeoYntWI2Vl99lhI2jmngsS/v/FgzF6gtp/8j3ckvqhvlRcR/ZxmdeTSlQi+EDnhsUE SkdcrHk5AvWk4DPII21tef8xMW8zx49qibK1ol8rEiUEZOXmnWW7MxgJrM/fEiHYv8Hp Uc/w== X-Gm-Message-State: AKS2vOxnelagz9ByoZhsiCAmmZGFiNKMZnZ8eI8F4hNDL4nURkwtJenx /9RqFJYqPlZ09S++ X-Received: by 10.223.181.132 with SMTP id c4mr4704193wre.167.1497544403274; Thu, 15 Jun 2017 09:33:23 -0700 (PDT) Received: from mms-0440.qualcomm.mm-sol.com ([37.157.136.206]) by smtp.gmail.com with ESMTPSA id m73sm541890wmi.25.2017.06.15.09.33.22 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 15 Jun 2017 09:33:22 -0700 (PDT) From: Stanimir Varbanov To: Mauro Carvalho Chehab , Hans Verkuil Cc: linux-media@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, Stanimir Varbanov , Mauro Carvalho Chehab Subject: [PATCH v11 19/19] media: venus: enable building with COMPILE_TEST Date: Thu, 15 Jun 2017 19:32:00 +0300 Message-Id: <1497544320-2269-20-git-send-email-stanimir.varbanov@linaro.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1497544320-2269-1-git-send-email-stanimir.varbanov@linaro.org> References: <1497544320-2269-1-git-send-email-stanimir.varbanov@linaro.org> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org We want all media drivers to build with COMPILE_TEST, as the Coverity instance we use on Kernel works only for x86. Also, our test workflow relies on it, in order to identify git bisect breakages. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Stanimir Varbanov --- drivers/media/platform/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) -- 2.7.4 diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 6027dbd4e04d..f9bbba5c5dd6 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -467,7 +467,7 @@ config VIDEO_TI_VPE_DEBUG config VIDEO_QCOM_VENUS tristate "Qualcomm Venus V4L2 encoder/decoder driver" depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA - depends on ARCH_QCOM && IOMMU_DMA + depends on (ARCH_QCOM && IOMMU_DMA) || COMPILE_TEST select QCOM_MDT_LOADER select VIDEOBUF2_DMA_SG select V4L2_MEM2MEM_DEV