Message ID | 1522946904-2089-11-git-send-email-vinod.koul@intel.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On 4/5/18 11:48 AM, Vinod Koul wrote: > Add support for Cadence port management and implement > master port ops. > > Signed-off-by: Sanyog Kale <sanyog.r.kale@intel.com> > Signed-off-by: Shreyas NC <shreyas.nc@intel.com> > Signed-off-by: Vinod Koul <vinod.koul@intel.com> > --- > drivers/soundwire/cadence_master.c | 256 +++++++++++++++++++++++++++++++++++++ > drivers/soundwire/cadence_master.h | 100 +++++++++++++++ > drivers/soundwire/intel.c | 1 + > 3 files changed, 357 insertions(+) > > diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c > index b0c09efd8f83..89a4ae86d36a 100644 > --- a/drivers/soundwire/cadence_master.c > +++ b/drivers/soundwire/cadence_master.c > @@ -669,6 +669,133 @@ int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns) > } > EXPORT_SYMBOL(sdw_cdns_enable_interrupt); > > +static int cdns_allocate_pdi(struct sdw_cdns *cdns, > + struct sdw_cdns_pdi **stream, > + u32 start, u32 num, u32 pdi_offset) the start parameter doesn't seem to be used, remove and remove the useless zeroes in all the calls to this routine? > +{ > + struct sdw_cdns_pdi *pdi; > + int i; > + > + if (!num) > + return 0; > + > + pdi = devm_kcalloc(cdns->dev, num, sizeof(*pdi), GFP_KERNEL); > + if (!pdi) > + return -ENOMEM; > + > + for (i = 0; i < num; i++) { > + pdi[i].num = i + pdi_offset; > + pdi[i].assigned = false; > + } > + > + *stream = pdi; > + return 0; > +} > + > +/** > + * sdw_cdns_pdi_init: PDI initialization routine > + * > + * @cdns: Cadence instance > + * @config: Stream configurations > + */ > +int sdw_cdns_pdi_init(struct sdw_cdns *cdns, > + struct sdw_cdns_stream_config config) > +{ > + struct sdw_cdns_streams *stream; > + int offset, i, ret; > + > + cdns->pcm.num_bd = config.pcm_bd; > + cdns->pcm.num_in = config.pcm_in; > + cdns->pcm.num_out = config.pcm_out; > + cdns->pdm.num_bd = config.pdm_bd; > + cdns->pdm.num_in = config.pdm_in; > + cdns->pdm.num_out = config.pdm_out; > + > + /* Allocate PDIs for PCMs */ > + stream = &cdns->pcm; > + > + /* First two PDIs are reserved for bulk transfers */ > + stream->num_bd -= CDNS_PCM_PDI_OFFSET; > + offset = CDNS_PCM_PDI_OFFSET; > + > + ret = cdns_allocate_pdi(cdns, &stream->bd, 0, > + stream->num_bd, offset); > + if (ret) > + goto pcm_error; > + > + offset += stream->num_bd; > + > + ret = cdns_allocate_pdi(cdns, &stream->in, 0, > + stream->num_in, offset); > + if (ret) > + goto pcm_error; > + > + > + offset += stream->num_in; > + > + ret = cdns_allocate_pdi(cdns, &stream->out, 0, > + stream->num_out, offset); > + if (ret) > + goto pcm_error; > + > + /* Update total number of PCM PDIs */ > + stream->num_pdi = stream->num_bd + stream->num_in + stream->num_out; > + cdns->num_ports = stream->num_pdi; > + > + /* Allocate PDIs for PDMs */ > + stream = &cdns->pdm; > + offset = CDNS_PDM_PDI_OFFSET; > + ret = cdns_allocate_pdi(cdns, &stream->bd, 0, > + stream->num_bd, offset); > + if (ret) > + goto pdm_error; > + > + offset += stream->num_bd; > + > + ret = cdns_allocate_pdi(cdns, &stream->in, 0, > + stream->num_in, offset); > + if (ret) > + goto pdm_error; > + > + offset += stream->num_in; > + > + ret = cdns_allocate_pdi(cdns, &stream->out, 0, > + stream->num_out, offset); > + if (ret) > + goto pdm_error; > + > + /* Update total number of PDM PDIs */ > + stream->num_pdi = stream->num_bd + stream->num_in + stream->num_out; > + cdns->num_ports += stream->num_pdi; > + > + cdns->ports = devm_kcalloc(cdns->dev, cdns->num_ports, > + sizeof(*cdns->ports), GFP_KERNEL); > + if (!cdns->ports) { > + ret = -ENOMEM; > + goto pdm_error; > + } > + > + for (i = 0; i < cdns->num_ports; i++) { > + cdns->ports[i].assigned = false; > + cdns->ports[i].num = i + 1; /* Port 0 reserved for bulk */ > + } > + > + return 0; > + > +pdm_error: > + kfree(stream->bd); > + kfree(stream->in); > + kfree(stream->out); > + > +pcm_error: > + stream = &cdns->pcm; > + kfree(stream->bd); > + kfree(stream->in); > + kfree(stream->out); call me a grumpy old fart if you want, I still don't like people freeing memory they never allocated. It may be legal but it's sloppy. > + return ret; > +} > +EXPORT_SYMBOL(sdw_cdns_pdi_init); > + > /** > * sdw_cdns_init() - Cadence initialization > * @cdns: Cadence instance > @@ -730,6 +857,134 @@ int sdw_cdns_init(struct sdw_cdns *cdns) > } > EXPORT_SYMBOL(sdw_cdns_init); > > +int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params) > +{ > + struct sdw_cdns *cdns = bus_to_cdns(bus); > + int mcp_clkctrl_off, mcp_clkctrl; > + int divider; > + > + if (!params->curr_dr_freq) { > + dev_err(cdns->dev, "NULL curr_dr_freq"); > + return -EINVAL; > + } > + > + divider = (params->max_dr_freq / params->curr_dr_freq) - 1; > + > + if (params->next_bank) > + mcp_clkctrl_off = CDNS_MCP_CLK_CTRL1; > + else > + mcp_clkctrl_off = CDNS_MCP_CLK_CTRL0; > + > + mcp_clkctrl = cdns_readl(cdns, mcp_clkctrl_off); > + mcp_clkctrl |= divider; > + cdns_writel(cdns, mcp_clkctrl_off, mcp_clkctrl); > + > + return 0; > +} > +EXPORT_SYMBOL(cdns_bus_conf); > + > +static int cdns_port_params(struct sdw_bus *bus, > + struct sdw_port_params *p_params, unsigned int bank) > +{ > + struct sdw_cdns *cdns = bus_to_cdns(bus); > + int dpn_config = 0, dpn_config_off; > + > + if (bank) > + dpn_config_off = CDNS_DPN_B1_CONFIG(p_params->num); > + else > + dpn_config_off = CDNS_DPN_B0_CONFIG(p_params->num); > + > + dpn_config = cdns_readl(cdns, dpn_config_off); > + > + dpn_config |= ((p_params->bps - 1) << > + SDW_REG_SHIFT(CDNS_DPN_CONFIG_WL)); > + dpn_config |= (p_params->flow_mode << > + SDW_REG_SHIFT(CDNS_DPN_CONFIG_PORT_FLOW)); > + dpn_config |= (p_params->data_mode << > + SDW_REG_SHIFT(CDNS_DPN_CONFIG_PORT_DAT)); > + > + cdns_writel(cdns, dpn_config_off, dpn_config); > + > + return 0; > +} > + > +static int cdns_transport_params(struct sdw_bus *bus, > + struct sdw_transport_params *t_params, > + enum sdw_reg_bank bank) > +{ > + struct sdw_cdns *cdns = bus_to_cdns(bus); > + int dpn_offsetctrl = 0, dpn_offsetctrl_off; > + int dpn_config = 0, dpn_config_off; > + int dpn_hctrl = 0, dpn_hctrl_off; > + int num = t_params->port_num; > + int dpn_samplectrl_off; > + > + /* > + * Note: Only full data port is supported on the Master side for > + * both PCM and PDM ports. > + */ > + > + if (bank) { > + dpn_config_off = CDNS_DPN_B1_CONFIG(num); > + dpn_samplectrl_off = CDNS_DPN_B1_SAMPLE_CTRL(num); > + dpn_hctrl_off = CDNS_DPN_B1_HCTRL(num); > + dpn_offsetctrl_off = CDNS_DPN_B1_OFFSET_CTRL(num); > + } else { > + dpn_config_off = CDNS_DPN_B0_CONFIG(num); > + dpn_samplectrl_off = CDNS_DPN_B0_SAMPLE_CTRL(num); > + dpn_hctrl_off = CDNS_DPN_B0_HCTRL(num); > + dpn_offsetctrl_off = CDNS_DPN_B0_OFFSET_CTRL(num); > + } > + > + dpn_config = cdns_readl(cdns, dpn_config_off); > + > + dpn_config |= (t_params->blk_grp_ctrl << > + SDW_REG_SHIFT(CDNS_DPN_CONFIG_BGC)); > + dpn_config |= (t_params->blk_pkg_mode << > + SDW_REG_SHIFT(CDNS_DPN_CONFIG_BPM)); > + cdns_writel(cdns, dpn_config_off, dpn_config); > + > + dpn_offsetctrl |= (t_params->offset1 << > + SDW_REG_SHIFT(CDNS_DPN_OFFSET_CTRL_1)); > + dpn_offsetctrl |= (t_params->offset2 << > + SDW_REG_SHIFT(CDNS_DPN_OFFSET_CTRL_2)); > + cdns_writel(cdns, dpn_offsetctrl_off, dpn_offsetctrl); > + > + dpn_hctrl |= (t_params->hstart << > + SDW_REG_SHIFT(CDNS_DPN_HCTRL_HSTART)); > + dpn_hctrl |= (t_params->hstop << SDW_REG_SHIFT(CDNS_DPN_HCTRL_HSTOP)); > + dpn_hctrl |= (t_params->lane_ctrl << > + SDW_REG_SHIFT(CDNS_DPN_HCTRL_LCTRL)); > + > + cdns_writel(cdns, dpn_hctrl_off, dpn_hctrl); > + cdns_writel(cdns, dpn_samplectrl_off, (t_params->sample_interval - 1)); > + > + return 0; > +} > + > +static int cdns_port_enable(struct sdw_bus *bus, > + struct sdw_enable_ch *enable_ch, unsigned int bank) > +{ > + struct sdw_cdns *cdns = bus_to_cdns(bus); > + int dpn_chnen_off, ch_mask; > + > + if (bank) > + dpn_chnen_off = CDNS_DPN_B1_CH_EN(enable_ch->num); > + else > + dpn_chnen_off = CDNS_DPN_B0_CH_EN(enable_ch->num); > + > + ch_mask = enable_ch->ch_mask * enable_ch->enable; > + cdns_writel(cdns, dpn_chnen_off, ch_mask); > + > + return 0; > +} > + > +static const struct sdw_master_port_ops cdns_port_ops = { > + .dpn_set_port_params = cdns_port_params, > + .dpn_set_port_transport_params = cdns_transport_params, > + .dpn_port_enable_ch = cdns_port_enable, > +}; > + > /** > * sdw_cdns_probe() - Cadence probe routine > * @cdns: Cadence instance > @@ -737,6 +992,7 @@ EXPORT_SYMBOL(sdw_cdns_init); > int sdw_cdns_probe(struct sdw_cdns *cdns) > { > init_completion(&cdns->tx_complete); > + cdns->bus.port_ops = &cdns_port_ops; > > return 0; > } > diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h > index 3ec74fa5f4f9..98a17f57918f 100644 > --- a/drivers/soundwire/cadence_master.h > +++ b/drivers/soundwire/cadence_master.h > @@ -5,6 +5,92 @@ > #define __SDW_CADENCE_H > > /** > + * struct sdw_cdns_pdi: PDI (Physical Data Interface) instance > + * > + * @assigned: pdi assigned > + * @num: pdi number > + * @intel_alh_id: link identifier > + * @l_ch_num: low channel for PDI > + * @h_ch_num: high channel for PDI > + * @ch_count: total channel count for PDI > + * @dir: data direction > + * @type: stream type, PDM or PCM > + */ > +struct sdw_cdns_pdi { > + bool assigned; > + int num; > + int intel_alh_id; > + int l_ch_num; > + int h_ch_num; > + int ch_count; > + enum sdw_data_direction dir; > + enum sdw_stream_type type; > +}; > + > +/** > + * struct sdw_cdns_port: Cadence port structure > + * > + * @num: port number > + * @assigned: port assigned > + * @ch: channel count > + * @direction: data port direction > + * @pdi: pdi for this port > + */ > +struct sdw_cdns_port { > + unsigned int num; > + bool assigned; > + unsigned int ch; > + enum sdw_data_direction direction; > + struct sdw_cdns_pdi *pdi; > +}; > + > +/** > + * struct sdw_cdns_streams: Cadence stream data structure > + * > + * @num_bd: number of bidirectional streams > + * @num_in: number of input streams > + * @num_out: number of output streams > + * @num_ch_bd: number of bidirectional stream channels > + * @num_ch_bd: number of input stream channels > + * @num_ch_bd: number of output stream channels > + * @num_pdi: total number of PDIs > + * @bd: bidirectional streams > + * @in: input streams > + * @out: output streams > + */ > +struct sdw_cdns_streams { > + unsigned int num_bd; > + unsigned int num_in; > + unsigned int num_out; > + unsigned int num_ch_bd; > + unsigned int num_ch_in; > + unsigned int num_ch_out; > + unsigned int num_pdi; > + struct sdw_cdns_pdi *bd; > + struct sdw_cdns_pdi *in; > + struct sdw_cdns_pdi *out; > +}; > + > +/** > + * struct sdw_cdns_stream_config: stream configuration > + * > + * @pcm_bd: number of bidirectional PCM streams supported > + * @pcm_in: number of input PCM streams supported > + * @pcm_out: number of output PCM streams supported > + * @pdm_bd: number of bidirectional PDM streams supported > + * @pdm_in: number of input PDM streams supported > + * @pdm_out: number of output PDM streams supported > + */ > +struct sdw_cdns_stream_config { > + unsigned int pcm_bd; > + unsigned int pcm_in; > + unsigned int pcm_out; > + unsigned int pdm_bd; > + unsigned int pdm_in; > + unsigned int pdm_out; > +}; > + > +/** > * struct sdw_cdns - Cadence driver context > * @dev: Linux device > * @bus: Bus handle > @@ -12,6 +98,10 @@ > * @response_buf: SoundWire response buffer > * @tx_complete: Tx completion > * @defer: Defer pointer > + * @ports: Data ports > + * @num_ports: Total number of data ports > + * @pcm: PCM streams > + * @pdm: PDM streams > * @registers: Cadence registers > * @link_up: Link status > * @msg_count: Messages sent on bus > @@ -25,6 +115,12 @@ struct sdw_cdns { > struct completion tx_complete; > struct sdw_defer *defer; > > + struct sdw_cdns_port *ports; > + int num_ports; > + > + struct sdw_cdns_streams pcm; > + struct sdw_cdns_streams pdm; > + > void __iomem *registers; > > bool link_up; > @@ -42,6 +138,8 @@ irqreturn_t sdw_cdns_irq(int irq, void *dev_id); > irqreturn_t sdw_cdns_thread(int irq, void *dev_id); > > int sdw_cdns_init(struct sdw_cdns *cdns); > +int sdw_cdns_pdi_init(struct sdw_cdns *cdns, > + struct sdw_cdns_stream_config config); > int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns); > > enum sdw_command_response > @@ -53,4 +151,6 @@ cdns_xfer_msg_defer(struct sdw_bus *bus, > > enum sdw_command_response > cdns_reset_page_addr(struct sdw_bus *bus, unsigned int dev_num); > + > +int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params); > #endif /* __SDW_CADENCE_H */ > diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c > index aa0c60133de5..a64f87a08cfd 100644 > --- a/drivers/soundwire/intel.c > +++ b/drivers/soundwire/intel.c > @@ -257,6 +257,7 @@ static struct sdw_master_ops sdw_intel_ops = { > .xfer_msg = cdns_xfer_msg, > .xfer_msg_defer = cdns_xfer_msg_defer, > .reset_page_addr = cdns_reset_page_addr, > + .set_bus_conf = cdns_bus_conf, > }; > > /* >
On Thu, Apr 05, 2018 at 07:19:43PM -0500, Pierre-Louis Bossart wrote: > On 4/5/18 11:48 AM, Vinod Koul wrote: > >+static int cdns_allocate_pdi(struct sdw_cdns *cdns, > >+ struct sdw_cdns_pdi **stream, > >+ u32 start, u32 num, u32 pdi_offset) > > the start parameter doesn't seem to be used, remove and remove the useless > zeroes in all the calls to this routine? Yes this seems to be the case, will fix > >+ > >+pdm_error: > >+ kfree(stream->bd); > >+ kfree(stream->in); > >+ kfree(stream->out); > >+ > >+pcm_error: > >+ stream = &cdns->pcm; > >+ kfree(stream->bd); > >+ kfree(stream->in); > >+ kfree(stream->out); > > call me a grumpy old fart if you want, I still don't like people freeing > memory they never allocated. It may be legal but it's sloppy. Actually looking at it freeing is not required as we are doing devm_ allocations, so this can be removed and return error should do the trick and we propagate the error and cleanup, thanks for the catch
On 4/6/18 3:55 AM, Vinod Koul wrote: > On Thu, Apr 05, 2018 at 07:19:43PM -0500, Pierre-Louis Bossart wrote: >> On 4/5/18 11:48 AM, Vinod Koul wrote: > >>> +static int cdns_allocate_pdi(struct sdw_cdns *cdns, >>> + struct sdw_cdns_pdi **stream, >>> + u32 start, u32 num, u32 pdi_offset) >> >> the start parameter doesn't seem to be used, remove and remove the useless >> zeroes in all the calls to this routine? > > Yes this seems to be the case, will fix > >>> + >>> +pdm_error: >>> + kfree(stream->bd); >>> + kfree(stream->in); >>> + kfree(stream->out); >>> + >>> +pcm_error: >>> + stream = &cdns->pcm; >>> + kfree(stream->bd); >>> + kfree(stream->in); >>> + kfree(stream->out); >> >> call me a grumpy old fart if you want, I still don't like people freeing >> memory they never allocated. It may be legal but it's sloppy. > > Actually looking at it freeing is not required as we are doing devm_ > allocations, so this can be removed and return error should do the trick and > we propagate the error and cleanup, thanks for the catch sounds good.
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index b0c09efd8f83..89a4ae86d36a 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -669,6 +669,133 @@ int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns) } EXPORT_SYMBOL(sdw_cdns_enable_interrupt); +static int cdns_allocate_pdi(struct sdw_cdns *cdns, + struct sdw_cdns_pdi **stream, + u32 start, u32 num, u32 pdi_offset) +{ + struct sdw_cdns_pdi *pdi; + int i; + + if (!num) + return 0; + + pdi = devm_kcalloc(cdns->dev, num, sizeof(*pdi), GFP_KERNEL); + if (!pdi) + return -ENOMEM; + + for (i = 0; i < num; i++) { + pdi[i].num = i + pdi_offset; + pdi[i].assigned = false; + } + + *stream = pdi; + return 0; +} + +/** + * sdw_cdns_pdi_init: PDI initialization routine + * + * @cdns: Cadence instance + * @config: Stream configurations + */ +int sdw_cdns_pdi_init(struct sdw_cdns *cdns, + struct sdw_cdns_stream_config config) +{ + struct sdw_cdns_streams *stream; + int offset, i, ret; + + cdns->pcm.num_bd = config.pcm_bd; + cdns->pcm.num_in = config.pcm_in; + cdns->pcm.num_out = config.pcm_out; + cdns->pdm.num_bd = config.pdm_bd; + cdns->pdm.num_in = config.pdm_in; + cdns->pdm.num_out = config.pdm_out; + + /* Allocate PDIs for PCMs */ + stream = &cdns->pcm; + + /* First two PDIs are reserved for bulk transfers */ + stream->num_bd -= CDNS_PCM_PDI_OFFSET; + offset = CDNS_PCM_PDI_OFFSET; + + ret = cdns_allocate_pdi(cdns, &stream->bd, 0, + stream->num_bd, offset); + if (ret) + goto pcm_error; + + offset += stream->num_bd; + + ret = cdns_allocate_pdi(cdns, &stream->in, 0, + stream->num_in, offset); + if (ret) + goto pcm_error; + + + offset += stream->num_in; + + ret = cdns_allocate_pdi(cdns, &stream->out, 0, + stream->num_out, offset); + if (ret) + goto pcm_error; + + /* Update total number of PCM PDIs */ + stream->num_pdi = stream->num_bd + stream->num_in + stream->num_out; + cdns->num_ports = stream->num_pdi; + + /* Allocate PDIs for PDMs */ + stream = &cdns->pdm; + offset = CDNS_PDM_PDI_OFFSET; + ret = cdns_allocate_pdi(cdns, &stream->bd, 0, + stream->num_bd, offset); + if (ret) + goto pdm_error; + + offset += stream->num_bd; + + ret = cdns_allocate_pdi(cdns, &stream->in, 0, + stream->num_in, offset); + if (ret) + goto pdm_error; + + offset += stream->num_in; + + ret = cdns_allocate_pdi(cdns, &stream->out, 0, + stream->num_out, offset); + if (ret) + goto pdm_error; + + /* Update total number of PDM PDIs */ + stream->num_pdi = stream->num_bd + stream->num_in + stream->num_out; + cdns->num_ports += stream->num_pdi; + + cdns->ports = devm_kcalloc(cdns->dev, cdns->num_ports, + sizeof(*cdns->ports), GFP_KERNEL); + if (!cdns->ports) { + ret = -ENOMEM; + goto pdm_error; + } + + for (i = 0; i < cdns->num_ports; i++) { + cdns->ports[i].assigned = false; + cdns->ports[i].num = i + 1; /* Port 0 reserved for bulk */ + } + + return 0; + +pdm_error: + kfree(stream->bd); + kfree(stream->in); + kfree(stream->out); + +pcm_error: + stream = &cdns->pcm; + kfree(stream->bd); + kfree(stream->in); + kfree(stream->out); + return ret; +} +EXPORT_SYMBOL(sdw_cdns_pdi_init); + /** * sdw_cdns_init() - Cadence initialization * @cdns: Cadence instance @@ -730,6 +857,134 @@ int sdw_cdns_init(struct sdw_cdns *cdns) } EXPORT_SYMBOL(sdw_cdns_init); +int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params) +{ + struct sdw_cdns *cdns = bus_to_cdns(bus); + int mcp_clkctrl_off, mcp_clkctrl; + int divider; + + if (!params->curr_dr_freq) { + dev_err(cdns->dev, "NULL curr_dr_freq"); + return -EINVAL; + } + + divider = (params->max_dr_freq / params->curr_dr_freq) - 1; + + if (params->next_bank) + mcp_clkctrl_off = CDNS_MCP_CLK_CTRL1; + else + mcp_clkctrl_off = CDNS_MCP_CLK_CTRL0; + + mcp_clkctrl = cdns_readl(cdns, mcp_clkctrl_off); + mcp_clkctrl |= divider; + cdns_writel(cdns, mcp_clkctrl_off, mcp_clkctrl); + + return 0; +} +EXPORT_SYMBOL(cdns_bus_conf); + +static int cdns_port_params(struct sdw_bus *bus, + struct sdw_port_params *p_params, unsigned int bank) +{ + struct sdw_cdns *cdns = bus_to_cdns(bus); + int dpn_config = 0, dpn_config_off; + + if (bank) + dpn_config_off = CDNS_DPN_B1_CONFIG(p_params->num); + else + dpn_config_off = CDNS_DPN_B0_CONFIG(p_params->num); + + dpn_config = cdns_readl(cdns, dpn_config_off); + + dpn_config |= ((p_params->bps - 1) << + SDW_REG_SHIFT(CDNS_DPN_CONFIG_WL)); + dpn_config |= (p_params->flow_mode << + SDW_REG_SHIFT(CDNS_DPN_CONFIG_PORT_FLOW)); + dpn_config |= (p_params->data_mode << + SDW_REG_SHIFT(CDNS_DPN_CONFIG_PORT_DAT)); + + cdns_writel(cdns, dpn_config_off, dpn_config); + + return 0; +} + +static int cdns_transport_params(struct sdw_bus *bus, + struct sdw_transport_params *t_params, + enum sdw_reg_bank bank) +{ + struct sdw_cdns *cdns = bus_to_cdns(bus); + int dpn_offsetctrl = 0, dpn_offsetctrl_off; + int dpn_config = 0, dpn_config_off; + int dpn_hctrl = 0, dpn_hctrl_off; + int num = t_params->port_num; + int dpn_samplectrl_off; + + /* + * Note: Only full data port is supported on the Master side for + * both PCM and PDM ports. + */ + + if (bank) { + dpn_config_off = CDNS_DPN_B1_CONFIG(num); + dpn_samplectrl_off = CDNS_DPN_B1_SAMPLE_CTRL(num); + dpn_hctrl_off = CDNS_DPN_B1_HCTRL(num); + dpn_offsetctrl_off = CDNS_DPN_B1_OFFSET_CTRL(num); + } else { + dpn_config_off = CDNS_DPN_B0_CONFIG(num); + dpn_samplectrl_off = CDNS_DPN_B0_SAMPLE_CTRL(num); + dpn_hctrl_off = CDNS_DPN_B0_HCTRL(num); + dpn_offsetctrl_off = CDNS_DPN_B0_OFFSET_CTRL(num); + } + + dpn_config = cdns_readl(cdns, dpn_config_off); + + dpn_config |= (t_params->blk_grp_ctrl << + SDW_REG_SHIFT(CDNS_DPN_CONFIG_BGC)); + dpn_config |= (t_params->blk_pkg_mode << + SDW_REG_SHIFT(CDNS_DPN_CONFIG_BPM)); + cdns_writel(cdns, dpn_config_off, dpn_config); + + dpn_offsetctrl |= (t_params->offset1 << + SDW_REG_SHIFT(CDNS_DPN_OFFSET_CTRL_1)); + dpn_offsetctrl |= (t_params->offset2 << + SDW_REG_SHIFT(CDNS_DPN_OFFSET_CTRL_2)); + cdns_writel(cdns, dpn_offsetctrl_off, dpn_offsetctrl); + + dpn_hctrl |= (t_params->hstart << + SDW_REG_SHIFT(CDNS_DPN_HCTRL_HSTART)); + dpn_hctrl |= (t_params->hstop << SDW_REG_SHIFT(CDNS_DPN_HCTRL_HSTOP)); + dpn_hctrl |= (t_params->lane_ctrl << + SDW_REG_SHIFT(CDNS_DPN_HCTRL_LCTRL)); + + cdns_writel(cdns, dpn_hctrl_off, dpn_hctrl); + cdns_writel(cdns, dpn_samplectrl_off, (t_params->sample_interval - 1)); + + return 0; +} + +static int cdns_port_enable(struct sdw_bus *bus, + struct sdw_enable_ch *enable_ch, unsigned int bank) +{ + struct sdw_cdns *cdns = bus_to_cdns(bus); + int dpn_chnen_off, ch_mask; + + if (bank) + dpn_chnen_off = CDNS_DPN_B1_CH_EN(enable_ch->num); + else + dpn_chnen_off = CDNS_DPN_B0_CH_EN(enable_ch->num); + + ch_mask = enable_ch->ch_mask * enable_ch->enable; + cdns_writel(cdns, dpn_chnen_off, ch_mask); + + return 0; +} + +static const struct sdw_master_port_ops cdns_port_ops = { + .dpn_set_port_params = cdns_port_params, + .dpn_set_port_transport_params = cdns_transport_params, + .dpn_port_enable_ch = cdns_port_enable, +}; + /** * sdw_cdns_probe() - Cadence probe routine * @cdns: Cadence instance @@ -737,6 +992,7 @@ EXPORT_SYMBOL(sdw_cdns_init); int sdw_cdns_probe(struct sdw_cdns *cdns) { init_completion(&cdns->tx_complete); + cdns->bus.port_ops = &cdns_port_ops; return 0; } diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index 3ec74fa5f4f9..98a17f57918f 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -5,6 +5,92 @@ #define __SDW_CADENCE_H /** + * struct sdw_cdns_pdi: PDI (Physical Data Interface) instance + * + * @assigned: pdi assigned + * @num: pdi number + * @intel_alh_id: link identifier + * @l_ch_num: low channel for PDI + * @h_ch_num: high channel for PDI + * @ch_count: total channel count for PDI + * @dir: data direction + * @type: stream type, PDM or PCM + */ +struct sdw_cdns_pdi { + bool assigned; + int num; + int intel_alh_id; + int l_ch_num; + int h_ch_num; + int ch_count; + enum sdw_data_direction dir; + enum sdw_stream_type type; +}; + +/** + * struct sdw_cdns_port: Cadence port structure + * + * @num: port number + * @assigned: port assigned + * @ch: channel count + * @direction: data port direction + * @pdi: pdi for this port + */ +struct sdw_cdns_port { + unsigned int num; + bool assigned; + unsigned int ch; + enum sdw_data_direction direction; + struct sdw_cdns_pdi *pdi; +}; + +/** + * struct sdw_cdns_streams: Cadence stream data structure + * + * @num_bd: number of bidirectional streams + * @num_in: number of input streams + * @num_out: number of output streams + * @num_ch_bd: number of bidirectional stream channels + * @num_ch_bd: number of input stream channels + * @num_ch_bd: number of output stream channels + * @num_pdi: total number of PDIs + * @bd: bidirectional streams + * @in: input streams + * @out: output streams + */ +struct sdw_cdns_streams { + unsigned int num_bd; + unsigned int num_in; + unsigned int num_out; + unsigned int num_ch_bd; + unsigned int num_ch_in; + unsigned int num_ch_out; + unsigned int num_pdi; + struct sdw_cdns_pdi *bd; + struct sdw_cdns_pdi *in; + struct sdw_cdns_pdi *out; +}; + +/** + * struct sdw_cdns_stream_config: stream configuration + * + * @pcm_bd: number of bidirectional PCM streams supported + * @pcm_in: number of input PCM streams supported + * @pcm_out: number of output PCM streams supported + * @pdm_bd: number of bidirectional PDM streams supported + * @pdm_in: number of input PDM streams supported + * @pdm_out: number of output PDM streams supported + */ +struct sdw_cdns_stream_config { + unsigned int pcm_bd; + unsigned int pcm_in; + unsigned int pcm_out; + unsigned int pdm_bd; + unsigned int pdm_in; + unsigned int pdm_out; +}; + +/** * struct sdw_cdns - Cadence driver context * @dev: Linux device * @bus: Bus handle @@ -12,6 +98,10 @@ * @response_buf: SoundWire response buffer * @tx_complete: Tx completion * @defer: Defer pointer + * @ports: Data ports + * @num_ports: Total number of data ports + * @pcm: PCM streams + * @pdm: PDM streams * @registers: Cadence registers * @link_up: Link status * @msg_count: Messages sent on bus @@ -25,6 +115,12 @@ struct sdw_cdns { struct completion tx_complete; struct sdw_defer *defer; + struct sdw_cdns_port *ports; + int num_ports; + + struct sdw_cdns_streams pcm; + struct sdw_cdns_streams pdm; + void __iomem *registers; bool link_up; @@ -42,6 +138,8 @@ irqreturn_t sdw_cdns_irq(int irq, void *dev_id); irqreturn_t sdw_cdns_thread(int irq, void *dev_id); int sdw_cdns_init(struct sdw_cdns *cdns); +int sdw_cdns_pdi_init(struct sdw_cdns *cdns, + struct sdw_cdns_stream_config config); int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns); enum sdw_command_response @@ -53,4 +151,6 @@ cdns_xfer_msg_defer(struct sdw_bus *bus, enum sdw_command_response cdns_reset_page_addr(struct sdw_bus *bus, unsigned int dev_num); + +int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params); #endif /* __SDW_CADENCE_H */ diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index aa0c60133de5..a64f87a08cfd 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -257,6 +257,7 @@ static struct sdw_master_ops sdw_intel_ops = { .xfer_msg = cdns_xfer_msg, .xfer_msg_defer = cdns_xfer_msg_defer, .reset_page_addr = cdns_reset_page_addr, + .set_bus_conf = cdns_bus_conf, }; /*