From patchwork Wed May 30 09:09:41 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Katsuhiro Suzuki X-Patchwork-Id: 137242 Delivered-To: patch@linaro.org Received: by 2002:a2e:9706:0:0:0:0:0 with SMTP id r6-v6csp5070391lji; Wed, 30 May 2018 02:11:12 -0700 (PDT) X-Google-Smtp-Source: ADUXVKJiiDf9aNUmPW4mdjGVqOGaTrOX8jGlObiUTY0yRyU//PxwHkkFi7r/Z5SyXliWwchuGS52 X-Received: by 2002:a17:902:aa4b:: with SMTP id c11-v6mr2050311plr.17.1527671471944; Wed, 30 May 2018 02:11:11 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1527671471; cv=none; d=google.com; s=arc-20160816; b=HmCn7QVw2jaN+CJKPlutFlt0xZTutokyRvVqnvBUllhj6YrgpBSgvaboVQiJANpVss BWn/SPPxlDRBBOb2jfyWOyLo2a17qnBxq97jgN/nPmwlRykSc2mPuin8k9fYqwsS/8sj Oa6Nx7yJAz/7GbYsWYbJpv0q6Iyfw4b0Nqap6ukqSlhphZsKgx4Ie7RUv6cWPk+FRU9m F95INXS59Y3aY0wVgGQefKyuy+WIOLyroaBvzXu1mlt1EVrLpEg27j+Aos13IlXMTNXO WGpAPR7w2Th6wQcyWGLyvYnhg96skluR97SEzRbmLjJgNcAxo8x0FaeuZc7VTQRb/kVl ym0g== 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:arc-authentication-results; bh=PA4Sb2ihlvnn+wUT5Os2dUSzP6ivklA5dVIzk37Q6f0=; b=ifTBrIox/vA20Gw+EI9FDhEPVGvDGe7CIFHr9fx8AuR0kS2+wdEbSu2+bueesAXcxH +1MdJVBxuS3T3MTnBcPOETKd0myfcbZ7zP7X0S2KNHbf+q1zihwmWt/RwKnIdYVZVfPg 8251JaN8UJfMZREH+7+Hswg6YypnM8GWiZYZ4yFQwQyBd69EyGLxGDhMP5/KHkoQhgy1 91nM5g0Y6LI+zTcy/jcMHNcfugzDhPUZ2UpAYPtYGUHKsxHvWcUttMD1B+GZ5DVYUCnl HeRDF8e2Q2CzjEo8ae5xAwtx9DIEBdPeDJgEJZsiSwgpYzIO9hZDgTGLO5WQLDFVT+zv 6ghQ== ARC-Authentication-Results: i=1; mx.google.com; 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 Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id e93-v6si24874101plk.386.2018.05.30.02.11.11; Wed, 30 May 2018 02:11:11 -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; 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 Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S968976AbeE3JLJ (ORCPT + 30 others); Wed, 30 May 2018 05:11:09 -0400 Received: from mx.socionext.com ([202.248.49.38]:35850 "EHLO mx.socionext.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S968819AbeE3JJw (ORCPT ); Wed, 30 May 2018 05:09:52 -0400 Received: from unknown (HELO kinkan-ex.css.socionext.com) ([172.31.9.52]) by mx.socionext.com with ESMTP; 30 May 2018 18:09:49 +0900 Received: from mail.mfilter.local (m-filter-1 [10.213.24.61]) by kinkan-ex.css.socionext.com (Postfix) with ESMTP id 3ED46180D73; Wed, 30 May 2018 18:09:49 +0900 (JST) Received: from 172.31.9.51 (172.31.9.51) by m-FILTER with ESMTP; Wed, 30 May 2018 18:09:49 +0900 Received: from yuzu.css.socionext.com (yuzu [172.31.8.45]) by kinkan.css.socionext.com (Postfix) with ESMTP id 08B201A01BB; Wed, 30 May 2018 18:09:49 +0900 (JST) Received: from aegis.e01.socionext.com (unknown [10.213.134.210]) by yuzu.css.socionext.com (Postfix) with ESMTP id D4B18120139; Wed, 30 May 2018 18:09:48 +0900 (JST) From: Katsuhiro Suzuki To: Mauro Carvalho Chehab , linux-media@vger.kernel.org Cc: Masami Hiramatsu , Jassi Brar , linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, Katsuhiro Suzuki Subject: [PATCH 3/8] media: uniphier: add submodules of HSC MPEG2-TS I/O driver Date: Wed, 30 May 2018 18:09:41 +0900 Message-Id: <20180530090946.1635-4-suzuki.katsuhiro@socionext.com> X-Mailer: git-send-email 2.17.0 In-Reply-To: <20180530090946.1635-1-suzuki.katsuhiro@socionext.com> References: <20180530090946.1635-1-suzuki.katsuhiro@socionext.com> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org This patch adds submodules of HSC for UniPhier SoCs. These work as follows: ucode: Load uCode and start subsystems css : Switch stream path ts : Receive MPEG2-TS clock and signal dma : Transfer MPEG2-TS data bytes to main memory Signed-off-by: Katsuhiro Suzuki --- drivers/media/platform/uniphier/Makefile | 3 + drivers/media/platform/uniphier/hsc-css.c | 258 ++++++++++++ drivers/media/platform/uniphier/hsc-dma.c | 302 ++++++++++++++ drivers/media/platform/uniphier/hsc-ts.c | 99 +++++ drivers/media/platform/uniphier/hsc-ucode.c | 436 ++++++++++++++++++++ 5 files changed, 1098 insertions(+) create mode 100644 drivers/media/platform/uniphier/hsc-css.c create mode 100644 drivers/media/platform/uniphier/hsc-dma.c create mode 100644 drivers/media/platform/uniphier/hsc-ts.c create mode 100644 drivers/media/platform/uniphier/hsc-ucode.c -- 2.17.0 diff --git a/drivers/media/platform/uniphier/Makefile b/drivers/media/platform/uniphier/Makefile index f66554cd5c45..92536bc56b31 100644 --- a/drivers/media/platform/uniphier/Makefile +++ b/drivers/media/platform/uniphier/Makefile @@ -1 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 +uniphier-dvb-y += hsc-ucode.o hsc-css.o hsc-ts.o hsc-dma.o + +obj-$(CONFIG_DVB_UNIPHIER) += uniphier-dvb.o diff --git a/drivers/media/platform/uniphier/hsc-css.c b/drivers/media/platform/uniphier/hsc-css.c new file mode 100644 index 000000000000..baa0a15ca98e --- /dev/null +++ b/drivers/media/platform/uniphier/hsc-css.c @@ -0,0 +1,258 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Socionext UniPhier DVB driver for High-speed Stream Controller (HSC). +// CSS (Cross Stream Switch) connects MPEG2-TS input port and output port. +// +// Copyright (c) 2018 Socionext Inc. + +#include +#include + +#include "hsc.h" +#include "hsc-reg.h" + +enum HSC_TS_IN hsc_css_out_to_ts_in(enum HSC_CSS_OUT out) +{ + if (out >= HSC_CSS_OUT_TSI0 && out <= HSC_CSS_OUT_TSI9) + return HSC_TSI0 + (out - HSC_CSS_OUT_TSI0); + + return -1; +} + +enum HSC_DPLL_SRC hsc_css_out_to_dpll_src(enum HSC_CSS_OUT out) +{ + if (out >= HSC_CSS_OUT_TSI0 && out <= HSC_CSS_OUT_TSI9) + return HSC_DPLL_SRC_TSI0 + (out - HSC_CSS_OUT_TSI0); + + return -1; +} + +static bool css_in_is_valid(struct hsc_chip *chip, enum HSC_CSS_IN in) +{ + if (in >= chip->spec->num_css_in) + return false; + + return true; +} + +static bool css_out_is_valid(struct hsc_chip *chip, enum HSC_CSS_OUT out) +{ + if (out >= chip->spec->num_css_out) + return false; + + return true; +} + +static const struct hsc_spec_css_in *css_in_get_spec(struct hsc_chip *chip, + enum HSC_CSS_IN in) +{ + const struct hsc_spec_css_in *spec = chip->spec->css_in; + + if (!css_in_is_valid(chip, in)) + return NULL; + + return &spec[in]; +} + +static const struct hsc_spec_css_out *css_out_get_spec(struct hsc_chip *chip, + enum HSC_CSS_OUT out) +{ + const struct hsc_spec_css_out *spec = chip->spec->css_out; + + if (!css_out_is_valid(chip, out)) + return NULL; + + return &spec[out]; +} + +int hsc_dpll_get_src(struct hsc_chip *chip, enum HSC_DPLL dpll, + enum HSC_DPLL_SRC *src) +{ + struct regmap *r = chip->regmap; + u32 v; + + if (!src || dpll >= HSC_DPLL_NUM) + return -EINVAL; + + regmap_read(r, CSS_DPCTRL(dpll), &v); + *src = ffs(v & CSS_DPCTRL_DPSEL_MASK) - 1; + + return 0; +} + +/** + * Select source clock of DPLL. + * + * @dpll: ID of DPLL + * @src : ID of clock source or HSC_DPLL_SRC_NONE to disconnect + */ +int hsc_dpll_set_src(struct hsc_chip *chip, enum HSC_DPLL dpll, + enum HSC_DPLL_SRC src) +{ + struct regmap *r = chip->regmap; + u32 v = 0; + + if (dpll >= HSC_DPLL_NUM || src >= HSC_DPLL_SRC_NUM) + return -EINVAL; + + if (src != HSC_DPLL_SRC_NONE) + v = 1 << src; + + regmap_write(r, CSS_DPCTRL(dpll), v); + + return 0; +} + +static int hsc_css_get_polarity(struct hsc_chip *chip, + const struct hsc_css_pol *pol, + bool *sync_bit, bool *val_bit, bool *clk_fall) +{ + struct regmap *r = chip->regmap; + u32 v; + + if (!sync_bit || !val_bit || !clk_fall || !pol->valid) + return -EINVAL; + + regmap_read(r, pol->reg, &v); + + *sync_bit = !!(v & BIT(pol->sft_sync)); + *val_bit = !!(v & BIT(pol->sft_val)); + *clk_fall = !!(v & BIT(pol->sft_clk)); + + return 0; +} + +/** + * Setup signal polarity of TS signals. + * + * @sync_bit : true : The sync signal keeps only 1bit period. + * false: The sync signal keeps during 8bits period. + * @valid_bit: true : The valid signal does not keep during 8bits period. + * false: The valid signal keeps during 8bits period. + * @clk_fall : true : Latch the data at falling edge of clock signal. + * false: Latch the data at rising edge of clock signal. + */ +static int hsc_css_set_polarity(struct hsc_chip *chip, + const struct hsc_css_pol *pol, + bool sync_bit, bool val_bit, bool clk_fall) +{ + struct regmap *r = chip->regmap; + u32 m = 0, v = 0; + + if (!pol->valid) + return -EINVAL; + + if (pol->sft_sync != -1) { + m |= BIT(pol->sft_sync); + if (sync_bit) + v |= BIT(pol->sft_sync); + } + + if (pol->sft_val != -1) { + m |= BIT(pol->sft_val); + if (val_bit) + v |= BIT(pol->sft_val); + } + + if (pol->sft_clk != -1) { + m |= BIT(pol->sft_clk); + if (clk_fall) + v |= BIT(pol->sft_clk); + } + + regmap_update_bits(r, pol->reg, m, v); + + return 0; +} + +int hsc_css_in_get_polarity(struct hsc_chip *chip, enum HSC_CSS_IN in, + bool *sync_bit, bool *val_bit, bool *clk_fall) +{ + const struct hsc_spec_css_in *speci = css_in_get_spec(chip, in); + + if (!speci) + return -EINVAL; + + return hsc_css_get_polarity(chip, &speci->pol, + sync_bit, val_bit, clk_fall); +} + +int hsc_css_in_set_polarity(struct hsc_chip *chip, enum HSC_CSS_IN in, + bool sync_bit, bool val_bit, bool clk_fall) +{ + const struct hsc_spec_css_in *speci = css_in_get_spec(chip, in); + + if (!speci) + return -EINVAL; + + return hsc_css_set_polarity(chip, &speci->pol, + sync_bit, val_bit, clk_fall); +} + +int hsc_css_out_get_polarity(struct hsc_chip *chip, enum HSC_CSS_OUT out, + bool *sync_bit, bool *val_bit, bool *clk_fall) +{ + const struct hsc_spec_css_out *speco = css_out_get_spec(chip, out); + + if (!speco) + return -EINVAL; + + return hsc_css_get_polarity(chip, &speco->pol, + sync_bit, val_bit, clk_fall); +} + +int hsc_css_out_set_polarity(struct hsc_chip *chip, enum HSC_CSS_OUT out, + bool sync_bit, bool val_bit, bool clk_fall) +{ + const struct hsc_spec_css_out *speco = css_out_get_spec(chip, out); + + if (!speco) + return -EINVAL; + + return hsc_css_set_polarity(chip, &speco->pol, + sync_bit, val_bit, clk_fall); +} + +int hsc_css_out_get_src(struct hsc_chip *chip, enum HSC_CSS_IN *in, + enum HSC_CSS_OUT out, bool *en) +{ + struct regmap *r = chip->regmap; + const struct hsc_spec_css_out *speco = css_out_get_spec(chip, out); + u32 v; + + if (!in || !en || !speco || !speco->sel.valid) + return -EINVAL; + + regmap_read(r, speco->sel.reg, &v); + *in = field_get(speco->sel.mask, v); + + regmap_read(r, CSS_OUTPUTENABLE, &v); + *en = !!(v & BIT(out)); + + return 0; +} + +/** + * Connect the input port and output port using CSS (Cross Stream Switch). + * + * @in : Input port number. + * @out : Output port number. + * @en : false: Disable this path. + * true : Enable this path. + */ +int hsc_css_out_set_src(struct hsc_chip *chip, enum HSC_CSS_IN in, + enum HSC_CSS_OUT out, bool en) +{ + struct regmap *r = chip->regmap; + const struct hsc_spec_css_out *speco = css_out_get_spec(chip, out); + + if (!css_in_is_valid(chip, in) || !speco || !speco->sel.valid) + return -EINVAL; + + regmap_update_bits(r, speco->sel.reg, speco->sel.mask, + field_prep(speco->sel.mask, in)); + + regmap_update_bits(r, CSS_OUTPUTENABLE, BIT(out), (en) ? ~0 : 0); + + return 0; +} diff --git a/drivers/media/platform/uniphier/hsc-dma.c b/drivers/media/platform/uniphier/hsc-dma.c new file mode 100644 index 000000000000..0b3e471a68f7 --- /dev/null +++ b/drivers/media/platform/uniphier/hsc-dma.c @@ -0,0 +1,302 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Socionext UniPhier DVB driver for High-speed Stream Controller (HSC). +// MPEG2-TS DMA control. +// +// Copyright (c) 2018 Socionext Inc. + +#include +#include + +#include "hsc.h" +#include "hsc-reg.h" + +u64 hsc_rb_cnt(struct hsc_dma_buf *buf) +{ + if (buf->rd_offs <= buf->wr_offs) + return buf->wr_offs - buf->rd_offs; + else + return buf->size - (buf->rd_offs - buf->wr_offs); +} + +u64 hsc_rb_cnt_to_end(struct hsc_dma_buf *buf) +{ + if (buf->rd_offs <= buf->wr_offs) + return buf->wr_offs - buf->rd_offs; + else + return buf->size - buf->rd_offs; +} + +u64 hsc_rb_space(struct hsc_dma_buf *buf) +{ + if (buf->rd_offs <= buf->wr_offs) + return buf->size - (buf->wr_offs - buf->rd_offs) - 8; + else + return buf->rd_offs - buf->wr_offs - 8; +} + +u64 hsc_rb_space_to_end(struct hsc_dma_buf *buf) +{ + if (buf->rd_offs > buf->wr_offs) + return buf->rd_offs - buf->wr_offs - 8; + else if (buf->rd_offs > 0) + return buf->size - buf->wr_offs; + else + return buf->size - buf->wr_offs - 8; +} + +static void dma_set_buffer(struct hsc_chip *chip, int dma_ch, + u64 bg, u64 ed) +{ + struct regmap *r = chip->regmap; + + regmap_write(r, CDMBC_RBBGNADRSD(dma_ch), bg); + regmap_write(r, CDMBC_RBBGNADRSU(dma_ch), bg >> 32); + regmap_write(r, CDMBC_RBENDADRSD(dma_ch), ed); + regmap_write(r, CDMBC_RBENDADRSU(dma_ch), ed >> 32); +} + +static u64 dma_get_rp(struct hsc_chip *chip, int dma_ch) +{ + struct regmap *r = chip->regmap; + u32 d, u; + + regmap_read(r, CDMBC_RBRDPTRD(dma_ch), &d); + regmap_read(r, CDMBC_RBRDPTRU(dma_ch), &u); + + return ((u64)u << 32) | d; +} + +static void dma_set_rp(struct hsc_chip *chip, int dma_ch, u64 pos) +{ + struct regmap *r = chip->regmap; + + regmap_write(r, CDMBC_RBRDPTRD(dma_ch), pos); + regmap_write(r, CDMBC_RBRDPTRU(dma_ch), pos >> 32); +} + +static u64 dma_get_wp(struct hsc_chip *chip, int dma_ch) +{ + struct regmap *r = chip->regmap; + u32 d, u; + + regmap_read(r, CDMBC_RBWRPTRD(dma_ch), &d); + regmap_read(r, CDMBC_RBWRPTRU(dma_ch), &u); + + return ((u64)u << 32) | d; +} + +static void dma_set_wp(struct hsc_chip *chip, int dma_ch, u64 pos) +{ + struct regmap *r = chip->regmap; + + regmap_write(r, CDMBC_RBWRPTRD(dma_ch), pos); + regmap_write(r, CDMBC_RBWRPTRU(dma_ch), pos >> 32); +} + +static void dma_set_chkp(struct hsc_chip *chip, int dma_ch, u64 pos) +{ + struct regmap *r = chip->regmap; + + regmap_write(r, CDMBC_CHIRADRSD(dma_ch), pos); + regmap_write(r, CDMBC_CHIRADRSU(dma_ch), pos >> 32); +} + +static void dma_set_enable(struct hsc_chip *chip, int dma_ch, + const struct hsc_dma_en *dma_en, bool en) +{ + struct regmap *r = chip->regmap; + u32 v; + bool now; + + regmap_read(r, dma_en->reg, &v); + now = !!(v & BIT(dma_en->sft_toggle)); + + /* Toggle DMA state if needed */ + if ((en && !now) || (!en && now)) + regmap_write(r, dma_en->reg, BIT(dma_en->sft_toggle)); +} + +static bool dma_in_is_valid(struct hsc_chip *chip, enum HSC_DMA_IN in) +{ + if (in >= chip->spec->num_dma_in || + !chip->spec->dma_in[in].intr.valid) + return false; + + return true; +} + +int hsc_dma_in_init(struct hsc_dma_in *dma_in, struct hsc_chip *chip, + enum HSC_DMA_IN in, struct hsc_dma_buf *buf) +{ + if (!dma_in || !dma_in_is_valid(chip, in)) + return -EINVAL; + + dma_in->chip = chip; + dma_in->id = in; + dma_in->spec = &chip->spec->dma_in[in]; + dma_in->buf = buf; + + return 0; +} + +void hsc_dma_in_start(struct hsc_dma_in *dma_in, bool en) +{ + struct hsc_chip *chip = dma_in->chip; + const struct hsc_spec_dma *spec = dma_in->spec; + struct hsc_dma_buf *buf = dma_in->buf; + struct regmap *r = chip->regmap; + u64 bg, ed; + u32 v; + + bg = buf->phys; + ed = buf->phys + buf->size; + dma_set_buffer(chip, spec->dma_ch, bg, ed); + + buf->rd_offs = 0; + buf->wr_offs = 0; + buf->chk_offs = buf->size_chk; + dma_set_rp(chip, spec->dma_ch, buf->rd_offs + buf->phys); + dma_set_wp(chip, spec->dma_ch, buf->wr_offs + buf->phys); + dma_set_chkp(chip, spec->dma_ch, buf->chk_offs + buf->phys); + + regmap_update_bits(r, CDMBC_CHSRCAMODE(spec->dma_ch), + CDMBC_CHAMODE_TYPE_RB, ~0); + regmap_update_bits(r, CDMBC_CHCTRL1(spec->dma_ch), + CDMBC_CHCTRL1_IND_SIZE_UND, ~0); + + v = (en) ? ~0 : 0; + regmap_update_bits(r, CDMBC_CHIE(spec->dma_ch), CDMBC_CHI_TRANSIT, v); + regmap_update_bits(r, spec->intr.reg, BIT(spec->intr.sft_intr), v); + + dma_set_enable(chip, spec->dma_ch, &spec->en, en); +} + +void hsc_dma_in_sync(struct hsc_dma_in *dma_in) +{ + struct hsc_chip *chip = dma_in->chip; + const struct hsc_spec_dma *spec = dma_in->spec; + struct hsc_dma_buf *buf = dma_in->buf; + + buf->rd_offs = dma_get_rp(chip, spec->dma_ch) - buf->phys; + dma_set_wp(chip, spec->dma_ch, buf->rd_offs + buf->phys); + dma_set_chkp(chip, spec->dma_ch, buf->chk_offs + buf->phys); +} + +int hsc_dma_in_get_intr(struct hsc_dma_in *dma_in, u32 *stat) +{ + struct regmap *r = dma_in->chip->regmap; + + if (!stat) + return -EINVAL; + + regmap_read(r, CDMBC_CHID(dma_in->spec->dma_ch), stat); + + return 0; +} + +void hsc_dma_in_clear_intr(struct hsc_dma_in *dma_in, u32 v) +{ + struct regmap *r = dma_in->chip->regmap; + + regmap_write(r, CDMBC_CHIR(dma_in->spec->dma_ch), v); +} + +static bool dma_out_is_valid(struct hsc_chip *chip, enum HSC_DMA_OUT out) +{ + if (out >= chip->spec->num_dma_out || + !chip->spec->dma_out[out].intr.valid) + return false; + + return true; +} + +int hsc_dma_out_init(struct hsc_dma_out *dma_out, struct hsc_chip *chip, + enum HSC_DMA_OUT out, struct hsc_dma_buf *buf) +{ + if (!dma_out || !dma_out_is_valid(chip, out)) + return -EINVAL; + + dma_out->chip = chip; + dma_out->id = out; + dma_out->spec = &chip->spec->dma_out[out]; + dma_out->buf = buf; + + return 0; +} + +void hsc_dma_out_set_src_ts_in(struct hsc_dma_out *dma_out, + enum HSC_TS_IN ts_in) +{ + struct regmap *r = dma_out->chip->regmap; + const struct hsc_spec_dma *spec = dma_out->spec; + u32 m, v; + + m = CDMBC_CHTDCTRLH_STREM_MASK | + CDMBC_CHTDCTRLH_ALL_EN; + v = FIELD_PREP(CDMBC_CHTDCTRLH_STREM_MASK, ts_in) | + CDMBC_CHTDCTRLH_ALL_EN; + regmap_update_bits(r, CDMBC_CHTDCTRLH(spec->dma_ch), m, v); +} + +void hsc_dma_out_start(struct hsc_dma_out *dma_out, bool en) +{ + struct hsc_chip *chip = dma_out->chip; + const struct hsc_spec_dma *spec = dma_out->spec; + struct hsc_dma_buf *buf = dma_out->buf; + struct regmap *r = chip->regmap; + u64 bg, ed; + u32 v; + + bg = buf->phys; + ed = buf->phys + buf->size; + dma_set_buffer(chip, spec->dma_ch, bg, ed); + + buf->rd_offs = 0; + buf->wr_offs = 0; + buf->chk_offs = buf->size_chk; + dma_set_rp(chip, spec->dma_ch, buf->rd_offs + buf->phys); + dma_set_wp(chip, spec->dma_ch, buf->wr_offs + buf->phys); + dma_set_chkp(chip, spec->dma_ch, buf->chk_offs + buf->phys); + + regmap_update_bits(r, CDMBC_CHDSTAMODE(spec->dma_ch), + CDMBC_CHAMODE_TYPE_RB, ~0); + regmap_update_bits(r, CDMBC_CHCTRL1(spec->dma_ch), + CDMBC_CHCTRL1_IND_SIZE_UND, ~0); + + v = (en) ? ~0 : 0; + regmap_update_bits(r, CDMBC_CHIE(spec->dma_ch), CDMBC_CHI_TRANSIT, v); + regmap_update_bits(r, spec->intr.reg, BIT(spec->intr.sft_intr), v); + + dma_set_enable(chip, spec->dma_ch, &spec->en, en); +} + +void hsc_dma_out_sync(struct hsc_dma_out *dma_out) +{ + struct hsc_chip *chip = dma_out->chip; + const struct hsc_spec_dma *spec = dma_out->spec; + struct hsc_dma_buf *buf = dma_out->buf; + + dma_set_rp(chip, spec->dma_ch, buf->rd_offs + buf->phys); + buf->wr_offs = dma_get_wp(chip, spec->dma_ch) - buf->phys; + dma_set_chkp(chip, spec->dma_ch, buf->chk_offs + buf->phys); +} + +int hsc_dma_out_get_intr(struct hsc_dma_out *dma_out, u32 *stat) +{ + struct regmap *r = dma_out->chip->regmap; + + if (!stat) + return -EINVAL; + + regmap_read(r, CDMBC_CHID(dma_out->spec->dma_ch), stat); + + return 0; +} + +void hsc_dma_out_clear_intr(struct hsc_dma_out *dma_out, u32 clear) +{ + struct regmap *r = dma_out->chip->regmap; + + regmap_write(r, CDMBC_CHIR(dma_out->spec->dma_ch), clear); +} diff --git a/drivers/media/platform/uniphier/hsc-ts.c b/drivers/media/platform/uniphier/hsc-ts.c new file mode 100644 index 000000000000..4539c3280021 --- /dev/null +++ b/drivers/media/platform/uniphier/hsc-ts.c @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Socionext UniPhier DVB driver for High-speed Stream Controller (HSC). +// MPEG2-TS input/output port setting. +// +// Copyright (c) 2018 Socionext Inc. + +#include +#include + +#include "hsc.h" +#include "hsc-reg.h" + +#define PARAMA_OFFSET_TS 0x02 +#define PARAMA_LOOPADDR_TS 0x31 +#define PARAMA_COUNT_TS 0xc4 + +static bool ts_in_is_valid(struct hsc_chip *chip, enum HSC_TS_IN in) +{ + if (in >= chip->spec->num_ts_in || !chip->spec->ts_in[in].intr.valid) + return false; + + return true; +} + +static const struct hsc_spec_ts *ts_in_get_spec(struct hsc_chip *chip, + enum HSC_TS_IN in) +{ + const struct hsc_spec_ts *spec = chip->spec->ts_in; + + if (!ts_in_is_valid(chip, in)) + return NULL; + + return &spec[in]; +} + +int hsc_ts_in_set_enable(struct hsc_chip *chip, enum HSC_TS_IN in, bool en) +{ + struct regmap *r = chip->regmap; + const struct hsc_spec_ts *speci = ts_in_get_spec(chip, in); + u32 m, v; + + if (!speci) + return -EINVAL; + + m = TSI_SYNCCNTROL_FRAME_MASK; + v = TSI_SYNCCNTROL_FRAME_EXTSYNC2; + regmap_update_bits(r, TSI_SYNCCNTROL(in), m, v); + + m = TSI_CONFIG_ATSMD_MASK | TSI_CONFIG_STCMD_MASK | + TSI_CONFIG_CHEN_START; + v = TSI_CONFIG_ATSMD_DPLL | TSI_CONFIG_STCMD_DPLL; + if (en) + v |= TSI_CONFIG_CHEN_START; + regmap_update_bits(r, TSI_CONFIG(in), m, v); + + v = (en) ? ~0 : 0; + regmap_update_bits(r, TSI_INTREN(in), + TSI_INTR_SERR | TSI_INTR_LOST, v); + regmap_update_bits(r, speci->intr.reg, BIT(speci->intr.sft_intr), v); + + return 0; +} + +int hsc_ts_in_set_dmaparam(struct hsc_chip *chip, enum HSC_TS_IN in, + enum HSC_TSIF_FMT ifmt) +{ + struct regmap *r = chip->regmap; + u32 v, ats, offset, loop, cnt; + + if (!ts_in_is_valid(chip, in)) + return -EINVAL; + + switch (ifmt) { + case HSC_TSIF_MPEG2_TS: + ats = 0; + offset = PARAMA_OFFSET_TS; + loop = PARAMA_LOOPADDR_TS; + cnt = PARAMA_COUNT_TS; + break; + case HSC_TSIF_MPEG2_TS_ATS: + ats = TSI_CONFIG_ATSADD_ON; + offset = PARAMA_OFFSET_TS; + loop = PARAMA_LOOPADDR_TS; + cnt = PARAMA_COUNT_TS; + break; + default: + return -EINVAL; + } + + regmap_update_bits(r, TSI_CONFIG(in), TSI_CONFIG_ATSADD_ON, ats); + + v = FIELD_PREP(SBC_DMAPARAMA_OFFSET_MASK, offset) | + FIELD_PREP(SBC_DMAPARAMA_LOOPADDR_MASK, loop) | + FIELD_PREP(SBC_DMAPARAMA_COUNT_MASK, cnt); + regmap_write(r, SBC_DMAPARAMA(in), v); + + return 0; +} diff --git a/drivers/media/platform/uniphier/hsc-ucode.c b/drivers/media/platform/uniphier/hsc-ucode.c new file mode 100644 index 000000000000..bad1d70b0968 --- /dev/null +++ b/drivers/media/platform/uniphier/hsc-ucode.c @@ -0,0 +1,436 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Socionext UniPhier DVB driver for High-speed Stream Controller (HSC). +// Core init and uCode loader. +// +// Copyright (c) 2018 Socionext Inc. + +#include +#include +#include + +#include "hsc.h" +#include "hsc-reg.h" + +struct hsc_cip_file_dma_param { + u32 cip_file_ch; + dma_addr_t cip_r_sdram; + dma_addr_t cip_w_sdram; + size_t inter_size; + size_t total_size; + u8 key_id1; + u8 key_id0; + u8 endian; + int id1_en; + int push; +}; + +static void core_start(struct hsc_chip *chip) +{ + struct regmap *r = chip->regmap; + + regmap_write(r, IOB_RESET0, ~0); + regmap_write(r, IOB_RESET1, ~0); + + regmap_write(r, IOB_CLKSTOP, 0); + /* Deassert all internal resets, but AP core is later for uCode */ + regmap_write(r, IOB_RESET0, IOB_RESET0_APCORE); + regmap_write(r, IOB_RESET1, 0); + + /* Halt SPU for uCode */ + regmap_write(r, IOB_DEBUG, IOB_DEBUG_SPUHALT); +} + +static void core_stop(struct hsc_chip *chip) +{ + struct regmap *r = chip->regmap; + + regmap_write(r, IOB_RESET0, 0); + regmap_write(r, IOB_RESET1, 0); + + regmap_write(r, IOB_CLKSTOP, ~0); +} + +static void core_clear_ram(struct hsc_chip *chip) +{ + struct regmap *r = chip->regmap; + const struct hsc_spec_init_ram *rams = chip->spec->init_rams; + size_t i, s; + + for (i = 0; i < chip->spec->num_init_rams; i++) + for (s = 0; s < rams[i].size; s += 4) + regmap_write(r, rams[i].addr + s, rams[i].pattern); +} + +static void core_start_spu(struct hsc_chip *chip) +{ + struct regmap *r = chip->regmap; + + regmap_write(r, IOB_DEBUG, 0); +} + +static void core_start_ap(struct hsc_chip *chip) +{ + struct regmap *r = chip->regmap; + + regmap_write(r, IOB_RESET0, 0); +} + +static int ucode_set_data_addr(struct hsc_chip *chip, int mode) +{ + struct regmap *r = chip->regmap; + dma_addr_t addr; + + switch (mode) { + case HSC_UCODE_SPU_0: + case HSC_UCODE_SPU_1: + addr = chip->ucode_spu.phys_data; + regmap_write(r, UCODE_DLADDR0, addr); + regmap_write(r, UCODE_DLADDR1, addr >> 32); + break; + case HSC_UCODE_ACE: + addr = chip->ucode_am.phys_data; + regmap_write(r, CIP_UCODEADDR_AM0, addr); + regmap_write(r, CIP_UCODEADDR_AM1, addr >> 32); + break; + default: + return -EINVAL; + } + + return 0; +} + +static void file_channel_dma_set(struct hsc_chip *chip, + struct hsc_cip_file_dma_param *p) +{ + struct regmap *r = chip->regmap; + u32 cipr_rsc_no; + u32 cipw_rsc_no; + dma_addr_t cipr_sdram_start; + dma_addr_t cipw_sdram_start; + dma_addr_t cipr_sdram_end; + dma_addr_t cipw_sdram_end; + u32 v; + + cipr_rsc_no = HSC_CIP_FILE_TO_CIPR_DMCH(p->cip_file_ch); + cipw_rsc_no = HSC_CIP_FILE_TO_CIPW_DMCH(p->cip_file_ch); + cipr_sdram_start = p->cip_r_sdram; + cipw_sdram_start = p->cip_w_sdram; + cipr_sdram_end = p->cip_r_sdram + p->total_size; + cipw_sdram_end = p->cip_w_sdram + p->total_size; + + /* For CIP Read */ + v = FIELD_PREP(CDMBC_CHCTRL1_LINKCH1_MASK, 1) | + FIELD_PREP(CDMBC_CHCTRL1_STATSEL_MASK, 4) | + CDMBC_CHCTRL1_TYPE_INTERMIT; + regmap_write(r, CDMBC_CHCTRL1(cipr_rsc_no), v); + + regmap_write(r, CDMBC_CHCAUSECTRL(cipr_rsc_no), 0); + + v = FIELD_PREP(CDMBC_CHAMODE_ENDIAN_MASK, p->endian) | + FIELD_PREP(CDMBC_CHAMODE_AUPDT_MASK, 0) | + CDMBC_CHAMODE_TYPE_RB; + regmap_write(r, CDMBC_CHSRCAMODE(cipr_rsc_no), v); + + v = FIELD_PREP(CDMBC_CHAMODE_ENDIAN_MASK, 1) | + FIELD_PREP(CDMBC_CHAMODE_AUPDT_MASK, 2); + regmap_write(r, CDMBC_CHDSTAMODE(cipr_rsc_no), v); + + v = FIELD_PREP(CDMBC_CHDSTSTRTADRS_TID_MASK, 0xc) | + FIELD_PREP(CDMBC_CHDSTSTRTADRS_ID1_EN_MASK, p->id1_en) | + FIELD_PREP(CDMBC_CHDSTSTRTADRS_KEY_ID1_MASK, p->key_id1) | + FIELD_PREP(CDMBC_CHDSTSTRTADRS_KEY_ID0_MASK, p->key_id0); + regmap_write(r, CDMBC_CHDSTSTRTADRSD(cipr_rsc_no), v); + + regmap_write(r, CDMBC_CHSIZE(cipr_rsc_no), p->inter_size); + + /* For CIP Write */ + v = FIELD_PREP(CDMBC_CHCTRL1_LINKCH1_MASK, 5) | + FIELD_PREP(CDMBC_CHCTRL1_STATSEL_MASK, 4) | + CDMBC_CHCTRL1_TYPE_INTERMIT | + CDMBC_CHCTRL1_IND_SIZE_UND; + regmap_write(r, CDMBC_CHCTRL1(cipw_rsc_no), v); + + v = FIELD_PREP(CDMBC_CHAMODE_ENDIAN_MASK, 1) | + FIELD_PREP(CDMBC_CHAMODE_AUPDT_MASK, 2); + regmap_write(r, CDMBC_CHSRCAMODE(cipw_rsc_no), v); + + v = FIELD_PREP(CDMBC_CHAMODE_ENDIAN_MASK, p->endian) | + FIELD_PREP(CDMBC_CHAMODE_AUPDT_MASK, 0) | + CDMBC_CHAMODE_TYPE_RB; + regmap_write(r, CDMBC_CHDSTAMODE(cipw_rsc_no), v); + + /* Transferring size */ + regmap_write(r, CDMBC_ITSTEPS(cipr_rsc_no), p->total_size); + + /* For ring buffer */ + regmap_write(r, CDMBC_RBBGNADRSD(cipr_rsc_no), cipr_sdram_start); + regmap_write(r, CDMBC_RBENDADRSD(cipr_rsc_no), cipr_sdram_end); + regmap_write(r, CDMBC_RBRDPTRD(cipr_rsc_no), cipr_sdram_start); + regmap_write(r, CDMBC_RBWRPTRD(cipr_rsc_no), cipr_sdram_end); + + regmap_write(r, CDMBC_RBBGNADRSU(cipr_rsc_no), cipr_sdram_start >> 32); + regmap_write(r, CDMBC_RBENDADRSU(cipr_rsc_no), cipr_sdram_end >> 32); + regmap_write(r, CDMBC_RBRDPTRU(cipr_rsc_no), cipr_sdram_start >> 32); + regmap_write(r, CDMBC_RBWRPTRU(cipr_rsc_no), cipr_sdram_end >> 32); + + regmap_write(r, CDMBC_RBBGNADRSD(cipw_rsc_no), cipw_sdram_start); + regmap_write(r, CDMBC_RBENDADRSD(cipw_rsc_no), cipw_sdram_end); + regmap_write(r, CDMBC_RBRDPTRD(cipw_rsc_no), cipw_sdram_end); + regmap_write(r, CDMBC_RBWRPTRD(cipw_rsc_no), cipw_sdram_start); + + regmap_write(r, CDMBC_RBBGNADRSU(cipw_rsc_no), cipw_sdram_start >> 32); + regmap_write(r, CDMBC_RBENDADRSU(cipw_rsc_no), cipw_sdram_end >> 32); + regmap_write(r, CDMBC_RBRDPTRU(cipw_rsc_no), cipw_sdram_end >> 32); + regmap_write(r, CDMBC_RBWRPTRU(cipw_rsc_no), cipw_sdram_start >> 32); + + /* Transfer settings */ + regmap_write(r, CDMBC_CIPMODE(p->cip_file_ch), + (p->push) ? CDMBC_CIPMODE_PUSH : 0); + + regmap_write(r, CDMBC_CIPPRIORITY(p->cip_file_ch), + FIELD_PREP(CDMBC_CIPPRIORITY_PRIOR_MASK, 3)); +} + +static void file_channel_start(struct hsc_chip *chip, int cip_file_ch, + bool push, bool mmu_en) +{ + struct regmap *r = chip->regmap; + int cipr_rsc_no, cipw_rsc_no; + int cipr_tdbp_no, cipw_tdbp_no; + u32 v = 0; + + cipr_rsc_no = HSC_CIP_FILE_TO_CIPR_DMCH(cip_file_ch); + cipw_rsc_no = HSC_CIP_FILE_TO_CIPW_DMCH(cip_file_ch); + cipr_tdbp_no = HSC_CIP_FILE_TO_CIPR(cip_file_ch); + cipw_tdbp_no = HSC_CIP_FILE_TO_CIPW(cip_file_ch); + + regmap_write(r, CDMBC_CIPMODE(cip_file_ch), + (push) ? CDMBC_CIPMODE_PUSH : 0); + + if (mmu_en) { + v = CDMBC_CHDDR_REG_LOAD_ON | CDMBC_CHDDR_AT_CHEN_ON; + + /* Enable IOMMU for CIP-R and CIP-W */ + regmap_write(r, CDMBC_CHDDR(cipr_rsc_no), + v | CDMBC_CHDDR_SET_MCB_RD); + regmap_write(r, CDMBC_CHDDR(cipw_rsc_no), + v | CDMBC_CHDDR_SET_MCB_WR); + } + + v = 0x01000000 | (1 << cipr_tdbp_no) | (1 << cipw_tdbp_no); + regmap_write(r, CDMBC_STRT(1), v); +} + +static int ucode_load_dma(struct hsc_chip *chip, int mode) +{ + struct regmap *r = chip->regmap; + struct hsc_ucode_buf *ucode; + struct hsc_cip_file_dma_param dma_p = {0}; + u32 cip_f_ctrl, v; + + switch (mode) { + case HSC_UCODE_SPU_0: + case HSC_UCODE_SPU_1: + ucode = &chip->ucode_spu; + cip_f_ctrl = 0x2f090001; + break; + case HSC_UCODE_ACE: + ucode = &chip->ucode_am; + cip_f_ctrl = 0x3f090001; + break; + default: + return -EINVAL; + } + + regmap_write(r, CIP_F_CTRL, cip_f_ctrl); + + regmap_write(r, IOB_INTREN(HSC_INTR_IOB_2), + INTR2_CIP_AUTH_S | + INTR2_MBC_CIP_R(0) | INTR2_MBC_CIP_W(0)); + + dma_p.cip_file_ch = HSC_CIP_FILE_NO_0; + dma_p.cip_r_sdram = ucode->phys_code; + dma_p.cip_w_sdram = 0; + dma_p.inter_size = ucode->size_code; + dma_p.total_size = ucode->size_code; + dma_p.key_id1 = 0; + dma_p.key_id0 = 0; + dma_p.endian = 1; + dma_p.id1_en = 0; + file_channel_dma_set(chip, &dma_p); + file_channel_start(chip, HSC_CIP_FILE_NO_0, true, false); + + do { + regmap_read(r, CDMBC_CHIR(HSC_MBC_DMCH_CIP0_R), &v); + msleep(20); + } while (!(v & INTR_MBC_CH_WDONE)); + regmap_write(r, CDMBC_CHIR(HSC_MBC_DMCH_CIP0_R), v); + + do { + regmap_read(r, CDMBC_CHIR(HSC_MBC_DMCH_CIP0_W), &v); + msleep(20); + } while (!(v & INTR_MBC_CH_WDONE)); + regmap_write(r, CDMBC_CHIR(HSC_MBC_DMCH_CIP0_W), v); + + regmap_read(r, CDMBC_RBIR(HSC_MBC_DMCH_CIP0_R), &v); + regmap_write(r, CDMBC_RBIR(HSC_MBC_DMCH_CIP0_R), v); + + regmap_read(r, CDMBC_RBIR(HSC_MBC_DMCH_CIP0_W), &v); + regmap_write(r, CDMBC_RBIR(HSC_MBC_DMCH_CIP0_W), v); + + /* Clear & disable interrupt */ + regmap_read(r, IOB_INTRST(HSC_INTR_IOB_2), &v); + regmap_write(r, IOB_INTRST(HSC_INTR_IOB_2), v); + + regmap_write(r, IOB_INTREN(HSC_INTR_IOB_2), 0); + + return 0; +} + +static int ucode_load(struct hsc_chip *chip, int mode) +{ + struct device *dev = &chip->pdev->dev; + const struct hsc_spec_ucode *spec; + struct hsc_ucode_buf *ucode; + const struct firmware *firm_code, *firm_data; + int ret; + + switch (mode) { + case HSC_UCODE_SPU_0: + case HSC_UCODE_SPU_1: + spec = &chip->spec->ucode_spu; + ucode = &chip->ucode_spu; + break; + case HSC_UCODE_ACE: + spec = &chip->spec->ucode_ace; + ucode = &chip->ucode_am; + break; + default: + return -EINVAL; + } + + ret = request_firmware(&firm_code, spec->name_code, dev); + if (ret) { + dev_err(dev, "Failed to load firmware '%s'.\n", + spec->name_code); + return ret; + } + + ret = request_firmware(&firm_data, spec->name_data, dev); + if (ret) { + dev_err(dev, "Failed to load firmware '%s'.\n", + spec->name_data); + goto err_firm_code; + } + + ucode->buf_code = dma_alloc_coherent(dev, firm_code->size, + &ucode->phys_code, GFP_KERNEL); + if (!ucode->buf_code) { + ret = -ENOMEM; + goto err_firm_data; + } + ucode->size_code = firm_code->size; + + ucode->buf_data = dma_alloc_coherent(dev, firm_data->size, + &ucode->phys_data, GFP_KERNEL); + if (!ucode->buf_data) { + ret = -ENOMEM; + goto err_buf_code; + } + ucode->size_data = firm_data->size; + + memcpy(ucode->buf_code, firm_code->data, firm_code->size); + memcpy(ucode->buf_data, firm_data->data, firm_data->size); + + ret = ucode_set_data_addr(chip, mode); + if (ret) + goto err_buf_data; + + ret = ucode_load_dma(chip, mode); + if (ret) + goto err_buf_data; + + release_firmware(firm_data); + release_firmware(firm_code); + + return 0; + +err_buf_data: + dma_free_coherent(dev, ucode->size_data, ucode->buf_data, + ucode->phys_data); + +err_buf_code: + dma_free_coherent(dev, ucode->size_code, ucode->buf_code, + ucode->phys_code); + +err_firm_data: + release_firmware(firm_data); + +err_firm_code: + release_firmware(firm_code); + + return ret; +} + +static int ucode_unload(struct hsc_chip *chip, int mode) +{ + struct device *dev = &chip->pdev->dev; + struct hsc_ucode_buf *ucode; + + switch (mode) { + case HSC_UCODE_SPU_0: + case HSC_UCODE_SPU_1: + ucode = &chip->ucode_spu; + break; + case HSC_UCODE_ACE: + ucode = &chip->ucode_am; + break; + default: + return -EINVAL; + } + + dma_free_coherent(dev, ucode->size_data, ucode->buf_data, + ucode->phys_data); + dma_free_coherent(dev, ucode->size_code, ucode->buf_code, + ucode->phys_code); + + return 0; +} + +int hsc_ucode_load_all(struct hsc_chip *chip) +{ + int ret; + + core_start(chip); + core_clear_ram(chip); + + ret = ucode_load(chip, HSC_UCODE_SPU_0); + if (ret) + return ret; + core_start_spu(chip); + + ret = ucode_load(chip, HSC_UCODE_ACE); + if (ret) + return ret; + core_start_ap(chip); + + return 0; +} + +int hsc_ucode_unload_all(struct hsc_chip *chip) +{ + int ret; + + core_stop(chip); + + ret = ucode_unload(chip, HSC_UCODE_SPU_0); + if (ret) + return ret; + + ret = ucode_unload(chip, HSC_UCODE_ACE); + if (ret) + return ret; + + return 0; +} From patchwork Wed May 30 09:09:42 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Katsuhiro Suzuki X-Patchwork-Id: 137243 Delivered-To: patch@linaro.org Received: by 2002:a2e:9706:0:0:0:0:0 with SMTP id r6-v6csp5071666lji; Wed, 30 May 2018 02:12:42 -0700 (PDT) X-Google-Smtp-Source: ADUXVKIFJFh7BmU8k1+4mm4prg5SpYBtI6V08tlfgX0bZyZ5UXkLPeEv6lA0HIfyjON73rhCPliO X-Received: by 2002:a62:d751:: with SMTP id v17-v6mr1988942pfl.39.1527671562512; Wed, 30 May 2018 02:12:42 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1527671562; cv=none; d=google.com; s=arc-20160816; b=T0ktiBd0UXM/mQT+ju5MVoMOPGU66H89OQRtBA4qWYOWAN+yxBxaoWMpiVELApjbD0 p+6jkHALVeq7+WoWF0Il0VoCCUHuL4rbIRos1nCemGYNpvSGqE7DYHgBydA0E+11dDfP V5hjLijrpJY+j40MqgM3xAl5zlsfTwp+G+Q7gBzMkDKFKDf1UdurJtY54WlZ26OiYxb5 TJFx1L6dm4Ydi3kIFthFRg/MyIF9EdHMWcjIxH6pZsN9uOyJrl5X7G3bVV90YMXFAoHt 85bZ+OO4aSFCeYQ6GA2yAgKGSAZq8FD+xnewHlRGnVt5cAJgIfHup2KE28BW48NvzZMK 3lAA== 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:arc-authentication-results; bh=ZA2bL3b1nkDlQXWw6JuBJ3UWuCHx5zeLKzPMRZ5r7jA=; b=es8p0q5I3Pfo7BeCJVNdLZ82UM9W6rT90eotz0he+fpoy8P6l5ypj9pOwym3O4O71N WLLWGp8bTQ6Qrm+ukKYoc2VsqJmKGAw6s5fV08dVvZrGy6repyTyBU8G8XwWGa/OURti 9D2Hz56cQi1IRkUT8pMCMrHSVEl6zWjKLt0e9nFXBQnwCuZcGngTfgM5yeyARkI7VQyb mK6j/dnyw/hvqakBRBi5nzupW8DqNthDX85yHcvjQVwk3fZWIuZyE3DdPEdioNeqDjGN DL2UA9eYOkZAG+My1U4/1qEoj51EkzP6e3tZov0m5ZvAOwRQl0ZSyRmIiPl0bfKgIeGl KkIQ== ARC-Authentication-Results: i=1; mx.google.com; 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 Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id z4-v6si11977494pfl.31.2018.05.30.02.12.42; Wed, 30 May 2018 02:12:42 -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; 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 Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S968923AbeE3JLA (ORCPT + 30 others); Wed, 30 May 2018 05:11:00 -0400 Received: from mx.socionext.com ([202.248.49.38]:35853 "EHLO mx.socionext.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S968831AbeE3JJw (ORCPT ); Wed, 30 May 2018 05:09:52 -0400 Received: from unknown (HELO iyokan-ex.css.socionext.com) ([172.31.9.54]) by mx.socionext.com with ESMTP; 30 May 2018 18:09:49 +0900 Received: from mail.mfilter.local (m-filter-1 [10.213.24.61]) by iyokan-ex.css.socionext.com (Postfix) with ESMTP id BCAE4600E8; Wed, 30 May 2018 18:09:49 +0900 (JST) Received: from 172.31.9.51 (172.31.9.51) by m-FILTER with ESMTP; Wed, 30 May 2018 18:09:49 +0900 Received: from yuzu.css.socionext.com (yuzu [172.31.8.45]) by kinkan.css.socionext.com (Postfix) with ESMTP id 327891A01BB; Wed, 30 May 2018 18:09:49 +0900 (JST) Received: from aegis.e01.socionext.com (unknown [10.213.134.210]) by yuzu.css.socionext.com (Postfix) with ESMTP id 0E134120139; Wed, 30 May 2018 18:09:49 +0900 (JST) From: Katsuhiro Suzuki To: Mauro Carvalho Chehab , linux-media@vger.kernel.org Cc: Masami Hiramatsu , Jassi Brar , linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, Katsuhiro Suzuki Subject: [PATCH 4/8] media: uniphier: add common module of HSC MPEG2-TS I/O driver Date: Wed, 30 May 2018 18:09:42 +0900 Message-Id: <20180530090946.1635-5-suzuki.katsuhiro@socionext.com> X-Mailer: git-send-email 2.17.0 In-Reply-To: <20180530090946.1635-1-suzuki.katsuhiro@socionext.com> References: <20180530090946.1635-1-suzuki.katsuhiro@socionext.com> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org This patch adds common module of HSC (High speed Stream Controller) driver for Socionext UniPhier SoCs. Signed-off-by: Katsuhiro Suzuki --- drivers/media/platform/uniphier/Makefile | 2 +- drivers/media/platform/uniphier/hsc-core.c | 514 +++++++++++++++++++++ 2 files changed, 515 insertions(+), 1 deletion(-) create mode 100644 drivers/media/platform/uniphier/hsc-core.c -- 2.17.0 diff --git a/drivers/media/platform/uniphier/Makefile b/drivers/media/platform/uniphier/Makefile index 92536bc56b31..88bc860b391f 100644 --- a/drivers/media/platform/uniphier/Makefile +++ b/drivers/media/platform/uniphier/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 -uniphier-dvb-y += hsc-ucode.o hsc-css.o hsc-ts.o hsc-dma.o +uniphier-dvb-y += hsc-core.o hsc-ucode.o hsc-css.o hsc-ts.o hsc-dma.o obj-$(CONFIG_DVB_UNIPHIER) += uniphier-dvb.o diff --git a/drivers/media/platform/uniphier/hsc-core.c b/drivers/media/platform/uniphier/hsc-core.c new file mode 100644 index 000000000000..cdb488e4df8c --- /dev/null +++ b/drivers/media/platform/uniphier/hsc-core.c @@ -0,0 +1,514 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Socionext UniPhier DVB driver for High-speed Stream Controller (HSC). +// +// Copyright (c) 2018 Socionext Inc. + +#include +#include +#include +#include +#include +#include +#include + +#include "hsc.h" +#include "hsc-reg.h" + +#define SZ_TS_PKT 188 +#define SZ_M2TS_PKT 192 + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nums); + +static int hsc_start_feed(struct dvb_demux_feed *feed) +{ + struct hsc_tsif *tsif = feed->demux->priv; + struct hsc_dmaif *dmaif = tsif->dmaif; + struct hsc_chip *chip = tsif->chip; + + tsif->running = true; + dmaif->running = true; + + hsc_ts_in_set_enable(chip, tsif->tsi, true); + + hsc_dma_out_set_src_ts_in(&dmaif->dma_out, tsif->tsi); + hsc_dma_out_start(&dmaif->dma_out, true); + + return 0; +} + +static int hsc_stop_feed(struct dvb_demux_feed *feed) +{ + struct hsc_tsif *tsif = feed->demux->priv; + struct hsc_dmaif *dmaif = tsif->dmaif; + struct hsc_chip *chip = tsif->chip; + + hsc_ts_in_set_enable(chip, tsif->tsi, false); + + hsc_dma_out_start(&dmaif->dma_out, false); + + tsif->running = false; + dmaif->running = false; + + return 0; +} + +int hsc_register_dvb(struct hsc_tsif *tsif) +{ + struct device *dev = &tsif->chip->pdev->dev; + int ret; + + tsif->adapter.priv = tsif; + ret = dvb_register_adapter(&tsif->adapter, "uniphier-hsc", + THIS_MODULE, dev, adapter_nums); + if (ret < 0) { + dev_err(dev, "Failed to register DVB adapter: %d\n", ret); + return ret; + } + tsif->valid_adapter = true; + + tsif->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING; + tsif->demux.priv = tsif; + tsif->demux.feednum = 256; + tsif->demux.filternum = 256; + tsif->demux.start_feed = hsc_start_feed; + tsif->demux.stop_feed = hsc_stop_feed; + tsif->demux.write_to_decoder = NULL; + ret = dvb_dmx_init(&tsif->demux); + if (ret) { + dev_err(dev, "Failed to register demux: %d\n", ret); + goto err_out_adapter; + } + tsif->valid_demux = true; + + tsif->dmxdev.filternum = 256; + tsif->dmxdev.demux = &tsif->demux.dmx; + tsif->dmxdev.capabilities = 0; + ret = dvb_dmxdev_init(&tsif->dmxdev, &tsif->adapter); + if (ret) { + dev_err(dev, "Failed to register demux dev: %d\n", ret); + goto err_out_dmx; + } + tsif->valid_dmxdev = true; + + ret = dvb_register_frontend(&tsif->adapter, tsif->fe); + if (ret) { + dev_err(dev, "Failed to register adapter: %d\n", ret); + goto err_out_dmxdev; + } + tsif->valid_fe = true; + + return 0; + +err_out_dmxdev: + dvb_dmxdev_release(&tsif->dmxdev); + +err_out_dmx: + dvb_dmx_release(&tsif->demux); + +err_out_adapter: + dvb_unregister_adapter(&tsif->adapter); + + return ret; +} + +void hsc_unregister_dvb(struct hsc_tsif *tsif) +{ + if (tsif->valid_fe) { + dvb_frontend_detach(tsif->fe); + dvb_unregister_frontend(tsif->fe); + } + if (tsif->valid_dmxdev) + dvb_dmxdev_release(&tsif->dmxdev); + if (tsif->valid_demux) + dvb_dmx_release(&tsif->demux); + if (tsif->valid_adapter) + dvb_unregister_adapter(&tsif->adapter); +} + +static bool is_tsi_error(struct hsc_tsif *tsif, u32 status) +{ + if (status & (TSI_INTR_SERR | TSI_INTR_SOF | TSI_INTR_TOF)) + return true; + + return false; +} + +static void hsc_tsif_recover_worker(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct hsc_tsif *tsif = container_of(dwork, + struct hsc_tsif, recover_work); + struct hsc_chip *chip = tsif->chip; + + if (!tsif->running) + return; + + hsc_ts_in_set_enable(chip, tsif->tsi, true); +} + +static irqreturn_t hsc_tsif_irq(int irq, void *p) +{ + struct platform_device *pdev = p; + struct device *dev = &pdev->dev; + struct hsc_chip *chip = platform_get_drvdata(pdev); + struct regmap *r = chip->regmap; + int i, ret = IRQ_NONE; + + for (i = 0; i < ARRAY_SIZE(chip->tsif); i++) { + struct hsc_tsif *tsif = &chip->tsif[i]; + u32 st; + + if (!tsif->running) + continue; + + regmap_read(r, TSI_SYNCSTATUS(tsif->tsi), &st); + if (!st) + continue; + + regmap_write(r, TSI_SYNCSTATUS(tsif->tsi), 0xffff); + + if (is_tsi_error(tsif, st)) { + /* Recovery */ + dev_info(dev, "TS %d Sync lost, try recovery.\n", + tsif->tsi); + schedule_delayed_work(&tsif->recover_work, + tsif->recover_delay); + } + + ret = IRQ_HANDLED; + } + + return ret; +} + +int hsc_tsif_init(struct hsc_tsif *tsif, const struct hsc_conf *conf) +{ + struct hsc_chip *chip = tsif->chip; + int ret; + + if (!conf) + return -EINVAL; + + tsif->css_in = conf->css_in; + tsif->css_out = conf->css_out; + tsif->dpll = conf->dpll; + + tsif->tsi = hsc_css_out_to_ts_in(tsif->css_out); + if (tsif->tsi == -1) + return -EINVAL; + + tsif->dpll_src = hsc_css_out_to_dpll_src(tsif->css_out); + if (tsif->dpll_src == -1) + return -EINVAL; + + ret = hsc_css_out_set_src(chip, tsif->css_in, tsif->css_out, true); + if (ret) + return ret; + + ret = hsc_css_in_set_polarity(chip, tsif->css_in, false, false, false); + if (ret) + return ret; + + ret = hsc_ts_in_set_dmaparam(chip, tsif->tsi, HSC_TSIF_MPEG2_TS_ATS); + if (ret) + return ret; + + ret = hsc_dpll_set_src(chip, tsif->dpll, tsif->dpll_src); + if (ret) + return ret; + + INIT_DELAYED_WORK(&tsif->recover_work, hsc_tsif_recover_worker); + tsif->recover_delay = HZ / 10; + + return 0; +} + +void hsc_tsif_release(struct hsc_tsif *tsif) +{ + cancel_delayed_work(&tsif->recover_work); +} + +static void hsc_dmaif_feed_worker(struct work_struct *work) +{ + struct hsc_dmaif *dmaif = container_of(work, + struct hsc_dmaif, feed_work); + struct hsc_tsif *tsif = dmaif->tsif; + struct hsc_dma_buf *buf = &dmaif->buf_out; + struct device *dev = &dmaif->chip->pdev->dev; + dma_addr_t dmapos; + u8 *pkt; + u64 cnt, i; + int wrap = 0; + + if (!dmaif->running) + return; + +retry: + spin_lock(&dmaif->lock); + hsc_dma_out_sync(&dmaif->dma_out); + spin_unlock(&dmaif->lock); + + dmapos = buf->phys + buf->rd_offs; + cnt = hsc_rb_cnt_to_end(buf); + cnt = DIV_ROUND_DOWN_ULL(cnt, SZ_M2TS_PKT) * SZ_M2TS_PKT; + + dma_sync_single_for_cpu(dev, dmapos, cnt, DMA_FROM_DEVICE); + for (i = 0; i < cnt; i += SZ_M2TS_PKT) { + pkt = buf->virt + buf->rd_offs + i; + + if (pkt[4] == 0x47 && pkt[5] == 0x1f && pkt[6] == 0xff) + continue; + if (pkt[5] & 0x80) + continue; + if (pkt[7] & 0xc0) + continue; + + dvb_dmx_swfilter_packets(&tsif->demux, &pkt[4], 1); + } + dma_sync_single_for_device(dev, dmapos, cnt, DMA_FROM_DEVICE); + + spin_lock(&dmaif->lock); + + buf->rd_offs += cnt; + if (buf->rd_offs >= buf->size) + buf->rd_offs -= buf->size; + + buf->chk_offs = buf->wr_offs + buf->size_chk; + if (buf->chk_offs >= buf->size) + buf->chk_offs -= buf->size; + + if (!wrap && hsc_rb_cnt(buf) >= buf->size_chk / 2) { + wrap = 1; + spin_unlock(&dmaif->lock); + goto retry; + } + + hsc_dma_out_sync(&dmaif->dma_out); + + spin_unlock(&dmaif->lock); +} + +static irqreturn_t hsc_dmaif_irq(int irq, void *p) +{ + struct platform_device *pdev = p; + struct hsc_chip *chip = platform_get_drvdata(pdev); + int i, ret = IRQ_NONE; + u32 st; + + for (i = 0; i < ARRAY_SIZE(chip->tsif); i++) { + struct hsc_dmaif *dmaif = &chip->dmaif[i]; + + if (!dmaif->running) + continue; + + hsc_dma_out_get_intr(&dmaif->dma_out, &st); + if (!st) + continue; + + hsc_dma_out_clear_intr(&dmaif->dma_out, 0xffff); + + spin_lock(&dmaif->lock); + hsc_dma_out_sync(&dmaif->dma_out); + spin_unlock(&dmaif->lock); + + schedule_work(&dmaif->feed_work); + + ret = IRQ_HANDLED; + } + + return ret; +} + +int hsc_dmaif_init(struct hsc_dmaif *dmaif, const struct hsc_conf *conf) +{ + struct hsc_chip *chip = dmaif->chip; + struct hsc_dma_buf *buf = &dmaif->buf_out; + struct device *dev = &chip->pdev->dev; + int ret; + + if (!conf) + return -EINVAL; + + ret = hsc_dma_out_init(&dmaif->dma_out, chip, conf->dma_out, buf); + if (ret) + return ret; + + buf->size = HSC_DMAIF_TS_BUFSIZE; + buf->size_chk = HSC_DMAIF_TS_BUFSIZE / 4; + buf->virt = dma_alloc_coherent(dev, buf->size, &buf->phys, GFP_KERNEL); + if (!buf->virt) + return -ENOMEM; + + spin_lock_init(&dmaif->lock); + INIT_WORK(&dmaif->feed_work, hsc_dmaif_feed_worker); + + return 0; +} + +void hsc_dmaif_release(struct hsc_dmaif *dmaif) +{ + struct hsc_chip *chip = dmaif->chip; + struct device *dev = &chip->pdev->dev; + + flush_scheduled_work(); + + dma_free_coherent(dev, dmaif->buf_out.size, dmaif->buf_out.virt, + dmaif->buf_out.phys); +} + +static const struct regmap_config hsc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xffffc, + .cache_type = REGCACHE_NONE, +}; + +static int hsc_probe(struct platform_device *pdev) +{ + struct hsc_chip *chip; + struct device *dev = &pdev->dev; + struct resource *res; + void __iomem *preg; + int irq, ret, i; + + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + chip->pdev = pdev; + + chip->spec = of_device_get_match_data(dev); + if (!chip->spec) + return -EINVAL; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + preg = devm_ioremap_resource(dev, res); + if (IS_ERR(preg)) + return PTR_ERR(preg); + + chip->regmap = devm_regmap_init_mmio(dev, preg, + &hsc_regmap_config); + if (IS_ERR(chip->regmap)) + return PTR_ERR(chip->regmap); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev, "Could not get irq for TS I/F\n"); + return irq; + } + + ret = devm_request_irq(dev, irq, hsc_tsif_irq, IRQF_SHARED, + dev_name(dev), pdev); + if (ret) + return ret; + + irq = platform_get_irq(pdev, 1); + if (irq < 0) { + dev_err(dev, "Could not get irq for DMA I/F\n"); + return irq; + } + + ret = devm_request_irq(dev, irq, hsc_dmaif_irq, IRQF_SHARED, + dev_name(dev), pdev); + if (ret) + return ret; + + chip->clk_stdmac = devm_clk_get(dev, "stdmac"); + if (IS_ERR(chip->clk_stdmac)) + return PTR_ERR(chip->clk_stdmac); + + chip->clk_hsc = devm_clk_get(dev, "hsc"); + if (IS_ERR(chip->clk_hsc)) + return PTR_ERR(chip->clk_hsc); + + chip->rst_stdmac = devm_reset_control_get_shared(dev, "stdmac"); + if (IS_ERR(chip->rst_stdmac)) + return PTR_ERR(chip->rst_stdmac); + + chip->rst_hsc = devm_reset_control_get_shared(dev, "hsc"); + if (IS_ERR(chip->rst_hsc)) + return PTR_ERR(chip->rst_hsc); + + ret = clk_prepare_enable(chip->clk_stdmac); + if (ret) + return ret; + + ret = clk_prepare_enable(chip->clk_hsc); + if (ret) + goto err_out_clk_stdmac; + + ret = reset_control_deassert(chip->rst_stdmac); + if (ret) + goto err_out_clk_hsc; + + ret = reset_control_deassert(chip->rst_hsc); + if (ret) + goto err_out_rst_stdmac; + + ret = hsc_ucode_load_all(chip); + if (ret) + goto err_out_rst_hsc; + + for (i = 0; i < HSC_STREAM_IF_NUM; i++) { + chip->dmaif[i].chip = chip; + chip->dmaif[i].tsif = &chip->tsif[i]; + chip->tsif[i].chip = chip; + chip->tsif[i].dmaif = &chip->dmaif[i]; + } + + platform_set_drvdata(pdev, chip); + + return 0; + +err_out_rst_hsc: + reset_control_assert(chip->rst_hsc); + +err_out_rst_stdmac: + reset_control_assert(chip->rst_stdmac); + +err_out_clk_hsc: + clk_disable_unprepare(chip->clk_hsc); + +err_out_clk_stdmac: + clk_disable_unprepare(chip->clk_stdmac); + + return ret; +} + +static int hsc_remove(struct platform_device *pdev) +{ + struct hsc_chip *chip = platform_get_drvdata(pdev); + + hsc_ucode_unload_all(chip); + + reset_control_assert(chip->rst_hsc); + reset_control_assert(chip->rst_stdmac); + clk_disable_unprepare(chip->clk_hsc); + clk_disable_unprepare(chip->clk_stdmac); + + return 0; +} + +static const struct of_device_id uniphier_hsc_of_match[] = { + { + .compatible = "socionext,uniphier-ld11-hsc", + .data = &uniphier_hsc_ld11_spec, + }, + { + .compatible = "socionext,uniphier-ld20-hsc", + .data = &uniphier_hsc_ld20_spec, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, uniphier_hsc_of_match); + +static struct platform_driver uniphier_hsc_driver = { + .driver = { + .name = "uniphier-hsc", + .of_match_table = of_match_ptr(uniphier_hsc_of_match), + }, + .probe = hsc_probe, + .remove = hsc_remove, +}; +module_platform_driver(uniphier_hsc_driver); From patchwork Wed May 30 09:09:46 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Katsuhiro Suzuki X-Patchwork-Id: 137237 Delivered-To: patch@linaro.org Received: by 2002:a2e:9706:0:0:0:0:0 with SMTP id r6-v6csp5069731lji; Wed, 30 May 2018 02:10:25 -0700 (PDT) X-Google-Smtp-Source: ADUXVKLGGrg5FQj4R4rXwkkJhnZiT31g522OLEEOrPg0saE49YWjPj/AawNgUZW0no5F4mfh6Qjk X-Received: by 2002:a17:902:5c6:: with SMTP id f64-v6mr2053185plf.50.1527671424928; Wed, 30 May 2018 02:10:24 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1527671424; cv=none; d=google.com; s=arc-20160816; b=P3CILHcPWTlYRzjGAVmmxhCU0HhSwm3iDgFVkv/c7NoDmvFoTE3CG0PhYRfOMmfm6l gRMDiP8MPlB+F9X5bKa0sqvey5fAIC6Uul3zC+nXGC1+lN6xyGFnkrbcc2VQvBnIhEsa d/8BYPqg7ryN7c4z9L4FMqqReER/PLtbYP8QpIiOQaihZiorKD6gebW+fH14Qy6tKAX/ VUXYj+8Y9MTO8WCDLVJtBFUXL1yltb/y43nyIE8LkFP8zB5Ho5dw5LLIx5gqS27rCQuV 5Zl2Maba2BSetfJ2B0VOnPZG8SD1+ZecIsJhtqHspCJIW3iDdea2wDr7pvGIsjX2gh3o VGHQ== 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:arc-authentication-results; bh=/cB0pKoUVGHEKA8qH+UytDN8cc3DHqy9YPAeas1Sgt0=; b=woFP0yvOUk+C9w6QVwEeBmf69FbRVlWs28ehDcsR+8O11kIYaNw7Uj5Tp5acKOPTA8 3W822XtHFNBMA5wzbTXk1+1QN4bw6MQYjtYBqxtU+8Hxywcu3Kv9saQI6PQ5eDxb3Eqc H6s5a3MKyzogtm891QyeLU+HJyjiqIgh98hrtczvn8UtyilgnyRiKvB1kkWTsQ2UVkWe ag72QDzw6Gs5rBiQ10I3/8kWxPgzDUdFtifl58LFes9sW+4aoTgGWsRjdwakjORj/iVI Dir2SUEGD3a5eEsZIj3D3Hxe4/9HhwlnUs2/FGVyO78Wl8gqAzI3RwRR1q9R0EujQ58l Bdug== ARC-Authentication-Results: i=1; mx.google.com; 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 Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id p6-v6si16037851pgn.73.2018.05.30.02.10.24; Wed, 30 May 2018 02:10:24 -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; 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 Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S968895AbeE3JKV (ORCPT + 30 others); Wed, 30 May 2018 05:10:21 -0400 Received: from mx.socionext.com ([202.248.49.38]:35847 "EHLO mx.socionext.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S968846AbeE3JJ4 (ORCPT ); Wed, 30 May 2018 05:09:56 -0400 Received: from unknown (HELO iyokan-ex.css.socionext.com) ([172.31.9.54]) by mx.socionext.com with ESMTP; 30 May 2018 18:09:50 +0900 Received: from mail.mfilter.local (m-filter-1 [10.213.24.61]) by iyokan-ex.css.socionext.com (Postfix) with ESMTP id 46146600E8; Wed, 30 May 2018 18:09:50 +0900 (JST) Received: from 172.31.9.51 (172.31.9.51) by m-FILTER with ESMTP; Wed, 30 May 2018 18:09:50 +0900 Received: from yuzu.css.socionext.com (yuzu [172.31.8.45]) by kinkan.css.socionext.com (Postfix) with ESMTP id DCD371A01BB; Wed, 30 May 2018 18:09:49 +0900 (JST) Received: from aegis.e01.socionext.com (unknown [10.213.134.210]) by yuzu.css.socionext.com (Postfix) with ESMTP id B80A0120139; Wed, 30 May 2018 18:09:49 +0900 (JST) From: Katsuhiro Suzuki To: Mauro Carvalho Chehab , linux-media@vger.kernel.org Cc: Masami Hiramatsu , Jassi Brar , linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, Katsuhiro Suzuki Subject: [PATCH 8/8] media: uniphier: add LD20 adapter driver for ISDB Date: Wed, 30 May 2018 18:09:46 +0900 Message-Id: <20180530090946.1635-9-suzuki.katsuhiro@socionext.com> X-Mailer: git-send-email 2.17.0 In-Reply-To: <20180530090946.1635-1-suzuki.katsuhiro@socionext.com> References: <20180530090946.1635-1-suzuki.katsuhiro@socionext.com> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org This patch adds UniPhier LD20 DVB adapter driver for ISDB-S/T that equipments SONY SUT-PJ series using CXD2858 tuner and Socionext MN884434 demodulator. Signed-off-by: Katsuhiro Suzuki --- drivers/media/platform/uniphier/Kconfig | 10 + drivers/media/platform/uniphier/Makefile | 1 + .../platform/uniphier/ld20-mn884434-helene.c | 274 ++++++++++++++++++ 3 files changed, 285 insertions(+) create mode 100644 drivers/media/platform/uniphier/ld20-mn884434-helene.c -- 2.17.0 diff --git a/drivers/media/platform/uniphier/Kconfig b/drivers/media/platform/uniphier/Kconfig index 0fc9bbfc170b..1ad2b0f2716c 100644 --- a/drivers/media/platform/uniphier/Kconfig +++ b/drivers/media/platform/uniphier/Kconfig @@ -25,3 +25,13 @@ config DVB_UNIPHIER_LD11_ISDB Demod: Socionext MN884433 Tuner: SONY HELENE (CXD2858ER) Say Y when you want to support this adapters. + +config DVB_UNIPHIER_LD20_ISDB + bool "Support UniPhier LD20 ISDB adapters" + depends on DVB_UNIPHIER + help + Driver for LD20 ISDB-S/T adapters which use + Demux: Socionext LD20 HSC + Demod: Socionext MN884434 + Tuner: SONY HELENE (CXD2858ER) + Say Y when you want to support this adapters. diff --git a/drivers/media/platform/uniphier/Makefile b/drivers/media/platform/uniphier/Makefile index e4b06f8a37b5..9c12456748f9 100644 --- a/drivers/media/platform/uniphier/Makefile +++ b/drivers/media/platform/uniphier/Makefile @@ -9,3 +9,4 @@ ccflags-y += -Idrivers/media/tuners/ uniphier-dvb-y += uniphier-adapter.o uniphier-dvb-$(CONFIG_DVB_UNIPHIER_LD11_ISDB) += ld11-mn884433-helene.o +uniphier-dvb-$(CONFIG_DVB_UNIPHIER_LD20_ISDB) += ld20-mn884434-helene.o diff --git a/drivers/media/platform/uniphier/ld20-mn884434-helene.c b/drivers/media/platform/uniphier/ld20-mn884434-helene.c new file mode 100644 index 000000000000..7ccbe52a50fb --- /dev/null +++ b/drivers/media/platform/uniphier/ld20-mn884434-helene.c @@ -0,0 +1,274 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Socionext UniPhier LD20 adapter driver for ISDB. +// Using Socionext MN884434 ISDB-S/ISDB-T demodulator and +// SONY HELENE tuner. +// +// Copyright (c) 2018 Socionext Inc. + +#include +#include +#include +#include +#include +#include + +#include "sc1501a.h" +#include "helene.h" +#include "hsc.h" +#include "uniphier-adapter.h" + +static struct sc1501a_config mn884434_conf[] = { + { .if_freq = LOW_IF_4MHZ, }, + { .if_freq = LOW_IF_4MHZ, }, +}; + +static int uniphier_adapter_demod_probe(struct uniphier_adapter_priv *priv) +{ + const struct uniphier_adapter_spec *spec = priv->spec; + struct device *dev = &priv->pdev->dev; + struct device_node *node; + int ret, i; + + priv->demod_mclk = devm_clk_get(dev, "demod-mclk"); + if (IS_ERR(priv->demod_mclk)) { + dev_err(dev, "Failed to request demod-mclk: %ld\n", + PTR_ERR(priv->demod_mclk)); + return PTR_ERR(priv->demod_mclk); + } + + priv->demod_gpio = devm_gpiod_get_optional(dev, "reset-demod", + GPIOD_OUT_HIGH); + if (IS_ERR(priv->demod_gpio)) { + dev_err(dev, "Failed to request demod_gpio: %ld\n", + PTR_ERR(priv->demod_gpio)); + return PTR_ERR(priv->demod_gpio); + } + + node = of_parse_phandle(dev->of_node, "demod-i2c-bus", 0); + if (!node) { + dev_err(dev, "Failed to parse demod-i2c-bus\n"); + return -ENODEV; + } + + priv->demod_i2c_adapter = of_find_i2c_adapter_by_node(node); + if (!priv->demod_i2c_adapter) { + dev_err(dev, "Failed to find demod i2c adapter\n"); + of_node_put(node); + return -ENODEV; + } + of_node_put(node); + + mn884434_conf[0].reset_gpio = priv->demod_gpio; + for (i = 0; i < spec->adapters; i++) { + struct i2c_client *c; + + mn884434_conf[i].mclk = priv->demod_mclk; + mn884434_conf[i].fe = &priv->fe[i].fe; + + c = dvb_module_probe(spec->demod_i2c_info[i].type, NULL, + priv->demod_i2c_adapter, + spec->demod_i2c_info[i].addr, + &mn884434_conf[i]); + if (!c) { + dev_err(dev, "Failed to probe demod\n"); + ret = -ENODEV; + goto err_out; + } + priv->fe[i].demod_i2c = c; + } + + return 0; + +err_out: + for (i = 0; i < spec->adapters; i++) + dvb_module_release(priv->fe[i].demod_i2c); + + return ret; +} + +static struct helene_config helene_conf[] = { + { .xtal = SONY_HELENE_XTAL_16000, }, + { .xtal = SONY_HELENE_XTAL_16000, }, + { .xtal = SONY_HELENE_XTAL_16000, }, +}; + +static int uniphier_adapter_tuner_probe(struct uniphier_adapter_priv *priv) +{ + const struct uniphier_adapter_spec *spec = priv->spec; + struct device *dev = &priv->pdev->dev; + struct device_node *node; + int ret, i; + + priv->tuner_gpio = devm_gpiod_get_optional(dev, "reset-tuner", + GPIOD_OUT_HIGH); + if (IS_ERR(priv->tuner_gpio)) { + dev_err(dev, "Failed to request tuner_gpio: %ld\n", + PTR_ERR(priv->tuner_gpio)); + return PTR_ERR(priv->tuner_gpio); + } + gpiod_set_value_cansleep(priv->tuner_gpio, 0); + + node = of_parse_phandle(dev->of_node, "tuner-i2c-bus", 0); + if (!node) { + dev_err(dev, "Failed to parse tuner-i2c-bus\n"); + return -ENODEV; + } + + priv->tuner_i2c_adapter = of_find_i2c_adapter_by_node(node); + if (!priv->tuner_i2c_adapter) { + dev_err(dev, "Failed to find tuner i2c adapter\n"); + of_node_put(node); + return -ENODEV; + } + of_node_put(node); + + for (i = 0; i < priv->spec->adapters; i++) { + struct i2c_client *c; + + helene_conf[i].fe = priv->fe[i].fe; + + c = dvb_module_probe(spec->tuner_i2c_info[i].type, NULL, + priv->tuner_i2c_adapter, + spec->tuner_i2c_info[i].addr, + &helene_conf[i]); + if (!c) { + dev_err(dev, "Failed to probe tuner\n"); + ret = -ENODEV; + goto err_out; + } + priv->fe[i].tuner_i2c = c; + } + + return 0; + +err_out: + for (i = 0; i < spec->adapters; i++) + dvb_module_release(priv->fe[i].tuner_i2c); + + return ret; +} + +static int uniphier_adapter_probe(struct platform_device *pdev) +{ + struct uniphier_adapter_priv *priv; + struct device *dev = &pdev->dev; + int i, ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + priv->pdev = pdev; + + priv->spec = of_device_get_match_data(dev); + if (!priv->spec) + return -EINVAL; + + priv->fe = devm_kzalloc(dev, sizeof(*priv->fe) * priv->spec->adapters, + GFP_KERNEL); + if (!priv->fe) + return -ENOMEM; + + ret = uniphier_adapter_demux_probe(priv); + if (ret) + return ret; + + ret = uniphier_adapter_demod_probe(priv); + if (ret) + return ret; + + ret = uniphier_adapter_tuner_probe(priv); + if (ret) + return ret; + + platform_set_drvdata(pdev, priv); + + for (i = 0; i < priv->spec->adapters; i++) { + priv->chip->tsif[i].fe = priv->fe[i].fe; + + ret = hsc_register_dvb(&priv->chip->tsif[i]); + if (ret) { + dev_err(dev, "Failed to register adapter\n"); + goto err_out_if; + } + } + + return 0; + +err_out_if: + for (i = 0; i < priv->spec->adapters; i++) + hsc_unregister_dvb(&priv->chip->tsif[i]); + + return ret; +} + +static int uniphier_adapter_remove(struct platform_device *pdev) +{ + struct uniphier_adapter_priv *priv = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < priv->spec->adapters; i++) { + hsc_dmaif_release(&priv->chip->dmaif[i]); + hsc_tsif_release(&priv->chip->tsif[i]); + hsc_unregister_dvb(&priv->chip->tsif[i]); + dvb_module_release(priv->fe[i].tuner_i2c); + dvb_module_release(priv->fe[i].demod_i2c); + } + + return 0; +} + +static const struct hsc_conf ld20_hsc_conf[] = { + { + .css_in = HSC_CSS_IN_SRLTS2, + .css_out = HSC_CSS_OUT_TSI0, + .dpll = HSC_DPLL0, + .dma_out = HSC_DMA_OUT0, + }, + { + .css_in = HSC_CSS_IN_SRLTS3, + .css_out = HSC_CSS_OUT_TSI1, + .dpll = HSC_DPLL1, + .dma_out = HSC_DMA_OUT1, + }, +}; + +static const struct i2c_board_info mn884434_i2c_info[] = { + { .type = "mn884434-0", .addr = 0x68, }, + { .type = "mn884434-1", .addr = 0x6a, }, +}; + +static const struct i2c_board_info helene_i2c_info[] = { + { .type = "helene", .addr = 0x60, }, + { .type = "helene", .addr = 0x61, }, +}; + +static const struct uniphier_adapter_spec ld20_mn884434_helene_spec = { + .adapters = 2, + .hsc_conf = ld20_hsc_conf, + .demod_i2c_info = mn884434_i2c_info, + .tuner_i2c_info = helene_i2c_info, +}; + +static const struct of_device_id uniphier_hsc_adapter_of_match[] = { + { + .compatible = "socionext,uniphier-ld20-mn884434-helene", + .data = &ld20_mn884434_helene_spec, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, uniphier_hsc_adapter_of_match); + +static struct platform_driver uniphier_hsc_adapter_driver = { + .driver = { + .name = "uniphier-ld20-isdb", + .of_match_table = of_match_ptr(uniphier_hsc_adapter_of_match), + }, + .probe = uniphier_adapter_probe, + .remove = uniphier_adapter_remove, +}; +module_platform_driver(uniphier_hsc_adapter_driver); + +MODULE_AUTHOR("Katsuhiro Suzuki "); +MODULE_DESCRIPTION("UniPhier LD20 adapter driver for ISDB."); +MODULE_LICENSE("GPL v2");