There is a very small window between then wm_adsp_compr_free gets call
and when the DSP is actually powered down. If we get an IRQ from the DSP
in this window then the wm_adsp_compr pointer will be NULL. This patch
adds a check for this into the IRQ handler to avoid any issues when this
happens.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
We should return a valid sample rate from the pointer callback, this
patch adds this into the driver.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Mainly this adds DAI links for the audio trace, however, it is also
necessary to update the data IRQ handler to check more cores. We have
the handler check every core so it should not be necessary to update
this function if more compressed firmwares are added in the future.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
The ADSP code should be agnostic of which CODEC it runs upon, currently
there is only one remaining part of the implementation that doesn't
follow this. When the DSP is booted on ADSP2 we read
ARIZONA_SYSTEM_CLOCK_1 and use that to set the initial speed for the DSP
clock. This patch factors that out into CODEC specific code, leaving the
ADSP code entirely CODEC agnostic.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
The audio trace firmware allows the capture of arbitrary streams of
audio from the DSP and commonly used for debugging other firmwares. This
patch adds support for this firwmare into the ADSP driver.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
The region definition will be shared by most firmwares so give this a
more generic name and whilst we are there improve the naming of the
voice control capabilities array as well.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Data is read in blocks of up to one fragment is size from the circular
buffer on the DSP and is re-packed to remove the padding byte that
exists in the DSP memory map.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Here support is added for responding to DSP IRQs that are used to
indicate data being available on the DSP. The idea is that we check the
amount of data available upon receipt of an IRQ and on subsequent calls
to the pointer callback we recheck once less than one fragment is
available (to avoid excessive SPI traffic), if there is truely less than
one fragment available we ack the last IRQ and wait for a new one.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
The stream is created whilst the compressed stream is opened and a
buffer is created when the DSP powers up. It is necessary at a point
once both the DSP has powered up and the the stream has been opened to
connect a stream to a buffer on the DSP. This is done in the trigger
callback as this is after the DSP has been powered and obviously the
stream must be open. Note that whilst the connect is currently trivial
it is expected that this will get more complex when support for multiple
buffers/streams per DSP is added.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Add code that locates and initialises the buffer of compressed data on
the DSP if the firmware supported compressed data capture. The buffer
struct (wm_adsp_compr_buf) is kept separate from the stream struct
(wm_adsp_compr) this will allow much easier support of multiple
streams of data from the one DSP in the future, although support for
this will not be added in this patch chain.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Allow user-space to open a compressed stream, although no data will be
passed yet, as part of this adding the ability to define supported
capabilities per firmware and check these match the stream being opened.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Older firmwares don't specify access flags for the controls,
unfortunately the usage of some of these firmware relies on being able
to read back values from the DSP. The current control code will only do
this for volatile controls. This patch will read the control from the
hardware if no flags are specified and the control is currently
enabled, which should cover these legacy use-cases.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Locking is currently missing from the DSP firmware controls, which can
lead to some race conditions if the controls are accessed as the DSP
powers up or down. This patch adds them to the new power lock.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
We should hold the DSP power lock whilst changing the firmware since we
need to check if it is running first.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Most events around the DSP just need to be locked to ensure that the DSP
can't change power state whilst they are happening. This includes the
debugfs entries and this will make sorting the rest of the locking
simpler.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Expand the list of available firmware names to include a good selection
of generic uses for the DSP cores.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
The rate controls are codec-specific, it's not possible to
generically say what the range or the meaning of each control
is (or even if they exist at all) - that depends on the
particular codec.
This is currently being handled for Arizona codecs by putting
an Arizona-specific table of controls inside the wm_adsp driver.
This creates a dependency between wm_adsp and arizona.c, and is an
awkward solution if the ADSP is used in another family of codecs
Fix this by moving the Arizona-specific rate controls into the
Arizona codec drivers.
Signed-off-by: Richard Fitzgerald <rf@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
This patch adds some debugfs nodes to get information
about the currently running firmware.
Signed-off-by: Richard Fitzgerald <rf@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Now that we have a codec_probe stage initialization in the wm_adsp
driver, we can make the wm_adsp driver create its own ALSA controls
instead of having that responsibility pushed to every codec driver.
Signed-off-by: Richard Fitzgerald <rf@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Currently the only init function in wm_adsp is called by the
codec driver early in its probe before the codec has been
registered with SOC.
This patch adds stubs for the codec_probe and codec_remove stages
and calls them from WM5102 and WM5110 codec drivers. This allows us
to hang anything that needs setup during the codec probe stage off
these functions without further modification of the codec drivers.
Signed-off-by: Richard Fitzgerald <rf@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
In theory the ADSP driver should not need to know anything
about the codec it is part of. But the WM5102 needs DVFS
control based on ADSP clocking speed. This was being handled
by bundling part of the knowledge of this into the ADSP driver.
This change moves this handling out of the ADSP driver and
into the WM5102 driver.
Signed-off-by: Richard Fitzgerald <rf@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
The SCRATCH registers are used by firmwares to hold diagnostic
information. Log this during shutdown to assist analysis and debug
of firmwares.
Signed-off-by: Richard Fitzgerald <rf@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
The DSP control information contains various hints about the usage of
the control use these when handling the control.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Using uXX for little endian data, was triggering some warnings through
sparse:
sound/soc/codecs/wm_adsp.c:716:26: sparse: cast to restricted __le16
sound/soc/codecs/wm_adsp.c:736:23: sparse: cast to restricted __le16
sound/soc/codecs/wm_adsp.c:739:23: sparse: cast to restricted __le32
Correct this by changing the casts to use __leXX instead of uXX.
Reported-by: Fengguang Wu <fengguang.wu@intel.com>
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
There are very few version 0 firmwares in the wild and at some point in
the future it would be nice to remove support for them from the driver,
as they require several work arounds to be present to create controls
properly.
This patch adds a depreciated warning if someone is using this file
format.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Version 2 of the firmware file format includes length fields for the
various strings associated with control creation, to reduce file size.
However this does increase the parsing complexity slightly. This patch
adds support for the revision of the file format.
This patch also adds a new naming scheme for controls created from rev 2
firmware files. This version of the file format is commonly used to
add multiple controls per algorithm per memory region and the old
control naming scheme would cause multiple controls to have the same
name in this case.. Note that the naming scheme for older firmware
versions is left intact to ensure backwards compatibility.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Revision one of the file format includes new algorithm and coefficient
blocks which provide additional information about the controls exported
by the firmware. This patch updates the processing to handle this
version of the file format. Note that whilst this version of the format
adds support for specifying a name for the control through the firmware
file this has not been used and to keep compatibility with existing
deployments no changes to the firmware control naming are made by this
patch.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
This is slightly logically better and avoids some unnecessary forward
declarations in the following refactoring.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
The private field in wm_coeff_ctl is currently unused and given the
controls are entirely handled within the ADSP code it is not clear what
it would be used for in the future. Remove the field for now it can be
readded if it is ever required.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Tidy up the code a little by factoring out the creation of the algorithm
regions.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Physically reading and writing controls to/from the DSP are handled by
two low level functions (wm_coeff_{write|read}_control, these currently
take in a snd_kcontrol pointer but immediately pull out a wm_coeff_ctl
pointer from the private data. These functions don't handle the kcontrols
at all they just shuttle data to and from the chip and all the call
sites have a wm_coeff_ctl pointer available. This patch just passes the
wm_coeff_ctl pointer straight into these functions.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Now we only allocate 44 bytes for the control name keep it on the stack
to avoid a lot of pointless memory allocation.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
ALSA only supports control names up to 44 bytes, so there is no point
allocating a whole page of memory to hold the control name, just limit
the control name to 44 bytes.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
The algorithm region information in the firmware doesn't contain a
length field, explicitly pass this to the create_control function rather
than bundling into wm_adsp_alg_region.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
We have wm_adsp_region, wm_adsp_alg_region, and wmfw_region, the
variables for which are all frequently called region, this can get quite
confusing when reviewing the code especially given some functions are
quite long. Consistently use mem for wm_adsp_regions, alg_region for
wm_adsp_alg_region and region for wmfw_region.
Additionally, we use a mix of adsp and dsp for pointers to the wm_adsp
structure standardise this on dsp.
Finally, we use algs to refer to the number of algorithms quite
frequently, change this to the more descriptive n_algs.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
The vast majority of the wm_adsp_setup_algs function is case statements
for ADSP1 or ADSP2, this patch splits this out into two separate
functions wm_adsp1_setup_algs and wm_adsp2_setup_algs. The small amount
of shared code between them is factored out into an extra helper
function. This makes the code a lot cleaner.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Whilst the existing code does correctly round to the next 4-byte boundary
it does so rather inefficiently. This patch changes the rounding to be
simpler and more efficient.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Currently DSP controls are persistent (across DSP On/Off) only
if they were set whilst the DSP is off. This change makes the
controls persistent irrespective of when they are set.
Signed-off-by: Nikesh Oswal <Nikesh.Oswal@wolfsonmicro.com>
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
The codec field of the snd_soc_widget struct is eventually going to be
removed, use snd_soc_dapm_to_codec(w->dapm) instead.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Acked-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Many firmwares do not wait for the start bit before they begin
processing audio, whilst this is a bug on the firmware side there are
too many such firmwares in the wild to ignore the situation. This patch
moves the core enable to happen at same time as the start, the firmware
looses the ability to overlap its own startup with the audio path bring
up but we ensure that all firmwares behave.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Signed-off-by: JS Park <aitdark.park@samsung.com>
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
We should not free any buffers associated with writing out coefficients
to the DSP until all the async writes have completed. This patch updates
the out of memory path when allocating a new buffer to include a call to
regmap_async_complete.
Reported-by: JS Park <aitdark.park@samsung.com>
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Cc: stable@vger.kernel.org
Use vmalloc to allocate the buffer for firmware/coefficient download and
rely on the SPI core to split this up into DMA-able chunks. This should
give better performance and means we no longer need to manually split
the download into page size chunks to avoid allocating overly large
continuous memory regions.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Both the snd_soc_codec and snd_soc_platform struct do have a pointer to the
parent card and both handle this pointer in mostly the same way. This patch
moves the card field to the component level which will allow further code
consolidation between platforms and CODECS.
Since there are only a handful of users of the snd_soc_codec struct's card field
(and none of the snd_soc_platform's) these are update in this patch as well,
which allows it to be removed from the snd_soc_codec struct.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Signed-off-by: Mark Brown <broonie@linaro.org>
Since MODULE_LICENSE is missing the module load fails,
so add this for module.
Signed-off-by: Praveen Diwakar <praveen.diwakar@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Reviewed-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@linaro.org>
Cc: stable@vger.kernel.org
We have defines for adsp messages best to consistently use them.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@linaro.org>
Reported-by: kbuild test robot <fengguang.wu@intel.com>
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@linaro.org>
For CODEC controls snd_kcontrol_chip() currently returns a pointer to the
CODEC that registered the control. With the upcoming consolidation of
platform and CODEC controls this will change. Prepare for this by introducing
the snd_soc_kcontrol_codec() helper function that will hide the implementation
details of how the CODEC for a control can be obtained. This will allow us to
change this easily in the future.
The patch also updates all CODEC drivers to use the new helper function.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Acked-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@linaro.org>
The firmware files can be quite large and allocating the whole firmware
a single DMA safe buffer can be problematic if the system is under a
high memory load. Ease the requirements slightly by writing the firmware
out in page sized chunks.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@linaro.org>
It can be useful for debugging purposes to see at what point the DSP has
powered down, so add a message to inform us of this.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@linaro.org>
Move the start of booting the DSP to earlier in the DAPM process, and
move the final starting of the DSP to later in the DAPM process. This
allows us to overlap some of the processing with other components of the
system being brought up.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@linaro.org>
Move the ADSP2 boot proceedure into a work structure in preparation for
running it asynchronously with the reset of the audio path bring up.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@linaro.org>
Some devices are getting very close to the limit whilst polling the RAM
start, this patch adds a small delay to this loop to give a longer
startup timeout.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@linaro.org>
Cc: stable@vger.kernel.org
This will allow a marginal speed improvement when used with a bus that
supports async I/O by reducing the amount of context thrashing between
writes, allowing the bus to be more fully utilised.
Signed-off-by: Mark Brown <broonie@linaro.org>
When locating the memory region relating to a coefficient block written
through a bin file we keep processing the list of regions even after we
have found the region we require. This patch adds a break, so we don't
process redundant list items.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@linaro.org>
This patch does:
- Move the sanity check with WARN_ON() in wm_adsp_region_to_reg() and
remove the checks in the callers,
- Fix wrong WARN_ON() usages, replaced with WARN(),
- Fix unreachable or wrong BUG_ON() usages and replace with WARN_ON().
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Reviewed-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@linaro.org>
Pad the ADSP word (3 bytes) to 4 bytes in the kernel and calculate
lengths based on padded ADSP words instead of treating them as bytes
Signed-off-by: Nariman Poushin <nariman@opensource.wolfsonmicro.com>
Signed-off-by: Dimitris Papastamos <dp@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@linaro.org>
Cc: stable@vger.kernel.org
Do it in a similar fashion as we do for ADSP2.
Signed-off-by: Dimitris Papastamos <dp@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@linaro.org>
No need to hold on to the `codec' pointer. We can use the `dsp'
pointer and grab all the information we need from there. This
makes the parameters for the functions a bit more sane and idiomatic.
Signed-off-by: Dimitris Papastamos <dp@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@linaro.org>
Get rid off the wm_coeff struct and the wm_coeff_add_kcontrol()
function. We are now using the snd_soc_card_kcontrol() function to
get the kcontrol pointers. No need to call into ALSA code to
register the kcontrols.
Signed-off-by: Dimitris Papastamos <dp@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@linaro.org>
Reported-by: Dennis May <dennis.may@wolfsonmicro.com>
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@linaro.org>
Rename `dirty' to `set' as it is a bit more descriptive. A set
control is any control that has been set by the user. We need to ensure
that everytime we boot the DSP we sync out any controls that were
set.
We could at some point start keeping track of the default values of
the controls to suppress some of the device I/O.
Signed-off-by: Dimitris Papastamos <dp@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
There are two return paths which don't kfree(name).
Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Add initial support for runtime tuning for the ADSP cores. This
is achieved by exposing the coefficient configuration blocks as
ALSA binary controls.
The current code assumes that no controls on the DSP are volatile.
Signed-off-by: Dimitris Papastamos <dp@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Firmwares may provide some firmware wide configuration regions which can
be configured by the coefficient files using the firmware ID as the
algorithm ID, include these in the algorithm list.
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Now that we have regular register mapped controls we should be splitting
the control sets for ADSP1 and ADSP2 as the register maps are not
identical. Do that.
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Since ASoC does not yet really have the framework features needed to
support propagating sample rates through the device well yet implement
basic support for the ISRCs equivalent to that we currently have for the
ASRCs. The user can opt for 8kHz or 16kHz as the rate for the DSP blocks
in addition to the main audio rate, these being the primary use cases.
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Older .bin files report the global coefficients as absolute address writes
to zero; maintain compatibility with them.
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
'file' is malloced in wm_adsp_load_coeff() and should be freed
before leaving from the error handling cases, otherwise it will cause
memory leak.
Signed-off-by: Wei Yongjun <yongjun_wei@trendmicro.com.cn>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Allow the regmap API to use asynchronous I/O where supported to minimise
the delay between transfers, reducing firmware download times.
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Maximise robustness for the widest range of firmwares by ensuring the DSP
is in a consistent state when halted.
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>