// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)// Copyright(c) 2015-18 Intel Corporation. /* * stream.c - SoundWire Bus stream operations. */ #include <linux/delay.h>#include <linux/device.h>#include <linux/init.h>#include <linux/iopoll.h>#include <linux/module.h>#include <linux/slab.h>#include <linux/soundwire/sdw_registers.h>#include <linux/soundwire/sdw.h>#include <linux/soundwire/sdw_type.h>#include <linux/string_choices.h>#include <sound/soc.h>#include "bus.h" #define SDW_PORT_PREP_POLL_USEC 1000 /* * Array of supported rows and columns as per MIPI SoundWire Specification 1.1 * * The rows are arranged as per the array index value programmed * in register. The index 15 has dummy value 0 in order to fill hole. */int sdw_rows[SDW_FRAME_ROWS] = {48, 50, 60, 64, 75, 80, 125, 147, 96, 100, 120, 128, 150, 160, 250, 0, 192, 200, 240, 256, 72, 144, 90, 180};EXPORT_SYMBOL(sdw_rows); int sdw_cols[SDW_FRAME_COLS] = {2, 4, 6, 8, 10, 12, 14, 16};EXPORT_SYMBOL(sdw_cols); int sdw_find_col_index(int col){ int i; for (i = 0; i < SDW_FRAME_COLS; i++) { if (sdw_cols[i] == col) return i; } pr_warn("Requested column not found, selecting lowest column no: 2\n"); return 0;}EXPORT_SYMBOL(sdw_find_col_index); int sdw_find_row_index(int row){ int i; for (i = 0; i < SDW_FRAME_ROWS; i++) { if (sdw_rows[i] == row) return i; } pr_warn("Requested row not found, selecting lowest row no: 48\n"); return 0;}EXPORT_SYMBOL(sdw_find_row_index); static int _sdw_program_slave_port_params(struct sdw_bus *bus, struct sdw_slave *slave, struct sdw_transport_params *t_params, enum sdw_dpn_type type){ u32 addr1, addr2, addr3, addr4; int ret; u16 wbuf; if (bus->params.next_bank) { addr1 = SDW_DPN_OFFSETCTRL2_B1(t_params->port_num); addr2 = SDW_DPN_BLOCKCTRL3_B1(t_params->port_num); addr3 = SDW_DPN_SAMPLECTRL2_B1(t_params->port_num); addr4 = SDW_DPN_HCTRL_B1(t_params->port_num); } else { addr1 = SDW_DPN_OFFSETCTRL2_B0(t_params->port_num); addr2 = SDW_DPN_BLOCKCTRL3_B0(t_params->port_num); addr3 = SDW_DPN_SAMPLECTRL2_B0(t_params->port_num); addr4 = SDW_DPN_HCTRL_B0(t_params->port_num); } /* Program DPN_OffsetCtrl2 registers */ ret = sdw_write_no_pm(slave, addr1, t_params->offset2); if (ret < 0) { dev_err(bus->dev, "DPN_OffsetCtrl2 register write failed\n"); return ret; } /* DP0 does not implement BlockCtrl3 */ if (t_params->port_num) { /* Program DPN_BlockCtrl3 register */ ret = sdw_write_no_pm(slave, addr2, t_params->blk_pkg_mode); if (ret < 0) { dev_err(bus->dev, "DPN_BlockCtrl3 register write failed\n"); return ret; } } /* * Data ports are FULL, SIMPLE and REDUCED. This function handles * FULL and REDUCED only and beyond this point only FULL is * handled, so bail out if we are not FULL data port type */ if (type != SDW_DPN_FULL) return ret; /* Program DPN_SampleCtrl2 register */ wbuf = FIELD_GET(SDW_DPN_SAMPLECTRL_HIGH, t_params->sample_interval - 1); ret = sdw_write_no_pm(slave, addr3, wbuf); if (ret < 0) { dev_err(bus->dev, "DPN_SampleCtrl2 register write failed\n"); return ret; } /* Program DPN_HCtrl register */ wbuf = FIELD_PREP(SDW_DPN_HCTRL_HSTART, t_params->hstart); wbuf |= FIELD_PREP(SDW_DPN_HCTRL_HSTOP, t_params->hstop); ret = sdw_write_no_pm(slave, addr4, wbuf); if (ret < 0) dev_err(bus->dev, "DPN_HCtrl register write failed\n"); return ret;} static int sdw_program_slave_port_params(struct sdw_bus *bus, struct sdw_slave_runtime *s_rt, struct sdw_port_runtime *p_rt){ struct sdw_transport_params *t_params = &p_rt->transport_params; struct sdw_port_params *p_params = &p_rt->port_params; struct sdw_slave_prop *slave_prop = &s_rt->slave->prop; u32 addr1, addr2, addr3, addr4, addr5, addr6; enum sdw_dpn_type port_type; bool read_only_wordlength; int ret; u8 wbuf; if (s_rt->slave->is_mockup_device) return 0; if (t_params->port_num) { struct sdw_dpn_prop *dpn_prop; dpn_prop = sdw_get_slave_dpn_prop(s_rt->slave, s_rt->direction, t_params->port_num); if (!dpn_prop) return -EINVAL; read_only_wordlength = dpn_prop->read_only_wordlength; port_type = dpn_prop->type; } else { read_only_wordlength = false; port_type = SDW_DPN_FULL; } addr1 = SDW_DPN_PORTCTRL(t_params->port_num); addr2 = SDW_DPN_BLOCKCTRL1(t_params->port_num); if (bus->params.next_bank) { addr3 = SDW_DPN_SAMPLECTRL1_B1(t_params->port_num); addr4 = SDW_DPN_OFFSETCTRL1_B1(t_params->port_num); addr5 = SDW_DPN_BLOCKCTRL2_B1(t_params->port_num); addr6 = SDW_DPN_LANECTRL_B1(t_params->port_num); } else { addr3 = SDW_DPN_SAMPLECTRL1_B0(t_params->port_num); addr4 = SDW_DPN_OFFSETCTRL1_B0(t_params->port_num); addr5 = SDW_DPN_BLOCKCTRL2_B0(t_params->port_num); addr6 = SDW_DPN_LANECTRL_B0(t_params->port_num); } /* Program DPN_PortCtrl register */ wbuf = FIELD_PREP(SDW_DPN_PORTCTRL_DATAMODE, p_params->data_mode); wbuf |= FIELD_PREP(SDW_DPN_PORTCTRL_FLOWMODE, p_params->flow_mode); ret = sdw_update_no_pm(s_rt->slave, addr1, 0xF, wbuf); if (ret < 0) { dev_err(&s_rt->slave->dev, "DPN_PortCtrl register write failed for port %d\n", t_params->port_num); return ret; } if (!read_only_wordlength) { /* Program DPN_BlockCtrl1 register */ ret = sdw_write_no_pm(s_rt->slave, addr2, (p_params->bps - 1)); if (ret < 0) { dev_err(&s_rt->slave->dev, "DPN_BlockCtrl1 register write failed for port %d\n", t_params->port_num); return ret; } } /* Program DPN_SampleCtrl1 register */ wbuf = (t_params->sample_interval - 1) & SDW_DPN_SAMPLECTRL_LOW; ret = sdw_write_no_pm(s_rt->slave, addr3, wbuf); if (ret < 0) { dev_err(&s_rt->slave->dev, "DPN_SampleCtrl1 register write failed for port %d\n", t_params->port_num); return ret; } /* Program DPN_OffsetCtrl1 registers */ ret = sdw_write_no_pm(s_rt->slave, addr4, t_params->offset1); if (ret < 0) { dev_err(&s_rt->slave->dev, "DPN_OffsetCtrl1 register write failed for port %d\n", t_params->port_num); return ret; } /* Program DPN_BlockCtrl2 register*/ if (t_params->blk_grp_ctrl_valid) { ret = sdw_write_no_pm(s_rt->slave, addr5, t_params->blk_grp_ctrl); if (ret < 0) { dev_err(&s_rt->slave->dev, "DPN_BlockCtrl2 reg write failed for port %d\n", t_params->port_num); return ret; } } /* program DPN_LaneCtrl register */ if (slave_prop->lane_control_support) { ret = sdw_write_no_pm(s_rt->slave, addr6, t_params->lane_ctrl); if (ret < 0) { dev_err(&s_rt->slave->dev, "DPN_LaneCtrl register write failed for port %d\n", t_params->port_num); return ret; } } if (port_type != SDW_DPN_SIMPLE) { ret = _sdw_program_slave_port_params(bus, s_rt->slave, t_params, port_type); if (ret < 0) dev_err(&s_rt->slave->dev, "Transport reg write failed for port: %d\n", t_params->port_num); } return ret;} static int sdw_program_master_port_params(struct sdw_bus *bus, struct sdw_port_runtime *p_rt){ int ret; /* * we need to set transport and port parameters for the port. * Transport parameters refers to the sample interval, offsets and * hstart/stop etc of the data. Port parameters refers to word * length, flow mode etc of the port */ ret = bus->port_ops->dpn_set_port_transport_params(bus, &p_rt->transport_params, bus->params.next_bank); if (ret < 0) return ret; return bus->port_ops->dpn_set_port_params(bus, &p_rt->port_params, bus->params.next_bank);} /** * sdw_program_port_params() - Programs transport parameters of Master(s) * and Slave(s) * * @m_rt: Master stream runtime */static int sdw_program_port_params(struct sdw_master_runtime *m_rt){ struct sdw_slave_runtime *s_rt; struct sdw_bus *bus = m_rt->bus; struct sdw_port_runtime *p_rt; int ret = 0; /* Program transport & port parameters for Slave(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_program_slave_port_params(bus, s_rt, p_rt); if (ret < 0) return ret; } } /* Program transport & port parameters for Master(s) */ list_for_each_entry(p_rt, &m_rt->port_list, port_node) { ret = sdw_program_master_port_params(bus, p_rt); if (ret < 0) return ret; } return 0;} /** * 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_write_no_pm(s_rt->slave, addr, p_rt->ch_mask); else ret = sdw_write_no_pm(s_rt->slave, addr, 0x0); if (ret < 0) dev_err(&s_rt->slave->dev, "Slave chn_en reg write failed:%d port:%d\n", 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; 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\n", ret, t_params->port_num); return ret; } } else { dev_err(bus->dev, "dpn_port_enable_ch not supported, %s failed\n", str_enable_disable(en)); 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; 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){ int ret = 0; struct sdw_slave *slave = s_rt->slave; mutex_lock(&slave->sdw_dev_lock); if (slave->probed) { struct device *dev = &slave->dev; struct sdw_driver *drv = drv_to_sdw_driver(dev->driver); if (drv->ops && drv->ops->port_prep) { ret = drv->ops->port_prep(slave, &prep_ch, cmd); if (ret < 0) dev_err(dev, "Slave Port Prep cmd %d failed: %d\n", cmd, ret); } } mutex_unlock(&slave->sdw_dev_lock); return ret;} 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 sdw_dpn_prop *dpn_prop; struct sdw_prepare_ch prep_ch; u32 imp_def_interrupts; bool simple_ch_prep_sm; u32 ch_prep_timeout; bool intr = false; int ret = 0, val; u32 addr; prep_ch.num = p_rt->num; prep_ch.ch_mask = p_rt->ch_mask; if (p_rt->num) { 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\n", prep_ch.num); return -EINVAL; } imp_def_interrupts = dpn_prop->imp_def_interrupts; simple_ch_prep_sm = dpn_prop->simple_ch_prep_sm; ch_prep_timeout = dpn_prop->ch_prep_timeout; } else { struct sdw_dp0_prop *dp0_prop = s_rt->slave->prop.dp0_prop; if (!dp0_prop) { dev_err(bus->dev, "Slave DP0 properties not found\n"); return -EINVAL; } imp_def_interrupts = dp0_prop->imp_def_interrupts; simple_ch_prep_sm = dp0_prop->simple_ch_prep_sm; ch_prep_timeout = dp0_prop->ch_prep_timeout; } prep_ch.prepare = prep; prep_ch.bank = bus->params.next_bank; if (imp_def_interrupts || !simple_ch_prep_sm || bus->params.s_data_mode != SDW_PORT_DATA_MODE_NORMAL) 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, imp_def_interrupts); if (ret < 0)Lines 1–500 of 2265. Load more
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) // Copyright(c) 2015-18 Intel Corporation. /* * stream.c - SoundWire Bus stream operations. */ #include <linux/delay.h> #include <linux/device.h> #include <linux/init.h> #include <linux/iopoll.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/soundwire/sdw_registers.h> #include <linux/soundwire/sdw.h> #include <linux/soundwire/sdw_type.h> #include <linux/string_choices.h> #include <sound/soc.h> #include "bus.h" #define SDW_PORT_PREP_POLL_USEC 1000 /* * Array of supported rows and columns as per MIPI SoundWire Specification 1.1 * * The rows are arranged as per the array index value programmed * in register. The index 15 has dummy value 0 in order to fill hole. */ int sdw_rows[SDW_FRAME_ROWS] = {48, 50, 60, 64, 75, 80, 125, 147, 96, 100, 120, 128, 150, 160, 250, 0, 192, 200, 240, 256, 72, 144, 90, 180}; EXPORT_SYMBOL(sdw_rows); int sdw_cols[SDW_FRAME_COLS] = {2, 4, 6, 8, 10, 12, 14, 16}; EXPORT_SYMBOL(sdw_cols); int sdw_find_col_index(int col) { int i; for (i = 0; i < SDW_FRAME_COLS; i++) { if (sdw_cols[i] == col) return i; } pr_warn("Requested column not found, selecting lowest column no: 2\n"); return 0; } EXPORT_SYMBOL(sdw_find_col_index); int sdw_find_row_index(int row) { int i; for (i = 0; i < SDW_FRAME_ROWS; i++) { if (sdw_rows[i] == row) return i; } pr_warn("Requested row not found, selecting lowest row no: 48\n"); return 0; } EXPORT_SYMBOL(sdw_find_row_index); static int _sdw_program_slave_port_params(struct sdw_bus *bus, struct sdw_slave *slave, struct sdw_transport_params *t_params, enum sdw_dpn_type type) { u32 addr1, addr2, addr3, addr4; int ret; u16 wbuf; if (bus->params.next_bank) { addr1 = SDW_DPN_OFFSETCTRL2_B1(t_params->port_num); addr2 = SDW_DPN_BLOCKCTRL3_B1(t_params->port_num); addr3 = SDW_DPN_SAMPLECTRL2_B1(t_params->port_num); addr4 = SDW_DPN_HCTRL_B1(t_params->port_num); } else { addr1 = SDW_DPN_OFFSETCTRL2_B0(t_params->port_num); addr2 = SDW_DPN_BLOCKCTRL3_B0(t_params->port_num); addr3 = SDW_DPN_SAMPLECTRL2_B0(t_params->port_num); addr4 = SDW_DPN_HCTRL_B0(t_params->port_num); } /* Program DPN_OffsetCtrl2 registers */ ret = sdw_write_no_pm(slave, addr1, t_params->offset2); if (ret < 0) { dev_err(bus->dev, "DPN_OffsetCtrl2 register write failed\n"); return ret; } /* DP0 does not implement BlockCtrl3 */ if (t_params->port_num) { /* Program DPN_BlockCtrl3 register */ ret = sdw_write_no_pm(slave, addr2, t_params->blk_pkg_mode); if (ret < 0) { dev_err(bus->dev, "DPN_BlockCtrl3 register write failed\n"); return ret; } } /* * Data ports are FULL, SIMPLE and REDUCED. This function handles * FULL and REDUCED only and beyond this point only FULL is * handled, so bail out if we are not FULL data port type */ if (type != SDW_DPN_FULL) return ret; /* Program DPN_SampleCtrl2 register */ wbuf = FIELD_GET(SDW_DPN_SAMPLECTRL_HIGH, t_params->sample_interval - 1); ret = sdw_write_no_pm(slave, addr3, wbuf); if (ret < 0) { dev_err(bus->dev, "DPN_SampleCtrl2 register write failed\n"); return ret; } /* Program DPN_HCtrl register */ wbuf = FIELD_PREP(SDW_DPN_HCTRL_HSTART, t_params->hstart); wbuf |= FIELD_PREP(SDW_DPN_HCTRL_HSTOP, t_params->hstop); ret = sdw_write_no_pm(slave, addr4, wbuf); if (ret < 0) dev_err(bus->dev, "DPN_HCtrl register write failed\n"); return ret; } static int sdw_program_slave_port_params(struct sdw_bus *bus, struct sdw_slave_runtime *s_rt, struct sdw_port_runtime *p_rt) { struct sdw_transport_params *t_params = &p_rt->transport_params; struct sdw_port_params *p_params = &p_rt->port_params; struct sdw_slave_prop *slave_prop = &s_rt->slave->prop; u32 addr1, addr2, addr3, addr4, addr5, addr6; enum sdw_dpn_type port_type; bool read_only_wordlength; int ret; u8 wbuf; if (s_rt->slave->is_mockup_device) return 0; if (t_params->port_num) { struct sdw_dpn_prop *dpn_prop; dpn_prop = sdw_get_slave_dpn_prop(s_rt->slave, s_rt->direction, t_params->port_num); if (!dpn_prop) return -EINVAL; read_only_wordlength = dpn_prop->read_only_wordlength; port_type = dpn_prop->type; } else { read_only_wordlength = false; port_type = SDW_DPN_FULL; } addr1 = SDW_DPN_PORTCTRL(t_params->port_num); addr2 = SDW_DPN_BLOCKCTRL1(t_params->port_num); if (bus->params.next_bank) { addr3 = SDW_DPN_SAMPLECTRL1_B1(t_params->port_num); addr4 = SDW_DPN_OFFSETCTRL1_B1(t_params->port_num); addr5 = SDW_DPN_BLOCKCTRL2_B1(t_params->port_num); addr6 = SDW_DPN_LANECTRL_B1(t_params->port_num); } else { addr3 = SDW_DPN_SAMPLECTRL1_B0(t_params->port_num); addr4 = SDW_DPN_OFFSETCTRL1_B0(t_params->port_num); addr5 = SDW_DPN_BLOCKCTRL2_B0(t_params->port_num); addr6 = SDW_DPN_LANECTRL_B0(t_params->port_num); } /* Program DPN_PortCtrl register */ wbuf = FIELD_PREP(SDW_DPN_PORTCTRL_DATAMODE, p_params->data_mode); wbuf |= FIELD_PREP(SDW_DPN_PORTCTRL_FLOWMODE, p_params->flow_mode); ret = sdw_update_no_pm(s_rt->slave, addr1, 0xF, wbuf); if (ret < 0) { dev_err(&s_rt->slave->dev, "DPN_PortCtrl register write failed for port %d\n", t_params->port_num); return ret; } if (!read_only_wordlength) { /* Program DPN_BlockCtrl1 register */ ret = sdw_write_no_pm(s_rt->slave, addr2, (p_params->bps - 1)); if (ret < 0) { dev_err(&s_rt->slave->dev, "DPN_BlockCtrl1 register write failed for port %d\n", t_params->port_num); return ret; } } /* Program DPN_SampleCtrl1 register */ wbuf = (t_params->sample_interval - 1) & SDW_DPN_SAMPLECTRL_LOW; ret = sdw_write_no_pm(s_rt->slave, addr3, wbuf); if (ret < 0) { dev_err(&s_rt->slave->dev, "DPN_SampleCtrl1 register write failed for port %d\n", t_params->port_num); return ret; } /* Program DPN_OffsetCtrl1 registers */ ret = sdw_write_no_pm(s_rt->slave, addr4, t_params->offset1); if (ret < 0) { dev_err(&s_rt->slave->dev, "DPN_OffsetCtrl1 register write failed for port %d\n", t_params->port_num); return ret; } /* Program DPN_BlockCtrl2 register*/ if (t_params->blk_grp_ctrl_valid) { ret = sdw_write_no_pm(s_rt->slave, addr5, t_params->blk_grp_ctrl); if (ret < 0) { dev_err(&s_rt->slave->dev, "DPN_BlockCtrl2 reg write failed for port %d\n", t_params->port_num); return ret; } } /* program DPN_LaneCtrl register */ if (slave_prop->lane_control_support) { ret = sdw_write_no_pm(s_rt->slave, addr6, t_params->lane_ctrl); if (ret < 0) { dev_err(&s_rt->slave->dev, "DPN_LaneCtrl register write failed for port %d\n", t_params->port_num); return ret; } } if (port_type != SDW_DPN_SIMPLE) { ret = _sdw_program_slave_port_params(bus, s_rt->slave, t_params, port_type); if (ret < 0) dev_err(&s_rt->slave->dev, "Transport reg write failed for port: %d\n", t_params->port_num); } return ret; } static int sdw_program_master_port_params(struct sdw_bus *bus, struct sdw_port_runtime *p_rt) { int ret; /* * we need to set transport and port parameters for the port. * Transport parameters refers to the sample interval, offsets and * hstart/stop etc of the data. Port parameters refers to word * length, flow mode etc of the port */ ret = bus->port_ops->dpn_set_port_transport_params(bus, &p_rt->transport_params, bus->params.next_bank); if (ret < 0) return ret; return bus->port_ops->dpn_set_port_params(bus, &p_rt->port_params, bus->params.next_bank); } /** * sdw_program_port_params() - Programs transport parameters of Master(s) * and Slave(s) * * @m_rt: Master stream runtime */ static int sdw_program_port_params(struct sdw_master_runtime *m_rt) { struct sdw_slave_runtime *s_rt; struct sdw_bus *bus = m_rt->bus; struct sdw_port_runtime *p_rt; int ret = 0; /* Program transport & port parameters for Slave(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_program_slave_port_params(bus, s_rt, p_rt); if (ret < 0) return ret; } } /* Program transport & port parameters for Master(s) */ list_for_each_entry(p_rt, &m_rt->port_list, port_node) { ret = sdw_program_master_port_params(bus, p_rt); if (ret < 0) return ret; } return 0; } /** * 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_write_no_pm(s_rt->slave, addr, p_rt->ch_mask); else ret = sdw_write_no_pm(s_rt->slave, addr, 0x0); if (ret < 0) dev_err(&s_rt->slave->dev, "Slave chn_en reg write failed:%d port:%d\n", 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; 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\n", ret, t_params->port_num); return ret; } } else { dev_err(bus->dev, "dpn_port_enable_ch not supported, %s failed\n", str_enable_disable(en)); 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; 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) { int ret = 0; struct sdw_slave *slave = s_rt->slave; mutex_lock(&slave->sdw_dev_lock); if (slave->probed) { struct device *dev = &slave->dev; struct sdw_driver *drv = drv_to_sdw_driver(dev->driver); if (drv->ops && drv->ops->port_prep) { ret = drv->ops->port_prep(slave, &prep_ch, cmd); if (ret < 0) dev_err(dev, "Slave Port Prep cmd %d failed: %d\n", cmd, ret); } } mutex_unlock(&slave->sdw_dev_lock); return ret; } 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 sdw_dpn_prop *dpn_prop; struct sdw_prepare_ch prep_ch; u32 imp_def_interrupts; bool simple_ch_prep_sm; u32 ch_prep_timeout; bool intr = false; int ret = 0, val; u32 addr; prep_ch.num = p_rt->num; prep_ch.ch_mask = p_rt->ch_mask; if (p_rt->num) { 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\n", prep_ch.num); return -EINVAL; } imp_def_interrupts = dpn_prop->imp_def_interrupts; simple_ch_prep_sm = dpn_prop->simple_ch_prep_sm; ch_prep_timeout = dpn_prop->ch_prep_timeout; } else { struct sdw_dp0_prop *dp0_prop = s_rt->slave->prop.dp0_prop; if (!dp0_prop) { dev_err(bus->dev, "Slave DP0 properties not found\n"); return -EINVAL; } imp_def_interrupts = dp0_prop->imp_def_interrupts; simple_ch_prep_sm = dp0_prop->simple_ch_prep_sm; ch_prep_timeout = dp0_prop->ch_prep_timeout; } prep_ch.prepare = prep; prep_ch.bank = bus->params.next_bank; if (imp_def_interrupts || !simple_ch_prep_sm || bus->params.s_data_mode != SDW_PORT_DATA_MODE_NORMAL) 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, imp_def_interrupts); if (ret < 0)