From patchwork Wed Apr 25 09:39:15 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vinod Koul X-Patchwork-Id: 141628 Delivered-To: patch@linaro.org Received: by 10.46.151.6 with SMTP id r6csp627351lji; Wed, 25 Apr 2018 02:35:44 -0700 (PDT) X-Google-Smtp-Source: AIpwx48Z0+F8L+MFtGh969fhID0ghgAcdV9EK92ad8zE9OmlmzAFVnwLpeVoWxSlbKhMAR+YstyL X-Received: by 2002:adf:d142:: with SMTP id b2-v6mr23739547wri.178.1524648944098; Wed, 25 Apr 2018 02:35:44 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1524648944; cv=none; d=google.com; s=arc-20160816; b=JcN8P5sMZ0dlbPjRe0KxsMBFtAN5xAVdg6lbunQJ4UDB7T/XTyiccR1tWL3j3jJ+I9 IVXbe/v6nH22fD02G8f4nFe5UKFj6w2MCsRBqS9YTFMiBF/cq/fR9uA5jPlVZecCb2t8 Ate1SGOTTaMlsV084GW9naAGEiDvncys32vFSZgHJK/mpEsfONyLMaNCbnPoGgG3paBA LLr+b4OliLd78SPgI0RYRQJw0BxXgURnJZKz69tCkcEzytZhm4lbeIRv+jgIvqDEvBov s4fBTjAtZ3OsA37hpwPKde2D+OtRZ3ilnF8yvHjYZ8TpI3atoLws9ucdgA1wO3X+rLP9 z4BA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:cc:references:in-reply-to:message-id :date:to:from:delivered-to:arc-authentication-results; bh=5vagKrGkiUVc8IDNjPwoqZOx0JFvzwDDVKJiNZ8N0Hs=; b=ouMmPPIoadQCuzeKDQurK3xMwAQSKKtEKCSn6XMNE19dXDZ7DL7ZmICrW7ddUtlXy/ UzL6DwfrmBG6OAhukv0ZFBk0khmygQpNeCn+5Zuo83Xf6fmI495oukZTBnp450EiKb+Z RwNquct+segJkqCWoWm1iS8dn9C/RaL9uJrtD9WR3vtbsIyyaaqkStWaUHUT5Gjj1Axt hIwT5OtGcGxw1kgY5dfKcLXhomczUu4gUnpSaHGiiGjmBsLM0kYyNScmjQtyeMmFCRaM 0EBPPfpVTqLe2q1yNB/jP8E33IkVYWluJgxwHvkLmoiUvfnfZgzgnI3tSuwY8pQ04Yvy p3KQ== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of alsa-devel-bounces@alsa-project.org designates 77.48.224.243 as permitted sender) smtp.mailfrom=alsa-devel-bounces@alsa-project.org Return-Path: Received: from alsa0.perex.cz (alsa0.perex.cz. [77.48.224.243]) by mx.google.com with ESMTP id l6-v6si9673578wrm.388.2018.04.25.02.35.43; Wed, 25 Apr 2018 02:35:44 -0700 (PDT) Received-SPF: pass (google.com: domain of alsa-devel-bounces@alsa-project.org designates 77.48.224.243 as permitted sender) client-ip=77.48.224.243; Authentication-Results: mx.google.com; spf=pass (google.com: domain of alsa-devel-bounces@alsa-project.org designates 77.48.224.243 as permitted sender) smtp.mailfrom=alsa-devel-bounces@alsa-project.org Received: from alsa0.perex.cz (localhost [127.0.0.1]) by alsa0.perex.cz (Postfix) with ESMTP id 7C31626775A; Wed, 25 Apr 2018 11:35:04 +0200 (CEST) X-Original-To: alsa-devel@alsa-project.org Delivered-To: alsa-devel@alsa-project.org Received: by alsa0.perex.cz (Postfix, from userid 1000) id 610B526777F; Wed, 25 Apr 2018 11:35:02 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on mail1.perex.cz X-Spam-Level: X-Spam-Status: No, score=-5.0 required=5.0 tests=RCVD_IN_DNSWL_HI autolearn=disabled version=3.4.0 Received: from mga11.intel.com (mga11.intel.com [192.55.52.93]) by alsa0.perex.cz (Postfix) with ESMTP id D300C26777C for ; Wed, 25 Apr 2018 11:34:59 +0200 (CEST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga007.fm.intel.com ([10.253.24.52]) by fmsmga102.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 25 Apr 2018 02:34:58 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.49,325,1520924400"; d="scan'208";a="34503829" Received: from vkoul-udesk7.iind.intel.com ([10.223.84.143]) by fmsmga007.fm.intel.com with ESMTP; 25 Apr 2018 02:34:56 -0700 From: Vinod Koul To: Greg KH Date: Wed, 25 Apr 2018 15:09:15 +0530 Message-Id: <1524649163-12088-6-git-send-email-vkoul@kernel.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1524649163-12088-1-git-send-email-vkoul@kernel.org> References: <1524649163-12088-1-git-send-email-vkoul@kernel.org> Cc: ALSA , tiwai@suse.de, Pierre-Louis Bossart , liam.r.girdwood@linux.intel.com, patches.audio@intel.com, Vinod Koul , broonie@kernel.org, Sanyog Kale Subject: [alsa-devel] [PATCH v5 05/13] soundwire: Add helpers for ports operations X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: alsa-devel-bounces@alsa-project.org Sender: alsa-devel-bounces@alsa-project.org From: Sanyog Kale Add helpers to configure, prepare, enable, disable and de-prepare ports. Signed-off-by: Sanyog Kale Signed-off-by: Shreyas NC Signed-off-by: Vinod Koul --- drivers/soundwire/bus.c | 26 ++++ drivers/soundwire/bus.h | 2 + drivers/soundwire/stream.c | 271 ++++++++++++++++++++++++++++++++++++++++++ include/linux/soundwire/sdw.h | 54 +++++++++ 4 files changed, 353 insertions(+) -- 2.7.4 _______________________________________________ Alsa-devel mailing list Alsa-devel@alsa-project.org http://mailman.alsa-project.org/mailman/listinfo/alsa-devel diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index abf046f6b188..b8c93f0ac0a0 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -577,6 +577,32 @@ static void sdw_modify_slave_status(struct sdw_slave *slave, mutex_unlock(&slave->bus->bus_lock); } +int sdw_configure_dpn_intr(struct sdw_slave *slave, + int port, bool enable, int mask) +{ + u32 addr; + int ret; + u8 val = 0; + + addr = SDW_DPN_INTMASK(port); + + /* Set/Clear port ready interrupt mask */ + if (enable) { + val |= mask; + val |= SDW_DPN_INT_PORT_READY; + } else { + val &= ~(mask); + val &= ~SDW_DPN_INT_PORT_READY; + } + + ret = sdw_update(slave, addr, (mask | SDW_DPN_INT_PORT_READY), val); + if (ret < 0) + dev_err(slave->bus->dev, + "SDW_DPN_INTMASK write failed:%d", val); + + return ret; +} + static int sdw_initialize_slave(struct sdw_slave *slave) { struct sdw_slave_prop *prop = &slave->prop; diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h index 133268ab8086..07da6c4b10c4 100644 --- a/drivers/soundwire/bus.h +++ b/drivers/soundwire/bus.h @@ -109,6 +109,8 @@ struct sdw_master_runtime { struct sdw_dpn_prop *sdw_get_slave_dpn_prop(struct sdw_slave *slave, enum sdw_data_direction direction, unsigned int port_num); +int sdw_configure_dpn_intr(struct sdw_slave *slave, int port, + bool enable, int mask); int sdw_transfer(struct sdw_bus *bus, struct sdw_msg *msg); int sdw_transfer_defer(struct sdw_bus *bus, struct sdw_msg *msg, diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 3b6e850f5e5e..bac4cca9b57d 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -246,6 +246,277 @@ static int sdw_program_port_params(struct sdw_master_runtime *m_rt) } /** + * sdw_enable_disable_slave_ports: Enable/disable slave data port + * + * @bus: bus instance + * @s_rt: slave runtime + * @p_rt: port runtime + * @en: enable or disable operation + * + * This function only sets the enable/disable bits in the relevant bank, the + * actual enable/disable is done with a bank switch + */ +static int sdw_enable_disable_slave_ports(struct sdw_bus *bus, + struct sdw_slave_runtime *s_rt, + struct sdw_port_runtime *p_rt, bool en) +{ + struct sdw_transport_params *t_params = &p_rt->transport_params; + u32 addr; + int ret; + + if (bus->params.next_bank) + addr = SDW_DPN_CHANNELEN_B1(p_rt->num); + else + addr = SDW_DPN_CHANNELEN_B0(p_rt->num); + + /* + * Since bus doesn't support sharing a port across two streams, + * it is safe to reset this register + */ + if (en) + ret = sdw_update(s_rt->slave, addr, 0xFF, p_rt->ch_mask); + else + ret = sdw_update(s_rt->slave, addr, 0xFF, 0x0); + + if (ret < 0) + dev_err(&s_rt->slave->dev, + "Slave chn_en reg write failed:%d port:%d", + ret, t_params->port_num); + + return ret; +} + +static int sdw_enable_disable_master_ports(struct sdw_master_runtime *m_rt, + struct sdw_port_runtime *p_rt, bool en) +{ + struct sdw_transport_params *t_params = &p_rt->transport_params; + struct sdw_bus *bus = m_rt->bus; + struct sdw_enable_ch enable_ch; + int ret = 0; + + enable_ch.port_num = p_rt->num; + enable_ch.ch_mask = p_rt->ch_mask; + enable_ch.enable = en; + + /* Perform Master port channel(s) enable/disable */ + if (bus->port_ops->dpn_port_enable_ch) { + ret = bus->port_ops->dpn_port_enable_ch(bus, + &enable_ch, bus->params.next_bank); + if (ret < 0) { + dev_err(bus->dev, + "Master chn_en write failed:%d port:%d", + ret, t_params->port_num); + return ret; + } + } else { + dev_err(bus->dev, + "dpn_port_enable_ch not supported, %s failed\n", + en ? "enable" : "disable"); + return -EINVAL; + } + + return 0; +} + +/** + * sdw_enable_disable_ports() - Enable/disable port(s) for Master and + * Slave(s) + * + * @m_rt: Master stream runtime + * @en: mode (enable/disable) + */ +static int sdw_enable_disable_ports(struct sdw_master_runtime *m_rt, bool en) +{ + struct sdw_port_runtime *s_port, *m_port; + struct sdw_slave_runtime *s_rt = NULL; + int ret = 0; + + /* Enable/Disable Slave port(s) */ + list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) { + list_for_each_entry(s_port, &s_rt->port_list, port_node) { + ret = sdw_enable_disable_slave_ports(m_rt->bus, s_rt, + s_port, en); + if (ret < 0) + return ret; + } + } + + /* Enable/Disable Master port(s) */ + list_for_each_entry(m_port, &m_rt->port_list, port_node) { + ret = sdw_enable_disable_master_ports(m_rt, m_port, en); + if (ret < 0) + return ret; + } + + return 0; +} + +static int sdw_do_port_prep(struct sdw_slave_runtime *s_rt, + struct sdw_prepare_ch prep_ch, enum sdw_port_prep_ops cmd) +{ + const struct sdw_slave_ops *ops = s_rt->slave->ops; + int ret; + + if (ops->port_prep) { + ret = ops->port_prep(s_rt->slave, &prep_ch, cmd); + if (ret < 0) { + dev_err(&s_rt->slave->dev, + "Slave Port Prep cmd %d failed: %d", cmd, ret); + return ret; + } + } + + return 0; +} + +static int sdw_prep_deprep_slave_ports(struct sdw_bus *bus, + struct sdw_slave_runtime *s_rt, + struct sdw_port_runtime *p_rt, bool prep) +{ + struct completion *port_ready = NULL; + struct sdw_dpn_prop *dpn_prop; + struct sdw_prepare_ch prep_ch; + unsigned int time_left; + bool intr = false; + int ret = 0, val; + u32 addr; + + prep_ch.num = p_rt->num; + prep_ch.ch_mask = p_rt->ch_mask; + + dpn_prop = sdw_get_slave_dpn_prop(s_rt->slave, + s_rt->direction, + prep_ch.num); + if (!dpn_prop) { + dev_err(bus->dev, + "Slave Port:%d properties not found", prep_ch.num); + return -EINVAL; + } + + prep_ch.prepare = prep; + + prep_ch.bank = bus->params.next_bank; + + if ((dpn_prop->device_interrupts) || (!dpn_prop->simple_ch_prep_sm)) + intr = true; + + /* + * Enable interrupt before Port prepare. + * For Port de-prepare, it is assumed that port + * was prepared earlier + */ + if (prep && intr) { + ret = sdw_configure_dpn_intr(s_rt->slave, p_rt->num, prep, + dpn_prop->device_interrupts); + if (ret < 0) + return ret; + } + + /* Inform slave about the impending port prepare */ + sdw_do_port_prep(s_rt, prep_ch, SDW_OPS_PORT_PRE_PREP); + + /* Prepare Slave port implementing CP_SM */ + if (!dpn_prop->simple_ch_prep_sm) { + addr = SDW_DPN_PREPARECTRL(p_rt->num); + + if (prep) + ret = sdw_update(s_rt->slave, addr, + 0xFF, p_rt->ch_mask); + else + ret = sdw_update(s_rt->slave, addr, 0xFF, 0x0); + + if (ret < 0) { + dev_err(&s_rt->slave->dev, + "Slave prep_ctrl reg write failed"); + return ret; + } + + /* Wait for completion on port ready */ + port_ready = &s_rt->slave->port_ready[prep_ch.num]; + time_left = wait_for_completion_timeout(port_ready, + msecs_to_jiffies(dpn_prop->ch_prep_timeout)); + + val = sdw_read(s_rt->slave, SDW_DPN_PREPARESTATUS(p_rt->num)); + val &= p_rt->ch_mask; + if (!time_left || val) { + dev_err(&s_rt->slave->dev, + "Chn prep failed for port:%d", prep_ch.num); + return -ETIMEDOUT; + } + } + + /* Inform slaves about ports prepared */ + sdw_do_port_prep(s_rt, prep_ch, SDW_OPS_PORT_POST_PREP); + + /* Disable interrupt after Port de-prepare */ + if ((!prep) && (intr)) + ret = sdw_configure_dpn_intr(s_rt->slave, p_rt->num, prep, + dpn_prop->device_interrupts); + + return ret; +} + +static int sdw_prep_deprep_master_ports(struct sdw_master_runtime *m_rt, + struct sdw_port_runtime *p_rt, bool prep) +{ + struct sdw_transport_params *t_params = &p_rt->transport_params; + struct sdw_bus *bus = m_rt->bus; + const struct sdw_master_port_ops *ops = bus->port_ops; + struct sdw_prepare_ch prep_ch; + int ret = 0; + + prep_ch.num = p_rt->num; + prep_ch.ch_mask = p_rt->ch_mask; + prep_ch.prepare = prep; /* Prepare/De-prepare */ + prep_ch.bank = bus->params.next_bank; + + /* Pre-prepare/Pre-deprepare port(s) */ + if (ops->dpn_port_prep) { + ret = ops->dpn_port_prep(bus, &prep_ch); + if (ret < 0) { + dev_err(bus->dev, "Port prepare failed for port:%d", + t_params->port_num); + return ret; + } + } + + return ret; +} + +/** + * sdw_prep_deprep_ports() - Prepare/De-prepare port(s) for Master(s) and + * Slave(s) + * + * @m_rt: Master runtime handle + * @prep: Prepare or De-prepare + */ +static int sdw_prep_deprep_ports(struct sdw_master_runtime *m_rt, bool prep) +{ + struct sdw_slave_runtime *s_rt = NULL; + struct sdw_port_runtime *p_rt; + int ret = 0; + + /* Prepare/De-prepare Slave port(s) */ + list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) { + list_for_each_entry(p_rt, &s_rt->port_list, port_node) { + ret = sdw_prep_deprep_slave_ports(m_rt->bus, s_rt, + p_rt, prep); + if (ret < 0) + return ret; + } + } + + /* Prepare/De-prepare Master port(s) */ + list_for_each_entry(p_rt, &m_rt->port_list, port_node) { + ret = sdw_prep_deprep_master_ports(m_rt, p_rt, prep); + if (ret < 0) + return ret; + } + + return ret; +} + +/** * sdw_release_stream() - Free the assigned stream runtime * * @stream: SoundWire stream runtime diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 453fe11b9eb2..6b52e6c53c22 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -377,6 +377,37 @@ enum sdw_reg_bank { }; /** + * struct sdw_prepare_ch: Prepare/De-prepare Data Port channel + * + * @num: Port number + * @ch_mask: Active channel mask + * @prepare: Prepare (true) /de-prepare (false) channel + * @bank: Register bank, which bank Slave/Master driver should program for + * implementation defined registers. This is always updated to next_bank + * value read from bus params. + * + */ +struct sdw_prepare_ch { + unsigned int num; + unsigned int ch_mask; + bool prepare; + unsigned int bank; +}; + +/** + * enum sdw_port_prep_ops: Prepare operations for Data Port + * + * @SDW_OPS_PORT_PRE_PREP: Pre prepare operation for the Port + * @SDW_OPS_PORT_PREP: Prepare operation for the Port + * @SDW_OPS_PORT_POST_PREP: Post prepare operation for the Port + */ +enum sdw_port_prep_ops { + SDW_OPS_PORT_PRE_PREP = 0, + SDW_OPS_PORT_PREP = 1, + SDW_OPS_PORT_POST_PREP = 2, +}; + +/** * struct sdw_bus_params: Structure holding bus configuration * * @curr_bank: Current bank in use (BANK0/BANK1) @@ -395,6 +426,7 @@ struct sdw_bus_params { * @interrupt_callback: Device interrupt notification (invoked in thread * context) * @update_status: Update Slave status + * @port_prep: Prepare the port with parameters */ struct sdw_slave_ops { int (*read_prop)(struct sdw_slave *sdw); @@ -402,6 +434,9 @@ struct sdw_slave_ops { struct sdw_slave_intr_status *status); int (*update_status)(struct sdw_slave *slave, enum sdw_slave_status status); + int (*port_prep)(struct sdw_slave *slave, + struct sdw_prepare_ch *prepare_ch, + enum sdw_port_prep_ops pre_ops); }; /** @@ -506,6 +541,19 @@ struct sdw_transport_params { }; /** + * struct sdw_enable_ch: Enable/disable Data Port channel + * + * @num: Port number + * @ch_mask: Active channel mask + * @enable: Enable (true) /disable (false) channel + */ +struct sdw_enable_ch { + unsigned int port_num; + unsigned int ch_mask; + bool enable; +}; + +/** * struct sdw_master_port_ops: Callback functions from bus to Master * driver to set Master Data ports. * @@ -513,6 +561,8 @@ struct sdw_transport_params { * Mandatory callback * @dpn_set_port_transport_params: Set transport parameters for the Master * Port. Mandatory callback + * @dpn_port_prep: Port prepare operations for the Master Data Port. + * @dpn_port_enable_ch: Enable the channels of Master Port. */ struct sdw_master_port_ops { int (*dpn_set_port_params)(struct sdw_bus *bus, @@ -521,6 +571,10 @@ struct sdw_master_port_ops { int (*dpn_set_port_transport_params)(struct sdw_bus *bus, struct sdw_transport_params *transport_params, enum sdw_reg_bank bank); + int (*dpn_port_prep)(struct sdw_bus *bus, + struct sdw_prepare_ch *prepare_ch); + int (*dpn_port_enable_ch)(struct sdw_bus *bus, + struct sdw_enable_ch *enable_ch, unsigned int bank); }; struct sdw_msg;