From nobody Sun Oct 5 23:48:03 2025 Received: from mail-wm1-f45.google.com (mail-wm1-f45.google.com [209.85.128.45]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B095F28B3FA for ; Tue, 29 Jul 2025 10:36:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.45 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1753785402; cv=none; b=Vg9I2agDRx35lpjGfydy9iEszoLdnXAajXFKH4SwTcuc/AwnK3ywFgslIzovbH1hFK827CIoHb8sqFxYuIzK+162P6pCUTYPGJelEDUuOjyNH7e3Ntg/GScBcHJP2gMFbI99TmuFWz2fUCN6JpjBFoWqPEiTeLdVcUTb122jibM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1753785402; c=relaxed/simple; bh=8SgViRt/rg/dYGQ12Q07Sv043x5SZDkHh/R0OGzTYEo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=JcfZJd7POxNoxMK5SGXVPMNd84fLn6jij1ouMfNrj8JqoyTfkWDtdSws4yNm+IzJO0xaCaurUFzEkBBCyUOJOKHx3w996S2BC61MyxKU5hsmJDtYrXZOoCWAYDP0LT99k8UEzo4TelPvtZO5H3QpRgX2SHMnSNSA28DO87RqiTA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=baylibre.com; spf=pass smtp.mailfrom=baylibre.com; dkim=pass (2048-bit key) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.b=Wb/bGXJO; arc=none smtp.client-ip=209.85.128.45 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=baylibre.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=baylibre.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.b="Wb/bGXJO" Received: by mail-wm1-f45.google.com with SMTP id 5b1f17b1804b1-4561514c7f0so51787555e9.0 for ; Tue, 29 Jul 2025 03:36:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20230601.gappssmtp.com; s=20230601; t=1753785398; x=1754390198; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=uEiED8GGvScDQ4pdNirKSqFAan2eh/uwLFtQfQ9k+BI=; b=Wb/bGXJOoatWy8OEJwrqjaHJPqPhMSOWFlF8EGGZtA4QpwQkbeXrniD5w/+5fKlGNR sJKP8ECwJt5EDSo2VEyWjFwwc/9eSuEy8LlH72wvJjWk7GayZ3dSXHv3SFKo2KUqUlrZ e7V2NsbsBh7WXFxX9pem42LUMR3Rm9BZw7rCxn4HDf4KlP1Rw91Dgvkj3pjYbyk23/x7 DnCXBB7th4qvmbR1oaI3BwOjUnD4z65ZIlmv1xuu/Cf0Gc7qMI3a3JSHK2PTU0+RJjuS BpgxlpOfMvrQmL5pwgbOZJHKOZArebXZ2ZytpXpW1OJ+cNFqXcndSUnhbKzDabHXQu8D R5gQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1753785398; x=1754390198; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=uEiED8GGvScDQ4pdNirKSqFAan2eh/uwLFtQfQ9k+BI=; b=oYbOlPMqJJu1QyVjkaWA+Iej5UiJhlvNJqblfDnSTPFc4u3xtNQoKxHQRFYK+0VKGb AkRjaMt6AwIeo4QUxyaOjJevsKVJJIghL5W6uoIU0uUF0UZXz71E0KQ/OzoTjc5BJUYa uWDwT+yRWGSI3bzu/RkZAXjQVdR79bmqNiJxkEH+ktirguXe/paf6zvEJ2HINFhmXLIe qRpvpmVLHIEolz/0K/0k5sRkbHHp6T+o7X8kH7NU1my6vbXRyAfbr5UJ9M5JkK7cjMLV 9ocYsE94Eli2NLQt2jlGwzvl6FvIaiRB2vHAT0UzFMhZ1MOtpPaSFGxmb4T4jZyL2tq0 lu8w== X-Forwarded-Encrypted: i=1; AJvYcCUYd4q41y5hOcH5MeYVcVajAP5SgCdceEIcvrmTtaYZCugl2tw6ijRr0X32e3dDu6cIsIZFcgWjFCpHNGY=@vger.kernel.org X-Gm-Message-State: AOJu0YzYececAQ42m61RZvLOKy9QwLWkJpZxVoE0f+m56Fqi2bOKBWxx 2l+hCJZEtYb8HXcTWfvnR2xdmng3ltlcXYHp8jaXg/eUeuszWFJDZWE1lYnp99gWCRg= X-Gm-Gg: ASbGnctq/N2lwepn7xWDxJAXvKMzwvBUyXK1f40cThFd821voxvbB2shgmy//HUxhRm R91xYE3m0wgCh5bugBSF/5MgspIsWu8PqpaUi3DRuApP3if3+Dbb8HU0r7q5W6NavucdFVD8Bzz 6qHL29IpH2MTPyqvCkwZELFI8y+iPXnLEhqTDrMl7+0ob+VhhVbJNMqvswCnqdc5MQuaHXkS8Nx e0QQYKGfwWHEfIcK3cXmZytDe2yBanihF9CNaH/BDNco9MMMVMErTYlcM3xXCVI9a55wRer3UEX ciAHiC79LmtzlGyytB6F/DHzbVHwM1ewMijhGtBIFg9MlHBBXUHZCRk4WpE4u95/VoA8W2tN0Gd yqwN2p9GK/zKzqDBazNvcYgbFRbZOK4NndbWLFmUG5h15qcXSa0aFfEhNgcMDXh11YGDdS1hxui g= X-Google-Smtp-Source: AGHT+IFf8Hzl3zLHh4WUfvZW0IgHqfWFb7jfiHvMFPiClvMOX1fd580vq9CmSQs+m5pcyA/37+laUQ== X-Received: by 2002:a05:600c:4e0c:b0:44a:b478:1387 with SMTP id 5b1f17b1804b1-45882a37dc7mr83918765e9.17.1753785397897; Tue, 29 Jul 2025 03:36:37 -0700 (PDT) Received: from localhost (p200300f65f06ab0400000000000001b9.dip0.t-ipconnect.de. [2003:f6:5f06:ab04::1b9]) by smtp.gmail.com with UTF8SMTPSA id 5b1f17b1804b1-4588e5e4aeasm21870085e9.29.2025.07.29.03.36.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 29 Jul 2025 03:36:37 -0700 (PDT) From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= To: linux-pwm@vger.kernel.org Cc: David Jander , Clemens Gruber , linux-kernel@vger.kernel.org Subject: [PATCH 5/5] pwm: pca9586: Convert to waveform API Date: Tue, 29 Jul 2025 12:36:04 +0200 Message-ID: <1927d115ae6797858e6c4537971dacf1d563854f.1753784092.git.u.kleine-koenig@baylibre.com> X-Mailer: git-send-email 2.50.0 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" X-Developer-Signature: v=1; a=openpgp-sha256; l=13712; i=u.kleine-koenig@baylibre.com; h=from:subject:message-id; bh=8SgViRt/rg/dYGQ12Q07Sv043x5SZDkHh/R0OGzTYEo=; b=owGbwMvMwMXY3/A7olbonx/jabUkhoyOJXIN+zKXyb2yD3JkvsTUEWOXIJLD9S7vkGFl3tXC/ oy9Ie86GY1ZGBi5GGTFFFnsG9dkWlXJRXau/XcZZhArE8gUBi5OAZhIYCz7P5uWqCJHGXH/fsFt LCvmJLl09tx14neL1roas/qIRrjtbrUihgC5drZFvQUac+TTGG5+f2JwJqTj+Lxg7z/XDZ5xcS9 WrH/Xr2V9mFEm9dtH5i2XS3aszrM5aif5Yuv/RcmLL1wr3CzBffbxVLYvU5erXdlfqrFeVGzx8e db3C7cTtNsM9i6Nsy17YWS/CWj3tC/WibmW6QYxMsm/guw8l/Bc/vO1Jmef3PKwvvEYyxfF9S2e i3Z7t60lkuuLTe1NP+/BJP1/Mz//B/jXf2//Xdcu9uEfcP/qVZdwcsfh/Xxf0qKecLySmq/mYzZ 80tVUoYzXnXnelicW6Nr5izI90I2Wja+tDFMliHs8x0A X-Developer-Key: i=u.kleine-koenig@baylibre.com; a=openpgp; fpr=0D2511F322BFAB1C1580266BE2DCDD9132669BD6 Content-Transfer-Encoding: quoted-printable This allows to expose the duty_offset feature that the chip supports, and so also emit inverted polarity waveforms. The conversion from a waveform to hardware settings (and vice versa) is aligned to the usual rounding rules silencing warnings with PWM_DEBUG. Signed-off-by: Uwe Kleine-K=C3=B6nig --- drivers/pwm/pwm-pca9685.c | 347 ++++++++++++++++++++------------------ 1 file changed, 185 insertions(+), 162 deletions(-) diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c index 3f04defd3718..107bebec3546 100644 --- a/drivers/pwm/pwm-pca9685.c +++ b/drivers/pwm/pwm-pca9685.c @@ -49,7 +49,14 @@ #define PCA9685_PRESCALE_MAX 0xFF /* =3D> min. frequency of 24 Hz */ =20 #define PCA9685_COUNTER_RANGE 4096 -#define PCA9685_OSC_CLOCK_MHZ 25 /* Internal oscillator with 25 MHz */ +#define PCA9685_OSC_CLOCK_HZ 25000000 /* Internal oscillator with 25 MHz */ + +/* + * The time value of one counter tick. Note that NSEC_PER_SEC is an integer + * multiple of PCA9685_OSC_CLOCK_HZ, so there is no rounding involved and = we're + * not loosing precision due to the early division. + */ +#define PCA9685_QUANTUM_NS(_prescale) ((NSEC_PER_SEC / PCA9685_OSC_CLOCK_H= Z) * (_prescale + 1)) =20 #define PCA9685_NUMREGS 0xFF #define PCA9685_MAXCHAN 0x10 @@ -141,202 +148,215 @@ static int pca9685_write_4reg(struct pwm_chip *chip= , unsigned int reg, u8 val[4] return err; } =20 -/* Helper function to set the duty cycle ratio to duty/4096 (e.g. duty=3D2= 048 -> 50%) */ -static void pca9685_pwm_set_duty(struct pwm_chip *chip, int channel, unsig= ned int duty) +static int pca9685_set_sleep_mode(struct pwm_chip *chip, bool enable) { - struct pwm_device *pwm =3D &chip->pwms[channel]; - unsigned int on, off; - - if (duty =3D=3D 0) { - /* Set the full OFF bit, which has the highest precedence */ - pca9685_write_reg(chip, REG_OFF_H(channel), LED_FULL); - return; - } else if (duty >=3D PCA9685_COUNTER_RANGE) { - /* Set the full ON bit and clear the full OFF bit */ - pca9685_write_4reg(chip, REG_ON_L(channel), (u8[4]){ 0, LED_FULL, 0, 0 }= ); - return; - } - - if (pwm->state.usage_power && channel < PCA9685_MAXCHAN) { - /* - * If usage_power is set, the pca9685 driver will phase shift - * the individual channels relative to their channel number. - * This improves EMI because the enabled channels no longer - * turn on at the same time, while still maintaining the - * configured duty cycle / power output. - */ - on =3D channel * PCA9685_COUNTER_RANGE / PCA9685_MAXCHAN; - } else - on =3D 0; - - off =3D (on + duty) % PCA9685_COUNTER_RANGE; - - /* implicitly clear full ON and full OFF bit */ - pca9685_write_4reg(chip, REG_ON_L(channel), - (u8[4]){ on & 0xff, (on >> 8) & 0xf, off & 0xff, (off >> 8) & 0xf }); -} - -static unsigned int pca9685_pwm_get_duty(struct pwm_chip *chip, int channe= l) -{ - struct pwm_device *pwm =3D &chip->pwms[channel]; - unsigned int off =3D 0, on =3D 0, val =3D 0; - - if (WARN_ON(channel >=3D PCA9685_MAXCHAN)) { - /* HW does not support reading state of "all LEDs" channel */ - return 0; - } - - pca9685_read_reg(chip, LED_N_OFF_H(channel), &off); - if (off & LED_FULL) { - /* Full OFF bit is set */ - return 0; - } - - pca9685_read_reg(chip, LED_N_ON_H(channel), &on); - if (on & LED_FULL) { - /* Full ON bit is set */ - return PCA9685_COUNTER_RANGE; - } - - pca9685_read_reg(chip, LED_N_OFF_L(channel), &val); - off =3D ((off & 0xf) << 8) | (val & 0xff); - if (!pwm->state.usage_power) - return off; - - /* Read ON register to calculate duty cycle of staggered output */ - if (pca9685_read_reg(chip, LED_N_ON_L(channel), &val)) { - /* Reset val to 0 in case reading LED_N_ON_L failed */ - val =3D 0; - } - on =3D ((on & 0xf) << 8) | (val & 0xff); - return (off - on) & (PCA9685_COUNTER_RANGE - 1); -} - -static void pca9685_set_sleep_mode(struct pwm_chip *chip, bool enable) -{ - struct device *dev =3D pwmchip_parent(chip); struct pca9685 *pca =3D to_pca(chip); - int err =3D regmap_update_bits(pca->regmap, PCA9685_MODE1, - MODE1_SLEEP, enable ? MODE1_SLEEP : 0); - if (err) { - dev_err(dev, "regmap_update_bits of register 0x%x failed: %pe\n", - PCA9685_MODE1, ERR_PTR(err)); - return; - } + int err; + + err =3D regmap_update_bits(pca->regmap, PCA9685_MODE1, + MODE1_SLEEP, enable ? MODE1_SLEEP : 0); + if (err) + return err; =20 if (!enable) { /* Wait 500us for the oscillator to be back up */ udelay(500); } + + return 0; } =20 -static int __pca9685_pwm_apply(struct pwm_chip *chip, struct pwm_device *p= wm, - const struct pwm_state *state) +struct pca9685_waveform { + u8 onoff[4]; + u8 prescale; +}; + +static int pca9685_round_waveform_tohw(struct pwm_chip *chip, struct pwm_d= evice *pwm, const struct pwm_waveform *wf, void *_wfhw) { + struct pca9685_waveform *wfhw =3D _wfhw; struct pca9685 *pca =3D to_pca(chip); - unsigned long long duty, prescale; - unsigned int val =3D 0; + unsigned int best_prescale; + u8 prescale; + unsigned int period_ns, duty; + int ret_tohw =3D 0; =20 - if (state->polarity !=3D PWM_POLARITY_NORMAL) - return -EINVAL; + if (!wf->period_length_ns) { + *wfhw =3D (typeof(*wfhw)){ + .onoff =3D { 0, 0, 0, LED_FULL, }, + .prescale =3D 0, + }; =20 - prescale =3D DIV_ROUND_CLOSEST_ULL(PCA9685_OSC_CLOCK_MHZ * state->period, - PCA9685_COUNTER_RANGE * 1000) - 1; - if (prescale < PCA9685_PRESCALE_MIN || prescale > PCA9685_PRESCALE_MAX) { - dev_err(pwmchip_parent(chip), "pwm not changed: period out of bounds!\n"= ); - return -EINVAL; - } + dev_dbg(&chip->dev, "pwm#%u: %lld/%lld [+%lld] -> [%hhx %hhx %hhx %hhx] = PSC:%hhx\n", + pwm->hwpwm, wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_n= s, + wfhw->onoff[0], wfhw->onoff[1], wfhw->onoff[2], wfhw->onoff[3], wfhw->p= rescale); =20 - if (!state->enabled) { - pca9685_pwm_set_duty(chip, pwm->hwpwm, 0); return 0; } =20 - pca9685_read_reg(chip, PCA9685_PRESCALE, &val); - if (prescale !=3D val) { - if (!pca9685_prescaler_can_change(pca, pwm->hwpwm)) { - dev_err(pwmchip_parent(chip), - "pwm not changed: periods of enabled pwms must match!\n"); - return -EBUSY; + if (wf->period_length_ns >=3D PCA9685_COUNTER_RANGE * PCA9685_QUANTUM_NS(= 255)) { + best_prescale =3D 255; + } else if (wf->period_length_ns < PCA9685_COUNTER_RANGE * PCA9685_QUANTUM= _NS(3)) { + best_prescale =3D 3; + ret_tohw =3D 1; + } else { + best_prescale =3D (unsigned int)wf->period_length_ns / (PCA9685_COUNTER_= RANGE * (NSEC_PER_SEC / PCA9685_OSC_CLOCK_HZ)) - 1; + } + + guard(mutex)(&pca->lock); + + if (!pca9685_prescaler_can_change(pca, pwm->hwpwm)) { + unsigned int current_prescale; + int ret; + + ret =3D regmap_read(pca->regmap, PCA9685_PRESCALE, ¤t_prescale); + if (ret) + return ret; + + if (current_prescale > best_prescale) + ret_tohw =3D 1; + + prescale =3D current_prescale; + } else { + prescale =3D best_prescale; + } + + period_ns =3D PCA9685_COUNTER_RANGE * PCA9685_QUANTUM_NS(prescale); + + duty =3D (unsigned)min_t(u64, wf->duty_length_ns, period_ns) / PCA9685_QU= ANTUM_NS(prescale); + + if (duty < PCA9685_COUNTER_RANGE) { + unsigned int on, off; + + on =3D (unsigned)min_t(u64, wf->duty_offset_ns, period_ns) / PCA9685_QUA= NTUM_NS(prescale); + off =3D (on + duty) % PCA9685_COUNTER_RANGE; + + /* + * With a zero duty cycle, it doesn't matter if period was + * rounded up + */ + if (!duty) + ret_tohw =3D 0; + + *wfhw =3D (typeof(*wfhw)){ + .onoff =3D { on & 0xff, (on >> 8) & 0xf, off & 0xff, (off >> 8) & 0xf }, + .prescale =3D prescale, + }; + } else { + *wfhw =3D (typeof(*wfhw)){ + .onoff =3D { 0, LED_FULL, 0, 0, }, + .prescale =3D prescale, + }; + } + + dev_dbg(&chip->dev, "pwm#%u: %lld/%lld [+%lld] -> %s[%hhx %hhx %hhx %hhx]= PSC:%hhx\n", + pwm->hwpwm, wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns, + ret_tohw ? "#" : "", wfhw->onoff[0], wfhw->onoff[1], wfhw->onoff[2], wfh= w->onoff[3], wfhw->prescale); + + return ret_tohw; +} + +static int pca9685_round_waveform_fromhw(struct pwm_chip *chip, struct pwm= _device *pwm, + const void *_wfhw, struct pwm_waveform *wf) +{ + const struct pca9685_waveform *wfhw =3D _wfhw; + struct pca9685 *pca =3D to_pca(chip); + unsigned int prescale; + + if (wfhw->prescale) + prescale =3D wfhw->prescale; + else + scoped_guard(mutex, &pca->lock) { + int ret; + + ret =3D regmap_read(pca->regmap, PCA9685_PRESCALE, &prescale); + if (ret) + return ret; } =20 - /* - * Putting the chip briefly into SLEEP mode - * at this point won't interfere with the - * pm_runtime framework, because the pm_runtime - * state is guaranteed active here. - */ - /* Put chip into sleep mode */ - pca9685_set_sleep_mode(chip, true); + wf->period_length_ns =3D PCA9685_COUNTER_RANGE * PCA9685_QUANTUM_NS(presc= ale); =20 - /* Change the chip-wide output frequency */ - pca9685_write_reg(chip, PCA9685_PRESCALE, prescale); + if (wfhw->onoff[3] & LED_FULL) { + wf->duty_length_ns =3D 0; + wf->duty_offset_ns =3D 0; + } else if (wfhw->onoff[1] & LED_FULL) { + wf->duty_length_ns =3D wf->period_length_ns; + wf->duty_offset_ns =3D 0; + } else { + unsigned int on =3D wfhw->onoff[0] | (wfhw->onoff[1] & 0xf) << 8; + unsigned int off =3D wfhw->onoff[2] | (wfhw->onoff[3] & 0xf) << 8; =20 - /* Wake the chip up */ - pca9685_set_sleep_mode(chip, false); + wf->duty_length_ns =3D (off - on) % PCA9685_COUNTER_RANGE * PCA9685_QUAN= TUM_NS(prescale); + wf->duty_offset_ns =3D on * PCA9685_QUANTUM_NS(prescale); } =20 - duty =3D PCA9685_COUNTER_RANGE * state->duty_cycle; - duty =3D DIV_ROUND_UP_ULL(duty, state->period); - pca9685_pwm_set_duty(chip, pwm->hwpwm, duty); + dev_dbg(&chip->dev, "pwm#%u: [%hhx %hhx %hhx %hhx] PSC:%hhx -> %lld/%lld = [+%lld]\n", + pwm->hwpwm, + wfhw->onoff[0], wfhw->onoff[1], wfhw->onoff[2], wfhw->onoff[3], wfhw->pr= escale, + wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns); + return 0; } =20 -static int pca9685_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, - const struct pwm_state *state) +static int pca9685_read_waveform(struct pwm_chip *chip, struct pwm_device = *pwm, void *_wfhw) { + struct pca9685_waveform *wfhw =3D _wfhw; struct pca9685 *pca =3D to_pca(chip); + unsigned int prescale; int ret; =20 - mutex_lock(&pca->lock); - ret =3D __pca9685_pwm_apply(chip, pwm, state); - if (ret =3D=3D 0) { - if (state->enabled) - set_bit(pwm->hwpwm, pca->pwms_enabled); - else - clear_bit(pwm->hwpwm, pca->pwms_enabled); - } - mutex_unlock(&pca->lock); + guard(mutex)(&pca->lock); =20 - return ret; -} + ret =3D regmap_bulk_read(pca->regmap, REG_ON_L(pwm->hwpwm), &wfhw->onoff,= 4); + if (ret) + return ret; =20 -static int pca9685_pwm_get_state(struct pwm_chip *chip, struct pwm_device = *pwm, - struct pwm_state *state) -{ - unsigned long long duty; - unsigned int val =3D 0; + ret =3D regmap_read(pca->regmap, PCA9685_PRESCALE, &prescale); + if (ret) + return ret; =20 - /* Calculate (chip-wide) period from prescale value */ - pca9685_read_reg(chip, PCA9685_PRESCALE, &val); - /* - * PCA9685_OSC_CLOCK_MHZ is 25, i.e. an integer divider of 1000. - * The following calculation is therefore only a multiplication - * and we are not losing precision. - */ - state->period =3D (PCA9685_COUNTER_RANGE * 1000 / PCA9685_OSC_CLOCK_MHZ) * - (val + 1); - - /* The (per-channel) polarity is fixed */ - state->polarity =3D PWM_POLARITY_NORMAL; - - if (pwm->hwpwm >=3D PCA9685_MAXCHAN) { - /* - * The "all LEDs" channel does not support HW readout - * Return 0 and disabled for backwards compatibility - */ - state->duty_cycle =3D 0; - state->enabled =3D false; - return 0; - } - - state->enabled =3D true; - duty =3D pca9685_pwm_get_duty(chip, pwm->hwpwm); - state->duty_cycle =3D DIV_ROUND_DOWN_ULL(duty * state->period, PCA9685_CO= UNTER_RANGE); + wfhw->prescale =3D prescale; =20 return 0; } =20 +static int pca9685_write_waveform(struct pwm_chip *chip, struct pwm_device= *pwm, const void *_wfhw) +{ + const struct pca9685_waveform *wfhw =3D _wfhw; + struct pca9685 *pca =3D to_pca(chip); + unsigned int current_prescale; + int ret; + + guard(mutex)(&pca->lock); + + if (wfhw->prescale) { + ret =3D regmap_read(pca->regmap, PCA9685_PRESCALE, ¤t_prescale); + if (ret) + return ret; + + if (current_prescale !=3D wfhw->prescale) { + if (!pca9685_prescaler_can_change(pca, pwm->hwpwm)) + return -EBUSY; + + /* Put chip into sleep mode */ + ret =3D pca9685_set_sleep_mode(chip, true); + if (ret) + return ret; + + /* Change the chip-wide output frequency */ + ret =3D regmap_write(pca->regmap, PCA9685_PRESCALE, wfhw->prescale); + if (ret) + return ret; + + /* Wake the chip up */ + ret =3D pca9685_set_sleep_mode(chip, false); + if (ret) + return ret; + } + } + + return regmap_bulk_write(pca->regmap, REG_ON_L(pwm->hwpwm), &wfhw->onoff,= 4); +} + static int pca9685_pwm_request(struct pwm_chip *chip, struct pwm_device *p= wm) { struct pca9685 *pca =3D to_pca(chip); @@ -365,8 +385,11 @@ static void pca9685_pwm_free(struct pwm_chip *chip, st= ruct pwm_device *pwm) } =20 static const struct pwm_ops pca9685_pwm_ops =3D { - .apply =3D pca9685_pwm_apply, - .get_state =3D pca9685_pwm_get_state, + .sizeof_wfhw =3D sizeof(struct pca9685_waveform), + .round_waveform_tohw =3D pca9685_round_waveform_tohw, + .round_waveform_fromhw =3D pca9685_round_waveform_fromhw, + .read_waveform =3D pca9685_read_waveform, + .write_waveform =3D pca9685_write_waveform, .request =3D pca9685_pwm_request, .free =3D pca9685_pwm_free, }; --=20 2.50.0