diff options
Diffstat (limited to 'sound/soc/codecs/wm_adsp.c')
| -rw-r--r-- | sound/soc/codecs/wm_adsp.c | 335 | 
1 files changed, 204 insertions, 131 deletions
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index b38f3506418..060027182dc 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -242,7 +242,7 @@ struct wm_coeff_ctl {  static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,  			  struct snd_ctl_elem_value *ucontrol)  { -	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); +	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);  	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;  	struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec); @@ -254,7 +254,7 @@ static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,  static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,  			  struct snd_ctl_elem_value *ucontrol)  { -	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); +	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);  	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;  	struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec); @@ -341,6 +341,8 @@ static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,  static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *region,  					  unsigned int offset)  { +	if (WARN_ON(!region)) +		return offset;  	switch (region->type) {  	case WMFW_ADSP1_PM:  		return region->base + (offset * 3); @@ -353,7 +355,7 @@ static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *region,  	case WMFW_ADSP1_ZM:  		return region->base + (offset * 2);  	default: -		WARN_ON(NULL != "Unknown memory region type"); +		WARN(1, "Unknown memory region type");  		return offset;  	}  } @@ -396,11 +398,12 @@ static int wm_coeff_write_control(struct snd_kcontrol *kcontrol,  	ret = regmap_raw_write(adsp->regmap, reg, scratch,  			       ctl->len);  	if (ret) { -		adsp_err(adsp, "Failed to write %zu bytes to %x\n", -			 ctl->len, reg); +		adsp_err(adsp, "Failed to write %zu bytes to %x: %d\n", +			 ctl->len, reg, ret);  		kfree(scratch);  		return ret;  	} +	adsp_dbg(adsp, "Wrote %zu bytes to %x\n", ctl->len, reg);  	kfree(scratch); @@ -450,11 +453,12 @@ static int wm_coeff_read_control(struct snd_kcontrol *kcontrol,  	ret = regmap_raw_read(adsp->regmap, reg, scratch, ctl->len);  	if (ret) { -		adsp_err(adsp, "Failed to read %zu bytes from %x\n", -			 ctl->len, reg); +		adsp_err(adsp, "Failed to read %zu bytes from %x: %d\n", +			 ctl->len, reg, ret);  		kfree(scratch);  		return ret;  	} +	adsp_dbg(adsp, "Read %zu bytes from %x\n", ctl->len, reg);  	memcpy(buf, scratch, ctl->len);  	kfree(scratch); @@ -568,6 +572,7 @@ static int wm_adsp_load(struct wm_adsp *dsp)  			 file, header->ver);  		goto out_fw;  	} +	adsp_info(dsp, "Firmware version: %d\n", header->ver);  	if (header->core != dsp->type) {  		adsp_err(dsp, "%s: invalid core %d != %d\n", @@ -602,7 +607,7 @@ static int wm_adsp_load(struct wm_adsp *dsp)  		break;  	default: -		BUG_ON(NULL == "Unknown DSP type"); +		WARN(1, "Unknown DSP type");  		goto out_fw;  	} @@ -642,27 +647,22 @@ static int wm_adsp_load(struct wm_adsp *dsp)  			reg = offset;  			break;  		case WMFW_ADSP1_PM: -			BUG_ON(!mem);  			region_name = "PM";  			reg = wm_adsp_region_to_reg(mem, offset);  			break;  		case WMFW_ADSP1_DM: -			BUG_ON(!mem);  			region_name = "DM";  			reg = wm_adsp_region_to_reg(mem, offset);  			break;  		case WMFW_ADSP2_XM: -			BUG_ON(!mem);  			region_name = "XM";  			reg = wm_adsp_region_to_reg(mem, offset);  			break;  		case WMFW_ADSP2_YM: -			BUG_ON(!mem);  			region_name = "YM";  			reg = wm_adsp_region_to_reg(mem, offset);  			break;  		case WMFW_ADSP1_ZM: -			BUG_ON(!mem);  			region_name = "ZM";  			reg = wm_adsp_region_to_reg(mem, offset);  			break; @@ -684,23 +684,38 @@ static int wm_adsp_load(struct wm_adsp *dsp)  		}  		if (reg) { -			buf = wm_adsp_buf_alloc(region->data, -						le32_to_cpu(region->len), -						&buf_list); -			if (!buf) { -				adsp_err(dsp, "Out of memory\n"); -				return -ENOMEM; -			} +			size_t to_write = PAGE_SIZE; +			size_t remain = le32_to_cpu(region->len); +			const u8 *data = region->data; + +			while (remain > 0) { +				if (remain < PAGE_SIZE) +					to_write = remain; + +				buf = wm_adsp_buf_alloc(data, +							to_write, +							&buf_list); +				if (!buf) { +					adsp_err(dsp, "Out of memory\n"); +					ret = -ENOMEM; +					goto out_fw; +				} -			ret = regmap_raw_write_async(regmap, reg, buf->buf, -						     le32_to_cpu(region->len)); -			if (ret != 0) { -				adsp_err(dsp, -					"%s.%d: Failed to write %d bytes at %d in %s: %d\n", -					file, regions, -					le32_to_cpu(region->len), offset, -					region_name, ret); -				goto out_fw; +				ret = regmap_raw_write_async(regmap, reg, +							     buf->buf, +							     to_write); +				if (ret != 0) { +					adsp_err(dsp, +						"%s.%d: Failed to write %zd bytes at %d in %s: %d\n", +						file, regions, +						to_write, offset, +						region_name, ret); +					goto out_fw; +				} + +				data += to_write; +				reg += to_write / 2; +				remain -= to_write;  			}  		} @@ -901,10 +916,8 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)  		break;  	} -	if (mem == NULL) { -		BUG_ON(mem != NULL); +	if (WARN_ON(!mem))  		return -EINVAL; -	}  	switch (dsp->type) {  	case WMFW_ADSP1: @@ -998,7 +1011,7 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)  		break;  	default: -		BUG_ON(NULL == "Unknown DSP type"); +		WARN(1, "Unknown DSP type");  		return -EINVAL;  	} @@ -1062,6 +1075,7 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)  			if (i + 1 < algs) {  				region->len = be32_to_cpu(adsp1_alg[i + 1].dm);  				region->len -= be32_to_cpu(adsp1_alg[i].dm); +				region->len *= 4;  				wm_adsp_create_control(dsp, region);  			} else {  				adsp_warn(dsp, "Missing length info for region DM with ID %x\n", @@ -1079,6 +1093,7 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)  			if (i + 1 < algs) {  				region->len = be32_to_cpu(adsp1_alg[i + 1].zm);  				region->len -= be32_to_cpu(adsp1_alg[i].zm); +				region->len *= 4;  				wm_adsp_create_control(dsp, region);  			} else {  				adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", @@ -1108,6 +1123,7 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)  			if (i + 1 < algs) {  				region->len = be32_to_cpu(adsp2_alg[i + 1].xm);  				region->len -= be32_to_cpu(adsp2_alg[i].xm); +				region->len *= 4;  				wm_adsp_create_control(dsp, region);  			} else {  				adsp_warn(dsp, "Missing length info for region XM with ID %x\n", @@ -1125,6 +1141,7 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)  			if (i + 1 < algs) {  				region->len = be32_to_cpu(adsp2_alg[i + 1].ym);  				region->len -= be32_to_cpu(adsp2_alg[i].ym); +				region->len *= 4;  				wm_adsp_create_control(dsp, region);  			} else {  				adsp_warn(dsp, "Missing length info for region YM with ID %x\n", @@ -1142,6 +1159,7 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)  			if (i + 1 < algs) {  				region->len = be32_to_cpu(adsp2_alg[i + 1].zm);  				region->len -= be32_to_cpu(adsp2_alg[i].zm); +				region->len *= 4;  				wm_adsp_create_control(dsp, region);  			} else {  				adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", @@ -1282,6 +1300,7 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)  					reg = wm_adsp_region_to_reg(mem,  								    reg);  					reg += offset; +					break;  				}  			} @@ -1313,8 +1332,8 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)  						     le32_to_cpu(blk->len));  			if (ret != 0) {  				adsp_err(dsp, -					"%s.%d: Failed to write to %x in %s\n", -					file, blocks, reg, region_name); +					"%s.%d: Failed to write to %x in %s: %d\n", +					file, blocks, reg, region_name, ret);  			}  		} @@ -1358,6 +1377,7 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,  	struct snd_soc_codec *codec = w->codec;  	struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);  	struct wm_adsp *dsp = &dsps[w->shift]; +	struct wm_adsp_alg_region *alg_region;  	struct wm_coeff_ctl *ctl;  	int ret;  	int val; @@ -1435,6 +1455,14 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,  		list_for_each_entry(ctl, &dsp->ctl_list, list)  			ctl->enabled = 0; + +		while (!list_empty(&dsp->alg_regions)) { +			alg_region = list_first_entry(&dsp->alg_regions, +						      struct wm_adsp_alg_region, +						      list); +			list_del(&alg_region->list); +			kfree(alg_region); +		}  		break;  	default: @@ -1455,19 +1483,23 @@ static int wm_adsp2_ena(struct wm_adsp *dsp)  	unsigned int val;  	int ret, count; -	ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, -				 ADSP2_SYS_ENA, ADSP2_SYS_ENA); +	ret = regmap_update_bits_async(dsp->regmap, dsp->base + ADSP2_CONTROL, +				       ADSP2_SYS_ENA, ADSP2_SYS_ENA);  	if (ret != 0)  		return ret;  	/* Wait for the RAM to start, should be near instantaneous */ -	count = 0; -	do { +	for (count = 0; count < 10; ++count) {  		ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1,  				  &val);  		if (ret != 0)  			return ret; -	} while (!(val & ADSP2_RAM_RDY) && ++count < 10); + +		if (val & ADSP2_RAM_RDY) +			break; + +		msleep(1); +	}  	if (!(val & ADSP2_RAM_RDY)) {  		adsp_err(dsp, "Failed to start DSP RAM\n"); @@ -1475,112 +1507,153 @@ static int wm_adsp2_ena(struct wm_adsp *dsp)  	}  	adsp_dbg(dsp, "RAM ready after %d polls\n", count); -	adsp_info(dsp, "RAM ready after %d polls\n", count);  	return 0;  } -int wm_adsp2_event(struct snd_soc_dapm_widget *w, -		   struct snd_kcontrol *kcontrol, int event) +static void wm_adsp2_boot_work(struct work_struct *work)  { -	struct snd_soc_codec *codec = w->codec; -	struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); -	struct wm_adsp *dsp = &dsps[w->shift]; -	struct wm_adsp_alg_region *alg_region; -	struct wm_coeff_ctl *ctl; -	unsigned int val; +	struct wm_adsp *dsp = container_of(work, +					   struct wm_adsp, +					   boot_work);  	int ret; +	unsigned int val; -	dsp->card = codec->card; +	/* +	 * For simplicity set the DSP clock rate to be the +	 * SYSCLK rate rather than making it configurable. +	 */ +	ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val); +	if (ret != 0) { +		adsp_err(dsp, "Failed to read SYSCLK state: %d\n", ret); +		return; +	} +	val = (val & ARIZONA_SYSCLK_FREQ_MASK) +		>> ARIZONA_SYSCLK_FREQ_SHIFT; -	switch (event) { -	case SND_SOC_DAPM_POST_PMU: -		/* -		 * For simplicity set the DSP clock rate to be the -		 * SYSCLK rate rather than making it configurable. -		 */ -		ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val); -		if (ret != 0) { -			adsp_err(dsp, "Failed to read SYSCLK state: %d\n", -				 ret); -			return ret; -		} -		val = (val & ARIZONA_SYSCLK_FREQ_MASK) -			>> ARIZONA_SYSCLK_FREQ_SHIFT; +	ret = regmap_update_bits_async(dsp->regmap, +				       dsp->base + ADSP2_CLOCKING, +				       ADSP2_CLK_SEL_MASK, val); +	if (ret != 0) { +		adsp_err(dsp, "Failed to set clock rate: %d\n", ret); +		return; +	} -		ret = regmap_update_bits(dsp->regmap, -					 dsp->base + ADSP2_CLOCKING, -					 ADSP2_CLK_SEL_MASK, val); +	if (dsp->dvfs) { +		ret = regmap_read(dsp->regmap, +				  dsp->base + ADSP2_CLOCKING, &val);  		if (ret != 0) { -			adsp_err(dsp, "Failed to set clock rate: %d\n", -				 ret); -			return ret; +			adsp_err(dsp, "Failed to read clocking: %d\n", ret); +			return;  		} -		if (dsp->dvfs) { -			ret = regmap_read(dsp->regmap, -					  dsp->base + ADSP2_CLOCKING, &val); +		if ((val & ADSP2_CLK_SEL_MASK) >= 3) { +			ret = regulator_enable(dsp->dvfs);  			if (ret != 0) { -				dev_err(dsp->dev, -					"Failed to read clocking: %d\n", ret); -				return ret; +				adsp_err(dsp, +					 "Failed to enable supply: %d\n", +					 ret); +				return;  			} -			if ((val & ADSP2_CLK_SEL_MASK) >= 3) { -				ret = regulator_enable(dsp->dvfs); -				if (ret != 0) { -					dev_err(dsp->dev, -						"Failed to enable supply: %d\n", -						ret); -					return ret; -				} - -				ret = regulator_set_voltage(dsp->dvfs, -							    1800000, -							    1800000); -				if (ret != 0) { -					dev_err(dsp->dev, -						"Failed to raise supply: %d\n", -						ret); -					return ret; -				} +			ret = regulator_set_voltage(dsp->dvfs, +						    1800000, +						    1800000); +			if (ret != 0) { +				adsp_err(dsp, +					 "Failed to raise supply: %d\n", +					 ret); +				return;  			}  		} +	} -		ret = wm_adsp2_ena(dsp); -		if (ret != 0) -			return ret; +	ret = wm_adsp2_ena(dsp); +	if (ret != 0) +		return; -		ret = wm_adsp_load(dsp); -		if (ret != 0) -			goto err; +	ret = wm_adsp_load(dsp); +	if (ret != 0) +		goto err; -		ret = wm_adsp_setup_algs(dsp); -		if (ret != 0) -			goto err; +	ret = wm_adsp_setup_algs(dsp); +	if (ret != 0) +		goto err; -		ret = wm_adsp_load_coeff(dsp); -		if (ret != 0) -			goto err; +	ret = wm_adsp_load_coeff(dsp); +	if (ret != 0) +		goto err; -		/* Initialize caches for enabled and unset controls */ -		ret = wm_coeff_init_control_caches(dsp); -		if (ret != 0) -			goto err; +	/* Initialize caches for enabled and unset controls */ +	ret = wm_coeff_init_control_caches(dsp); +	if (ret != 0) +		goto err; -		/* Sync set controls */ -		ret = wm_coeff_sync_controls(dsp); -		if (ret != 0) -			goto err; +	/* Sync set controls */ +	ret = wm_coeff_sync_controls(dsp); +	if (ret != 0) +		goto err; + +	ret = regmap_update_bits_async(dsp->regmap, +				       dsp->base + ADSP2_CONTROL, +				       ADSP2_CORE_ENA, +				       ADSP2_CORE_ENA); +	if (ret != 0) +		goto err; + +	dsp->running = true; + +	return; + +err: +	regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, +			   ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0); +} + +int wm_adsp2_early_event(struct snd_soc_dapm_widget *w, +		   struct snd_kcontrol *kcontrol, int event) +{ +	struct snd_soc_codec *codec = w->codec; +	struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); +	struct wm_adsp *dsp = &dsps[w->shift]; + +	dsp->card = codec->card; + +	switch (event) { +	case SND_SOC_DAPM_PRE_PMU: +		queue_work(system_unbound_wq, &dsp->boot_work); +		break; +	default: +		break; +	} + +	return 0; +} +EXPORT_SYMBOL_GPL(wm_adsp2_early_event); + +int wm_adsp2_event(struct snd_soc_dapm_widget *w, +		   struct snd_kcontrol *kcontrol, int event) +{ +	struct snd_soc_codec *codec = w->codec; +	struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); +	struct wm_adsp *dsp = &dsps[w->shift]; +	struct wm_adsp_alg_region *alg_region; +	struct wm_coeff_ctl *ctl; +	int ret; + +	switch (event) { +	case SND_SOC_DAPM_POST_PMU: +		flush_work(&dsp->boot_work); + +		if (!dsp->running) +			return -EIO;  		ret = regmap_update_bits(dsp->regmap,  					 dsp->base + ADSP2_CONTROL, -					 ADSP2_CORE_ENA | ADSP2_START, -					 ADSP2_CORE_ENA | ADSP2_START); +					 ADSP2_START, +					 ADSP2_START);  		if (ret != 0)  			goto err; - -		dsp->running = true;  		break;  	case SND_SOC_DAPM_PRE_PMD: @@ -1599,15 +1672,15 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,  			ret = regulator_set_voltage(dsp->dvfs, 1200000,  						    1800000);  			if (ret != 0) -				dev_warn(dsp->dev, -					 "Failed to lower supply: %d\n", -					 ret); +				adsp_warn(dsp, +					  "Failed to lower supply: %d\n", +					  ret);  			ret = regulator_disable(dsp->dvfs);  			if (ret != 0) -				dev_err(dsp->dev, -					"Failed to enable supply: %d\n", -					ret); +				adsp_err(dsp, +					 "Failed to enable supply: %d\n", +					 ret);  		}  		list_for_each_entry(ctl, &dsp->ctl_list, list) @@ -1620,6 +1693,8 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,  			list_del(&alg_region->list);  			kfree(alg_region);  		} + +		adsp_dbg(dsp, "Shutdown complete\n");  		break;  	default: @@ -1651,33 +1726,31 @@ int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs)  	INIT_LIST_HEAD(&adsp->alg_regions);  	INIT_LIST_HEAD(&adsp->ctl_list); +	INIT_WORK(&adsp->boot_work, wm_adsp2_boot_work);  	if (dvfs) {  		adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD");  		if (IS_ERR(adsp->dvfs)) {  			ret = PTR_ERR(adsp->dvfs); -			dev_err(adsp->dev, "Failed to get DCVDD: %d\n", ret); +			adsp_err(adsp, "Failed to get DCVDD: %d\n", ret);  			return ret;  		}  		ret = regulator_enable(adsp->dvfs);  		if (ret != 0) { -			dev_err(adsp->dev, "Failed to enable DCVDD: %d\n", -				ret); +			adsp_err(adsp, "Failed to enable DCVDD: %d\n", ret);  			return ret;  		}  		ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000);  		if (ret != 0) { -			dev_err(adsp->dev, "Failed to initialise DVFS: %d\n", -				ret); +			adsp_err(adsp, "Failed to initialise DVFS: %d\n", ret);  			return ret;  		}  		ret = regulator_disable(adsp->dvfs);  		if (ret != 0) { -			dev_err(adsp->dev, "Failed to disable DCVDD: %d\n", -				ret); +			adsp_err(adsp, "Failed to disable DCVDD: %d\n", ret);  			return ret;  		}  	}  | 
