From 0563501683d7f9e665eeb0da0c6ef443f5cf5efd Mon Sep 17 00:00:00 2001 From: The-going <48602507+The-going@users.noreply.github.com> Date: Tue, 29 Apr 2025 16:36:16 +0300 Subject: [PATCH 1/7] sunxi-6.14: Add megous patches --- ...entation-and-rotation-for-PinePhone-.patch | 31 + .../ARM-dts-axp813-Add-charger-LED.patch | 30 + ...Book-Touch-Lux-3-display-ctp-support.patch | 185 + .../ARM-dts-sun5i-Add-soc-handle.patch | 26 + ...book-touch-lux-3-Add-RTC-clock-cells.patch | 27 + .../ARM-dts-sun8i-a83t-Add-MBUS-node.patch | 39 + ...us-video-codec-support-to-A83T-untes.patch | 37 + ...M-dts-sun8i-a83t-Add-hdmi-sound-card.patch | 48 + ...un8i-a83t-Add-missing-GPU-trip-point.patch | 35 + ...-hdmi-sound-card-on-boards-with-hdmi.patch | 69 + ...prove-CPU-OPP-tables-go-up-to-1.8GHz.patch | 134 + ...s-sun8i-a83t-Set-fifo-size-for-uarts.patch | 59 + ...-a83t-tbs-a711-Add-PN544-NFC-support.patch | 66 + ...711-Add-camera-sensors-HM5065-GC2145.patch | 180 + ...-a83t-tbs-a711-Add-flash-led-support.patch | 32 + ...-Add-powerup-down-support-for-the-3G.patch | 61 + ...-Add-regulators-to-the-accelerometer.patch | 30 + ...11-Add-sound-support-via-AC100-codec.patch | 131 + ...1-Add-support-for-the-vibrator-motor.patch | 39 + ...8i-a83t-tbs-a711-Enable-charging-LED.patch | 31 + ...-Give-Linux-more-privileges-over-SCP.patch | 47 + ...711-Increase-voltage-on-the-vibrator.patch | 30 + ...s-bananapi-m2-zero-Enable-HDMI-audio.patch | 42 + ...-hdmi-sound-card-on-boards-with-hdmi.patch | 272 ++ ...se-my-own-more-aggressive-OPPs-on-H3.patch | 72 + ...-pi-one-Enable-all-gpio-header-UARTs.patch | 49 + ...pc-Increase-max-CPUX-voltage-to-1.4V.patch | 31 + ...RM-dts-sun8i-r40-Add-hdmi-sound-card.patch | 48 + ...-bananapi-m2-ultra-Enable-HDMI-audio.patch | 42 + ...-bananapi-m2-berry-Enable-HDMI-audio.patch | 42 + .../ARM-dts-suni-a83t-Add-i2s0-pins.patch | 29 + .../ARM-dts-sunxi-Add-aliases-for-MMC.patch | 66 + ...ARM-dts-sunxi-a83t-Add-SCPI-protocol.patch | 52 + ...RM-dts-sunxi-h3-h5-Add-SCPI-protocol.patch | 81 + ...-dts-sunxi-h3-h5-Add-hdmi-sound-card.patch | 48 + ...suspend-to-memory-implementation-for.patch | 88 + ...-send-suspend-message-to-SCP-on-A83T.patch | 54 + ...g_support_set-is-not-supported-on-A8.patch | 27 + ...9i-hdmi-audio-Initial-implementation.patch | 233 + ...-New-codec-driver-for-the-EC25-modem.patch | 154 + ...of-playback-speed-after-system-sleep.patch | 45 + ...-to-define-pins-for-aux-jack-devices.patch | 67 + ...-Add-debug-output-for-jack-detection.patch | 129 + ...-jack-type-to-be-set-via-device-tree.patch | 47 + ...codec-Set-jack_type-from-DT-in-probe.patch | 48 + ...C-sun8i-codec-define-button-keycodes.patch | 31 + ...-with-information-and-u-boot-patches.patch | 215 + ...upport-for-my-private-Sapomat-device.patch | 66 + .../Defconfigs-for-all-my-devices.patch | 4002 +++++++++++++++++ ...lwinner-sram-dependency-on-h616-h618.patch | 53 + .../patches.megous/Fix-intptr_t-typedef.patch | 32 + ...INTAINERS-Add-entry-for-Himax-HM5065.patch | 30 + ...-Pi-PC-and-PC-2-work-as-power-off-bu.patch | 39 + ...async-probe-with-PROBE_PREFER_ASYNCH.patch | 40 + .../Move-a-node-to-avoid-merge-conflict.patch | 115 + .../Revert-Input-cyttsp4-remove-driver.patch | 3232 +++++++++++++ ...-sun4i-lvds-Invert-the-LVDS-polarity.patch | 28 + ...ister-existing-source-caps-before-re.patch | 39 + ...able-K101-IM2BYL02-panel-for-PineTab.patch | 35 + ...ner-Enforce-consistent-MMC-numbering.patch | 66 + ...ts-allwinner-a64-Add-hdmi-sound-card.patch | 48 + ...ble-hdmi-sound-card-on-boards-with-h.patch | 204 + ...s-allwinner-a64-Fix-LRADC-compatible.patch | 32 + ...lwinner-a64-pinetab-add-front-camera.patch | 99 + ...le-hdmi-sound-card-on-boards-with-hd.patch | 113 + ...dts-allwinner-h6-Add-hdmi-sound-card.patch | 56 + ...le-hdmi-sound-card-on-boards-with-hd.patch | 123 + ...llwinner-orange-pi-3-Enable-ethernet.patch | 105 + .../arm64-dts-rk3399-Add-dmc_opp_table.patch | 62 + ...-dts-rockchip-rk3399-s-Add-DMC-table.patch | 62 + ...56x-Fix-PCIe-register-map-and-ranges.patch | 47 + ...a64-pinephone-Define-jack-pins-in-DT.patch | 30 + ...entation-and-rotation-for-PinePhone-.patch | 31 + ...s-sun50i-a64-Set-fifo-size-for-uarts.patch | 59 + ...one-Add-Type-C-support-for-all-PP-va.patch | 416 ++ ...one-Add-detailed-OCV-to-capactiy-con.patch | 187 + ...a64-pinephone-Add-front-back-cameras.patch | 130 + ...pinephone-Add-interrupt-pin-for-WiFi.patch | 26 + ...64-pinephone-Add-modem-power-manager.patch | 119 + ...inephone-Add-power-supply-to-stk3311.patch | 27 + ...a64-pinephone-Add-reboot-mode-driver.patch | 80 + ...one-Add-supply-for-i2c-bus-to-anx768.patch | 54 + ...hone-Add-support-for-Bluetooth-audio.patch | 33 + ...one-Add-support-for-Pinephone-1.2-be.patch | 54 + ...one-Add-support-for-Pinephone-keyboa.patch | 170 + ...inephone-Add-support-for-modem-audio.patch | 71 + ...nephone-Bump-I2C-frequency-to-400kHz.patch | 35 + ...one-Don-t-make-lradc-keys-a-wakeup-s.patch | 29 + ...one-Enable-Pinephone-Keyboard-power-.patch | 34 + ...-pinephone-Enable-internal-HMIC-bias.patch | 29 + ...ephone-Fix-BH-modem-manager-behavior.patch | 29 + ...one-Power-off-the-touch-controller-i.patch | 28 + ...one-Set-minimum-backlight-duty-cycle.patch | 73 + ...one-Shorten-post-power-on-delay-on-m.patch | 43 + ...one-Use-newer-jack-detection-impleme.patch | 35 + ...one-Workaround-broken-HDMI-HPD-signa.patch | 84 + ...sun50i-a64-pinetab-Add-accelerometer.patch | 38 + ...-a64-pinetab-Name-sound-card-PineTab.patch | 28 + ...4-pinetab-enable-RTL8723CS-bluetooth.patch | 44 + ...sun50i-h5-Add-missing-GPU-trip-point.patch | 35 + ...se-my-own-more-aggressive-OPPs-on-H5.patch | 97 + ...s-without-benchmark-to-speed-up-boot.patch | 45 + ..._rts_on_open-true-behavior-on-bcm207.patch | 35 + ...-t-re-initialize-rtl8723cs-on-resume.patch | 28 + ...PLL-gating-and-CPUX-reparenting-to-H.patch | 55 + ...sunxi-ng-Export-CLK_DRAM-for-devfreq.patch | 59 + ...sunxi-ng-Mark-TWD-clocks-as-critical.patch | 56 + ...and-M-factors-to-1-for-H3-pll-cpux-c.patch | 59 + ...64-Increase-PLL_AUDIO-base-frequency.patch | 87 + ...tch-parent-of-MIPI-DSI-to-periph0-1x.patch | 48 + ...etected-CPU-bin-for-easier-debugging.patch | 29 + ...-accept-HPD-status-from-other-driver.patch | 152 + ...e-dw-hdmi-Report-HDMI-hotplug-events.patch | 41 + ...-timings-to-make-refresh-rate-exactl.patch | 37 + ...reboot-when-DRM-device-fails-to-bind.patch | 36 + ...rockchip-Fix-ISP1-PHY-initialization.patch | 35 + ...drm-sun4i-Implement-gamma-correction.patch | 131 + ...one-of-the-UI-planes-as-a-cursor-one.patch | 73 + ...er-display-pipeline-state-from-p-boo.patch | 547 +++ ...ing-dclk-rate-upon-ancestor-clock-ch.patch | 179 + ...20x-adc-allow-to-use-TS-pin-as-GPADC.patch | 34 + ...-vibrator-Don-t-require-enable-gpios.patch | 35 + ...Add-a-binding-for-AXP813-charger-led.patch | 46 + ...dings-for-Himax-HM5065-camera-sensor.patch | 71 + ...dec-related-properties-to-AC100-PMIC.patch | 84 + ...ack-type-property-to-sun8i-a33-codec.patch | 36 + ...port-unidirectional-mailbox-channels.patch | 127 + ...or-sending-a-SCPI_CMD_SET_SYS_PWR_ST.patch | 87 + ...nd-soft-powerdown-message-on-suspend.patch | 64 + .../hm5065-yaml-bindings-wip.patch | 109 + ...ss-when-pinctrl-recovery-state-is-no.patch | 35 + ...dc-allow-to-set-TS-pin-to-GPADC-mode.patch | 42 + ...ow-to-use-sun5i-a13-gpadc-iio-from-D.patch | 61 + ...-error-when-the-device-is-not-presen.patch | 39 + ...-Clear-the-ids-buffer-in-a-saner-way.patch | 32 + ...scate-MT-signals-setup-platform-data.patch | 263 ++ ...-De-obfuscate-platform-data-for-keys.patch | 85 + ...-ENOSYS-error-is-ok-when-powering-up.patch | 29 + ...ter-recovery-from-failed-wakeup-HACK.patch | 31 + .../input-cyttsp4-Fix-compile-issue.patch | 28 + .../input-cyttsp4-Fix-probe-oops.patch | 29 + .../input-cyttsp4-Fix-warnings.patch | 47 + ...river-not-hog-the-system-s-workqueue.patch | 105 + ...-the-driver-to-use-device-properties.patch | 590 +++ ...t-cyttsp4-Remove-unused-enable_vkeys.patch | 27 + ...s-indirection-with-driver-platform-d.patch | 718 +++ ...keup-wakeup-by-I2C-read-doesn-t-work.patch | 125 + ...i2c-spi-names-directly-in-the-driver.patch | 64 + ...se-vcc-supply-alone-to-control-the-v.patch | 76 + ...ort-charger-LED-on-AXP20x-like-PMICs.patch | 345 ++ ...ilbox-while-timekeeping-is-suspended.patch | 64 + ...o-clean-up-hardware-on-probe-failure.patch | 35 + ...IXEL_RATE-HBLANK-and-VBLANK-controls.patch | 549 +++ .../media-gc2145-Added-BGGR-bayer-mode.patch | 30 + .../media-gc2145-Disable-debug-output.patch | 37 + ...2145-Galaxycore-camera-module-driver.patch | 2306 ++++++++++ ...edia-gc2145-fix-white-balance-colors.patch | 183 + ...edia-gc2145-implement-system-suspend.patch | 72 + ...river-for-Himax-HM5065-camera-sensor.patch | 2268 ++++++++++ ...-Move-upstream-driver-out-of-the-way.patch | 52 + ...gc2145-Parse-and-register-properties.patch | 43 + ...40-Add-read-only-property-for-vblank.patch | 24 + ...werup-the-sensor-during-driver-probe.patch | 119 + ...-Try-to-disable-denoising-sharpening.patch | 50 + ...cus-commands-blocking-until-complete.patch | 113 + .../media-ov5640-Implement-autofocus.patch | 395 ++ ...media-ov5640-Improve-error-reporting.patch | 43 + ...ia-ov5640-Improve-firmware-load-time.patch | 177 + ...weroff-to-ensure-next-poweron-is-not.patch | 29 + ...a-ov5640-set-default-ae-target-lower.patch | 27 + ..._force_suspend-resume-for-system-sus.patch | 31 + ...Fix-call-to-pm_runtime_set_suspended.patch | 29 + ...multicamera-support-for-parallel-bus.patch | 166 + ...6i-csi-add-V4L2_CAP_IO_MC-capability.patch | 82 + ...-subdev-operation-to-access-bridge-f.patch | 128 + ...csi-implement-vidioc_enum_framesizes.patch | 65 + ..._csi_formats-and-sun6i_csi_formats_m.patch | 431 ++ ...subdev-active-state-to-store-active-.patch | 346 ++ ...mfd-axp20x-Add-battery-IRQ-resources.patch | 102 + ...modem-power-Power-manager-for-modems.patch | 2048 +++++++++ ...dd-delay-after-power-class-selection.patch | 38 + ...rockchip-fix-sdmmc-after-soft-reboot.patch | 52 + .../mmc-sunxi-mmc-Remove-runtime-PM.patch | 143 + ...-spi-nor-Add-Alliance-memory-support.patch | 85 + ...td-spi-nor-Add-vdd-regulator-support.patch | 33 + ...t-for-enabling-a-regulator-for-PHY-I.patch | 89 + ...-regulator-variable-to-regulator_phy.patch | 81 + ...devm_regulator_get-for-PHY-regulator.patch | 84 + ...Add-support-for-VBAT-PVDD-regulators.patch | 106 + ...devlink-Support-allwinner-sram-links.patch | 58 + ...ror-when-no-regulator-is-defined-in-.patch | 31 + ...-on-poweroff-reboot-on-Orange-Pi-5-P.patch | 32 + ...-usb-Add-support-for-usb_role_switch.patch | 146 + ...Allow-to-set-target-voltage-to-4.35V.patch | 31 + ...upts-for-low-battery-power-condition.patch | 43 + ...r-USB_BC_ENABLED-and-USB_DCP_INPUT_C.patch | 45 + ...-Add-support-for-POWER_SUPPLY_PROP_E.patch | 90 + ...-battery-Enable-poweron-by-RTC-alarm.patch | 35 + ...attery-Improve-probe-error-reporting.patch | 69 + ...-Support-POWER_SUPPLY_PROP_CHARGE_BE.patch | 86 + ...20x-usb-power-Add-missing-interrupts.patch | 59 + ...wer-Change-Vbus-hold-voltage-to-4.5V.patch | 38 + ...attery-Add-support-for-reporting-OCV.patch | 63 + ..._battery-Fix-charging-done-detection.patch | 40 + ...xp20x_battery-Monitor-battery-health.patch | 117 + ...tery-Send-uevents-for-status-changes.patch | 149 + ...-Setup-thermal-regulation-experiment.patch | 82 + ...-for-enabling-a-regulator-from-users.patch | 192 + ...support-for-vin-supply-for-drivevbus.patch | 61 + ...r-temperature-protection-and-16s-res.patch | 41 + ...SEN-to-input-on-x-powers-sense-vbus-.patch | 53 + ...hwmon-device-for-reading-temperature.patch | 235 + ...tp65185x-eInk-panel-regulator-driver.patch | 487 ++ ...-which-error-caused-RTC-read-failure.patch | 31 + ...un6i-Allow-RTC-wakeup-after-shutdown.patch | 69 + ...rt-analog-part-of-X-Powers-AC100-cod.patch | 1074 +++++ ...upport-for-digital-part-of-the-AC100.patch | 371 ++ ..._probe-to-handle-EPROBE_DEFER-errors.patch | 150 + ...ermal-sun8i-Be-loud-when-probe-fails.patch | 88 + ...gling-pointer-in-netdev-private-data.patch | 307 ++ ...ng-host-side-code-on-SoCs-where-it-s.patch | 107 + ...set_resume-callback-for-WWAN-devices.patch | 33 + ...ayport-Respect-DP_CAP_RECEPTACLE-bit.patch | 99 + ...driver-for-ANX7688-USB-C-HDMI-bridge.patch | 2333 ++++++++++ ...usb-typec-anx7688-Port-to-Linux-6.10.patch | 28 + .../usb-typec-anx7688-Port-to-Linux-6.9.patch | 110 + ...-typec-fusb302-Add-OF-extcon-support.patch | 38 + ...-interrupts-before-we-start-toggling.patch | 40 + ...bugging-interface-with-driver-state-.patch | 112 + ...pec-fusb302-Fix-register-definitions.patch | 49 + ...seful-of-logging-status-on-interrupt.patch | 189 + ...ding-of-CC-pins-status-if-activity-i.patch | 76 + ...-the-current-before-enabling-pullups.patch | 50 + ...-increase-wait-time-for-BC1.2-result.patch | 33 + ...US-state-even-if-VBUS-interrupt-is-n.patch | 43 + ...PD-devices-capabilities-registration.patch | 95 + .../usb-typec-tcpm-Improve-logs.patch | 64 + ...altmodes-before-registering-new-ones.patch | 52 + ...xtcon-Add-typec-extcon-bridge-driver.patch | 384 ++ ...ow-to-force-reset-on-each-mux-change.patch | 48 + ...ypec-extcon-Enable-debugging-for-now.patch | 25 + ...lay-driver-for-A13-based-PocketBooks.patch | 1274 ++++++ ...w-to-change-lth_brightness-via-sysfs.patch | 126 + patch/kernel/archive/sunxi-6.14/series.conf | 249 + patch/kernel/archive/sunxi-6.14/series.megous | 248 + 245 files changed, 40484 insertions(+) create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/2-arm64-dts-sun50i-Define-orientation-and-rotation-for-PinePhone-.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-axp813-Add-charger-LED.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun5i-Add-PocketBook-Touch-Lux-3-display-ctp-support.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun5i-Add-soc-handle.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun5i-a13-pocketbook-touch-lux-3-Add-RTC-clock-cells.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-Add-MBUS-node.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-Add-cedrus-video-codec-support-to-A83T-untes.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-Add-hdmi-sound-card.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-Add-missing-GPU-trip-point.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-Enable-hdmi-sound-card-on-boards-with-hdmi.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-Improve-CPU-OPP-tables-go-up-to-1.8GHz.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-Set-fifo-size-for-uarts.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-PN544-NFC-support.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-camera-sensors-HM5065-GC2145.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-flash-led-support.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-powerup-down-support-for-the-3G.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-regulators-to-the-accelerometer.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-sound-support-via-AC100-codec.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-support-for-the-vibrator-motor.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Enable-charging-LED.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Give-Linux-more-privileges-over-SCP.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Increase-voltage-on-the-vibrator.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-h2-plus-bananapi-m2-zero-Enable-HDMI-audio.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-h3-Enable-hdmi-sound-card-on-boards-with-hdmi.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-h3-Use-my-own-more-aggressive-OPPs-on-H3.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-h3-orange-pi-one-Enable-all-gpio-header-UARTs.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-h3-orange-pi-pc-Increase-max-CPUX-voltage-to-1.4V.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-r40-Add-hdmi-sound-card.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-r40-bananapi-m2-ultra-Enable-HDMI-audio.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-v40-bananapi-m2-berry-Enable-HDMI-audio.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-suni-a83t-Add-i2s0-pins.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sunxi-Add-aliases-for-MMC.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sunxi-a83t-Add-SCPI-protocol.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sunxi-h3-h5-Add-SCPI-protocol.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sunxi-h3-h5-Add-hdmi-sound-card.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ARM-sunxi-Add-experimental-suspend-to-memory-implementation-for.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ARM-sunxi-Use-SCPI-to-send-suspend-message-to-SCP-on-A83T.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ARM-sunxi-sunxi_cpu0_hotplug_support_set-is-not-supported-on-A8.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ASOC-sun9i-hdmi-audio-Initial-implementation.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ASoC-ec25-New-codec-driver-for-the-EC25-modem.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ASoC-rockchip-Fix-doubling-of-playback-speed-after-system-sleep.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ASoC-simple-card-Allow-to-define-pins-for-aux-jack-devices.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ASoC-sun8i-codec-Add-debug-output-for-jack-detection.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ASoC-sun8i-codec-Allow-the-jack-type-to-be-set-via-device-tree.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ASoC-sun8i-codec-Set-jack_type-from-DT-in-probe.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/ASoC-sun8i-codec-define-button-keycodes.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/Add-README.md-with-information-and-u-boot-patches.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/Add-support-for-my-private-Sapomat-device.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/Defconfigs-for-all-my-devices.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/Fix-broken-allwinner-sram-dependency-on-h616-h618.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/Fix-intptr_t-typedef.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/MAINTAINERS-Add-entry-for-Himax-HM5065.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/Make-microbuttons-on-Orange-Pi-PC-and-PC-2-work-as-power-off-bu.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/Mark-some-slow-drivers-for-async-probe-with-PROBE_PREFER_ASYNCH.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/Move-a-node-to-avoid-merge-conflict.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/Revert-Input-cyttsp4-remove-driver.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/Revert-drm-sun4i-lvds-Invert-the-LVDS-polarity.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/Revert-usb-typec-tcpm-unregister-existing-source-caps-before-re.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-allwinner-dts-a64-enable-K101-IM2BYL02-panel-for-PineTab.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-allwinner-Enforce-consistent-MMC-numbering.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-allwinner-a64-Add-hdmi-sound-card.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-allwinner-a64-Enable-hdmi-sound-card-on-boards-with-h.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-allwinner-a64-Fix-LRADC-compatible.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-allwinner-a64-pinetab-add-front-camera.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-allwinner-h5-Enable-hdmi-sound-card-on-boards-with-hd.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-allwinner-h6-Add-hdmi-sound-card.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-allwinner-h6-Enable-hdmi-sound-card-on-boards-with-hd.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-allwinner-orange-pi-3-Enable-ethernet.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-rk3399-Add-dmc_opp_table.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-rockchip-rk3399-s-Add-DMC-table.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-rockchip-rk356x-Fix-PCIe-register-map-and-ranges.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50-a64-pinephone-Define-jack-pins-in-DT.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-Define-orientation-and-rotation-for-PinePhone-.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-Set-fifo-size-for-uarts.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-Type-C-support-for-all-PP-va.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-detailed-OCV-to-capactiy-con.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-front-back-cameras.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-interrupt-pin-for-WiFi.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-modem-power-manager.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-power-supply-to-stk3311.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-reboot-mode-driver.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-supply-for-i2c-bus-to-anx768.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-support-for-Bluetooth-audio.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-support-for-Pinephone-1.2-be.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-support-for-Pinephone-keyboa.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-support-for-modem-audio.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Bump-I2C-frequency-to-400kHz.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Don-t-make-lradc-keys-a-wakeup-s.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Enable-Pinephone-Keyboard-power-.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Enable-internal-HMIC-bias.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Fix-BH-modem-manager-behavior.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Power-off-the-touch-controller-i.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Set-minimum-backlight-duty-cycle.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Shorten-post-power-on-delay-on-m.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Use-newer-jack-detection-impleme.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Workaround-broken-HDMI-HPD-signa.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinetab-Add-accelerometer.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinetab-Name-sound-card-PineTab.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinetab-enable-RTL8723CS-bluetooth.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-h5-Add-missing-GPU-trip-point.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-h5-Use-my-own-more-aggressive-OPPs-on-H5.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/arm64-xor-Select-32regs-without-benchmark-to-speed-up-boot.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/bluetooth-bcm-Restore-drive_rts_on_open-true-behavior-on-bcm207.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/bluetooth-h5-Don-t-re-initialize-rtl8723cs-on-resume.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/clk-sunxi-ng-Don-t-use-CPU-PLL-gating-and-CPUX-reparenting-to-H.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/clk-sunxi-ng-Export-CLK_DRAM-for-devfreq.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/clk-sunxi-ng-Mark-TWD-clocks-as-critical.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/clk-sunxi-ng-Set-maximum-P-and-M-factors-to-1-for-H3-pll-cpux-c.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/clk-sunxi-ng-a64-Increase-PLL_AUDIO-base-frequency.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/clk-sunxi-ng-sun50i-a64-Switch-parent-of-MIPI-DSI-to-periph0-1x.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/cpufreq-sun50i-Show-detected-CPU-bin-for-easier-debugging.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/drm-bridge-dw-hdmi-Allow-to-accept-HPD-status-from-other-driver.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/drm-bridge-dw-hdmi-Report-HDMI-hotplug-events.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/drm-panel-st7703-Fix-xbd599-timings-to-make-refresh-rate-exactl.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/drm-rockchip-Fix-panic-on-reboot-when-DRM-device-fails-to-bind.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/drm-rockchip-dw-mipi-dsi-rockchip-Fix-ISP1-PHY-initialization.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/drm-sun4i-Implement-gamma-correction.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/drm-sun4i-Mark-one-of-the-UI-planes-as-a-cursor-one.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/drm-sun4i-Support-taking-over-display-pipeline-state-from-p-boo.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/drm-sun4i-tcon-Support-keeping-dclk-rate-upon-ancestor-clock-ch.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/dt-bindings-axp20x-adc-allow-to-use-TS-pin-as-GPADC.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/dt-bindings-input-gpio-vibrator-Don-t-require-enable-gpios.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/dt-bindings-leds-Add-a-binding-for-AXP813-charger-led.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/dt-bindings-media-Add-bindings-for-Himax-HM5065-camera-sensor.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/dt-bindings-mfd-Add-codec-related-properties-to-AC100-PMIC.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/dt-bindings-sound-Add-jack-type-property-to-sun8i-a33-codec.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/firmware-arm_scpi-Support-unidirectional-mailbox-channels.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/firmware-scpi-Add-support-for-sending-a-SCPI_CMD_SET_SYS_PWR_ST.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/gnss-ubx-Send-soft-powerdown-message-on-suspend.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/hm5065-yaml-bindings-wip.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/i2c-mv64xxx-Don-t-make-a-fuss-when-pinctrl-recovery-state-is-no.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/iio-adc-axp20x_adc-allow-to-set-TS-pin-to-GPADC-mode.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/iio-adc-sun4i-gpadc-iio-Allow-to-use-sun5i-a13-gpadc-iio-from-D.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/iio-st_sensors-Don-t-report-error-when-the-device-is-not-presen.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Clear-the-ids-buffer-in-a-saner-way.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-De-obfuscate-MT-signals-setup-platform-data.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-De-obfuscate-platform-data-for-keys.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-ENOSYS-error-is-ok-when-powering-up.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Faster-recovery-from-failed-wakeup-HACK.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Fix-compile-issue.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Fix-probe-oops.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Fix-warnings.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Make-the-driver-not-hog-the-system-s-workqueue.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Port-the-driver-to-use-device-properties.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Remove-unused-enable_vkeys.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Remove-useless-indirection-with-driver-platform-d.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Restart-on-wakeup-wakeup-by-I2C-read-doesn-t-work.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Use-i2c-spi-names-directly-in-the-driver.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/input-gpio-vibra-Allow-to-use-vcc-supply-alone-to-control-the-v.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/leds-axp20x-Support-charger-LED-on-AXP20x-like-PMICs.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/mailbox-Allow-to-run-mailbox-while-timekeeping-is-suspended.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/media-cedrus-Fix-failure-to-clean-up-hardware-on-probe-failure.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/media-gc2145-Add-PIXEL_RATE-HBLANK-and-VBLANK-controls.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/media-gc2145-Added-BGGR-bayer-mode.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/media-gc2145-Disable-debug-output.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/media-gc2145-Galaxycore-camera-module-driver.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/media-gc2145-fix-white-balance-colors.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/media-gc2145-implement-system-suspend.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/media-hm5065-Add-subdev-driver-for-Himax-HM5065-camera-sensor.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/media-i2c-gc2145-Move-upstream-driver-out-of-the-way.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/media-i2c-gc2145-Parse-and-register-properties.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-Add-read-only-property-for-vblank.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-Don-t-powerup-the-sensor-during-driver-probe.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-Experiment-Try-to-disable-denoising-sharpening.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-Fix-focus-commands-blocking-until-complete.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-Implement-autofocus.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-Improve-error-reporting.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-Improve-firmware-load-time.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-Sleep-after-poweroff-to-ensure-next-poweron-is-not.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-set-default-ae-target-lower.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-use-pm_runtime_force_suspend-resume-for-system-sus.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5648-Fix-call-to-pm_runtime_set_suspended.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/media-sun6i-csi-Add-multicamera-support-for-parallel-bus.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/media-sun6i-csi-add-V4L2_CAP_IO_MC-capability.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/media-sun6i-csi-capture-Use-subdev-operation-to-access-bridge-f.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/media-sun6i-csi-implement-vidioc_enum_framesizes.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/media-sun6i-csi-merge-sun6i_csi_formats-and-sun6i_csi_formats_m.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/media-sun6i-csi-subdev-Use-subdev-active-state-to-store-active-.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/mfd-axp20x-Add-battery-IRQ-resources.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/misc-modem-power-Power-manager-for-modems.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/mmc-add-delay-after-power-class-selection.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/mmc-dw-mmc-rockchip-fix-sdmmc-after-soft-reboot.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/mmc-sunxi-mmc-Remove-runtime-PM.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/mtd-spi-nor-Add-Alliance-memory-support.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/mtd-spi-nor-Add-vdd-regulator-support.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/net-stmmac-sun8i-Add-support-for-enabling-a-regulator-for-PHY-I.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/net-stmmac-sun8i-Rename-PHY-regulator-variable-to-regulator_phy.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/net-stmmac-sun8i-Use-devm_regulator_get-for-PHY-regulator.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/nfc-pn544-Add-support-for-VBAT-PVDD-regulators.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/of-property-fw_devlink-Support-allwinner-sram-links.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/opp-core-Avoid-confusing-error-when-no-regulator-is-defined-in-.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/pci-Workaround-ITS-timeouts-on-poweroff-reboot-on-Orange-Pi-5-P.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/phy-allwinner-sun4i-usb-Add-support-for-usb_role_switch.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/power-axp20x_battery-Allow-to-set-target-voltage-to-4.35V.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/power-axp803-Add-interrupts-for-low-battery-power-condition.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-Add-support-for-USB_BC_ENABLED-and-USB_DCP_INPUT_C.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x-battery-Add-support-for-POWER_SUPPLY_PROP_E.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x-battery-Enable-poweron-by-RTC-alarm.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x-battery-Improve-probe-error-reporting.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x-battery-Support-POWER_SUPPLY_PROP_CHARGE_BE.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x-usb-power-Add-missing-interrupts.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x-usb-power-Change-Vbus-hold-voltage-to-4.5V.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x_battery-Add-support-for-reporting-OCV.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x_battery-Fix-charging-done-detection.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x_battery-Monitor-battery-health.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x_battery-Send-uevents-for-status-changes.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x_battery-Setup-thermal-regulation-experiment.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/regulator-Add-simple-driver-for-enabling-a-regulator-from-users.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/regulator-axp20x-Add-support-for-vin-supply-for-drivevbus.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/regulator-axp20x-Enable-over-temperature-protection-and-16s-res.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/regulator-axp20x-Turn-N_VBUSEN-to-input-on-x-powers-sense-vbus-.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/regulator-tp65185-Add-hwmon-device-for-reading-temperature.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/regulator-tp65185x-Add-tp65185x-eInk-panel-regulator-driver.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/rtc-Print-which-error-caused-RTC-read-failure.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/rtc-sun6i-Allow-RTC-wakeup-after-shutdown.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/sound-soc-ac100-codec-Support-analog-part-of-X-Powers-AC100-cod.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/sound-soc-sun8i-codec-Add-support-for-digital-part-of-the-AC100.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/sunxi-Use-dev_err_probe-to-handle-EPROBE_DEFER-errors.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/thermal-sun8i-Be-loud-when-probe-fails.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/usb-gadget-Fix-dangling-pointer-in-netdev-private-data.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/usb-musb-sunxi-Avoid-enabling-host-side-code-on-SoCs-where-it-s.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/usb-serial-option-add-reset_resume-callback-for-WWAN-devices.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-altmodes-displayport-Respect-DP_CAP_RECEPTACLE-bit.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-anx7688-Add-driver-for-ANX7688-USB-C-HDMI-bridge.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-anx7688-Port-to-Linux-6.10.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-anx7688-Port-to-Linux-6.9.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-fusb302-Add-OF-extcon-support.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-fusb302-Clear-interrupts-before-we-start-toggling.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-fusb302-Extend-debugging-interface-with-driver-state-.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-fusb302-Fix-register-definitions.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-fusb302-More-useful-of-logging-status-on-interrupt.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-fusb302-Retry-reading-of-CC-pins-status-if-activity-i.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-fusb302-Set-the-current-before-enabling-pullups.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-fusb302-Slightly-increase-wait-time-for-BC1.2-result.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-fusb302-Update-VBUS-state-even-if-VBUS-interrupt-is-n.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-tcpm-Fix-PD-devices-capabilities-registration.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-tcpm-Improve-logs.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-tcpm-Unregister-altmodes-before-registering-new-ones.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-typec-extcon-Add-typec-extcon-bridge-driver.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-typec-extcon-Allow-to-force-reset-on-each-mux-change.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-typec-extcon-Enable-debugging-for-now.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/video-fbdev-eInk-display-driver-for-A13-based-PocketBooks.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.megous/video-pwm_bl-Allow-to-change-lth_brightness-via-sysfs.patch create mode 100644 patch/kernel/archive/sunxi-6.14/series.conf create mode 100644 patch/kernel/archive/sunxi-6.14/series.megous diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/2-arm64-dts-sun50i-Define-orientation-and-rotation-for-PinePhone-.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/2-arm64-dts-sun50i-Define-orientation-and-rotation-for-PinePhone-.patch new file mode 100644 index 000000000000..d66e2953e969 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/2-arm64-dts-sun50i-Define-orientation-and-rotation-for-PinePhone-.patch @@ -0,0 +1,31 @@ +From 63ede72498fdb55584228f62a27c7564a9bd3289 Mon Sep 17 00:00:00 2001 +From: Arnav Singh +Date: Mon, 6 May 2024 16:36:06 -0700 +Subject: arm64: dts: sun50i: Define orientation and rotation for PinePhone + front camera + +This enables libcamera to detect the correct orientation from +the device tree and propagate it further to e.g. Pipewire. + +Signed-off-by: Arnav Singh +--- + arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi +index 1a57720c25ef..f0f1367c5868 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi +@@ -593,6 +593,9 @@ gc2145: front-camera@3c { + reset-gpios = <&pio 4 16 (GPIO_ACTIVE_LOW | GPIO_OPEN_DRAIN)>; /* PE16 */ + enable-gpios = <&pio 4 17 (GPIO_ACTIVE_LOW | GPIO_OPEN_DRAIN)>; /* PE17 */ + ++ orientation = <0>; ++ rotation = <90>; ++ + port { + gc2145_ep: endpoint { + remote-endpoint = <&csi_gc2145_ep>; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-axp813-Add-charger-LED.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-axp813-Add-charger-LED.patch new file mode 100644 index 000000000000..c86157bdd809 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-axp813-Add-charger-LED.patch @@ -0,0 +1,30 @@ +From 6f7050f399283cdef2e0a1598a41279fb1d2ab01 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Tue, 14 Nov 2017 02:47:51 +0100 +Subject: ARM: dts: axp813: Add charger LED + +PMIC supports charging status indication via a LED. Add support +for it. + +Signed-off-by: Ondrej Jirman +--- + arch/arm/boot/dts/allwinner/axp81x.dtsi | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/arch/arm/boot/dts/allwinner/axp81x.dtsi b/arch/arm/boot/dts/allwinner/axp81x.dtsi +index ebaf1c3ce8db..a4b1be159b02 100644 +--- a/arch/arm/boot/dts/allwinner/axp81x.dtsi ++++ b/arch/arm/boot/dts/allwinner/axp81x.dtsi +@@ -161,4 +161,9 @@ reg_drivevbus: drivevbus { + usb_power_supply: usb-power { + compatible = "x-powers,axp813-usb-power-supply"; + }; ++ ++ charger_led: charger-led { ++ compatible = "x-powers,axp813-charger-led"; ++ status = "disabled"; ++ }; + }; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun5i-Add-PocketBook-Touch-Lux-3-display-ctp-support.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun5i-Add-PocketBook-Touch-Lux-3-display-ctp-support.patch new file mode 100644 index 000000000000..773819230a37 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun5i-Add-PocketBook-Touch-Lux-3-display-ctp-support.patch @@ -0,0 +1,185 @@ +From 1fdeabb9597a51122d1c07678c6dd94c15e40abf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sun, 23 Feb 2020 01:23:10 +0100 +Subject: ARM: dts: sun5i: Add PocketBook Touch Lux 3 display/ctp support + +Add support for display and touchscreen via out-of-tree drivers. + +Signed-off-by: Ondrej Jirman +--- + .../sun5i-a13-pocketbook-touch-lux-3.dts | 105 +++++++++++++++++- + 1 file changed, 103 insertions(+), 2 deletions(-) + +diff --git a/arch/arm/boot/dts/allwinner/sun5i-a13-pocketbook-touch-lux-3.dts b/arch/arm/boot/dts/allwinner/sun5i-a13-pocketbook-touch-lux-3.dts +index d60407772e5d..db28ab621804 100644 +--- a/arch/arm/boot/dts/allwinner/sun5i-a13-pocketbook-touch-lux-3.dts ++++ b/arch/arm/boot/dts/allwinner/sun5i-a13-pocketbook-touch-lux-3.dts +@@ -62,6 +62,11 @@ key-left { + }; + }; + ++ regulator-eink { ++ compatible = "custom,reg-userspace-consumer"; ++ controlled-supply = <&tp65185x>; ++ }; ++ + reg_1v8: regulator-1v8 { + compatible = "regulator-fixed"; + regulator-name = "vdd-1v8-nor-ctp"; +@@ -79,7 +84,6 @@ reg_1v8_nor: regulator-nor { + gpio = <&pio 2 14 GPIO_ACTIVE_HIGH>; + enable-active-high; + vin-supply = <®_1v8>; +- regulator-always-on; + }; + + reg_1v8_ctp: regulator-ctp { +@@ -112,6 +116,7 @@ &ehci0 { + + &i2c0 { + status = "okay"; ++ //XXX: bsp has pullup on PB0/PB1 pins enabled, but it works without it + + axp209: pmic@34 { + reg = <0x34>; +@@ -128,12 +133,42 @@ pcf8563: rtc@51 { + compatible = "nxp,pcf8563"; + reg = <0x51>; + }; ++ ++ // hacky PMIC driver for eInk display ++ tp65185x: regulator@68 { ++ compatible = "tp,tp65185x"; ++ reg = <0x68>; ++ ++ regulator-min-microvolt = <3120000>; ++ regulator-max-microvolt = <3120000>; ++ regulator-name = "eink-vcom"; ++ ++ //XXX: we don't use interrupts in the driver right now ++ //interrupt-parent = <&pio>; ++ //interrupts = <1 10 IRQ_TYPE_LEVEL_LOW>; /* PB10 */ ++ ++ wakeup-gpios = <&pio 3 18 GPIO_ACTIVE_HIGH>; /* PD18 */ ++ powerup-gpios = <&pio 4 7 GPIO_ACTIVE_HIGH>; /* PE7 */ ++ vcom-gpios = <&pio 3 19 GPIO_ACTIVE_HIGH>; /* PD19 */ ++ //XXX: not a pullup on BSP (might have an external resistor) ++ powergood-gpios = <&pio 4 6 (GPIO_ACTIVE_HIGH | GPIO_PULL_UP)>; /* PE6 */ ++ }; + }; + + &i2c2 { + status = "okay"; + +- /* Touchpanel is connected here. */ ++ touchpanel@24 { ++ compatible = "cypress,cyttsp4_i2c_adapter"; ++ reg = <0x24>; ++ ++ interrupt-parent = <&pio>; ++ interrupts = <6 11 IRQ_TYPE_EDGE_FALLING>; /* PG11 */ ++ ++ vdd-supply = <®_1v8_ctp>; ++ power-gpios = <&pio 4 5 GPIO_ACTIVE_LOW>; /* PE5 */ ++ reset-gpios = <&pio 1 3 GPIO_ACTIVE_LOW>; /* PB3 */ ++ }; + }; + + &lradc { +@@ -179,6 +214,20 @@ &otg_sram { + status = "okay"; + }; + ++&pio { ++ eink_pins_active: eink-pins-lcd { ++ pins = "PD3", "PD4", "PD5", "PD6", "PD7", "PD10", "PD11", "PD12", ++ "PD13", "PD15", "PD20", "PD21", "PD22", "PD23", "PD24"; ++ function = "lcd0"; ++ }; ++ ++ eink_pins_idle: eink-pins-gpio { ++ pins = "PD3", "PD4", "PD5", "PD6", "PD7", "PD10", "PD11", "PD12", ++ "PD13", "PD15", "PD20", "PD21", "PD22", "PD23", "PD24"; ++ function = "gpio_out"; ++ }; ++}; ++ + &pwm { + pinctrl-names = "default"; + pinctrl-0 = <&pwm0_pin>; +@@ -219,6 +268,55 @@ ®_ldo3 { + regulator-ramp-delay = <1600>; + }; + ++&soc { ++ display-controller@1c0c000 { ++ pinctrl-names = "idle", "active"; ++ pinctrl-0 = <&eink_pins_idle>; ++ pinctrl-1 = <&eink_pins_active>; ++ ++ all-gpios = <&pio 3 3 GPIO_ACTIVE_HIGH>, ++ <&pio 3 4 GPIO_ACTIVE_HIGH>, ++ <&pio 3 5 GPIO_ACTIVE_HIGH>, ++ <&pio 3 6 GPIO_ACTIVE_HIGH>, ++ <&pio 3 7 GPIO_ACTIVE_HIGH>, ++ <&pio 3 10 GPIO_ACTIVE_HIGH>, ++ <&pio 3 11 GPIO_ACTIVE_HIGH>, ++ <&pio 3 12 GPIO_ACTIVE_HIGH>, ++ <&pio 3 13 GPIO_ACTIVE_HIGH>, ++ <&pio 3 15 GPIO_ACTIVE_HIGH>, ++ <&pio 3 20 GPIO_ACTIVE_HIGH>, ++ <&pio 3 21 GPIO_ACTIVE_HIGH>, ++ <&pio 3 22 GPIO_ACTIVE_HIGH>, ++ <&pio 3 23 GPIO_ACTIVE_HIGH>, ++ <&pio 3 24 GPIO_ACTIVE_HIGH>; ++ ++ compatible = "custom,pocketbook-touch-lux-3-tcon0-ed060xd4-display"; ++ reg = <0x01c0c000 0x1000>, // TCON0 regs ++ <0x01e60000 0x10000>; // DEBE regs ++ ++ interrupts = <44>; // TCON interrupt ++ ++ resets = <&ccu RST_LCD>, <&ccu RST_DE_BE>; ++ reset-names = "tcon", "be"; ++ ++ clocks = <&ccu CLK_AHB_LCD>, <&ccu CLK_TCON_CH0>, ++ <&ccu CLK_AHB_DE_BE>, <&ccu CLK_DE_BE>, ++ <&ccu CLK_DRAM_DE_BE>; ++ clock-names = "tcon_bus", "tcon_mod", ++ "be_bus", "be_mod", ++ "be_ram"; ++ ++ assigned-clocks = <&ccu CLK_DE_BE>; ++ assigned-clock-rates = <300000000>; ++ ++ interconnects = <&mbus 18>; ++ interconnect-names = "dma-mem"; ++ ++ panel-supply = <&tp65185x>; ++ control-device-name = "eink-panel"; ++ }; ++}; ++ + &spi2 { + pinctrl-names = "default"; + pinctrl-0 = <&spi2_pe_pins>, <&spi2_cs0_pe_pin>; +@@ -230,6 +328,7 @@ epd_flash: flash@0 { + compatible = "macronix,mx25u4033", "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <4000000>; ++ vdd-supply = <®_1v8_nor>; + }; + }; + +@@ -253,6 +352,8 @@ &usb_power_supply { + }; + + &usbphy { ++ //XXX: might not work, need to test ++ usb0_vbus_det-gpios = <&pio 6 1 GPIO_ACTIVE_HIGH>; /* PG1 */ + usb1_vbus-supply = <®_ldo3>; + status = "okay"; + }; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun5i-Add-soc-handle.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun5i-Add-soc-handle.patch new file mode 100644 index 000000000000..6476558677fb --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun5i-Add-soc-handle.patch @@ -0,0 +1,26 @@ +From 87c27d42818037b43a49d67edc1d2de68036d1af Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sat, 5 Oct 2019 15:04:43 +0200 +Subject: ARM: dts: sun5i: Add soc handle + +Signed-off-by: Ondrej Jirman +--- + arch/arm/boot/dts/allwinner/sun5i.dtsi | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/arm/boot/dts/allwinner/sun5i.dtsi b/arch/arm/boot/dts/allwinner/sun5i.dtsi +index e6836fd4b8dc..126b80f3ca70 100644 +--- a/arch/arm/boot/dts/allwinner/sun5i.dtsi ++++ b/arch/arm/boot/dts/allwinner/sun5i.dtsi +@@ -123,7 +123,7 @@ default-pool { + }; + }; + +- soc { ++ soc: soc { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun5i-a13-pocketbook-touch-lux-3-Add-RTC-clock-cells.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun5i-a13-pocketbook-touch-lux-3-Add-RTC-clock-cells.patch new file mode 100644 index 000000000000..8dec8d9ff3ee --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun5i-a13-pocketbook-touch-lux-3-Add-RTC-clock-cells.patch @@ -0,0 +1,27 @@ +From 7cd038980cc2a9a28a5d908f8c66a4a2c92ad36b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Thu, 25 Jun 2020 01:57:27 +0200 +Subject: ARM: dts: sun5i-a13-pocketbook-touch-lux-3: Add RTC clock-cells + +This stop the driver from complaining in dmesg. + +Signed-off-by: Ondrej Jirman +--- + arch/arm/boot/dts/allwinner/sun5i-a13-pocketbook-touch-lux-3.dts | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/arm/boot/dts/allwinner/sun5i-a13-pocketbook-touch-lux-3.dts b/arch/arm/boot/dts/allwinner/sun5i-a13-pocketbook-touch-lux-3.dts +index db28ab621804..ee81db68e546 100644 +--- a/arch/arm/boot/dts/allwinner/sun5i-a13-pocketbook-touch-lux-3.dts ++++ b/arch/arm/boot/dts/allwinner/sun5i-a13-pocketbook-touch-lux-3.dts +@@ -132,6 +132,7 @@ &i2c1 { + pcf8563: rtc@51 { + compatible = "nxp,pcf8563"; + reg = <0x51>; ++ #clock-cells = <0>; + }; + + // hacky PMIC driver for eInk display +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-Add-MBUS-node.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-Add-MBUS-node.patch new file mode 100644 index 000000000000..c1428e08bb55 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-Add-MBUS-node.patch @@ -0,0 +1,39 @@ +From 671a2d6f20ba155cfcadf114e6d740936ee7088a Mon Sep 17 00:00:00 2001 +From: Samuel Holland +Date: Sat, 3 Apr 2021 20:53:26 -0500 +Subject: ARM: dts: sun8i: a83t: Add MBUS node + +Signed-off-by: Samuel Holland +--- + arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi b/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi +index ef62ced26684..04b33b42b414 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi ++++ b/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi +@@ -1056,6 +1056,21 @@ mdio: mdio { + }; + }; + ++ mbus: dram-controller@1c62000 { ++ compatible = "allwinner,sun8i-a83t-mbus"; ++ reg = <0x01c62000 0x1000>, ++ <0x01c63000 0x1000>; ++ reg-names = "mbus", "dram"; ++ clocks = <&ccu CLK_BUS_DRAM>, ++ <&ccu CLK_DRAM>, ++ <&ccu CLK_MBUS>; ++ clock-names = "apb", "dram", "mbus"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ dma-ranges = <0x00000000 0x40000000 0xc0000000>; ++ #interconnect-cells = <1>; ++ }; ++ + gic: interrupt-controller@1c81000 { + compatible = "arm,gic-400"; + reg = <0x01c81000 0x1000>, +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-Add-cedrus-video-codec-support-to-A83T-untes.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-Add-cedrus-video-codec-support-to-A83T-untes.patch new file mode 100644 index 000000000000..6c111c2f122a --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-Add-cedrus-video-codec-support-to-A83T-untes.patch @@ -0,0 +1,37 @@ +From 01a3291530c88353c36fb80d55ce984363614c28 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Mon, 9 Sep 2019 06:02:10 +0200 +Subject: ARM: dts: sun8i-a83t: Add cedrus video codec support to A83T + [untested] + +Not tested at all. + +Signed-off-by: Ondrej Jirman +--- + arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi b/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi +index addf0cb0f465..ed3d8e692d84 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi ++++ b/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi +@@ -441,6 +441,16 @@ syscon: syscon@1c00000 { + reg = <0x01c00000 0x1000>; + }; + ++ video-codec@01c0e000 { ++ compatible = "allwinner,sun8i-a83t-video-engine"; ++ reg = <0x01c0e000 0x1000>; ++ clocks = <&ccu CLK_BUS_VE>, <&ccu CLK_VE>, ++ <&ccu CLK_DRAM_VE>; ++ clock-names = "ahb", "mod", "ram"; ++ resets = <&ccu RST_BUS_VE>; ++ interrupts = ; ++ }; ++ + dma: dma-controller@1c02000 { + compatible = "allwinner,sun8i-a83t-dma"; + reg = <0x01c02000 0x1000>; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-Add-hdmi-sound-card.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-Add-hdmi-sound-card.patch new file mode 100644 index 000000000000..be6abc769398 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-Add-hdmi-sound-card.patch @@ -0,0 +1,48 @@ +From bcee56858d98fe3523d52de0f6f1dd2750ee7c77 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sun, 9 May 2021 11:32:57 +0200 +Subject: ARM: dts: sun8i: a83t: Add hdmi sound card + +A83t support HDMI audio. Add a sound card node for it. + +Signed-off-by: Jernej Skrabec +--- + arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi b/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi +index 026c9b1b2217..cc86d3755036 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi ++++ b/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi +@@ -150,6 +150,20 @@ cpu103: cpu@103 { + }; + }; + ++ sound_hdmi: sound_hdmi { ++ compatible = "allwinner,sun9i-a80-hdmi-audio", ++ "allwinner,sun8i-a83t-hdmi-audio"; ++ status = "disabled"; ++ ++ codec { ++ sound-dai = <&hdmi>; ++ }; ++ ++ cpu { ++ sound-dai = <&i2s2>; ++ }; ++ }; ++ + timer { + compatible = "arm,armv7-timer"; + interrupts = , +@@ -1059,6 +1073,7 @@ csi: camera@1cb0000 { + }; + + hdmi: hdmi@1ee0000 { ++ #sound-dai-cells = <0>; + compatible = "allwinner,sun8i-a83t-dw-hdmi"; + reg = <0x01ee0000 0x10000>; + reg-io-width = <1>; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-Add-missing-GPU-trip-point.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-Add-missing-GPU-trip-point.patch new file mode 100644 index 000000000000..16e978379055 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-Add-missing-GPU-trip-point.patch @@ -0,0 +1,35 @@ +From 79b5366ae1cedac8596a6188275c25db406c6f1f Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Mon, 13 Mar 2023 06:02:27 +0100 +Subject: ARM: dts: sun8i-a83t: Add missing GPU trip point + +Without this, thermal sensor driver fails to probe. + +Signed-off-by: Ondrej Jirman +--- + arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi b/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi +index 27f8e42467af..2ab73558aaca 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi ++++ b/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi +@@ -1307,6 +1307,15 @@ gpu_thermal: gpu-thermal { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&ths 2>; ++ ++ trips { ++ gpu_crit: gpu-crit { ++ /* milliCelsius */ ++ temperature = <100000>; ++ hysteresis = <2000>; ++ type = "critical"; ++ }; ++ }; + }; + }; + }; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-Enable-hdmi-sound-card-on-boards-with-hdmi.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-Enable-hdmi-sound-card-on-boards-with-hdmi.patch new file mode 100644 index 000000000000..8ae870f641bc --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-Enable-hdmi-sound-card-on-boards-with-hdmi.patch @@ -0,0 +1,69 @@ +From e82fb342a69f47923f2e504b7e7e2fff952dac98 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sun, 9 May 2021 11:34:43 +0200 +Subject: ARM: dts: sun8i: a83t: Enable hdmi sound card on boards with hdmi + +Each board that has HDMI connector can also transmit audio through it. +Enable HDMI sound card on all A83t boards with HDMI connector. + +Signed-off-by: Jernej Skrabec +--- + arch/arm/boot/dts/allwinner/sun8i-a83t-bananapi-m3.dts | 8 ++++++++ + .../arm/boot/dts/allwinner/sun8i-a83t-cubietruck-plus.dts | 8 ++++++++ + 2 files changed, 16 insertions(+) + +diff --git a/arch/arm/boot/dts/allwinner/sun8i-a83t-bananapi-m3.dts b/arch/arm/boot/dts/allwinner/sun8i-a83t-bananapi-m3.dts +index 32e811fa23e2..9a7117bca70d 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-a83t-bananapi-m3.dts ++++ b/arch/arm/boot/dts/allwinner/sun8i-a83t-bananapi-m3.dts +@@ -162,6 +162,10 @@ hdmi_out_con: endpoint { + }; + }; + ++&i2s2 { ++ status = "okay"; ++}; ++ + &mdio { + rgmii_phy: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; +@@ -380,6 +384,10 @@ ®_sw { + regulator-name = "vcc-ephy"; + }; + ++&sound_hdmi { ++ status = "okay"; ++}; ++ + &uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_pb_pins>; +diff --git a/arch/arm/boot/dts/allwinner/sun8i-a83t-cubietruck-plus.dts b/arch/arm/boot/dts/allwinner/sun8i-a83t-cubietruck-plus.dts +index d5e6ddaffbce..d4d834084b12 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-a83t-cubietruck-plus.dts ++++ b/arch/arm/boot/dts/allwinner/sun8i-a83t-cubietruck-plus.dts +@@ -197,6 +197,10 @@ hdmi_out_con: endpoint { + }; + }; + ++&i2s2 { ++ status = "okay"; ++}; ++ + &mdio { + rgmii_phy: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; +@@ -417,6 +421,10 @@ ®_sw { + regulator-name = "vcc-wifi-io"; + }; + ++&sound_hdmi { ++ status = "okay"; ++}; ++ + &spdif { + status = "okay"; + }; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-Improve-CPU-OPP-tables-go-up-to-1.8GHz.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-Improve-CPU-OPP-tables-go-up-to-1.8GHz.patch new file mode 100644 index 000000000000..aa6c75dabd61 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-Improve-CPU-OPP-tables-go-up-to-1.8GHz.patch @@ -0,0 +1,134 @@ +From 5968a3853998a21a9c2dd8aa2e4efc3a16e7ccfb Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sun, 1 Sep 2019 23:56:49 +0200 +Subject: ARM: dts: sun8i-a83t: Improve CPU OPP tables (go up to 1.8GHz) + +This table is for the worst SoC bin, so it should work on all A83T +bins. + +Signed-off-by: Ondrej Jirman +--- + arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi | 80 ++++++--------------- + 1 file changed, 22 insertions(+), 58 deletions(-) + +diff --git a/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi b/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi +index 04b33b42b414..026c9b1b2217 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi ++++ b/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi +@@ -210,45 +210,27 @@ opp-480000000 { + clock-latency-ns = <244144>; /* 8 32k periods */ + }; + +- opp-600000000 { +- opp-hz = /bits/ 64 <600000000>; +- opp-microvolt = <840000>; +- clock-latency-ns = <244144>; /* 8 32k periods */ +- }; +- +- opp-720000000 { +- opp-hz = /bits/ 64 <720000000>; +- opp-microvolt = <840000>; +- clock-latency-ns = <244144>; /* 8 32k periods */ +- }; +- +- opp-864000000 { +- opp-hz = /bits/ 64 <864000000>; +- opp-microvolt = <840000>; +- clock-latency-ns = <244144>; /* 8 32k periods */ +- }; +- +- opp-912000000 { +- opp-hz = /bits/ 64 <912000000>; ++ opp-1008000000 { ++ opp-hz = /bits/ 64 <1008000000>; + opp-microvolt = <840000>; + clock-latency-ns = <244144>; /* 8 32k periods */ + }; + +- opp-1008000000 { +- opp-hz = /bits/ 64 <1008000000>; +- opp-microvolt = <840000>; ++ opp-1412000000 { ++ opp-hz = /bits/ 64 <1412000000>; ++ opp-microvolt = <920000>; + clock-latency-ns = <244144>; /* 8 32k periods */ + }; + +- opp-1128000000 { +- opp-hz = /bits/ 64 <1128000000>; +- opp-microvolt = <840000>; ++ opp-1608000000 { ++ opp-hz = /bits/ 64 <1608000000>; ++ opp-microvolt = <1000000>; + clock-latency-ns = <244144>; /* 8 32k periods */ + }; + +- opp-1200000000 { +- opp-hz = /bits/ 64 <1200000000>; +- opp-microvolt = <840000>; ++ opp-1800000000 { ++ opp-hz = /bits/ 64 <1800000000>; ++ opp-microvolt = <1080000>; + clock-latency-ns = <244144>; /* 8 32k periods */ + }; + }; +@@ -263,45 +245,27 @@ opp-480000000 { + clock-latency-ns = <244144>; /* 8 32k periods */ + }; + +- opp-600000000 { +- opp-hz = /bits/ 64 <600000000>; +- opp-microvolt = <840000>; +- clock-latency-ns = <244144>; /* 8 32k periods */ +- }; +- +- opp-720000000 { +- opp-hz = /bits/ 64 <720000000>; +- opp-microvolt = <840000>; +- clock-latency-ns = <244144>; /* 8 32k periods */ +- }; +- +- opp-864000000 { +- opp-hz = /bits/ 64 <864000000>; +- opp-microvolt = <840000>; +- clock-latency-ns = <244144>; /* 8 32k periods */ +- }; +- +- opp-912000000 { +- opp-hz = /bits/ 64 <912000000>; ++ opp-1008000000 { ++ opp-hz = /bits/ 64 <1008000000>; + opp-microvolt = <840000>; + clock-latency-ns = <244144>; /* 8 32k periods */ + }; + +- opp-1008000000 { +- opp-hz = /bits/ 64 <1008000000>; +- opp-microvolt = <840000>; ++ opp-1412000000 { ++ opp-hz = /bits/ 64 <1412000000>; ++ opp-microvolt = <920000>; + clock-latency-ns = <244144>; /* 8 32k periods */ + }; + +- opp-1128000000 { +- opp-hz = /bits/ 64 <1128000000>; +- opp-microvolt = <840000>; ++ opp-1608000000 { ++ opp-hz = /bits/ 64 <1608000000>; ++ opp-microvolt = <1000000>; + clock-latency-ns = <244144>; /* 8 32k periods */ + }; + +- opp-1200000000 { +- opp-hz = /bits/ 64 <1200000000>; +- opp-microvolt = <840000>; ++ opp-1800000000 { ++ opp-hz = /bits/ 64 <1800000000>; ++ opp-microvolt = <1080000>; + clock-latency-ns = <244144>; /* 8 32k periods */ + }; + }; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-Set-fifo-size-for-uarts.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-Set-fifo-size-for-uarts.patch new file mode 100644 index 000000000000..425121a33481 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-Set-fifo-size-for-uarts.patch @@ -0,0 +1,59 @@ +From 1343ffd6e889e9f9ddfe35f87c6b9bd5006a9a49 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sat, 1 Feb 2020 23:41:47 +0100 +Subject: ARM: dts: sun8i-a83t: Set fifo-size for uarts + +Boot time optimization. + +Signed-off-by: Ondrej Jirman +--- + arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi b/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi +index cc86d3755036..0bbf43850c95 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi ++++ b/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi +@@ -926,6 +926,7 @@ uart0: serial@1c28000 { + interrupts = ; + reg-shift = <2>; + reg-io-width = <4>; ++ fifo-size = <64>; + clocks = <&ccu CLK_BUS_UART0>; + resets = <&ccu RST_BUS_UART0>; + status = "disabled"; +@@ -937,6 +938,7 @@ uart1: serial@1c28400 { + interrupts = ; + reg-shift = <2>; + reg-io-width = <4>; ++ fifo-size = <64>; + clocks = <&ccu CLK_BUS_UART1>; + resets = <&ccu RST_BUS_UART1>; + status = "disabled"; +@@ -948,6 +950,7 @@ uart2: serial@1c28800 { + interrupts = ; + reg-shift = <2>; + reg-io-width = <4>; ++ fifo-size = <64>; + clocks = <&ccu CLK_BUS_UART2>; + resets = <&ccu RST_BUS_UART2>; + status = "disabled"; +@@ -959,6 +962,7 @@ uart3: serial@1c28c00 { + interrupts = ; + reg-shift = <2>; + reg-io-width = <4>; ++ fifo-size = <64>; + clocks = <&ccu CLK_BUS_UART3>; + resets = <&ccu RST_BUS_UART3>; + status = "disabled"; +@@ -970,6 +974,7 @@ uart4: serial@1c29000 { + interrupts = ; + reg-shift = <2>; + reg-io-width = <4>; ++ fifo-size = <64>; + clocks = <&ccu CLK_BUS_UART4>; + resets = <&ccu RST_BUS_UART4>; + status = "disabled"; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-PN544-NFC-support.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-PN544-NFC-support.patch new file mode 100644 index 000000000000..81ad7ed87b17 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-PN544-NFC-support.patch @@ -0,0 +1,66 @@ +From af628e240ad757d9fc0258f2d64165ade363cd86 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Fri, 10 Nov 2017 14:33:28 +0100 +Subject: ARM: dts: sun8i-a83t-tbs-a711: Add PN544 NFC support + +Regulators on the schematic are named incorrectly. Both cameras are +is in fact connected to the dvdd-csi-f regulator and dvdd-csi-r is +in reality used for digital pad supply for NFC chip. + +At the same time vcc-mipi regulator is not used for MIPI, but for NFC +VBAT power. + +Interpreting schematics is an art form! :D + +Signed-off-by: Ondrej Jirman +--- + .../boot/dts/allwinner/sun8i-a83t-tbs-a711.dts | 18 +++++++++++++++--- + 1 file changed, 15 insertions(+), 3 deletions(-) + +diff --git a/arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts b/arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts +index 43982b106a4d..5cedbe2455db 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts ++++ b/arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts +@@ -190,6 +190,16 @@ accelerometer@18 { + interrupt-parent = <&pio>; + interrupts = <7 10 IRQ_TYPE_EDGE_RISING>; /* PH10 / EINT10 */ + }; ++ ++ /* NFC (NPC 100) */ ++ npc100: nfc@28 { ++ compatible = "nxp,nxp-nci-i2c"; ++ reg = <0x28>; ++ interrupt-parent = <&r_pio>; ++ interrupts = <0 6 IRQ_TYPE_LEVEL_HIGH>; /* PL6 */ ++ enable-gpios = <&pio 3 2 GPIO_ACTIVE_HIGH>; /* PD2 */ ++ firmware-gpios = <&pio 3 3 GPIO_ACTIVE_HIGH>; /* PD3 */ ++ }; + }; + + &mmc0 { +@@ -368,9 +378,10 @@ ®_dldo1 { + }; + + ®_dldo2 { +- regulator-min-microvolt = <2800000>; ++ regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <4200000>; +- regulator-name = "vcc-mipi"; ++ regulator-name = "vbat-nfc"; ++ regulator-always-on; + }; + + ®_dldo3 { +@@ -393,7 +404,8 @@ ®_drivevbus { + ®_eldo1 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1800000>; +- regulator-name = "dvdd-csi-r"; ++ regulator-name = "pvdd-nfc"; ++ regulator-always-on; + }; + + ®_eldo2 { +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-camera-sensors-HM5065-GC2145.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-camera-sensors-HM5065-GC2145.patch new file mode 100644 index 000000000000..c0040054f011 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-camera-sensors-HM5065-GC2145.patch @@ -0,0 +1,180 @@ +From 6b05b5d01cedf65ba4a82cd08ad552eb26817e9e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Tue, 23 Jun 2020 19:43:24 +0200 +Subject: ARM: dts: sun8i-a83t-tbs-a711: Add camera sensors (HM5065, GC2145) + +The tablet has two cameras, that can be switched between. Add +support for the both of them. + +Sensor is connected via parallel bus to CSI and via I2C bus to +PE14/PE15 pins. Enable CSI module and add the node for HM5065 +camera sensor. + +Camera sensors are connected via I2C to PE14/PE15 pins on A83T. +Unfortunately while the A83T datasheet suggests TWI2 I2C controller +can be configured to have SDA/SCL on these pins, this configuration +doesn't work in reality. We need to either use CCI I2C controller +that is part of the CSI module, or as is done in this patch, use GPIO +based bitbanging I2C driver. + +Reduce camera IOVDD voltage. + +Force dvdd-csi-r/f regulators to 1.8V. + +This is required by camera sensors that are connected to them. + +Signed-off-by: Ondrej Jirman +--- + .../dts/allwinner/sun8i-a83t-tbs-a711.dts | 101 +++++++++++++++++- + 1 file changed, 99 insertions(+), 2 deletions(-) + +diff --git a/arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts b/arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts +index ce1a8d98b548..ba3ea3199c69 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts ++++ b/arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts +@@ -70,6 +70,16 @@ backlight: backlight { + default-brightness-level = <9>; + }; + ++ i2c_gpio: i2c-gpio { ++ compatible = "i2c-gpio"; ++ /* PE15 = sda, PE14 = scl */ ++ sda-gpios = <&pio 4 15 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; ++ scl-gpios = <&pio 4 14 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; ++ i2c-gpio,delay-us = <1>; /* ~100 kHz */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ + panel { + compatible = "tbs,a711-panel", "panel-lvds"; + backlight = <&backlight>; +@@ -215,6 +225,13 @@ link2_codec: codec { + }; + }; + ++&ccu { ++ /* Use a stable clock source with known fixed rate for MCLK */ ++ assigned-clocks = <&ccu CLK_CSI_MCLK>; ++ assigned-clock-parents = <&osc24M>; ++ assigned-clock-rates = <24000000>; ++}; ++ + &cpu0 { + cpu-supply = <®_dcdc2>; + }; +@@ -223,6 +240,37 @@ &cpu100 { + cpu-supply = <®_dcdc3>; + }; + ++&csi { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&csi_8bit_parallel_pins>, <&csi_mclk_pin>; ++ status = "okay"; ++ ++ port { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ csi_hm5065_ep: endpoint@0 { ++ reg = <0>; ++ remote-endpoint = <&hm5065_ep>; ++ bus-width = <8>; ++ data-active = <1>; ++ pclk-sample = <0>; ++ hsync-active = <0>; ++ vsync-active = <1>; ++ }; ++ ++ csi_gc2145_ep: endpoint@1 { ++ reg = <1>; ++ remote-endpoint = <&gc2145_ep>; ++ bus-width = <8>; ++ hsync-active = <1>; ++ vsync-active = <1>; ++ data-active = <1>; ++ pclk-sample = <1>; ++ }; ++ }; ++}; ++ + &de { + status = "okay"; + }; +@@ -283,6 +331,55 @@ npc100: nfc@28 { + }; + }; + ++&i2c_gpio { ++ hm5065: rear-camera@1f { ++ compatible = "himax,hm5065"; ++ reg = <0x1f>; ++ clocks = <&ccu CLK_CSI_MCLK>; ++ clock-names = "xclk"; ++ IOVDD-supply = <®_dldo3>; ++ AVDD-supply = <®_dldo4>; ++ DVDD-supply = <®_eldo3>; ++ AFVDD-supply = <®_dldo3>; ++ reset-gpios = <&pio 4 18 GPIO_ACTIVE_LOW>; /* PE18 */ ++ enable-gpios = <&pio 4 19 GPIO_ACTIVE_HIGH>; /* PE19 */ ++ ++ port { ++ hm5065_ep: endpoint { ++ remote-endpoint = <&csi_hm5065_ep>; ++ bus-width = <8>; ++ data-active = <1>; ++ pclk-sample = <0>; ++ hsync-active = <0>; ++ vsync-active = <1>; ++ }; ++ }; ++ }; ++ ++ gc2145: front-camera@3c { ++ compatible = "galaxycore,gc2145"; ++ reg = <0x3c>; ++ clocks = <&ccu CLK_CSI_MCLK>; ++ clock-names = "xclk"; ++ IOVDD-supply = <®_dldo3>; ++ AVDD-supply = <®_dldo4>; ++ DVDD-supply = <®_eldo3>; ++ reset-gpios = <&pio 4 16 (GPIO_ACTIVE_LOW | GPIO_OPEN_DRAIN)>; /* PE16 */ ++ enable-gpios = <&pio 4 17 (GPIO_ACTIVE_LOW | GPIO_OPEN_DRAIN)>; /* PE17 */ ++ ++ port { ++ gc2145_ep: endpoint { ++ remote-endpoint = <&csi_gc2145_ep>; ++ bus-width = <8>; ++ hsync-active = <1>; ++ vsync-active = <1>; ++ data-active = <1>; ++ pclk-sample = <1>; ++ }; ++ }; ++ }; ++}; ++ + &i2s0 { + status = "okay"; + pinctrl-names = "default"; +@@ -500,7 +597,7 @@ ®_drivevbus { + }; + + ®_eldo1 { +- regulator-min-microvolt = <1200000>; ++ regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-name = "pvdd-nfc"; + regulator-always-on; +@@ -513,7 +610,7 @@ ®_eldo2 { + }; + + ®_eldo3 { +- regulator-min-microvolt = <1200000>; ++ regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-name = "dvdd-csi-f"; + }; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-flash-led-support.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-flash-led-support.patch new file mode 100644 index 000000000000..a620e0bed983 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-flash-led-support.patch @@ -0,0 +1,32 @@ +From f5ba77da4d5a3374ade9ce2c9a98697724e2f418 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Wed, 8 Nov 2017 21:57:45 +0100 +Subject: ARM: dts: sun8i-a83t-tbs-a711: Add flash led support + +--- + arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts b/arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts +index ba3ea3199c69..23eb11e99b98 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts ++++ b/arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts +@@ -61,6 +61,15 @@ chosen { + stdout-path = "serial0:115200n8"; + }; + ++ leds { ++ compatible = "gpio-leds"; ++ ++ flash_led { ++ label = "flash"; ++ gpios = <&pio 1 3 GPIO_ACTIVE_HIGH>; ++ }; ++ }; ++ + backlight: backlight { + compatible = "pwm-backlight"; + pwms = <&pwm 0 50000 PWM_POLARITY_INVERTED>; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-powerup-down-support-for-the-3G.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-powerup-down-support-for-the-3G.patch new file mode 100644 index 000000000000..79bd2f03690e --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-powerup-down-support-for-the-3G.patch @@ -0,0 +1,61 @@ +From 0da088ce86e596f4d2bdff7c255cc579579be341 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Myl=C3=A8ne=20Josserand?= + +Date: Thu, 6 Jul 2017 10:57:55 +0200 +Subject: ARM: dts: sun8i-a83t-tbs-a711: Add powerup/down support for the 3G + modem +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The modem needs tree gpios to be powered-up: + - PL10 = reset + - PL8 = On/Off + - PL9 = vbat +Because of that, the PL9 corresponds to the regulator's gpio whereas +the PL8 (on/off) will be a power-gpio of the power sequence. + +Thanks to that, the modem is powered up: + # lsusb + Bus 001 Device 004: ID 19d2:ffeb + +Signed-off-by: Ondrej Jirman +Signed-off-by: Mylène Josserand +--- + arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts | 13 ++++++++++++- + 1 file changed, 12 insertions(+), 1 deletion(-) + +diff --git a/arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts b/arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts +index 5cedbe2455db..f7d8e5425233 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts ++++ b/arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts +@@ -118,7 +118,7 @@ reg_vmain: reg-vmain { + regulator-name = "vmain"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; +- gpio = <&r_pio 0 9 GPIO_ACTIVE_HIGH>; ++ gpio = <&r_pio 0 9 GPIO_ACTIVE_HIGH>; /* PL9 */ + enable-active-high; + vin-supply = <®_vbat>; + }; +@@ -134,6 +134,17 @@ wifi_pwrseq: pwrseq { + clocks = <&ac100_rtc 1>; + clock-names = "ext_clock"; + }; ++ ++ modem { ++ compatible = "zte,mg3732"; ++ char-device-name = "modem-power"; ++ ++ power-supply = <®_vmain>; ++ ++ enable-gpios = <&r_pio 0 8 GPIO_ACTIVE_HIGH>; /* PL8 */ ++ reset-gpios = <&r_pio 0 10 GPIO_ACTIVE_HIGH>; /* PL10 */ ++ wakeup-gpios = <&r_pio 0 11 GPIO_ACTIVE_HIGH>; /* PL11 */ ++ }; + }; + + &cpu0 { +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-regulators-to-the-accelerometer.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-regulators-to-the-accelerometer.patch new file mode 100644 index 000000000000..7590fd546009 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-regulators-to-the-accelerometer.patch @@ -0,0 +1,30 @@ +From 3388c7e4f1d60f11b0377cfe30d0bfaaa20183ba Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Tue, 23 Jun 2020 19:15:37 +0200 +Subject: ARM: dts: sun8i-a83t-tbs-a711: Add regulators to the accelerometer + +The bosch,bma250 driver will fail to probe if the regulators are +not specified in DT, because it tries to set voltage on dummy +regulators, which fails with EINVAL. + +Signed-off-by: Ondrej Jirman +--- + arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts b/arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts +index 925891a1d6d9..ce1a8d98b548 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts ++++ b/arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts +@@ -268,6 +268,8 @@ accelerometer@18 { + reg = <0x18>; + interrupt-parent = <&pio>; + interrupts = <7 10 IRQ_TYPE_EDGE_RISING>; /* PH10 / EINT10 */ ++ vdd-supply = <®_dcdc1>; ++ vddio-supply = <®_dcdc1>; + }; + + /* NFC (NPC 100) */ +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-sound-support-via-AC100-codec.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-sound-support-via-AC100-codec.patch new file mode 100644 index 000000000000..4f8b75f79fcb --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-sound-support-via-AC100-codec.patch @@ -0,0 +1,131 @@ +From 06bc6b6ae01fd5e2e9cdc570581b5795cc7cd019 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sat, 22 Feb 2020 23:40:29 +0100 +Subject: ARM: dts: sun8i-a83t-tbs-a711: Add sound support via AC100 codec + +... + +Signed-off-by: Ondrej Jirman +--- + .../dts/allwinner/sun8i-a83t-tbs-a711.dts | 85 +++++++++++++++++++ + 1 file changed, 85 insertions(+) + +diff --git a/arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts b/arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts +index f7d8e5425233..925891a1d6d9 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts ++++ b/arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts +@@ -145,6 +145,74 @@ modem { + reset-gpios = <&r_pio 0 10 GPIO_ACTIVE_HIGH>; /* PL10 */ + wakeup-gpios = <&r_pio 0 11 GPIO_ACTIVE_HIGH>; /* PL11 */ + }; ++ ++ bt_sco_codec: bt-sco-codec { ++ #sound-dai-cells = <1>; ++ compatible = "linux,bt-sco"; ++ sound-name-prefix = "Bluetooth"; ++ }; ++ ++ speaker_amp: audio-amplifier { ++ compatible = "simple-audio-amplifier"; ++ enable-gpios = <&pio 6 13 GPIO_ACTIVE_HIGH>; /* PG13 */ ++ sound-name-prefix = "Speaker Amp"; ++ }; ++ ++ sound: sound { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ compatible = "simple-audio-card"; ++ simple-audio-card,name = "ac100-audio"; ++ simple-audio-card,aux-devs = <&ac100_codec_analog>, <&speaker_amp>; ++ simple-audio-card,widgets = "Microphone", "Headset Microphone", ++ "Microphone", "Internal Microphone", ++ "Headphone", "Headphone Jack", ++ "Speaker", "Internal Speaker"; ++ simple-audio-card,routing = ++ "Headphone Jack", "HP", ++ "Internal Speaker", "Speaker Amp OUTL", ++ "Speaker Amp INL", "SPKOUTL", ++ "Left DAC", "DACL", ++ "Right DAC", "DACR", ++ "ADCL", "Left ADC", ++ "ADCR", "Right ADC", ++ "Internal Microphone", "MBIAS", ++ "MIC1", "Internal Microphone", ++ "Headset Microphone", "HBIAS", ++ "MIC2", "Headset Microphone"; ++ ++ simple-audio-card,dai-link@0 { ++ format = "i2s"; ++ frame-master = <&link0_cpu>; ++ bitclock-master = <&link0_cpu>; ++ mclk-fs = <512>; ++ ++ link0_cpu: cpu { ++ sound-dai = <&i2s0>; ++ }; ++ ++ link0_codec: codec { ++ sound-dai = <&ac100_codec 0>; ++ }; ++ }; ++ ++ simple-audio-card,dai-link@2 { ++ format = "dsp_a"; ++ frame-master = <&link2_codec>; ++ bitclock-master = <&link2_codec>; ++ bitclock-inversion; ++ ++ link2_cpu: cpu { ++ sound-dai = <&bt_sco_codec 0>; ++ }; ++ ++ link2_codec: codec { ++ sound-dai = <&ac100_codec 2>; ++ dai-tdm-slot-num = <2>; ++ dai-tdm-slot-width = <16>; ++ }; ++ }; ++ }; + }; + + &cpu0 { +@@ -213,6 +281,12 @@ npc100: nfc@28 { + }; + }; + ++&i2s0 { ++ status = "okay"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2s0_pins>; ++}; ++ + &mmc0 { + vmmc-supply = <®_dcdc1>; + pinctrl-names = "default"; +@@ -293,12 +367,23 @@ ac100: codec@e89 { + compatible = "x-powers,ac100"; + reg = <0xe89>; + ++ ac100_codec_analog: codec-analog { ++ compatible = "x-powers,ac100-codec-analog"; ++ cpvdd-supply = <®_aldo2>; ++ }; ++ + ac100_codec: codec { ++ #sound-dai-cells = <1>; + compatible = "x-powers,ac100-codec"; + interrupt-parent = <&r_pio>; + interrupts = <0 12 IRQ_TYPE_LEVEL_LOW>; /* PL12 */ + #clock-cells = <0>; + clock-output-names = "4M_adda"; ++ ++ LDOIN-supply = <®_aldo2>; ++ AVCC-supply = <®_aldo3>; ++ VDDIO1-supply = <®_dcdc1>; ++ VDDIO2-supply = <®_dldo1>; + }; + + ac100_rtc: rtc { +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-support-for-the-vibrator-motor.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-support-for-the-vibrator-motor.patch new file mode 100644 index 000000000000..2edc3c829fba --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-support-for-the-vibrator-motor.patch @@ -0,0 +1,39 @@ +From 9b8f0e47429380b0d58597e2581abfe86d04f1e7 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sat, 9 Nov 2019 23:55:48 +0100 +Subject: ARM: dts: sun8i-a83t-tbs-a711: Add support for the vibrator motor + +The board has a vibrator mottor. Hook it to the input subsystem. + +According to the PMIC specification, LDO needs to be enabled (value 0b11) +to achieve the specified max driving current of 150mA. We can't drive +the motor with just GPIO mode. + +In GPIO mode the chip is probably just using the regular CMOS logic +output circuitry (typically limited to around 20-35mA, but not specified +in this datasheet). + +Signed-off-by: Ondrej Jirman +--- + arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts b/arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts +index 23eb11e99b98..81e1271482ed 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts ++++ b/arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts +@@ -118,6 +118,11 @@ panel_input: endpoint { + }; + }; + ++ vibrator { ++ compatible = "gpio-vibrator"; ++ vcc-supply = <®_ldo_io1>; ++ }; ++ + reg_gps: reg-gps { + compatible = "regulator-fixed"; + regulator-name = "gps"; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Enable-charging-LED.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Enable-charging-LED.patch new file mode 100644 index 000000000000..b74a76cd67f4 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Enable-charging-LED.patch @@ -0,0 +1,31 @@ +From c099653062731dfb0f13e60373a890b7ba88c149 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sun, 23 Feb 2020 13:21:58 +0100 +Subject: ARM: dts: sun8i-a83t-tbs-a711: Enable charging LED + +The tablet has a LED connected to the PMIC. The LED is visible in the +top right corner of the tablet. Enable it. + +Signed-off-by: Ondrej Jirman +--- + arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts b/arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts +index 6a90763d8736..6edeaf8ed431 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts ++++ b/arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts +@@ -519,6 +519,10 @@ &battery_power_supply { + status = "okay"; + }; + ++&charger_led { ++ status = "okay"; ++}; ++ + ®_aldo1 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Give-Linux-more-privileges-over-SCP.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Give-Linux-more-privileges-over-SCP.patch new file mode 100644 index 000000000000..1b9c82691405 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Give-Linux-more-privileges-over-SCP.patch @@ -0,0 +1,47 @@ +From fd7b0ea1f64ea844570ebcb802cb2a4934414fba Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Tue, 23 Jun 2020 19:19:46 +0200 +Subject: ARM: dts: sun8i-a83t-tbs-a711: Give Linux more privileges over SCPI + +This is needed since on A83T there's no PSCI/ATF implementation. + +Signed-off-by: Ondrej Jirman +--- + arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi b/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi +index 1ef9b4a581ac..27f8e42467af 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi ++++ b/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi +@@ -286,7 +286,7 @@ opp-1800000000 { + + scpi_protocol: scpi { + compatible = "arm,scpi"; +- mboxes = <&msgbox 2>, <&msgbox 3>; ++ mboxes = <&msgbox 0>, <&msgbox 1>; + mbox-names = "tx", "rx"; + shmem = <&scpi_sram>; + }; +@@ -424,6 +424,9 @@ syscon: syscon@1c00000 { + compatible = "allwinner,sun8i-a83t-system-controller", + "syscon"; + reg = <0x01c00000 0x1000>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; + + sram_a2: sram@40000 { + compatible = "mmio-sram"; +@@ -434,7 +437,7 @@ sram_a2: sram@40000 { + + scpi_sram: scp-shmem@13c00 { + compatible = "arm,scp-shmem"; +- reg = <0x13c00 0x200>; ++ reg = <0x13e00 0x200>; + }; + }; + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Increase-voltage-on-the-vibrator.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Increase-voltage-on-the-vibrator.patch new file mode 100644 index 000000000000..1be57b5d0c39 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Increase-voltage-on-the-vibrator.patch @@ -0,0 +1,30 @@ +From 7bf0e248c3dbd81edf1482bfebe5e99e0c8d30e2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Fri, 8 Dec 2017 12:44:22 +0100 +Subject: ARM: dts: sun8i-a83t-tbs-a711: Increase voltage on the vibrator + +Vibrator motor is weak at the current voltage. Increase the voltage. + +Signed-off-by: Ondrej Jirman +--- + arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts b/arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts +index 81e1271482ed..6a90763d8736 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts ++++ b/arch/arm/boot/dts/allwinner/sun8i-a83t-tbs-a711.dts +@@ -650,8 +650,8 @@ ®_ldo_io0 { + }; + + ®_ldo_io1 { +- regulator-min-microvolt = <3100000>; +- regulator-max-microvolt = <3100000>; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; + regulator-name = "vcc-vb"; + status = "okay"; + }; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-h2-plus-bananapi-m2-zero-Enable-HDMI-audio.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-h2-plus-bananapi-m2-zero-Enable-HDMI-audio.patch new file mode 100644 index 000000000000..e95ddef3f19d --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-h2-plus-bananapi-m2-zero-Enable-HDMI-audio.patch @@ -0,0 +1,42 @@ +From 4a04f862d0a85d8aac6e3bbd698c1a40bad66059 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sun, 9 May 2021 11:14:51 +0200 +Subject: ARM: dts: sun8i: h2-plus: bananapi-m2-zero: Enable HDMI audio + +BananaPi M2 Zero has HDMI output and thus supports HDMI audio. Enable +it. + +Signed-off-by: Jernej Skrabec +--- + .../boot/dts/allwinner/sun8i-h2-plus-bananapi-m2-zero.dts | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/arch/arm/boot/dts/allwinner/sun8i-h2-plus-bananapi-m2-zero.dts b/arch/arm/boot/dts/allwinner/sun8i-h2-plus-bananapi-m2-zero.dts +index d3a7c9fa23e4..f40c2d0483b5 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-h2-plus-bananapi-m2-zero.dts ++++ b/arch/arm/boot/dts/allwinner/sun8i-h2-plus-bananapi-m2-zero.dts +@@ -133,6 +133,10 @@ hdmi_out_con: endpoint { + }; + }; + ++&i2s2 { ++ status = "okay"; ++}; ++ + &mmc0 { + vmmc-supply = <®_vcc3v3>; + bus-width = <4>; +@@ -256,6 +260,10 @@ &r_pio { + "", "", "", "", "", "", "", ""; + }; + ++&sound_hdmi { ++ status = "okay"; ++}; ++ + &usb_otg { + dr_mode = "otg"; + status = "okay"; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-h3-Enable-hdmi-sound-card-on-boards-with-hdmi.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-h3-Enable-hdmi-sound-card-on-boards-with-hdmi.patch new file mode 100644 index 000000000000..7f5c11d8ec31 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-h3-Enable-hdmi-sound-card-on-boards-with-hdmi.patch @@ -0,0 +1,272 @@ +From e01f6a6ca5c4883ee5afbfa1eeaf9aa6c53e058b Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sun, 9 May 2021 11:06:37 +0200 +Subject: ARM: dts: sun8i: h3: Enable hdmi sound card on boards with hdmi + +Each board that has HDMI connector can also transmit audio through it. +Enable HDMI sound card on all H3 boards with HDMI connector. + +Signed-off-by: Jernej Skrabec +--- + arch/arm/boot/dts/allwinner/sun8i-h3-beelink-x2.dts | 8 ++++++++ + .../dts/allwinner/sun8i-h3-emlid-neutis-n5h3-devboard.dts | 8 ++++++++ + arch/arm/boot/dts/allwinner/sun8i-h3-nanopi-m1-plus.dts | 8 ++++++++ + arch/arm/boot/dts/allwinner/sun8i-h3-nanopi-m1.dts | 8 ++++++++ + arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-2.dts | 8 ++++++++ + arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-lite.dts | 8 ++++++++ + arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-one.dts | 8 ++++++++ + arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-pc.dts | 8 ++++++++ + .../boot/dts/allwinner/sun8i-h3-orangepi-zero-plus2.dts | 8 ++++++++ + arch/arm/boot/dts/allwinner/sun8i-h3-rervision-dvk.dts | 8 ++++++++ + 10 files changed, 80 insertions(+) + +diff --git a/arch/arm/boot/dts/allwinner/sun8i-h3-beelink-x2.dts b/arch/arm/boot/dts/allwinner/sun8i-h3-beelink-x2.dts +index 5b77300307de..2fa04f508216 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-h3-beelink-x2.dts ++++ b/arch/arm/boot/dts/allwinner/sun8i-h3-beelink-x2.dts +@@ -159,6 +159,10 @@ hdmi_out_con: endpoint { + }; + }; + ++&i2s2 { ++ status = "okay"; ++}; ++ + &ir { + linux,rc-map-name = "rc-tanix-tx3mini"; + pinctrl-names = "default"; +@@ -213,6 +217,10 @@ ®_usb0_vbus { + status = "okay"; + }; + ++&sound_hdmi { ++ status = "okay"; ++}; ++ + &spdif { + pinctrl-names = "default"; + pinctrl-0 = <&spdif_tx_pin>; +diff --git a/arch/arm/boot/dts/allwinner/sun8i-h3-emlid-neutis-n5h3-devboard.dts b/arch/arm/boot/dts/allwinner/sun8i-h3-emlid-neutis-n5h3-devboard.dts +index 02fbe00cde97..61d6529dcc70 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-h3-emlid-neutis-n5h3-devboard.dts ++++ b/arch/arm/boot/dts/allwinner/sun8i-h3-emlid-neutis-n5h3-devboard.dts +@@ -70,3 +70,11 @@ hdmi_out_con: endpoint { + &i2c1 { + status = "okay"; + }; ++ ++&i2s2 { ++ status = "okay"; ++}; ++ ++&sound_hdmi { ++ status = "okay"; ++}; +diff --git a/arch/arm/boot/dts/allwinner/sun8i-h3-nanopi-m1-plus.dts b/arch/arm/boot/dts/allwinner/sun8i-h3-nanopi-m1-plus.dts +index 59bd0746acf8..10ae138b14f0 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-h3-nanopi-m1-plus.dts ++++ b/arch/arm/boot/dts/allwinner/sun8i-h3-nanopi-m1-plus.dts +@@ -118,6 +118,10 @@ hdmi_out_con: endpoint { + }; + }; + ++&i2s2 { ++ status = "okay"; ++}; ++ + &ir { + pinctrl-names = "default"; + pinctrl-0 = <&r_ir_rx_pin>; +@@ -159,6 +163,10 @@ &ohci2 { + status = "okay"; + }; + ++&sound_hdmi { ++ status = "okay"; ++}; ++ + &uart3 { + pinctrl-names = "default"; + pinctrl-0 = <&uart3_pins>, <&uart3_rts_cts_pins>; +diff --git a/arch/arm/boot/dts/allwinner/sun8i-h3-nanopi-m1.dts b/arch/arm/boot/dts/allwinner/sun8i-h3-nanopi-m1.dts +index 69243dcb30a6..9393779f33b6 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-h3-nanopi-m1.dts ++++ b/arch/arm/boot/dts/allwinner/sun8i-h3-nanopi-m1.dts +@@ -91,6 +91,10 @@ hdmi_out_con: endpoint { + }; + }; + ++&i2s2 { ++ status = "okay"; ++}; ++ + &ir { + pinctrl-names = "default"; + pinctrl-0 = <&r_ir_rx_pin>; +@@ -104,3 +108,7 @@ &ohci1 { + &ohci2 { + status = "okay"; + }; ++ ++&sound_hdmi { ++ status = "okay"; ++}; +diff --git a/arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-2.dts b/arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-2.dts +index d2ae47b074bf..7fac0c8906e8 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-2.dts ++++ b/arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-2.dts +@@ -145,6 +145,10 @@ hdmi_out_con: endpoint { + }; + }; + ++&i2s2 { ++ status = "okay"; ++}; ++ + &ir { + pinctrl-names = "default"; + pinctrl-0 = <&r_ir_rx_pin>; +@@ -179,6 +183,10 @@ ®_usb1_vbus { + status = "okay"; + }; + ++&sound_hdmi { ++ status = "okay"; ++}; ++ + &uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_pa_pins>; +diff --git a/arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-lite.dts b/arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-lite.dts +index 6a4316a52469..28ec2a5b1a5c 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-lite.dts ++++ b/arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-lite.dts +@@ -120,6 +120,10 @@ hdmi_out_con: endpoint { + }; + }; + ++&i2s2 { ++ status = "okay"; ++}; ++ + &ir { + pinctrl-names = "default"; + pinctrl-0 = <&r_ir_rx_pin>; +@@ -156,6 +160,10 @@ &ohci2 { + status = "okay"; + }; + ++&sound_hdmi { ++ status = "okay"; ++}; ++ + &uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_pa_pins>; +diff --git a/arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-one.dts b/arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-one.dts +index 59f6f6d5e7ca..927fd1bab07d 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-one.dts ++++ b/arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-one.dts +@@ -146,6 +146,10 @@ hdmi_out_con: endpoint { + }; + }; + ++&i2s2 { ++ status = "okay"; ++}; ++ + &mmc0 { + vmmc-supply = <®_vcc3v3>; + bus-width = <4>; +@@ -166,6 +170,10 @@ ®_usb0_vbus { + status = "okay"; + }; + ++&sound_hdmi { ++ status = "okay"; ++}; ++ + &uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_pa_pins>; +diff --git a/arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-pc.dts b/arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-pc.dts +index 92a58e362004..624e248e3ffc 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-pc.dts ++++ b/arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-pc.dts +@@ -147,6 +147,10 @@ hdmi_out_con: endpoint { + }; + }; + ++&i2s2 { ++ status = "okay"; ++}; ++ + &ir { + pinctrl-names = "default"; + pinctrl-0 = <&r_ir_rx_pin>; +@@ -206,6 +210,10 @@ ®_usb0_vbus { + status = "okay"; + }; + ++&sound_hdmi { ++ status = "okay"; ++}; ++ + &uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_pa_pins>; +diff --git a/arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-zero-plus2.dts b/arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-zero-plus2.dts +index 7a6444a10e25..4073d34af23c 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-zero-plus2.dts ++++ b/arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-zero-plus2.dts +@@ -117,6 +117,10 @@ hdmi_out_con: endpoint { + }; + }; + ++&i2s2 { ++ status = "okay"; ++}; ++ + &mmc0 { + vmmc-supply = <®_vcc3v3>; + bus-width = <4>; +@@ -155,6 +159,10 @@ &ohci0 { + status = "okay"; + }; + ++&sound_hdmi { ++ status = "okay"; ++}; ++ + &uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_pa_pins>; +diff --git a/arch/arm/boot/dts/allwinner/sun8i-h3-rervision-dvk.dts b/arch/arm/boot/dts/allwinner/sun8i-h3-rervision-dvk.dts +index 4738f3a9efe4..ee645912ee39 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-h3-rervision-dvk.dts ++++ b/arch/arm/boot/dts/allwinner/sun8i-h3-rervision-dvk.dts +@@ -69,6 +69,10 @@ hdmi_out_con: endpoint { + }; + }; + ++&i2s2 { ++ status = "okay"; ++}; ++ + &mmc0 { + bus-width = <4>; + cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ +@@ -98,6 +102,10 @@ &ohci3 { + status = "okay"; + }; + ++&sound_hdmi { ++ status = "okay"; ++}; ++ + &uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_pa_pins>; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-h3-Use-my-own-more-aggressive-OPPs-on-H3.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-h3-Use-my-own-more-aggressive-OPPs-on-H3.patch new file mode 100644 index 000000000000..99f0ac907bd8 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-h3-Use-my-own-more-aggressive-OPPs-on-H3.patch @@ -0,0 +1,72 @@ +From 3b5ba528f3dee489d2a4d789c2e1dc83af43273e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Mon, 17 Aug 2020 23:43:53 +0200 +Subject: ARM: dts: sun8i-h3: Use my own more aggressive OPPs on H3 + +Signed-off-by: Ondrej Jirman +--- + arch/arm/boot/dts/allwinner/sun8i-h3.dtsi | 36 +++++++++++++++++++++++ + 1 file changed, 36 insertions(+) + +diff --git a/arch/arm/boot/dts/allwinner/sun8i-h3.dtsi b/arch/arm/boot/dts/allwinner/sun8i-h3.dtsi +index eac2349a2380..da1991c991cb 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-h3.dtsi ++++ b/arch/arm/boot/dts/allwinner/sun8i-h3.dtsi +@@ -48,6 +48,12 @@ cpu0_opp_table: opp-table-cpu { + compatible = "operating-points-v2"; + opp-shared; + ++ opp-480000000 { ++ opp-hz = /bits/ 64 <480000000>; ++ opp-microvolt = <1040000 1040000 1300000>; ++ clock-latency-ns = <244144>; /* 8 32k periods */ ++ }; ++ + opp-648000000 { + opp-hz = /bits/ 64 <648000000>; + opp-microvolt = <1040000 1040000 1300000>; +@@ -60,11 +66,41 @@ opp-816000000 { + clock-latency-ns = <244144>; /* 8 32k periods */ + }; + ++ opp-960000000 { ++ opp-hz = /bits/ 64 <960000000>; ++ opp-microvolt = <1200000 1200000 1300000>; ++ clock-latency-ns = <244144>; /* 8 32k periods */ ++ }; ++ + opp-1008000000 { + opp-hz = /bits/ 64 <1008000000>; + opp-microvolt = <1200000 1200000 1300000>; + clock-latency-ns = <244144>; /* 8 32k periods */ + }; ++ ++ opp-1104000000 { ++ opp-hz = /bits/ 64 <1104000000>; ++ opp-microvolt = <1320000 1320000 1320000>; ++ clock-latency-ns = <244144>; /* 8 32k periods */ ++ }; ++ ++ opp-1200000000 { ++ opp-hz = /bits/ 64 <1200000000>; ++ opp-microvolt = <1320000 1320000 1320000>; ++ clock-latency-ns = <244144>; /* 8 32k periods */ ++ }; ++ ++ opp-1296000000 { ++ opp-hz = /bits/ 64 <1296000000>; ++ opp-microvolt = <1340000 1340000 1340000>; ++ clock-latency-ns = <244144>; /* 8 32k periods */ ++ }; ++ ++ opp-1368000000 { ++ opp-hz = /bits/ 64 <1368000000>; ++ opp-microvolt = <1400000 1400000 1400000>; ++ clock-latency-ns = <244144>; /* 8 32k periods */ ++ }; + }; + + cpus { +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-h3-orange-pi-one-Enable-all-gpio-header-UARTs.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-h3-orange-pi-one-Enable-all-gpio-header-UARTs.patch new file mode 100644 index 000000000000..3aec34335507 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-h3-orange-pi-one-Enable-all-gpio-header-UARTs.patch @@ -0,0 +1,49 @@ +From 230f694a6df3aad6214f652209d03b381999b4ac Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sat, 2 Apr 2022 02:24:26 +0200 +Subject: ARM: dts: sun8i-h3-orange-pi-one: Enable all gpio header UARTs + +--- + arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-one.dts | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-one.dts b/arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-one.dts +index f5476cdab8a4..64c489e356ce 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-one.dts ++++ b/arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-one.dts +@@ -54,6 +54,9 @@ / { + aliases { + ethernet0 = &emac; + serial0 = &uart0; ++ serial1 = &uart1; ++ serial2 = &uart2; ++ serial3 = &uart3; + }; + + chosen { +@@ -183,19 +186,19 @@ &uart0 { + &uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_pins>; +- status = "disabled"; ++ status = "okay"; + }; + + &uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&uart2_pins>; +- status = "disabled"; ++ status = "okay"; + }; + + &uart3 { + pinctrl-names = "default"; + pinctrl-0 = <&uart3_pins>; +- status = "disabled"; ++ status = "okay"; + }; + + &usb_otg { +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-h3-orange-pi-pc-Increase-max-CPUX-voltage-to-1.4V.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-h3-orange-pi-pc-Increase-max-CPUX-voltage-to-1.4V.patch new file mode 100644 index 000000000000..0b8effec54c5 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-h3-orange-pi-pc-Increase-max-CPUX-voltage-to-1.4V.patch @@ -0,0 +1,31 @@ +From 6f11d654b5650b8563abeab166342c07899d4f39 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sun, 13 May 2018 21:00:43 +0200 +Subject: ARM: dts: sun8i-h3-orange-pi-pc: Increase max CPUX voltage to 1.4V + +When using thermal regulation we can afford to go higher. Also add +regulator-ramp-delay, because regulator takes some time to change +voltage. + +Signed-off-by: Ondrej Jirman +--- + arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-pc.dts | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-pc.dts b/arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-pc.dts +index b96e015f54ee..92a58e362004 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-pc.dts ++++ b/arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-pc.dts +@@ -194,7 +194,8 @@ reg_vdd_cpux: regulator@65 { + * Use 1.0V as the minimum voltage instead. + */ + regulator-min-microvolt = <1000000>; +- regulator-max-microvolt = <1300000>; ++ regulator-max-microvolt = <1400000>; ++ regulator-ramp-delay = <200>; + regulator-boot-on; + regulator-always-on; + }; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-r40-Add-hdmi-sound-card.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-r40-Add-hdmi-sound-card.patch new file mode 100644 index 000000000000..c86ced5d8862 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-r40-Add-hdmi-sound-card.patch @@ -0,0 +1,48 @@ +From 453f3a74fadd774b213e9e99d7512f382f5ce2ca Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sun, 9 May 2021 11:50:56 +0200 +Subject: ARM: dts: sun8i: r40: Add hdmi sound card + +R40 support HDMI audio. Add a sound card node for it. + +Signed-off-by: Jernej Skrabec +--- + arch/arm/boot/dts/allwinner/sun8i-r40.dtsi | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/arch/arm/boot/dts/allwinner/sun8i-r40.dtsi b/arch/arm/boot/dts/allwinner/sun8i-r40.dtsi +index a5b1f1e3900d..d73828d6b3c0 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-r40.dtsi ++++ b/arch/arm/boot/dts/allwinner/sun8i-r40.dtsi +@@ -124,6 +124,20 @@ de: display-engine { + status = "disabled"; + }; + ++ sound_hdmi: sound_hdmi { ++ compatible = "allwinner,sun9i-a80-hdmi-audio", ++ "allwinner,sun8i-r40-hdmi-audio"; ++ status = "disabled"; ++ ++ codec { ++ sound-dai = <&hdmi>; ++ }; ++ ++ cpu { ++ sound-dai = <&i2s2>; ++ }; ++ }; ++ + thermal-zones { + cpu_thermal: cpu0-thermal { + /* milliseconds */ +@@ -1265,6 +1279,7 @@ gic: interrupt-controller@1c81000 { + }; + + hdmi: hdmi@1ee0000 { ++ #sound-dai-cells = <0>; + compatible = "allwinner,sun8i-r40-dw-hdmi", + "allwinner,sun8i-a83t-dw-hdmi"; + reg = <0x01ee0000 0x10000>; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-r40-bananapi-m2-ultra-Enable-HDMI-audio.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-r40-bananapi-m2-ultra-Enable-HDMI-audio.patch new file mode 100644 index 000000000000..c041094b176f --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-r40-bananapi-m2-ultra-Enable-HDMI-audio.patch @@ -0,0 +1,42 @@ +From 9f7f66239ad389f9b77720ef467ba7d2225868b3 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sun, 9 May 2021 11:52:43 +0200 +Subject: ARM: dts: sun8i: r40: bananapi-m2-ultra: Enable HDMI audio + +BananaPi M2 Ultra has HDMI output and thus supports HDMI audio. Enable +it. + +Signed-off-by: Jernej Skrabec +--- + .../boot/dts/allwinner/sun8i-r40-bananapi-m2-ultra.dts | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/arch/arm/boot/dts/allwinner/sun8i-r40-bananapi-m2-ultra.dts b/arch/arm/boot/dts/allwinner/sun8i-r40-bananapi-m2-ultra.dts +index cd2351acc32f..a91213e8d2c1 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-r40-bananapi-m2-ultra.dts ++++ b/arch/arm/boot/dts/allwinner/sun8i-r40-bananapi-m2-ultra.dts +@@ -169,6 +169,10 @@ axp22x: pmic@34 { + + #include "axp22x.dtsi" + ++&i2s2 { ++ status = "okay"; ++}; ++ + &ir0 { + status = "okay"; + }; +@@ -304,6 +308,10 @@ ®_eldo3 { + regulator-name = "vdd1v2-sata"; + }; + ++&sound_hdmi { ++ status = "okay"; ++}; ++ + &tcon_tv0 { + status = "okay"; + }; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-v40-bananapi-m2-berry-Enable-HDMI-audio.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-v40-bananapi-m2-berry-Enable-HDMI-audio.patch new file mode 100644 index 000000000000..e7057521bb0f --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sun8i-v40-bananapi-m2-berry-Enable-HDMI-audio.patch @@ -0,0 +1,42 @@ +From c60e834e1aae8950464e736acaa2177b3497b307 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sun, 9 May 2021 12:00:46 +0200 +Subject: ARM: dts: sun8i: v40: bananapi-m2-berry: Enable HDMI audio + +BananaPi M2 Berry has HDMI output and thus supports HDMI audio. Enable +it. + +Signed-off-by: Jernej Skrabec +--- + .../boot/dts/allwinner/sun8i-v40-bananapi-m2-berry.dts | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/arch/arm/boot/dts/allwinner/sun8i-v40-bananapi-m2-berry.dts b/arch/arm/boot/dts/allwinner/sun8i-v40-bananapi-m2-berry.dts +index 6575ef274453..0e1395a3d052 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-v40-bananapi-m2-berry.dts ++++ b/arch/arm/boot/dts/allwinner/sun8i-v40-bananapi-m2-berry.dts +@@ -160,6 +160,10 @@ axp22x: pmic@34 { + + #include "axp22x.dtsi" + ++&i2s2 { ++ status = "okay"; ++}; ++ + &mmc0 { + vmmc-supply = <®_dcdc1>; + bus-width = <4>; +@@ -275,6 +279,10 @@ ®_eldo3 { + regulator-name = "vdd1v2-sata"; + }; + ++&sound_hdmi { ++ status = "okay"; ++}; ++ + &tcon_tv0 { + status = "okay"; + }; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-suni-a83t-Add-i2s0-pins.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-suni-a83t-Add-i2s0-pins.patch new file mode 100644 index 000000000000..c7b510846e4a --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-suni-a83t-Add-i2s0-pins.patch @@ -0,0 +1,29 @@ +From 95bfb93adb3ec8a52d09dc47bb16f5b0c6d74382 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sun, 12 Nov 2017 19:03:39 +0100 +Subject: ARM: dts: suni-a83t: Add i2s0 pins + +Signed-off-by: Ondrej Jirman +--- + arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi b/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi +index ed3d8e692d84..ef62ced26684 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi ++++ b/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi +@@ -829,6 +829,11 @@ spdif_tx_pin: spdif-tx-pin { + function = "spdif"; + }; + ++ i2s0_pins: i2s0-pins { ++ pins = "PB4", "PB5", "PB6", "PB7", "PB8"; ++ function = "i2s0"; ++ }; ++ + uart0_pb_pins: uart0-pb-pins { + pins = "PB9", "PB10"; + function = "uart0"; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sunxi-Add-aliases-for-MMC.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sunxi-Add-aliases-for-MMC.patch new file mode 100644 index 000000000000..a3c58ae695d1 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sunxi-Add-aliases-for-MMC.patch @@ -0,0 +1,66 @@ +From 499d97a0f3f21070604d41562e7c376ccfb9c192 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Mon, 25 Jan 2021 01:15:58 +0100 +Subject: ARM: dts: sunxi: Add aliases for MMC + +Signed-off-by: Ondrej Jirman +--- + arch/arm/boot/dts/allwinner/sun5i.dtsi | 6 ++++++ + arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi | 6 ++++++ + arch/arm/boot/dts/allwinner/sun8i-h3.dtsi | 6 ++++++ + 3 files changed, 18 insertions(+) + +diff --git a/arch/arm/boot/dts/allwinner/sun5i.dtsi b/arch/arm/boot/dts/allwinner/sun5i.dtsi +index 126b80f3ca70..a15effbbbc51 100644 +--- a/arch/arm/boot/dts/allwinner/sun5i.dtsi ++++ b/arch/arm/boot/dts/allwinner/sun5i.dtsi +@@ -51,6 +51,12 @@ / { + #address-cells = <1>; + #size-cells = <1>; + ++ aliases { ++ mmc0 = &mmc0; ++ mmc1 = &mmc1; ++ mmc2 = &mmc2; ++ }; ++ + cpus { + #address-cells = <1>; + #size-cells = <0>; +diff --git a/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi b/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi +index 2ab73558aaca..a1aaa99ac3c4 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi ++++ b/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi +@@ -57,6 +57,12 @@ / { + #address-cells = <1>; + #size-cells = <1>; + ++ aliases { ++ mmc0 = &mmc0; ++ mmc1 = &mmc1; ++ mmc2 = &mmc2; ++ }; ++ + cpus { + #address-cells = <1>; + #size-cells = <0>; +diff --git a/arch/arm/boot/dts/allwinner/sun8i-h3.dtsi b/arch/arm/boot/dts/allwinner/sun8i-h3.dtsi +index e601317d5de2..30d72d3b670e 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-h3.dtsi ++++ b/arch/arm/boot/dts/allwinner/sun8i-h3.dtsi +@@ -44,6 +44,12 @@ + #include + + / { ++ aliases { ++ mmc0 = &mmc0; ++ mmc1 = &mmc1; ++ mmc2 = &mmc2; ++ }; ++ + cpu0_opp_table: opp-table-cpu { + compatible = "operating-points-v2"; + opp-shared; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sunxi-a83t-Add-SCPI-protocol.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sunxi-a83t-Add-SCPI-protocol.patch new file mode 100644 index 000000000000..16e15997e159 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sunxi-a83t-Add-SCPI-protocol.patch @@ -0,0 +1,52 @@ +From 2f243bf529a9e369b05118e64cd1553a0112f9d0 Mon Sep 17 00:00:00 2001 +From: Samuel Holland +Date: Wed, 1 Jan 2020 16:14:29 -0600 +Subject: ARM: dts: sunxi: a83t: Add SCPI protocol + +Signed-off-by: Samuel Holland +--- + arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi | 21 +++++++++++++++++++++ + 1 file changed, 21 insertions(+) + +diff --git a/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi b/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi +index 0bbf43850c95..1ef9b4a581ac 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi ++++ b/arch/arm/boot/dts/allwinner/sun8i-a83t.dtsi +@@ -284,6 +284,13 @@ opp-1800000000 { + }; + }; + ++ scpi_protocol: scpi { ++ compatible = "arm,scpi"; ++ mboxes = <&msgbox 2>, <&msgbox 3>; ++ mbox-names = "tx", "rx"; ++ shmem = <&scpi_sram>; ++ }; ++ + soc { + compatible = "simple-bus"; + #address-cells = <1>; +@@ -417,6 +424,20 @@ syscon: syscon@1c00000 { + compatible = "allwinner,sun8i-a83t-system-controller", + "syscon"; + reg = <0x01c00000 0x1000>; ++ ++ sram_a2: sram@40000 { ++ compatible = "mmio-sram"; ++ reg = <0x00040000 0x14000>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges = <0 0x00040000 0x14000>; ++ ++ scpi_sram: scp-shmem@13c00 { ++ compatible = "arm,scp-shmem"; ++ reg = <0x13c00 0x200>; ++ }; ++ }; ++ + }; + + video-codec@01c0e000 { +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sunxi-h3-h5-Add-SCPI-protocol.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sunxi-h3-h5-Add-SCPI-protocol.patch new file mode 100644 index 000000000000..594b982c3e1c --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sunxi-h3-h5-Add-SCPI-protocol.patch @@ -0,0 +1,81 @@ +From f8e5ffe17a72e7fb942255154f5c9ff6f8b4a6bc Mon Sep 17 00:00:00 2001 +From: Samuel Holland +Date: Wed, 1 Jan 2020 16:12:36 -0600 +Subject: ARM: dts: sunxi: h3/h5: Add SCPI protocol + +Signed-off-by: Samuel Holland +--- + arch/arm/boot/dts/allwinner/sun8i-h3.dtsi | 13 +++++++++++++ + arch/arm/boot/dts/allwinner/sunxi-h3-h5.dtsi | 7 +++++++ + arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi | 13 +++++++++++++ + 3 files changed, 33 insertions(+) + +diff --git a/arch/arm/boot/dts/allwinner/sun8i-h3.dtsi b/arch/arm/boot/dts/allwinner/sun8i-h3.dtsi +index da1991c991cb..e601317d5de2 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-h3.dtsi ++++ b/arch/arm/boot/dts/allwinner/sun8i-h3.dtsi +@@ -206,6 +206,19 @@ syscon: system-control@1c00000 { + #size-cells = <1>; + ranges; + ++ sram_a2: sram@40000 { ++ compatible = "mmio-sram"; ++ reg = <0x00040000 0xc000>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges = <0 0x00040000 0xc000>; ++ ++ scpi_sram: scp-shmem@bc00 { ++ compatible = "arm,scp-shmem"; ++ reg = <0xbc00 0x200>; ++ }; ++ }; ++ + sram_c: sram@1d00000 { + compatible = "mmio-sram"; + reg = <0x01d00000 0x80000>; +diff --git a/arch/arm/boot/dts/allwinner/sunxi-h3-h5.dtsi b/arch/arm/boot/dts/allwinner/sunxi-h3-h5.dtsi +index a872fc586ab6..ebc64458084c 100644 +--- a/arch/arm/boot/dts/allwinner/sunxi-h3-h5.dtsi ++++ b/arch/arm/boot/dts/allwinner/sunxi-h3-h5.dtsi +@@ -120,6 +120,13 @@ de: display-engine { + status = "disabled"; + }; + ++ scpi_protocol: scpi { ++ compatible = "arm,scpi"; ++ mboxes = <&msgbox 2>, <&msgbox 3>; ++ mbox-names = "tx", "rx"; ++ shmem = <&scpi_sram>; ++ }; ++ + soc { + compatible = "simple-bus"; + #address-cells = <1>; +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi +index d3caf27b6a55..e02a3848003e 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi +@@ -86,6 +86,19 @@ syscon: system-control@1c00000 { + #size-cells = <1>; + ranges; + ++ sram_a2: sram@40000 { ++ compatible = "mmio-sram"; ++ reg = <0x00040000 0x14000>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges = <0 0x00040000 0x14000>; ++ ++ scpi_sram: scp-shmem@13c00 { ++ compatible = "arm,scp-shmem"; ++ reg = <0x13c00 0x200>; ++ }; ++ }; ++ + sram_c1: sram@18000 { + compatible = "mmio-sram"; + reg = <0x00018000 0x1c000>; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sunxi-h3-h5-Add-hdmi-sound-card.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sunxi-h3-h5-Add-hdmi-sound-card.patch new file mode 100644 index 000000000000..23ce3e3a83e8 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-dts-sunxi-h3-h5-Add-hdmi-sound-card.patch @@ -0,0 +1,48 @@ +From 797e9a51924977c4a8ed90d9f4c5c2af75c97dda Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sun, 9 May 2021 11:04:58 +0200 +Subject: ARM: dts: sunxi: h3/h5: Add hdmi sound card + +H3 and H5 support HDMI audio. Add a sound card node for it. + +Signed-off-by: Jernej Skrabec +--- + arch/arm/boot/dts/allwinner/sunxi-h3-h5.dtsi | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/arch/arm/boot/dts/allwinner/sunxi-h3-h5.dtsi b/arch/arm/boot/dts/allwinner/sunxi-h3-h5.dtsi +index 7df60515a903..0f19e7c00c50 100644 +--- a/arch/arm/boot/dts/allwinner/sunxi-h3-h5.dtsi ++++ b/arch/arm/boot/dts/allwinner/sunxi-h3-h5.dtsi +@@ -106,6 +106,20 @@ de: display-engine { + status = "disabled"; + }; + ++ sound_hdmi: sound_hdmi { ++ compatible = "allwinner,sun9i-a80-hdmi-audio", ++ "allwinner,sun8i-h3-hdmi-audio"; ++ status = "disabled"; ++ ++ codec { ++ sound-dai = <&hdmi>; ++ }; ++ ++ cpu { ++ sound-dai = <&i2s2>; ++ }; ++ }; ++ + soc { + compatible = "simple-bus"; + #address-cells = <1>; +@@ -818,6 +832,7 @@ csi: camera@1cb0000 { + }; + + hdmi: hdmi@1ee0000 { ++ #sound-dai-cells = <0>; + compatible = "allwinner,sun8i-h3-dw-hdmi", + "allwinner,sun8i-a83t-dw-hdmi"; + reg = <0x01ee0000 0x10000>; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-sunxi-Add-experimental-suspend-to-memory-implementation-for.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-sunxi-Add-experimental-suspend-to-memory-implementation-for.patch new file mode 100644 index 000000000000..f3407c758d89 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-sunxi-Add-experimental-suspend-to-memory-implementation-for.patch @@ -0,0 +1,88 @@ +From 64d4474e732197bc47adf374dd1dcf6bb24840e2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Wed, 23 Oct 2019 05:06:29 +0200 +Subject: ARM: sunxi: Add experimental suspend to memory implementation for + A83T + +This just ends with WFI, but all secondary CPUs will be shut down. + +Signed-off-by: Ondrej Jirman +--- + arch/arm/mach-sunxi/sunxi.c | 46 +++++++++++++++++++++++++++++++++++++ + 1 file changed, 46 insertions(+) + +diff --git a/arch/arm/mach-sunxi/sunxi.c b/arch/arm/mach-sunxi/sunxi.c +index e1b7945aac99..2d6b7b4528e9 100644 +--- a/arch/arm/mach-sunxi/sunxi.c ++++ b/arch/arm/mach-sunxi/sunxi.c +@@ -12,10 +12,13 @@ + #include + #include + #include ++#include + #include ++#include + + #include + #include ++#include + + static const char * const sunxi_board_dt_compat[] = { + "allwinner,sun4i-a10", +@@ -86,10 +89,53 @@ static const char * const sun8i_a83t_cntvoff_board_dt_compat[] = { + NULL, + }; + ++#ifdef CONFIG_PM_SLEEP ++static int sun8i_a83t_pm_valid(suspend_state_t state) ++{ ++ return state == PM_SUSPEND_MEM; ++} ++ ++static int sun8i_a83t_suspend_finish(unsigned long val) ++{ ++ // don't do much ++ cpu_do_idle(); ++ return 0; ++} ++ ++static int sun8i_a83t_pm_enter(suspend_state_t state) ++{ ++ switch (state) { ++ case PM_SUSPEND_MEM: ++ cpu_suspend(0, sun8i_a83t_suspend_finish); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static const struct platform_suspend_ops sun8i_a83t_pm_ops = { ++ .enter = sun8i_a83t_pm_enter, ++ .valid = sun8i_a83t_pm_valid, ++}; ++#define SUN8I_A83T_PM_OPS &sun8i_a83t_pm_ops ++#else ++#define SUN8I_A83T_PM_OPS NULL ++#endif ++ ++static void __init sun8i_a83t_init_machine(void) ++{ ++ suspend_set_ops(SUN8I_A83T_PM_OPS); ++ ++ of_platform_default_populate(NULL, NULL, NULL); ++} ++ + DT_MACHINE_START(SUN8I_A83T_CNTVOFF_DT, "Allwinner A83t board") + .init_early = sun8i_a83t_cntvoff_init, + .init_time = sun6i_timer_init, + .dt_compat = sun8i_a83t_cntvoff_board_dt_compat, ++ .init_machine = sun8i_a83t_init_machine, + MACHINE_END + + static const char * const sun9i_board_dt_compat[] = { +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-sunxi-Use-SCPI-to-send-suspend-message-to-SCP-on-A83T.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-sunxi-Use-SCPI-to-send-suspend-message-to-SCP-on-A83T.patch new file mode 100644 index 000000000000..f35af20790b6 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-sunxi-Use-SCPI-to-send-suspend-message-to-SCP-on-A83T.patch @@ -0,0 +1,54 @@ +From 2c268237b942725705a5de8365c4142d45a346b5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sat, 2 Nov 2019 15:21:04 +0100 +Subject: ARM: sunxi: Use SCPI to send suspend message to SCP on A83T + +We use undefined value of 3, to mean SUSPEND_SYSTEM. SCP should: + +- kill CPU0 +- kill cluster 0 +- shutdown power to both clusters (Linux MCPM doesn't do that) +... +- reverse all of the above on interrupt + +Signed-off-by: Ondrej Jirman +--- + arch/arm/mach-sunxi/sunxi.c | 15 +++++++++++++-- + 1 file changed, 13 insertions(+), 2 deletions(-) + +diff --git a/arch/arm/mach-sunxi/sunxi.c b/arch/arm/mach-sunxi/sunxi.c +index 2d6b7b4528e9..d4c25c33455d 100644 +--- a/arch/arm/mach-sunxi/sunxi.c ++++ b/arch/arm/mach-sunxi/sunxi.c +@@ -14,6 +14,7 @@ + #include + #include + #include ++#include + #include + + #include +@@ -97,8 +98,18 @@ static int sun8i_a83t_pm_valid(suspend_state_t state) + + static int sun8i_a83t_suspend_finish(unsigned long val) + { +- // don't do much +- cpu_do_idle(); ++ struct scpi_ops *scpi; ++ ++ scpi = get_scpi_ops(); ++ if (scpi && scpi->sys_set_power_state) { ++ //HACK: use invalid state to mean: suspend last CPU and the system ++ scpi->sys_set_power_state(3); ++ cpu_do_idle(); ++ } else { ++ // don't do much if scpi is not available ++ cpu_do_idle(); ++ } ++ + return 0; + } + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-sunxi-sunxi_cpu0_hotplug_support_set-is-not-supported-on-A8.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-sunxi-sunxi_cpu0_hotplug_support_set-is-not-supported-on-A8.patch new file mode 100644 index 000000000000..cd6627ce570e --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ARM-sunxi-sunxi_cpu0_hotplug_support_set-is-not-supported-on-A8.patch @@ -0,0 +1,27 @@ +From 6410bf1e86927b02deae0fe3ddb51d41347f555b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Wed, 23 Oct 2019 05:08:04 +0200 +Subject: ARM: sunxi: sunxi_cpu0_hotplug_support_set is not supported on A83T + +Signed-off-by: Ondrej Jirman +--- + arch/arm/mach-sunxi/mc_smp.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/arch/arm/mach-sunxi/mc_smp.c b/arch/arm/mach-sunxi/mc_smp.c +index 277f6aa8e6c2..ca237d50db4b 100644 +--- a/arch/arm/mach-sunxi/mc_smp.c ++++ b/arch/arm/mach-sunxi/mc_smp.c +@@ -146,6 +146,9 @@ static int sunxi_cpu_power_switch_set(unsigned int cpu, unsigned int cluster, + + static void sunxi_cpu0_hotplug_support_set(bool enable) + { ++ if (is_a83t) ++ return; ++ + if (enable) { + writel(CPU0_SUPPORT_HOTPLUG_MAGIC0, sram_b_smp_base); + writel(CPU0_SUPPORT_HOTPLUG_MAGIC1, sram_b_smp_base + 0x4); +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ASOC-sun9i-hdmi-audio-Initial-implementation.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ASOC-sun9i-hdmi-audio-Initial-implementation.patch new file mode 100644 index 000000000000..d9898d8e8946 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ASOC-sun9i-hdmi-audio-Initial-implementation.patch @@ -0,0 +1,233 @@ +From 0ab708fedde7fb0c6a3246c34cdc4490d7ce232a Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Tue, 10 Nov 2020 20:42:44 +0100 +Subject: ASOC: sun9i-hdmi-audio: Initial implementation + +This implements HDMI audio sound card which is used to enable HDMI audio +on all Allwinner SoCs with DW-HDMI core. First such SoC is A80, but it's +been used on plenty of others, like A64, A83t, H2+, H3, H5, H6, R40 and +V40. + +Signed-off-by: Jernej Skrabec +--- + sound/soc/sunxi/Kconfig | 8 ++ + sound/soc/sunxi/Makefile | 1 + + sound/soc/sunxi/sun9i-hdmi-audio.c | 178 +++++++++++++++++++++++++++++ + 3 files changed, 187 insertions(+) + create mode 100644 sound/soc/sunxi/sun9i-hdmi-audio.c + +diff --git a/sound/soc/sunxi/Kconfig b/sound/soc/sunxi/Kconfig +index f87b061f59b2..753c38c5d554 100644 +--- a/sound/soc/sunxi/Kconfig ++++ b/sound/soc/sunxi/Kconfig +@@ -76,6 +76,14 @@ config SND_SUN50I_DMIC + Say Y or M to add support for the DMIC audio block in the Allwinner + H6 and affiliated SoCs. + ++config SND_SUN9I_HDMI_AUDIO ++ tristate "Allwinner sun9i HDMI Audio Sound Card" ++ depends on OF ++ depends on SND_SUN4I_I2S ++ help ++ Say Y or M to add support for the HDMI Audio sound card for Allwinner ++ SoCs with DW-HDMI core. ++ + config SND_SUN8I_ADDA_PR_REGMAP + tristate + select REGMAP +diff --git a/sound/soc/sunxi/Makefile b/sound/soc/sunxi/Makefile +index 6131aea97efa..19800277105c 100644 +--- a/sound/soc/sunxi/Makefile ++++ b/sound/soc/sunxi/Makefile +@@ -8,3 +8,4 @@ obj-$(CONFIG_SND_SUN50I_CODEC_ANALOG) += sun50i-codec-analog.o + obj-$(CONFIG_SND_SUN8I_CODEC) += sun8i-codec.o + obj-$(CONFIG_SND_SUN8I_ADDA_PR_REGMAP) += sun8i-adda-pr-regmap.o + obj-$(CONFIG_SND_SUN50I_DMIC) += sun50i-dmic.o ++obj-$(CONFIG_SND_SUN9I_HDMI_AUDIO) += sun9i-hdmi-audio.o +diff --git a/sound/soc/sunxi/sun9i-hdmi-audio.c b/sound/soc/sunxi/sun9i-hdmi-audio.c +new file mode 100644 +index 000000000000..32eb6d9decd3 +--- /dev/null ++++ b/sound/soc/sunxi/sun9i-hdmi-audio.c +@@ -0,0 +1,178 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// ++// sun9i hdmi audio sound card ++// ++// Copyright (C) 2021 Jernej Skrabec ++ ++#include ++#include ++ ++#include ++#include ++ ++static int sun9i_hdmi_audio_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params) ++{ ++ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); ++ unsigned int mclk; ++ ++ mclk = params_rate(params) * 128; ++ ++ return snd_soc_dai_set_sysclk(snd_soc_rtd_to_cpu(rtd, 0), 0, mclk, ++ SND_SOC_CLOCK_OUT); ++} ++ ++static const struct snd_soc_ops sun9i_hdmi_audio_ops = { ++ .hw_params = sun9i_hdmi_audio_hw_params, ++}; ++ ++static int sun9i_hdmi_audio_dai_init(struct snd_soc_pcm_runtime *rtd) ++{ ++ int ret; ++ ++ /* TODO: switch to custom api once it's implemented in sun4i-i2s */ ++ ret = snd_soc_dai_set_tdm_slot(snd_soc_rtd_to_cpu(rtd, 0), 0, 0, 2, 32); ++ if (ret) { ++ dev_err(snd_soc_rtd_to_cpu(rtd, 0)->dev, ++ "setting tdm link slots failed\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int sun9i_hdmi_audio_parse_dai(struct device_node *node, ++ struct snd_soc_dai_link_component *dlc) ++{ ++ struct of_phandle_args args; ++ int ret; ++ ++ if (!node) ++ return 0; ++ ++ ret = of_parse_phandle_with_args(node, "sound-dai", ++ "#sound-dai-cells", 0, &args); ++ if (ret) ++ return ret; ++ ++ ret = snd_soc_get_dai_name(&args, &dlc->dai_name); ++ if (ret < 0) { ++ of_node_put(args.np); ++ ++ return ret; ++ } ++ ++ dlc->of_node = args.np; ++ ++ return 0; ++} ++ ++static int sun9i_hdmi_audio_probe(struct platform_device *pdev) ++{ ++ struct snd_soc_dai_link_component *dlc; ++ struct device *dev = &pdev->dev; ++ struct snd_soc_dai_link *link; ++ struct snd_soc_card *card; ++ struct device_node *child; ++ int ret; ++ ++ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); ++ if (!card) ++ return -ENOMEM; ++ ++ link = devm_kzalloc(dev, sizeof(*link), GFP_KERNEL); ++ if (!link) ++ return -ENOMEM; ++ ++ dlc = devm_kzalloc(dev, sizeof(*dlc) * 3, GFP_KERNEL); ++ if (!dlc) ++ return -ENOMEM; ++ ++ child = of_get_child_by_name(dev->of_node, "codec"); ++ if (!child) ++ return -ENODEV; ++ ++ ret = sun9i_hdmi_audio_parse_dai(child, &dlc[1]); ++ of_node_put(child); ++ if (ret) ++ return ret; ++ ++ child = of_get_child_by_name(dev->of_node, "cpu"); ++ if (!child) { ++ ret = -ENODEV; ++ goto out_err; ++ } ++ ++ ret = sun9i_hdmi_audio_parse_dai(child, &dlc[0]); ++ of_node_put(child); ++ if (ret) ++ goto out_err; ++ ++ dlc[2].of_node = dlc[0].of_node; ++ ++ platform_set_drvdata(pdev, card); ++ ++ link->cpus = &dlc[0]; ++ link->codecs = &dlc[1]; ++ link->platforms = &dlc[2]; ++ ++ link->num_cpus = 1; ++ link->num_codecs = 1; ++ link->num_platforms = 1; ++ ++ link->playback_only = 1; ++ ++ link->name = "SUN9I-HDMI"; ++ link->stream_name = "SUN9I-HDMI PCM"; ++ ++ link->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_CBS_CFS; ++ ++ link->ops = &sun9i_hdmi_audio_ops; ++ link->init = sun9i_hdmi_audio_dai_init; ++ ++ card->dai_link = link; ++ card->num_links = 1; ++ card->owner = THIS_MODULE; ++ card->dev = dev; ++ card->name = "sun9i-hdmi"; ++ ++ ret = devm_snd_soc_register_card(dev, card); ++ if (ret) ++ goto out_err; ++ ++ return 0; ++ ++out_err: ++ of_node_put(dlc[0].of_node); ++ of_node_put(dlc[1].of_node); ++ ++ return ret; ++} ++ ++static void sun9i_hdmi_audio_remove(struct platform_device *pdev) ++{ ++ struct snd_soc_card *card = platform_get_drvdata(pdev); ++ ++ of_node_put(card->dai_link->cpus->of_node); ++ of_node_put(card->dai_link->codecs->of_node); ++} ++ ++static const struct of_device_id sun9i_hdmi_audio_match[] = { ++ { .compatible = "allwinner,sun9i-a80-hdmi-audio" }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, sun9i_hdmi_audio_match); ++ ++static struct platform_driver sun9i_hdmi_audio_driver = { ++ .probe = sun9i_hdmi_audio_probe, ++ .remove = sun9i_hdmi_audio_remove, ++ .driver = { ++ .name = "sun9i-hdmi-audio", ++ .of_match_table = sun9i_hdmi_audio_match, ++ }, ++}; ++module_platform_driver(sun9i_hdmi_audio_driver); ++ ++MODULE_DESCRIPTION("sun9i HDMI Audio Sound Card"); ++MODULE_AUTHOR("Jernej Skrabec "); ++MODULE_LICENSE("GPL v2"); +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ASoC-ec25-New-codec-driver-for-the-EC25-modem.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ASoC-ec25-New-codec-driver-for-the-EC25-modem.patch new file mode 100644 index 000000000000..f5bbec78f5e2 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ASoC-ec25-New-codec-driver-for-the-EC25-modem.patch @@ -0,0 +1,154 @@ +From 1fa825de245c820af4b50883f14eed193a680a5e Mon Sep 17 00:00:00 2001 +From: Samuel Holland +Date: Fri, 25 Sep 2020 21:42:52 -0500 +Subject: ASoC: ec25: New codec driver for the EC25 modem + +The EC25 LTE modem has a PCM interface, and supports 16-bit PCM audio at +an 8 kHz or 16 kHz sameple rate. + +Suggested-by: Luca Weiss +Signed-off-by: Samuel Holland +--- + sound/soc/codecs/Kconfig | 3 ++ + sound/soc/codecs/Makefile | 2 + + sound/soc/codecs/ec25.c | 94 +++++++++++++++++++++++++++++++++++++++ + 3 files changed, 99 insertions(+) + create mode 100644 sound/soc/codecs/ec25.c + +diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig +index ee35f3aa5521..6d67a9fc5304 100644 +--- a/sound/soc/codecs/Kconfig ++++ b/sound/soc/codecs/Kconfig +@@ -1148,6 +1148,9 @@ config SND_SOC_HDMI_CODEC + select SND_PCM_IEC958 + select HDMI + ++config SND_SOC_EC25 ++ tristate "EC25 LTE module CODEC" ++ + config SND_SOC_ES7134 + tristate "Everest Semi ES7134 CODEC" + +diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile +index d7ad795603c1..7e12f3ae4f7d 100644 +--- a/sound/soc/codecs/Makefile ++++ b/sound/soc/codecs/Makefile +@@ -122,6 +122,7 @@ snd-soc-da7219-y := da7219.o da7219-aad.o + snd-soc-da732x-y := da732x.o + snd-soc-da9055-y := da9055.o + snd-soc-dmic-y := dmic.o ++snd-soc-ec25-y := ec25.o + snd-soc-es7134-y := es7134.o + snd-soc-es7241-y := es7241.o + snd-soc-es83xx-dsm-common-y := es83xx-dsm-common.o +@@ -539,6 +540,7 @@ obj-$(CONFIG_SND_SOC_DA7219) += snd-soc-da7219.o + obj-$(CONFIG_SND_SOC_DA732X) += snd-soc-da732x.o + obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o + obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o ++obj-$(CONFIG_SND_SOC_EC25) += snd-soc-ec25.o + obj-$(CONFIG_SND_SOC_ES7134) += snd-soc-es7134.o + obj-$(CONFIG_SND_SOC_ES7241) += snd-soc-es7241.o + obj-$(CONFIG_SND_SOC_ES83XX_DSM_COMMON) += snd-soc-es83xx-dsm-common.o +diff --git a/sound/soc/codecs/ec25.c b/sound/soc/codecs/ec25.c +new file mode 100644 +index 000000000000..4f9b6b06cce7 +--- /dev/null ++++ b/sound/soc/codecs/ec25.c +@@ -0,0 +1,94 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++ ++#include ++#include ++ ++static const struct snd_soc_dapm_widget ec25_dapm_widgets[] = { ++ SND_SOC_DAPM_OUTPUT("AOUT"), ++ SND_SOC_DAPM_INPUT("AIN"), ++}; ++ ++static const struct snd_soc_dapm_route ec25_dapm_routes[] = { ++ { "AOUT", NULL, "Playback" }, ++ { "AOUT", NULL, "Wideband Playback" }, ++ { "Capture", NULL, "AIN" }, ++ { "Wideband Capture", NULL, "AIN" }, ++}; ++ ++static const struct snd_soc_component_driver ec25_component_driver = { ++ .dapm_widgets = ec25_dapm_widgets, ++ .num_dapm_widgets = ARRAY_SIZE(ec25_dapm_widgets), ++ .dapm_routes = ec25_dapm_routes, ++ .num_dapm_routes = ARRAY_SIZE(ec25_dapm_routes), ++ .endianness = 1, ++}; ++ ++static struct snd_soc_dai_driver ec25_dais[] = { ++ { ++ .name = "ec25", ++ .capture = { ++ .stream_name = "Capture", ++ .channels_min = 1, ++ .channels_max = 1, ++ .rates = SNDRV_PCM_RATE_8000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ }, ++ .playback = { ++ .stream_name = "Playback", ++ .channels_min = 1, ++ .channels_max = 1, ++ .rates = SNDRV_PCM_RATE_8000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ }, ++ .symmetric_rate = 1, ++ .symmetric_channels = 1, ++ .symmetric_sample_bits = 1, ++ }, ++ { ++ .name = "ec25-wb", ++ .capture = { ++ .stream_name = "Wideband Capture", ++ .channels_min = 1, ++ .channels_max = 1, ++ .rates = SNDRV_PCM_RATE_16000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ }, ++ .playback = { ++ .stream_name = "Wideband Playback", ++ .channels_min = 1, ++ .channels_max = 1, ++ .rates = SNDRV_PCM_RATE_16000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ }, ++ .symmetric_rate = 1, ++ .symmetric_channels = 1, ++ .symmetric_sample_bits = 1, ++ }, ++}; ++ ++static int ec25_codec_probe(struct platform_device *pdev) ++{ ++ return devm_snd_soc_register_component(&pdev->dev, &ec25_component_driver, ++ ec25_dais, ARRAY_SIZE(ec25_dais)); ++} ++ ++static const struct of_device_id ec25_codec_of_match[] = { ++ { .compatible = "quectel,ec25", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, ec25_codec_of_match); ++ ++static struct platform_driver ec25_codec_driver = { ++ .driver = { ++ .name = "ec25", ++ .of_match_table = of_match_ptr(ec25_codec_of_match), ++ }, ++ .probe = ec25_codec_probe, ++}; ++ ++module_platform_driver(ec25_codec_driver); ++ ++MODULE_DESCRIPTION("ASoC ec25 driver"); ++MODULE_AUTHOR("Samuel Holland "); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:ec25"); +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ASoC-rockchip-Fix-doubling-of-playback-speed-after-system-sleep.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ASoC-rockchip-Fix-doubling-of-playback-speed-after-system-sleep.patch new file mode 100644 index 000000000000..93010e489442 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ASoC-rockchip-Fix-doubling-of-playback-speed-after-system-sleep.patch @@ -0,0 +1,45 @@ +From c8ae813c02fca7c7c43317e894a6516e09012bd8 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Mon, 15 Aug 2022 02:23:37 +0200 +Subject: ASoC: rockchip: Fix doubling of playback speed after system sleep + +There is some issue with CRU on RK3399 that can be reproduced by: + +- playing some audio and stopping the playback (so that runtime PM suspends I2S) +- putting system to deep sleep and resuming it (using mainline TF-A) +- playing some audio (audio plays at 2x the normal speed) + +If the audio is kept playing during system sleep cycle, the issue +does not manifest. Relevant registers in CRU are identical in bug/ +no-bug scenarios, so this patch is just touching the CRU registers +without actually changing anything in the end. Touching the registers +fixes the playback speed, though. + +Inspired by: https://github.com/djselbeck/linux/commit/cb4be5dec3fa18c4b344c11fed3fc57aa3bea424 + +Signed-off-by: Ondrej Jirman +--- + sound/soc/rockchip/rockchip_i2s.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c +index 4315da4a47c1..b6602eaea194 100644 +--- a/sound/soc/rockchip/rockchip_i2s.c ++++ b/sound/soc/rockchip/rockchip_i2s.c +@@ -114,6 +114,13 @@ static int i2s_runtime_resume(struct device *dev) + if (ret) + clk_disable_unprepare(i2s->mclk); + ++ if (ret == 0) { ++ unsigned long rate = clk_get_rate(i2s->mclk); ++ ++ clk_set_rate(i2s->mclk, rate - 1); ++ clk_set_rate(i2s->mclk, rate); ++ } ++ + return ret; + } + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ASoC-simple-card-Allow-to-define-pins-for-aux-jack-devices.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ASoC-simple-card-Allow-to-define-pins-for-aux-jack-devices.patch new file mode 100644 index 000000000000..415f1f5bb511 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ASoC-simple-card-Allow-to-define-pins-for-aux-jack-devices.patch @@ -0,0 +1,67 @@ +From 60f86341890692981eed6c4c538b46d41788ce56 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Tue, 14 May 2024 10:46:10 +0200 +Subject: ASoC: simple-card: Allow to define pins for aux jack devices + +This restores original behavior of jack pins on Pinephone, as it was +before upstreaming jack detection to codec driver. + +Signed-off-by: Ondrej Jirman +--- + sound/soc/generic/simple-card-utils.c | 40 +++++++++++++++++++++++++++ + 1 file changed, 40 insertions(+) + +diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c +index 32efb30c55d6..7df92e045c5c 100644 +--- a/sound/soc/generic/simple-card-utils.c ++++ b/sound/soc/generic/simple-card-utils.c +@@ -852,6 +852,46 @@ int simple_util_init_aux_jacks(struct simple_util_priv *priv, char *prefix) + continue; + + (void)snd_soc_component_set_jack(component, jack, NULL); ++ ++ /* add pins to the jack */ ++ int count = of_property_count_strings(card->dev->of_node, "simple-audio-card,jack-pins"); ++ if (count < 0) { ++ if (count != -EINVAL) ++ dev_warn(card->dev, "Invalid jack-pins property\n"); ++ continue; ++ } ++ if (count % 2) { ++ dev_warn(card->dev, "jack-pins property must have even number of strings\n"); ++ continue; ++ } ++ ++ for (int idx = 0; idx < count; idx += 2) { ++ const char *aux_dev_name, *pin_name; ++ u32 mask; ++ ++ ret = of_property_read_string_index(card->dev->of_node, "simple-audio-card,jack-pins", idx, &aux_dev_name); ++ if (ret < 0) ++ continue; ++ ret = of_property_read_string_index(card->dev->of_node, "simple-audio-card,jack-pins", idx + 1, &pin_name); ++ if (ret < 0) ++ continue; ++ ret = of_property_read_u32_index(card->dev->of_node, "simple-audio-card,jack-pins-mask", idx / 2, &mask); ++ if (ret < 0) { ++ dev_warn(card->dev, "jack-pins-mask for jack pin %s/%s can't be read\n", aux_dev_name, pin_name); ++ continue; ++ } ++ ++ if (!strcmp(aux_dev_name, component->name)) { ++ struct snd_soc_jack_pin *pin = devm_kzalloc(card->dev, sizeof(*pin), GFP_KERNEL); ++ ++ pin->pin = pin_name; ++ pin->mask = mask; ++ ++ dev_info(card->dev, "Adding jack %s pin %s (mask %d)\n", ++ aux_dev_name, pin_name, pin->mask); ++ snd_soc_jack_add_pins(jack, 1, pin); ++ } ++ } + } + return 0; + } +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ASoC-sun8i-codec-Add-debug-output-for-jack-detection.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ASoC-sun8i-codec-Add-debug-output-for-jack-detection.patch new file mode 100644 index 000000000000..b5958b5863fb --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ASoC-sun8i-codec-Add-debug-output-for-jack-detection.patch @@ -0,0 +1,129 @@ +From 5ceac3477d9df0651a27ac60f88c8d67f516d60b Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sat, 17 Feb 2024 01:13:39 +0100 +Subject: ASoC: sun8i-codec: Add debug output for jack detection + +Signed-off-by: Ondrej Jirman +--- + sound/soc/sunxi/sun8i-codec.c | 32 +++++++++++++++++++++++++++++++- + 1 file changed, 31 insertions(+), 1 deletion(-) + +diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c +index c5948b650777..6a9e147a6a1b 100644 +--- a/sound/soc/sunxi/sun8i-codec.c ++++ b/sound/soc/sunxi/sun8i-codec.c +@@ -9,6 +9,8 @@ + * Mylène Josserand + */ + ++#define DEBUG ++ + #include + #include + #include +@@ -234,6 +236,7 @@ struct sun8i_codec { + int jack_status; + int jack_type; + int jack_last_sample; ++ unsigned jack_last_btn; + ktime_t jack_hbias_ready; + struct mutex jack_mutex; + int last_hmic_irq; +@@ -1344,6 +1347,8 @@ static void sun8i_codec_set_hmic_bias(struct sun8i_codec *scodec, bool enable) + + snd_soc_dapm_sync(dapm); + ++ dev_dbg(scodec->component->dev, "HMIC bias %s\n", enable ? "on" : "off"); ++ + regmap_update_bits(scodec->regmap, SUN8I_HMIC_CTRL1, + irq_mask, enable ? irq_mask : 0); + } +@@ -1352,6 +1357,7 @@ static void sun8i_codec_jack_work(struct work_struct *work) + { + struct sun8i_codec *scodec = container_of(work, struct sun8i_codec, + jack_work.work); ++ struct device *dev = scodec->component->dev; + unsigned int mdata; + int type; + +@@ -1362,6 +1368,7 @@ static void sun8i_codec_jack_work(struct work_struct *work) + return; + + scodec->jack_last_sample = -1; ++ scodec->jack_last_btn = 0; + + if (scodec->jack_type & SND_JACK_MICROPHONE) { + /* +@@ -1419,6 +1426,8 @@ static void sun8i_codec_jack_work(struct work_struct *work) + + snd_soc_jack_report(scodec->jack, type, scodec->jack_type); + scodec->jack_status = SUN8I_JACK_STATUS_CONNECTED; ++ ++ dev_dbg(dev, "jack: plug-in reported\n"); + } else if (scodec->jack_status == SUN8I_JACK_STATUS_CONNECTED) { + if (scodec->last_hmic_irq != SUN8I_HMIC_STS_JACK_OUT_IRQ_ST) + return; +@@ -1428,14 +1437,18 @@ static void sun8i_codec_jack_work(struct work_struct *work) + sun8i_codec_set_hmic_bias(scodec, false); + + snd_soc_jack_report(scodec->jack, 0, scodec->jack_type); ++ ++ dev_dbg(dev, "jack: plug-out reported\n"); + } + } + + static irqreturn_t sun8i_codec_jack_irq(int irq, void *dev_id) + { + struct sun8i_codec *scodec = dev_id; ++ struct device *dev = scodec->component->dev; + int type = SND_JACK_HEADSET; + unsigned int status, value; ++ unsigned btn_chg = 0; + + guard(mutex)(&scodec->jack_mutex); + +@@ -1447,6 +1460,8 @@ static irqreturn_t sun8i_codec_jack_irq(int irq, void *dev_id) + * 100ms after each interrupt.. + */ + if (status & BIT(SUN8I_HMIC_STS_JACK_OUT_IRQ_ST)) { ++ dev_dbg(dev, "jack: irq plug-out\n"); ++ + /* + * Out interrupt has priority over in interrupt so that if + * we get both, we assume the disconnected state, which is +@@ -1456,6 +1471,8 @@ static irqreturn_t sun8i_codec_jack_irq(int irq, void *dev_id) + mod_delayed_work(system_power_efficient_wq, &scodec->jack_work, + msecs_to_jiffies(100)); + } else if (status & BIT(SUN8I_HMIC_STS_JACK_IN_IRQ_ST)) { ++ dev_dbg(dev, "jack: irq plug-in\n"); ++ + scodec->last_hmic_irq = SUN8I_HMIC_STS_JACK_IN_IRQ_ST; + mod_delayed_work(system_power_efficient_wq, &scodec->jack_work, + msecs_to_jiffies(100)); +@@ -1489,9 +1506,22 @@ static irqreturn_t sun8i_codec_jack_irq(int irq, void *dev_id) + * samples are identical. + */ + if (scodec->jack_last_sample >= 0 && +- scodec->jack_last_sample == value) ++ scodec->jack_last_sample == value) { + snd_soc_jack_report(scodec->jack, type, + scodec->jack_type); ++ btn_chg = (scodec->jack_last_btn ^ type) & SUN8I_CODEC_BUTTONS; ++ scodec->jack_last_btn = type; ++ } ++ ++ if (btn_chg & SND_JACK_BTN_0) ++ dev_dbg(dev, "jack: key_%spress BTN_0 (%#x)\n", ++ type & SND_JACK_BTN_0 ? "" : "de", value); ++ if (btn_chg & SND_JACK_BTN_1) ++ dev_dbg(dev, "jack: key_%spress BTN_1 (%#x)\n", ++ type & SND_JACK_BTN_1 ? "" : "de", value); ++ if (btn_chg & SND_JACK_BTN_2) ++ dev_dbg(dev, "jack: key_%spress BTN_2 (%#x)\n", ++ type & SND_JACK_BTN_2 ? "" : "de", value); + + scodec->jack_last_sample = value; + } +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ASoC-sun8i-codec-Allow-the-jack-type-to-be-set-via-device-tree.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ASoC-sun8i-codec-Allow-the-jack-type-to-be-set-via-device-tree.patch new file mode 100644 index 000000000000..c78992b5cd8c --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ASoC-sun8i-codec-Allow-the-jack-type-to-be-set-via-device-tree.patch @@ -0,0 +1,47 @@ +From 6badb648bf732dd8480424ab5ddbeb0f1c9736a8 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sat, 2 Mar 2024 14:55:31 +0100 +Subject: ASoC: sun8i-codec: Allow the jack type to be set via device tree + +This makes the codec's jack detection functionality usable from within +simple-sound-card. + +Signed-off-by: Ondrej Jirman +--- + sound/soc/sunxi/sun8i-codec.c | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c +index 8c645e04d571..838880cf5bd4 100644 +--- a/sound/soc/sunxi/sun8i-codec.c ++++ b/sound/soc/sunxi/sun8i-codec.c +@@ -1563,6 +1563,18 @@ static void sun8i_codec_disable_jack_detect(struct snd_soc_component *component) + scodec->jack = NULL; + } + ++static int sun8i_codec_component_get_jack_type(struct snd_soc_component *component) ++{ ++ struct device_node *node = component->dev->of_node; ++ ++ if (of_property_match_string(node, "jack-type", "headset") >= 0) ++ return SND_JACK_HEADSET | SUN8I_CODEC_BUTTONS; ++ else if (of_property_match_string(node, "jack-type", "headphone") >= 0) ++ return SND_JACK_HEADPHONE; ++ ++ return 0; ++} ++ + static int sun8i_codec_component_set_jack(struct snd_soc_component *component, + struct snd_soc_jack *jack, void *data) + { +@@ -1584,6 +1596,7 @@ static const struct snd_soc_component_driver sun8i_soc_component = { + .dapm_routes = sun8i_codec_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(sun8i_codec_dapm_routes), + .set_jack = sun8i_codec_component_set_jack, ++ .get_jack_type = sun8i_codec_component_get_jack_type, + .probe = sun8i_codec_component_probe, + .idle_bias_on = 1, + .suspend_bias_off = 1, +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ASoC-sun8i-codec-Set-jack_type-from-DT-in-probe.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ASoC-sun8i-codec-Set-jack_type-from-DT-in-probe.patch new file mode 100644 index 000000000000..0890c58da722 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ASoC-sun8i-codec-Set-jack_type-from-DT-in-probe.patch @@ -0,0 +1,48 @@ +From bcd4fc8b4628893abdc7738afb815887ac8951d5 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Tue, 26 Mar 2024 17:38:38 +0100 +Subject: ASoC: sun8i-codec: Set jack_type from DT in probe + +Jack type needs to be set in probe so that it can be used later on. + +Signed-off-by: Ondrej Jirman +--- + sound/soc/sunxi/sun8i-codec.c | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c +index 6a9e147a6a1b..b195ddc5d5cf 100644 +--- a/sound/soc/sunxi/sun8i-codec.c ++++ b/sound/soc/sunxi/sun8i-codec.c +@@ -1602,14 +1602,9 @@ static void sun8i_codec_disable_jack_detect(struct snd_soc_component *component) + + static int sun8i_codec_component_get_jack_type(struct snd_soc_component *component) + { +- struct device_node *node = component->dev->of_node; +- +- if (of_property_match_string(node, "jack-type", "headset") >= 0) +- return SND_JACK_HEADSET | SUN8I_CODEC_BUTTONS; +- else if (of_property_match_string(node, "jack-type", "headphone") >= 0) +- return SND_JACK_HEADPHONE; ++ struct sun8i_codec *scodec = snd_soc_component_get_drvdata(component); + +- return 0; ++ return scodec->jack_type; + } + + static int sun8i_codec_component_set_jack(struct snd_soc_component *component, +@@ -1671,6 +1666,11 @@ static int sun8i_codec_probe(struct platform_device *pdev) + + platform_set_drvdata(pdev, scodec); + ++ if (of_property_match_string(pdev->dev.of_node, "jack-type", "headset") >= 0) ++ scodec->jack_type = SND_JACK_HEADSET | SUN8I_CODEC_BUTTONS; ++ else if (of_property_match_string(pdev->dev.of_node, "jack-type", "headphone") >= 0) ++ scodec->jack_type = SND_JACK_HEADPHONE; ++ + if (scodec->quirks->bus_clock) { + scodec->clk_bus = devm_clk_get(&pdev->dev, "bus"); + if (IS_ERR(scodec->clk_bus)) { +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/ASoC-sun8i-codec-define-button-keycodes.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/ASoC-sun8i-codec-define-button-keycodes.patch new file mode 100644 index 000000000000..0abda11785ec --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/ASoC-sun8i-codec-define-button-keycodes.patch @@ -0,0 +1,31 @@ +From 14100a8e3e83aeb304a3f136eb5aad44b7026feb Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Fri, 23 Feb 2024 01:58:40 +0100 +Subject: ASoC: sun8i-codec: define button keycodes + +This is likely not upstreamable, but simple. :) +--- + sound/soc/sunxi/sun8i-codec.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c +index 838880cf5bd4..c5948b650777 100644 +--- a/sound/soc/sunxi/sun8i-codec.c ++++ b/sound/soc/sunxi/sun8i-codec.c +@@ -1541,6 +1541,13 @@ static int sun8i_codec_enable_jack_detect(struct snd_soc_component *component, + if (ret) + return ret; + ++ if (jack->jack->type & SND_JACK_MICROPHONE) { ++ snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); ++ snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOLUMEUP); ++ snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN); ++ snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOICECOMMAND); ++ } ++ + return 0; + } + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/Add-README.md-with-information-and-u-boot-patches.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/Add-README.md-with-information-and-u-boot-patches.patch new file mode 100644 index 000000000000..cb947ec3c214 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/Add-README.md-with-information-and-u-boot-patches.patch @@ -0,0 +1,215 @@ +From 71cc8805841a3e8e908770977756c51c555ac801 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Tue, 22 Nov 2016 01:01:49 +0100 +Subject: Add README.md with information and u-boot patches + +Signed-off-by: Ondrej Jirman +--- + ...Fix-PLL1-setup-to-never-use-dividers.patch | 33 ++++ + README.md | 154 ++++++++++++++++++ + 2 files changed, 187 insertions(+) + create mode 100644 0001-sunxi-h3-Fix-PLL1-setup-to-never-use-dividers.patch + create mode 100644 README.md + +diff --git a/0001-sunxi-h3-Fix-PLL1-setup-to-never-use-dividers.patch b/0001-sunxi-h3-Fix-PLL1-setup-to-never-use-dividers.patch +new file mode 100644 +index 000000000000..2b892e805a2b +--- /dev/null ++++ b/0001-sunxi-h3-Fix-PLL1-setup-to-never-use-dividers.patch +@@ -0,0 +1,33 @@ ++From 7f5071f906f79bdc99d6b4b0ccf0cb280abe740b Mon Sep 17 00:00:00 2001 ++From: Ondrej Jirman ++Date: Tue, 20 Dec 2016 11:25:12 +0100 ++Subject: [PATCH] sunxi: h3: Fix PLL1 setup to never use dividers ++ ++Kernel would lower the divider on first CLK change and cause the ++lock up. ++--- ++ arch/arm/mach-sunxi/clock_sun6i.c | 7 +++---- ++ 1 file changed, 3 insertions(+), 4 deletions(-) ++ ++diff --git a/arch/arm/mach-sunxi/clock_sun6i.c b/arch/arm/mach-sunxi/clock_sun6i.c ++index 50fb302a19..91aa2a0478 100644 ++--- a/arch/arm/mach-sunxi/clock_sun6i.c +++++ b/arch/arm/mach-sunxi/clock_sun6i.c ++@@ -94,11 +94,10 @@ void clock_set_pll1(unsigned int clk) ++ int k = 1; ++ int m = 1; ++ ++- if (clk > 1152000000) { ++- k = 2; ++- } else if (clk > 768000000) { +++ if (clk >= 1368000000) { ++ k = 3; ++- m = 2; +++ } else if (clk >= 768000000) { +++ k = 2; ++ } ++ ++ /* Switch to 24MHz clock while changing PLL1 */ ++-- ++2.11.0 ++ +diff --git a/README.md b/README.md +new file mode 100644 +index 000000000000..74c4c16416d3 +--- /dev/null ++++ b/README.md +@@ -0,0 +1,154 @@ ++Mainline linux kernel for Orange Pi PC/PC2/PC3/One, TBS A711, PinePhone (Pro), PocketBook Touch Lux 3 ++------------------------------------------------------------------------------------------------------ ++ ++This kernel tree is meant for: ++ ++- Orange Pi One ++- Orange Pi PC ++- Orange Pi PC 2 ++- Orange Pi 3 ++- PinePhone 1.0, 1.1 and 1.2(a/b) ++- TBS A711 Tablet ++- PocketBook Touch Lux 3 ++- Pinebook Pro ++- Pinephone Pro ++ ++Features in addition to mainline: ++ ++- [Orange Pi One/PC/PC2] More aggressive OPPs for CPU ++- [All] Mark one of DRM planes as a cursor plane, speeding up Xorg based desktop with modesetting driver ++- [Orange Pi One/PC/PC2] Configure on-board micro-switches to perform system power off function ++- [Orange Pi One/PC/PC2/3] HDMI audio ++- [Orange Pi 3] Ethernet ++- [TBS A711] HM5065 (back camera) / GC2145 (front camera) ++- [PinePhone] WiFi, Bluetooth, Audio, Modem power, HDMI out over USB-C, USB-C support, cameras, PMIC improvements, power management, fixes here and there ++- [PocketBook Touch Lux 3] Display and Touchscreen support ++- [Pinephone Pro] Everything ++ ++Pre-built u-boot and kernels are available at https://xff.cz/kernels/ ++ ++You may need some firmware files for some part of the functionality. Those are ++available at: https://megous.com/git/linux-firmware ++ ++If you want to reproduce my pre-built kernels exactly, you'll need to uncomment ++CONFIG_EXTRA_FIRMWARE_DIR and CONFIG_EXTRA_FIRMWARE in the defconfigs, and ++point CONFIG_EXTRA_FIRMWARE_DIR to a directory on your computer where the ++clone of https://megous.com/git/linux-firmware resides. ++ ++You can also leave those two config options commented out, and copy the contents ++of https://megous.com/git/linux-firmware to /lib/firmware/ on the target device. ++ ++You can use this kernel to run a desktop environment on Orange Pi SBCs, ++Arch Linux on your Pinephone, or to have a completely opensource OS on ++a Pocketbook e-ink book reader. ++ ++Have fun! ++ ++ ++Build instructions ++------------------ ++ ++These are rudimentary instructions and you need to understand what you're doing. ++These are just core steps required to build the ATF/u-boot/kernel. Downloading, ++verifying, renaming to correct directories is not described or mentioned. You ++should be able to infer missing necessary steps yourself for your particular needs. ++ ++Get necessary toolchains from: ++ ++- https://releases.linaro.org/components/toolchain/binaries/latest/aarch64-linux-gnu/ for 64bit Orange Pi PC2 and Orange Pi 3, PinePhone ++- https://releases.linaro.org/components/toolchain/binaries/latest/arm-linux-gnueabihf/ for 32bit Orange Pis, Pocketbook, TBS tablet ++ ++Extract toolchains and prepare the environment: ++ ++ CWD=`pwd` ++ OUT=$CWD/builds ++ SRC=$CWD/u-boot ++ export PATH="$PATH:$CWD/Toolchains/arm/bin:$CWD/Toolchains/aarch64/bin" ++ ++For Orange Pi PC2, Orange Pi 3 or PinePhone: ++ ++ export CROSS_COMPILE=aarch64-linux-gnu- ++ export KBUILD_OUTPUT=$OUT/.tmp/uboot-pc2 ++ rm -rf "$KBUILD_OUTPUT" ++ mkdir -p $KBUILD_OUTPUT $OUT/pc2 ++ ++Get and build ATF from https://github.com/ARM-software/arm-trusted-firmware: ++ ++ make -C "$CWD/arm-trusted-firmware" PLAT=sun50i_a64 DEBUG=1 bl31 ++ cp "$CWD/arm-trusted-firmware/build/sun50i_a64/debug/bl31.bin" "$KBUILD_OUTPUT" ++ ++Use sun50i_a64 for Orange Pi PC2 or PinePhone and sun50i_h6 for Orange Pi 3. ++ ++Build u-boot from https://megous.com/git/u-boot/ (opi-v2020.04 branch) with appropriate ++defconfig (orangepi_one_defconfig, orangepi_pc2_defconfig, orangepi_pc_defconfig, orangepi_3_defconfig, tbs_a711_defconfig, pinephone_defconfig). ++ ++My u-boot branch already has all the necessary patches integrated and is configured for quick u-boot/kernel startup. ++ ++ make -C u-boot orangepi_pc2_defconfig ++ make -C u-boot -j5 ++ ++ cp $KBUILD_OUTPUT/.config $OUT/pc2/uboot.config ++ cat $KBUILD_OUTPUT/{spl/sunxi-spl.bin,u-boot.itb} > $OUT/pc2/uboot.bin ++ ++Get kernel from this repository and checkout the latest orange-pi-5.18 branch. ++ ++Build the kernel for 64-bit boards: ++ ++ export ARCH=arm64 ++ export CROSS_COMPILE=aarch64-linux-gnu- ++ export KBUILD_OUTPUT=$OUT/.tmp/linux-arm64 ++ mkdir -p $KBUILD_OUTPUT $OUT/pc2 ++ ++ make -C linux orangepi_defconfig ++ # or make -C linux pocketbook_touch_lux_3_defconfig ++ # or make -C linux tbs_a711_defconfig ++ make -C linux -j5 clean ++ make -C linux -j5 Image dtbs ++ ++ cp -f $KBUILD_OUTPUT/arch/arm64/boot/Image $OUT/pc2/ ++ cp -f $KBUILD_OUTPUT/.config $OUT/pc2/linux.config ++ cp -f $KBUILD_OUTPUT/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-pc2.dtb $OUT/pc2/board.dtb ++ ++Build the kernel for 32-bit boards: ++ ++ export ARCH=arm ++ export CROSS_COMPILE=arm-linux-gnueabihf- ++ export KBUILD_OUTPUT=$OUT/.tmp/linux-arm ++ mkdir -p $KBUILD_OUTPUT $OUT/pc ++ ++ make orangepi_defconfig ++ # or make pinephone_defconfig ++ make -C linux orangepi_defconfig ++ make -C linux -j5 clean ++ make -C linux -j5 zImage dtbs ++ ++ cp -f $KBUILD_OUTPUT/arch/arm/boot/zImage $OUT/pc/ ++ cp -f $KBUILD_OUTPUT/.config $OUT/pc/linux.config ++ cp -f $KBUILD_OUTPUT/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dtb $OUT/pc/board.dtb ++ # Or use sun8i-h3-orangepi-one.dtb for Orange Pi One ++ ++ ++PinePhone ++--------- ++ ++I don't run u-boot on PinePhone, so my pre-built kernel packages don't come ++with u-boot built for PinePhone. ++ ++ ++Kernel lockup issues ++-------------------- ++ ++*If you're getting lockups on boot or later during thermal regulation, ++you're missing an u-boot patch.* ++ ++This patch is necessary to run this kernel! ++ ++These lockups are caused by improper NKMP clock factors selection ++in u-boot for PLL_CPUX. (M divider should not be used. P divider ++should be used only for frequencies below 240MHz.) ++ ++This patch for u-boot fixes it: ++ ++ 0001-sunxi-h3-Fix-PLL1-setup-to-never-use-dividers.patch ++ ++Kernel side is already fixed in this kernel tree. +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/Add-support-for-my-private-Sapomat-device.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/Add-support-for-my-private-Sapomat-device.patch new file mode 100644 index 000000000000..ed1ab0785c87 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/Add-support-for-my-private-Sapomat-device.patch @@ -0,0 +1,66 @@ +From 072caacd4e78332b3de5ed7621ded977eeb6c5e5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Fri, 18 Aug 2017 13:56:06 +0200 +Subject: Add support for my private Sapomat device + +--- + arch/arm/boot/dts/allwinner/Makefile | 1 + + .../sun8i-h3-orangepi-pc-sapomat.dts | 34 +++++++++++++++++++ + 2 files changed, 35 insertions(+) + create mode 100644 arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-pc-sapomat.dts + +diff --git a/arch/arm/boot/dts/allwinner/Makefile b/arch/arm/boot/dts/allwinner/Makefile +index 48666f73e638..1356e9553bd6 100644 +--- a/arch/arm/boot/dts/allwinner/Makefile ++++ b/arch/arm/boot/dts/allwinner/Makefile +@@ -239,6 +239,7 @@ dtb-$(CONFIG_MACH_SUN8I) += \ + sun8i-h3-orangepi-lite.dtb \ + sun8i-h3-orangepi-one.dtb \ + sun8i-h3-orangepi-pc.dtb \ ++ sun8i-h3-orangepi-pc-sapomat.dtb \ + sun8i-h3-orangepi-pc-plus.dtb \ + sun8i-h3-orangepi-plus.dtb \ + sun8i-h3-orangepi-plus2e.dtb \ +diff --git a/arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-pc-sapomat.dts b/arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-pc-sapomat.dts +new file mode 100644 +index 000000000000..55c82d5fb63f +--- /dev/null ++++ b/arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-pc-sapomat.dts +@@ -0,0 +1,34 @@ ++#include ++#include ++#include "sun8i-h3-orangepi-pc.dts" ++ ++/ { ++ model = "Xunlong Orange Pi PC Sapomat"; ++ ++ sapomat_gpio_keys { ++ compatible = "gpio-keys-polled"; ++ poll-interval = <50>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&sapomat_btn_pins>; ++ ++ red_btn { ++ label = "Red Button"; ++ linux,code = ; ++ gpios = <&pio 2 4 GPIO_ACTIVE_LOW>; /* PC4 */ ++ }; ++ ++ green_btn { ++ label = "Green Button"; ++ linux,code = ; ++ gpios = <&pio 2 7 GPIO_ACTIVE_LOW>; /* PC7 */ ++ }; ++ }; ++}; ++ ++&pio { ++ sapomat_btn_pins: btn_pins@0 { ++ pins = "PC4", "PC7"; ++ function = "gpio_in"; ++ bias-pull-up; ++ }; ++}; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/Defconfigs-for-all-my-devices.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/Defconfigs-for-all-my-devices.patch new file mode 100644 index 000000000000..4701a17f799d --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/Defconfigs-for-all-my-devices.patch @@ -0,0 +1,4002 @@ +From b5cd928a177e0a489156d5634dc9b10b6d568ab6 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Mon, 16 Sep 2019 00:35:50 +0200 +Subject: Defconfigs for all my devices + +Signed-off-by: Ondrej Jirman +--- + arch/arm/configs/orangepi_defconfig | 715 ++++++++++++ + .../configs/pocketbook_touch_lux_3_defconfig | 416 +++++++ + arch/arm/configs/tbs_a711_defconfig | 524 +++++++++ + arch/arm64/configs/orangepi_defconfig | 1037 +++++++++++++++++ + arch/arm64/configs/pinephone_defconfig | 650 +++++++++++ + .../configs/pinephone_multidist_defconfig | 600 ++++++++++ + 6 files changed, 3942 insertions(+) + create mode 100644 arch/arm/configs/orangepi_defconfig + create mode 100644 arch/arm/configs/pocketbook_touch_lux_3_defconfig + create mode 100644 arch/arm/configs/tbs_a711_defconfig + create mode 100644 arch/arm64/configs/orangepi_defconfig + create mode 100644 arch/arm64/configs/pinephone_defconfig + create mode 100644 arch/arm64/configs/pinephone_multidist_defconfig + +diff --git a/arch/arm/configs/orangepi_defconfig b/arch/arm/configs/orangepi_defconfig +new file mode 100644 +index 000000000000..b76f1dc0eba9 +--- /dev/null ++++ b/arch/arm/configs/orangepi_defconfig +@@ -0,0 +1,715 @@ ++CONFIG_KERNEL_LZ4=y ++CONFIG_SYSVIPC=y ++CONFIG_POSIX_MQUEUE=y ++CONFIG_NO_HZ=y ++CONFIG_HIGH_RES_TIMERS=y ++CONFIG_BPF_SYSCALL=y ++# CONFIG_BPF_UNPRIV_DEFAULT_OFF is not set ++CONFIG_BSD_PROCESS_ACCT=y ++CONFIG_BSD_PROCESS_ACCT_V3=y ++CONFIG_TASKSTATS=y ++CONFIG_TASK_DELAY_ACCT=y ++CONFIG_TASK_XACCT=y ++CONFIG_TASK_IO_ACCOUNTING=y ++CONFIG_MEMCG=y ++CONFIG_BLK_CGROUP=y ++CONFIG_RT_GROUP_SCHED=y ++CONFIG_CGROUP_PIDS=y ++CONFIG_CPUSETS=y ++# CONFIG_PROC_PID_CPUSET is not set ++CONFIG_CGROUP_DEVICE=y ++CONFIG_CGROUP_BPF=y ++CONFIG_USER_NS=y ++CONFIG_SCHED_AUTOGROUP=y ++# CONFIG_RD_BZIP2 is not set ++# CONFIG_RD_LZMA is not set ++# CONFIG_RD_XZ is not set ++# CONFIG_RD_LZO is not set ++CONFIG_BOOT_CONFIG=y ++CONFIG_PERF_EVENTS=y ++CONFIG_ARCH_BCM=y ++CONFIG_ARCH_BCM2835=y ++CONFIG_ARCH_SUNXI=y ++# CONFIG_MACH_SUN9I is not set ++CONFIG_ARM_LPAE=y ++CONFIG_ARM_ERRATA_814220=y ++CONFIG_SMP=y ++CONFIG_SCHED_MC=y ++CONFIG_MCPM=y ++CONFIG_NR_CPUS=8 ++CONFIG_HOTPLUG_CPU=y ++CONFIG_HZ_300=y ++CONFIG_HIGHMEM=y ++# CONFIG_ARM_MODULE_PLTS is not set ++CONFIG_ARCH_FORCE_MAX_ORDER=11 ++CONFIG_ZBOOT_ROM_TEXT=0 ++CONFIG_ZBOOT_ROM_BSS=0 ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_STAT=y ++CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y ++CONFIG_CPU_FREQ_GOV_POWERSAVE=y ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++CONFIG_CPU_FREQ_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y ++CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y ++CONFIG_CPUFREQ_DT=y ++CONFIG_ARM_ALLWINNER_SUN50I_CPUFREQ_NVMEM=y ++CONFIG_ARM_RASPBERRYPI_CPUFREQ=y ++CONFIG_CPU_IDLE=y ++CONFIG_CPU_IDLE_GOV_LADDER=y ++CONFIG_ARM_CPUIDLE=y ++CONFIG_ARM_PSCI_CPUIDLE=y ++CONFIG_VFP=y ++CONFIG_NEON=y ++CONFIG_KERNEL_MODE_NEON=y ++# CONFIG_SUSPEND is not set ++CONFIG_PM=y ++CONFIG_APM_EMULATION=y ++CONFIG_WQ_POWER_EFFICIENT_DEFAULT=y ++CONFIG_JUMP_LABEL=y ++# CONFIG_STACKPROTECTOR is not set ++CONFIG_MODULES=y ++CONFIG_BLK_DEV_THROTTLING=y ++CONFIG_BLK_WBT=y ++CONFIG_BLK_CGROUP_IOLATENCY=y ++CONFIG_BLK_CGROUP_IOCOST=y ++# CONFIG_MQ_IOSCHED_KYBER is not set ++CONFIG_CMA=y ++CONFIG_CMA_DEBUGFS=y ++CONFIG_CMA_AREAS=7 ++CONFIG_NET=y ++CONFIG_PACKET=y ++CONFIG_PACKET_DIAG=y ++CONFIG_UNIX=y ++CONFIG_UNIX_DIAG=y ++CONFIG_NET_KEY=y ++CONFIG_INET=y ++CONFIG_IP_MULTICAST=y ++CONFIG_IP_ADVANCED_ROUTER=y ++CONFIG_IP_MULTIPLE_TABLES=y ++CONFIG_IP_ROUTE_MULTIPATH=y ++CONFIG_IP_ROUTE_VERBOSE=y ++CONFIG_IP_MROUTE=y ++CONFIG_IP_MROUTE_MULTIPLE_TABLES=y ++CONFIG_SYN_COOKIES=y ++CONFIG_INET_UDP_DIAG=y ++CONFIG_INET_RAW_DIAG=y ++CONFIG_INET_DIAG_DESTROY=y ++CONFIG_IPV6_ROUTER_PREF=y ++CONFIG_IPV6_ROUTE_INFO=y ++CONFIG_IPV6_OPTIMISTIC_DAD=y ++CONFIG_IPV6_MIP6=y ++# CONFIG_IPV6_SIT is not set ++CONFIG_IPV6_MULTIPLE_TABLES=y ++CONFIG_IPV6_SUBTREES=y ++CONFIG_IPV6_MROUTE=y ++CONFIG_IPV6_MROUTE_MULTIPLE_TABLES=y ++CONFIG_NETFILTER=y ++CONFIG_BRIDGE_NETFILTER=y ++CONFIG_NETFILTER_NETLINK_ACCT=y ++CONFIG_NETFILTER_NETLINK_QUEUE=y ++CONFIG_NETFILTER_NETLINK_LOG=y ++CONFIG_NF_CONNTRACK=y ++CONFIG_NF_CONNTRACK_MARK=y ++CONFIG_NF_CONNTRACK_FTP=y ++CONFIG_NF_CONNTRACK_H323=y ++CONFIG_NF_CONNTRACK_IRC=y ++CONFIG_NF_CONNTRACK_NETBIOS_NS=y ++CONFIG_NF_CONNTRACK_SNMP=y ++CONFIG_NF_CONNTRACK_PPTP=y ++CONFIG_NF_CONNTRACK_SIP=y ++CONFIG_NF_CONNTRACK_TFTP=y ++CONFIG_NF_CT_NETLINK=y ++CONFIG_NETFILTER_NETLINK_GLUE_CT=y ++CONFIG_NF_TABLES=y ++CONFIG_NF_TABLES_INET=y ++CONFIG_NF_TABLES_NETDEV=y ++CONFIG_NFT_NUMGEN=y ++CONFIG_NFT_CT=y ++CONFIG_NFT_CONNLIMIT=y ++CONFIG_NFT_LOG=y ++CONFIG_NFT_LIMIT=y ++CONFIG_NFT_MASQ=y ++CONFIG_NFT_REDIR=y ++CONFIG_NFT_NAT=y ++CONFIG_NFT_TUNNEL=y ++CONFIG_NFT_QUEUE=y ++CONFIG_NFT_QUOTA=y ++CONFIG_NFT_REJECT=y ++CONFIG_NFT_HASH=y ++CONFIG_NFT_FIB_INET=y ++CONFIG_NFT_SOCKET=y ++CONFIG_NFT_OSF=y ++CONFIG_NFT_TPROXY=y ++CONFIG_NFT_DUP_NETDEV=y ++CONFIG_NFT_FWD_NETDEV=y ++CONFIG_NFT_FIB_NETDEV=y ++CONFIG_NFT_REJECT_NETDEV=y ++CONFIG_NF_FLOW_TABLE_INET=y ++CONFIG_NF_FLOW_TABLE=y ++CONFIG_IP_SET=y ++CONFIG_IP_SET_BITMAP_IP=y ++CONFIG_IP_SET_BITMAP_IPMAC=y ++CONFIG_IP_SET_BITMAP_PORT=y ++CONFIG_IP_SET_HASH_IP=y ++CONFIG_IP_SET_HASH_IPMARK=y ++CONFIG_IP_SET_HASH_IPPORT=y ++CONFIG_IP_SET_HASH_IPPORTIP=y ++CONFIG_IP_SET_HASH_IPPORTNET=y ++CONFIG_IP_SET_HASH_IPMAC=y ++CONFIG_IP_SET_HASH_MAC=y ++CONFIG_IP_SET_HASH_NETPORTNET=y ++CONFIG_IP_SET_HASH_NET=y ++CONFIG_IP_SET_HASH_NETNET=y ++CONFIG_IP_SET_HASH_NETPORT=y ++CONFIG_IP_SET_HASH_NETIFACE=y ++CONFIG_IP_SET_LIST_SET=y ++CONFIG_NFT_DUP_IPV4=y ++CONFIG_NFT_FIB_IPV4=y ++CONFIG_NF_TABLES_ARP=y ++CONFIG_NF_LOG_ARP=y ++CONFIG_NF_LOG_IPV4=y ++# CONFIG_NF_NAT_SNMP_BASIC is not set ++CONFIG_NFT_DUP_IPV6=y ++CONFIG_NFT_FIB_IPV6=y ++CONFIG_NF_LOG_IPV6=y ++CONFIG_NF_TABLES_BRIDGE=y ++CONFIG_NFT_BRIDGE_REJECT=y ++CONFIG_L2TP=y ++CONFIG_L2TP_V3=y ++CONFIG_L2TP_IP=y ++CONFIG_BRIDGE=y ++CONFIG_BRIDGE_VLAN_FILTERING=y ++CONFIG_BRIDGE_MRP=y ++CONFIG_BRIDGE_CFM=y ++CONFIG_VLAN_8021Q=y ++CONFIG_VLAN_8021Q_GVRP=y ++CONFIG_VLAN_8021Q_MVRP=y ++CONFIG_NET_SCHED=y ++CONFIG_NET_SCH_HTB=y ++CONFIG_NET_SCH_PRIO=y ++CONFIG_NET_SCH_SFQ=y ++CONFIG_NET_SCH_CODEL=y ++CONFIG_NET_SCH_FQ_CODEL=y ++CONFIG_NET_CLS_BASIC=y ++CONFIG_NET_CLS_ROUTE4=y ++CONFIG_NET_CLS_FW=y ++CONFIG_NET_CLS_U32=y ++CONFIG_CLS_U32_PERF=y ++CONFIG_CLS_U32_MARK=y ++CONFIG_NET_CLS_FLOW=y ++CONFIG_NET_CLS_CGROUP=y ++CONFIG_NET_CLS_BPF=y ++CONFIG_NET_CLS_MATCHALL=y ++CONFIG_NET_CLS_ACT=y ++CONFIG_NET_ACT_POLICE=y ++CONFIG_NET_ACT_GACT=y ++CONFIG_NET_ACT_VLAN=y ++CONFIG_NET_ACT_CONNMARK=y ++CONFIG_NET_ACT_CTINFO=y ++CONFIG_NET_TC_SKB_EXT=y ++CONFIG_BT=y ++CONFIG_BT_RFCOMM=y ++CONFIG_BT_RFCOMM_TTY=y ++CONFIG_BT_BNEP=y ++CONFIG_BT_BNEP_MC_FILTER=y ++CONFIG_BT_BNEP_PROTO_FILTER=y ++CONFIG_BT_HIDP=y ++CONFIG_BT_LEDS=y ++CONFIG_BT_HCIBTSDIO=y ++CONFIG_BT_HCIUART=y ++CONFIG_BT_HCIUART_BCM=y ++CONFIG_CFG80211=y ++CONFIG_CFG80211_DEBUGFS=y ++CONFIG_MAC80211=y ++CONFIG_MAC80211_LEDS=y ++CONFIG_RFKILL=y ++CONFIG_RFKILL_GPIO=y ++CONFIG_NFC=y ++CONFIG_NFC_DIGITAL=y ++CONFIG_NFC_NCI=y ++CONFIG_NFC_NXP_NCI=y ++CONFIG_NFC_NXP_NCI_I2C=y ++CONFIG_PAGE_POOL_STATS=y ++CONFIG_UEVENT_HELPER=y ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++#CONFIG_EXTRA_FIRMWARE="regulatory.db regulatory.db.p7s edid/asus.bin" ++#CONFIG_EXTRA_FIRMWARE_DIR="/workspace/megous.com/orangepi-pc/firmware" ++CONFIG_RASPBERRYPI_FIRMWARE=y ++CONFIG_GNSS=y ++CONFIG_GNSS_SIRF_SERIAL=y ++CONFIG_GNSS_UBX_SERIAL=y ++CONFIG_OF_OVERLAY=y ++CONFIG_ZRAM=y ++CONFIG_ZRAM_WRITEBACK=y ++CONFIG_ZRAM_MEMORY_TRACKING=y ++CONFIG_BLK_DEV_LOOP=y ++CONFIG_BLK_DEV_NBD=y ++CONFIG_SCSI=y ++CONFIG_BLK_DEV_SD=y ++# CONFIG_SCSI_LOWLEVEL is not set ++CONFIG_MD=y ++CONFIG_BLK_DEV_DM=y ++CONFIG_DM_CRYPT=y ++CONFIG_DM_INIT=y ++CONFIG_DM_INTEGRITY=y ++CONFIG_NETDEVICES=y ++CONFIG_WIREGUARD=y ++CONFIG_TUN=y ++CONFIG_VETH=y ++# CONFIG_NET_VENDOR_ALACRITECH is not set ++# CONFIG_NET_VENDOR_ALLWINNER is not set ++# CONFIG_NET_VENDOR_AMAZON is not set ++# CONFIG_NET_VENDOR_AQUANTIA is not set ++# CONFIG_NET_VENDOR_ARC is not set ++# CONFIG_NET_VENDOR_BROADCOM is not set ++# CONFIG_NET_VENDOR_CADENCE is not set ++# CONFIG_NET_VENDOR_CAVIUM is not set ++# CONFIG_NET_VENDOR_CIRRUS is not set ++# CONFIG_NET_VENDOR_CORTINA is not set ++# CONFIG_NET_VENDOR_EZCHIP is not set ++# CONFIG_NET_VENDOR_FARADAY is not set ++# CONFIG_NET_VENDOR_GOOGLE is not set ++# CONFIG_NET_VENDOR_HISILICON is not set ++# CONFIG_NET_VENDOR_HUAWEI is not set ++# CONFIG_NET_VENDOR_INTEL is not set ++# CONFIG_NET_VENDOR_MARVELL is not set ++# CONFIG_NET_VENDOR_MELLANOX is not set ++# CONFIG_NET_VENDOR_MICREL is not set ++# CONFIG_NET_VENDOR_MICROCHIP is not set ++# CONFIG_NET_VENDOR_MICROSEMI is not set ++# CONFIG_NET_VENDOR_NI is not set ++# CONFIG_NET_VENDOR_NATSEMI is not set ++# CONFIG_NET_VENDOR_NETRONOME is not set ++# CONFIG_NET_VENDOR_PENSANDO is not set ++# CONFIG_NET_VENDOR_QUALCOMM is not set ++# CONFIG_NET_VENDOR_RENESAS is not set ++# CONFIG_NET_VENDOR_ROCKER is not set ++# CONFIG_NET_VENDOR_SAMSUNG is not set ++# CONFIG_NET_VENDOR_SEEQ is not set ++# CONFIG_NET_VENDOR_SOLARFLARE is not set ++# CONFIG_NET_VENDOR_SMSC is not set ++# CONFIG_NET_VENDOR_SOCIONEXT is not set ++CONFIG_STMMAC_ETH=y ++# CONFIG_NET_VENDOR_SYNOPSYS is not set ++# CONFIG_NET_VENDOR_VIA is not set ++# CONFIG_NET_VENDOR_WIZNET is not set ++# CONFIG_NET_VENDOR_XILINX is not set ++CONFIG_LED_TRIGGER_PHY=y ++CONFIG_MDIO_BUS_MUX_MULTIPLEXER=y ++CONFIG_PPP=y ++CONFIG_PPP_BSDCOMP=y ++CONFIG_PPP_DEFLATE=y ++CONFIG_PPP_FILTER=y ++CONFIG_PPP_MPPE=y ++CONFIG_PPPOL2TP=y ++CONFIG_PPP_ASYNC=y ++CONFIG_PPP_SYNC_TTY=y ++CONFIG_USB_RTL8152=y ++CONFIG_USB_USBNET=y ++# CONFIG_USB_NET_AX8817X is not set ++# CONFIG_USB_NET_AX88179_178A is not set ++CONFIG_USB_NET_CDC_EEM=y ++# CONFIG_USB_NET_CDC_NCM is not set ++CONFIG_USB_NET_SMSC95XX=y ++# CONFIG_USB_NET_NET1080 is not set ++# CONFIG_USB_NET_CDC_SUBSET is not set ++# CONFIG_USB_NET_ZAURUS is not set ++# CONFIG_WLAN_VENDOR_ADMTEK is not set ++# CONFIG_WLAN_VENDOR_ATH is not set ++# CONFIG_WLAN_VENDOR_ATMEL is not set ++CONFIG_BRCMFMAC=y ++# CONFIG_WLAN_VENDOR_INTEL is not set ++# CONFIG_WLAN_VENDOR_INTERSIL is not set ++# CONFIG_WLAN_VENDOR_MARVELL is not set ++# CONFIG_WLAN_VENDOR_MEDIATEK is not set ++# CONFIG_WLAN_VENDOR_MICROCHIP is not set ++CONFIG_RT2X00=y ++CONFIG_RT2800USB=y ++# CONFIG_RT2800USB_RT35XX is not set ++CONFIG_RT2800USB_RT53XX=y ++# CONFIG_WLAN_VENDOR_REALTEK is not set ++# CONFIG_WLAN_VENDOR_RSI is not set ++# CONFIG_WLAN_VENDOR_ST is not set ++# CONFIG_WLAN_VENDOR_TI is not set ++# CONFIG_WLAN_VENDOR_ZYDAS is not set ++# CONFIG_WLAN_VENDOR_QUANTENNA is not set ++CONFIG_INPUT_MOUSEDEV=y ++CONFIG_INPUT_MOUSEDEV_PSAUX=y ++CONFIG_INPUT_EVDEV=y ++CONFIG_KEYBOARD_ADC=y ++# CONFIG_KEYBOARD_ATKBD is not set ++CONFIG_KEYBOARD_GPIO=y ++CONFIG_KEYBOARD_GPIO_POLLED=y ++CONFIG_KEYBOARD_SUN4I_LRADC=y ++# CONFIG_INPUT_MOUSE is not set ++CONFIG_INPUT_TOUCHSCREEN=y ++CONFIG_TOUCHSCREEN_EDT_FT5X06=y ++CONFIG_INPUT_MISC=y ++CONFIG_INPUT_GPIO_VIBRA=y ++CONFIG_INPUT_AXP20X_PEK=y ++# CONFIG_SERIO is not set ++# CONFIG_LEGACY_PTYS is not set ++CONFIG_SERIAL_8250=y ++# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set ++CONFIG_SERIAL_8250_CONSOLE=y ++CONFIG_SERIAL_8250_NR_UARTS=8 ++CONFIG_SERIAL_8250_RUNTIME_UARTS=8 ++CONFIG_SERIAL_8250_EXTENDED=y ++CONFIG_SERIAL_8250_SHARE_IRQ=y ++CONFIG_SERIAL_8250_BCM2835AUX=y ++CONFIG_SERIAL_8250_DW=y ++CONFIG_SERIAL_OF_PLATFORM=y ++CONFIG_SERIAL_AMBA_PL011=y ++CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++CONFIG_SERIAL_DEV_BUS=y ++CONFIG_HW_RANDOM=y ++# CONFIG_HW_RANDOM_IPROC_RNG200 is not set ++CONFIG_I2C_CHARDEV=y ++CONFIG_I2C_MUX=y ++CONFIG_I2C_BCM2835=y ++CONFIG_I2C_GPIO=y ++CONFIG_I2C_MV64XXX=y ++CONFIG_I2C_SUN6I_P2WI=y ++CONFIG_SPI=y ++CONFIG_SPI_BCM2835=y ++CONFIG_SPI_BCM2835AUX=y ++CONFIG_SPI_SUN4I=y ++CONFIG_SPI_SUN6I=y ++CONFIG_SPI_SPIDEV=y ++CONFIG_PINCTRL_AXP209=y ++CONFIG_POWER_RESET=y ++CONFIG_POWER_RESET_GPIO=y ++CONFIG_POWER_RESET_GPIO_RESTART=y ++CONFIG_POWER_SUPPLY=y ++CONFIG_CHARGER_AXP20X=y ++CONFIG_BATTERY_AXP20X=y ++CONFIG_AXP20X_POWER=y ++CONFIG_SENSORS_RASPBERRYPI_HWMON=y ++CONFIG_THERMAL=y ++CONFIG_THERMAL_STATISTICS=y ++CONFIG_THERMAL_GOV_FAIR_SHARE=y ++CONFIG_THERMAL_GOV_BANG_BANG=y ++CONFIG_CPU_THERMAL=y ++CONFIG_SUN8I_THERMAL=y ++CONFIG_BCM2835_THERMAL=y ++CONFIG_WATCHDOG=y ++CONFIG_SUNXI_WATCHDOG=y ++CONFIG_BCM2835_WDT=y ++CONFIG_MFD_SUN4I_GPADC=y ++CONFIG_MFD_AXP20X_I2C=y ++CONFIG_MFD_AXP20X_RSB=y ++CONFIG_REGULATOR=y ++CONFIG_REGULATOR_FIXED_VOLTAGE=y ++CONFIG_REGULATOR_USERSPACE_CONSUMER=y ++CONFIG_REGULATOR_AXP20X=y ++CONFIG_REGULATOR_GPIO=y ++CONFIG_REGULATOR_PWM=y ++CONFIG_REGULATOR_SY8106A=y ++CONFIG_RC_CORE=y ++CONFIG_BPF_LIRC_MODE2=y ++CONFIG_LIRC=y ++CONFIG_RC_DECODERS=y ++CONFIG_IR_JVC_DECODER=y ++CONFIG_IR_MCE_KBD_DECODER=y ++CONFIG_IR_NEC_DECODER=y ++CONFIG_IR_RC5_DECODER=y ++CONFIG_IR_RC6_DECODER=y ++CONFIG_IR_SANYO_DECODER=y ++CONFIG_IR_SHARP_DECODER=y ++CONFIG_IR_SONY_DECODER=y ++CONFIG_IR_XMP_DECODER=y ++CONFIG_RC_DEVICES=y ++CONFIG_IR_GPIO_CIR=y ++CONFIG_IR_SUNXI=y ++CONFIG_MEDIA_CEC_RC=y ++CONFIG_MEDIA_CEC_SUPPORT=y ++CONFIG_MEDIA_SUPPORT=y ++# CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set ++CONFIG_MEDIA_CAMERA_SUPPORT=y ++CONFIG_MEDIA_PLATFORM_SUPPORT=y ++CONFIG_MEDIA_USB_SUPPORT=y ++CONFIG_USB_VIDEO_CLASS=y ++CONFIG_V4L_PLATFORM_DRIVERS=y ++CONFIG_V4L_MEM2MEM_DRIVERS=y ++CONFIG_VIDEO_MEM2MEM_DEINTERLACE=y ++CONFIG_VIDEO_SUN6I_CSI=y ++CONFIG_VIDEO_SUN8I_DEINTERLACE=y ++CONFIG_VIDEO_SUN8I_ROTATE=y ++# CONFIG_VIDEO_IR_I2C is not set ++CONFIG_DRM=y ++CONFIG_DRM_LOAD_EDID_FIRMWARE=y ++CONFIG_DRM_SUN4I=y ++CONFIG_DRM_SUN4I_HDMI_CEC=y ++# CONFIG_DRM_SUN6I_DSI is not set ++CONFIG_DRM_PANEL_LVDS=y ++CONFIG_DRM_DW_HDMI_AHB_AUDIO=y ++CONFIG_DRM_DW_HDMI_I2S_AUDIO=y ++CONFIG_DRM_DW_HDMI_CEC=y ++CONFIG_DRM_VC4=y ++CONFIG_DRM_VC4_HDMI_CEC=y ++CONFIG_DRM_LIMA=y ++CONFIG_FB=y ++CONFIG_LCD_CLASS_DEVICE=y ++CONFIG_BACKLIGHT_CLASS_DEVICE=y ++CONFIG_BACKLIGHT_PWM=y ++CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER=y ++CONFIG_LOGO=y ++# CONFIG_LOGO_LINUX_MONO is not set ++# CONFIG_LOGO_LINUX_VGA16 is not set ++CONFIG_SOUND=y ++CONFIG_SND=y ++CONFIG_SND_OSSEMUL=y ++CONFIG_SND_MIXER_OSS=y ++CONFIG_SND_PCM_OSS=y ++CONFIG_SND_HRTIMER=y ++CONFIG_SND_SEQUENCER=y ++CONFIG_SND_SEQUENCER_OSS=y ++CONFIG_SND_DUMMY=y ++CONFIG_SND_ALOOP=y ++# CONFIG_SND_SPI is not set ++CONFIG_SND_USB_AUDIO=y ++CONFIG_SND_SOC=y ++CONFIG_SND_BCM2835_SOC_I2S=y ++CONFIG_SND_SUN4I_CODEC=y ++CONFIG_SND_SUN8I_CODEC=y ++CONFIG_SND_SUN8I_CODEC_ANALOG=y ++CONFIG_SND_SUN4I_I2S=y ++CONFIG_SND_SUN4I_SPDIF=y ++CONFIG_SND_SOC_SPDIF=y ++CONFIG_SND_SIMPLE_CARD=y ++CONFIG_SND_AUDIO_GRAPH_CARD=y ++CONFIG_HIDRAW=y ++# CONFIG_HID_A4TECH is not set ++# CONFIG_HID_APPLE is not set ++# CONFIG_HID_BELKIN is not set ++# CONFIG_HID_CHERRY is not set ++# CONFIG_HID_CHICONY is not set ++# CONFIG_HID_CYPRESS is not set ++# CONFIG_HID_EZKEY is not set ++# CONFIG_HID_ITE is not set ++# CONFIG_HID_KENSINGTON is not set ++# CONFIG_HID_LOGITECH is not set ++# CONFIG_HID_REDRAGON is not set ++# CONFIG_HID_MICROSOFT is not set ++# CONFIG_HID_MONTEREY is not set ++CONFIG_USB_HIDDEV=y ++CONFIG_USB_LED_TRIG=y ++CONFIG_USB=y ++CONFIG_USB_ANNOUNCE_NEW_DEVICES=y ++CONFIG_USB_OTG=y ++CONFIG_USB_OTG_FSM=y ++CONFIG_USB_LEDS_TRIGGER_USBPORT=y ++CONFIG_USB_EHCI_HCD=y ++CONFIG_USB_EHCI_HCD_PLATFORM=y ++CONFIG_USB_OHCI_HCD=y ++CONFIG_USB_OHCI_HCD_PLATFORM=y ++CONFIG_USB_ACM=y ++CONFIG_USB_PRINTER=y ++CONFIG_USB_WDM=y ++CONFIG_USB_STORAGE=y ++CONFIG_USB_UAS=y ++CONFIG_USBIP_CORE=y ++CONFIG_USBIP_VHCI_HCD=y ++CONFIG_USBIP_HOST=y ++CONFIG_USBIP_VUDC=y ++CONFIG_USB_MUSB_HDRC=y ++CONFIG_USB_MUSB_SUNXI=y ++CONFIG_MUSB_PIO_ONLY=y ++CONFIG_USB_DWC2=y ++CONFIG_USB_SERIAL=y ++CONFIG_USB_SERIAL_CH341=y ++CONFIG_USB_SERIAL_CP210X=y ++CONFIG_USB_SERIAL_FTDI_SIO=y ++CONFIG_USB_SERIAL_PL2303=y ++CONFIG_USB_SERIAL_OPTION=y ++CONFIG_NOP_USB_XCEIV=y ++CONFIG_USB_GADGET=y ++CONFIG_U_SERIAL_CONSOLE=y ++CONFIG_USB_CONFIGFS=y ++CONFIG_USB_CONFIGFS_SERIAL=y ++CONFIG_USB_CONFIGFS_ACM=y ++CONFIG_USB_CONFIGFS_OBEX=y ++CONFIG_USB_CONFIGFS_NCM=y ++CONFIG_USB_CONFIGFS_ECM=y ++CONFIG_USB_CONFIGFS_ECM_SUBSET=y ++CONFIG_USB_CONFIGFS_RNDIS=y ++CONFIG_USB_CONFIGFS_EEM=y ++CONFIG_USB_CONFIGFS_MASS_STORAGE=y ++CONFIG_USB_CONFIGFS_F_LB_SS=y ++CONFIG_USB_CONFIGFS_F_FS=y ++CONFIG_USB_CONFIGFS_F_UAC1=y ++CONFIG_USB_CONFIGFS_F_UAC2=y ++CONFIG_USB_CONFIGFS_F_MIDI=y ++CONFIG_USB_CONFIGFS_F_HID=y ++CONFIG_USB_CONFIGFS_F_PRINTER=y ++CONFIG_USB_CDC_COMPOSITE=y ++CONFIG_MMC=y ++CONFIG_MMC_SUNXI=y ++CONFIG_MMC_BCM2835=y ++CONFIG_NEW_LEDS=y ++CONFIG_LEDS_CLASS=y ++CONFIG_LEDS_GPIO=y ++CONFIG_LEDS_TRIGGER_TIMER=y ++CONFIG_LEDS_TRIGGER_ONESHOT=y ++CONFIG_LEDS_TRIGGER_HEARTBEAT=y ++CONFIG_LEDS_TRIGGER_CPU=y ++CONFIG_LEDS_TRIGGER_ACTIVITY=y ++CONFIG_LEDS_TRIGGER_DEFAULT_ON=y ++CONFIG_LEDS_TRIGGER_PANIC=y ++CONFIG_LEDS_TRIGGER_NETDEV=y ++CONFIG_LEDS_TRIGGER_PATTERN=y ++CONFIG_RTC_CLASS=y ++# CONFIG_RTC_INTF_SYSFS is not set ++# CONFIG_RTC_INTF_PROC is not set ++CONFIG_RTC_DRV_AC100=y ++CONFIG_RTC_DRV_SUNXI=y ++CONFIG_DMADEVICES=y ++CONFIG_DMA_BCM2835=y ++CONFIG_DMA_SUN6I=y ++CONFIG_DMABUF_HEAPS=y ++CONFIG_DMABUF_HEAPS_SYSTEM=y ++CONFIG_DMABUF_HEAPS_CMA=y ++# CONFIG_VIRTIO_MENU is not set ++# CONFIG_VHOST_MENU is not set ++CONFIG_STAGING=y ++CONFIG_STAGING_MEDIA=y ++CONFIG_VIDEO_SUNXI=y ++CONFIG_VIDEO_SUNXI_CEDRUS=y ++# CONFIG_BCM_VIDEOCORE is not set ++CONFIG_CLK_RASPBERRYPI=y ++# CONFIG_CLK_SUNXI_CLOCKS is not set ++# CONFIG_CLK_SUNXI_PRCM_SUN9I is not set ++# CONFIG_SUN4I_A10_CCU is not set ++# CONFIG_SUN5I_CCU is not set ++# CONFIG_SUN6I_A31_CCU is not set ++# CONFIG_SUN8I_A23_CCU is not set ++# CONFIG_SUN8I_A33_CCU is not set ++# CONFIG_SUN8I_V3S_CCU is not set ++# CONFIG_SUN8I_R40_CCU is not set ++CONFIG_MAILBOX=y ++CONFIG_BCM2835_MBOX=y ++# CONFIG_SUN6I_MSGBOX is not set ++# CONFIG_IOMMU_SUPPORT is not set ++CONFIG_RASPBERRYPI_POWER=y ++CONFIG_DEVFREQ_GOV_PERFORMANCE=y ++CONFIG_DEVFREQ_GOV_POWERSAVE=y ++CONFIG_DEVFREQ_GOV_USERSPACE=y ++CONFIG_DEVFREQ_GOV_PASSIVE=y ++CONFIG_PM_DEVFREQ_EVENT=y ++CONFIG_IIO=y ++CONFIG_IIO_CONFIGFS=y ++CONFIG_BMA180=y ++CONFIG_AXP20X_ADC=y ++CONFIG_LTR501=y ++CONFIG_AK8975=y ++CONFIG_PWM=y ++CONFIG_PWM_BCM2835=y ++CONFIG_PWM_SUN4I=y ++CONFIG_RESET_RASPBERRYPI=y ++CONFIG_PHY_SUN4I_USB=y ++CONFIG_ARM_CCI_PMU=y ++# CONFIG_ARM_CCI5xx_PMU is not set ++CONFIG_NVMEM_SUNXI_SID=y ++CONFIG_VALIDATE_FS_PARSER=y ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_FS_POSIX_ACL=y ++CONFIG_XFS_FS=y ++CONFIG_XFS_POSIX_ACL=y ++CONFIG_F2FS_FS=y ++CONFIG_F2FS_FS_SECURITY=y ++CONFIG_F2FS_CHECK_FS=y ++CONFIG_F2FS_FS_COMPRESSION=y ++CONFIG_FS_ENCRYPTION=y ++CONFIG_FANOTIFY=y ++CONFIG_AUTOFS_FS=y ++CONFIG_FUSE_FS=y ++CONFIG_CUSE=y ++CONFIG_OVERLAY_FS=y ++CONFIG_OVERLAY_FS_INDEX=y ++CONFIG_OVERLAY_FS_METACOPY=y ++CONFIG_VFAT_FS=y ++CONFIG_PROC_CHILDREN=y ++CONFIG_TMPFS=y ++CONFIG_TMPFS_POSIX_ACL=y ++CONFIG_ECRYPT_FS=y ++CONFIG_ECRYPT_FS_MESSAGING=y ++CONFIG_SQUASHFS=y ++CONFIG_SQUASHFS_FILE_DIRECT=y ++CONFIG_SQUASHFS_XATTR=y ++CONFIG_SQUASHFS_LZ4=y ++CONFIG_SQUASHFS_LZO=y ++CONFIG_SQUASHFS_XZ=y ++CONFIG_SQUASHFS_ZSTD=y ++CONFIG_NFS_FS=y ++# CONFIG_NFS_V3 is not set ++CONFIG_NFS_V4=y ++CONFIG_NFS_V4_1=y ++CONFIG_NFS_V4_2=y ++CONFIG_NFS_FSCACHE=y ++# CONFIG_NFS_V4_2_READ_PLUS is not set ++CONFIG_NFSD=y ++CONFIG_NFSD_V3_ACL=y ++CONFIG_NFSD_V4=y ++# CONFIG_NFSD_LEGACY_CLIENT_TRACKING is not set ++CONFIG_CIFS=y ++# CONFIG_CIFS_STATS2 is not set ++CONFIG_CIFS_XATTR=y ++CONFIG_CIFS_POSIX=y ++# CONFIG_CIFS_DEBUG is not set ++CONFIG_NLS_CODEPAGE_437=y ++CONFIG_NLS_CODEPAGE_852=y ++CONFIG_NLS_ISO8859_1=y ++CONFIG_NLS_ISO8859_2=y ++CONFIG_NLS_UTF8=y ++CONFIG_PERSISTENT_KEYRINGS=y ++CONFIG_ENCRYPTED_KEYS=y ++CONFIG_LSM="yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor" ++CONFIG_INIT_STACK_NONE=y ++CONFIG_CRYPTO_PCRYPT=y ++CONFIG_CRYPTO_CURVE25519=y ++CONFIG_CRYPTO_AES_TI=y ++CONFIG_CRYPTO_ARC4=y ++CONFIG_CRYPTO_CHACHA20POLY1305=y ++CONFIG_CRYPTO_SEQIV=y ++CONFIG_CRYPTO_ECHAINIV=y ++CONFIG_CRYPTO_BLAKE2B=y ++CONFIG_CRYPTO_MD4=y ++CONFIG_CRYPTO_DEFLATE=y ++CONFIG_CRYPTO_LZO=y ++CONFIG_CRYPTO_LZ4=y ++CONFIG_CRYPTO_ZSTD=y ++CONFIG_CRYPTO_ANSI_CPRNG=y ++CONFIG_CRYPTO_USER_API_HASH=y ++CONFIG_CRYPTO_USER_API_SKCIPHER=y ++CONFIG_CRYPTO_USER_API_RNG=y ++CONFIG_CRYPTO_USER_API_AEAD=y ++CONFIG_CRYPTO_GHASH_ARM_CE=y ++CONFIG_CRYPTO_SHA1_ARM_NEON=y ++CONFIG_CRYPTO_SHA1_ARM_CE=y ++CONFIG_CRYPTO_SHA2_ARM_CE=y ++CONFIG_CRYPTO_SHA512_ARM=y ++CONFIG_CRYPTO_AES_ARM_BS=y ++CONFIG_CRYPTO_AES_ARM_CE=y ++CONFIG_CRYPTO_CRC32_ARM_CE=y ++CONFIG_CRYPTO_CRCT10DIF_ARM_CE=y ++CONFIG_CRYPTO_DEV_SUN4I_SS=y ++CONFIG_CRYPTO_DEV_SUN4I_SS_PRNG=y ++CONFIG_CRYPTO_DEV_SUN8I_CE=y ++CONFIG_CRYPTO_DEV_SUN8I_SS=y ++CONFIG_PKCS8_PRIVATE_KEY_PARSER=y ++CONFIG_DMA_CMA=y ++CONFIG_CMA_SIZE_MBYTES=128 ++CONFIG_CONSOLE_LOGLEVEL_DEFAULT=15 ++CONFIG_MESSAGE_LOGLEVEL_DEFAULT=7 ++CONFIG_DEBUG_KERNEL=y ++CONFIG_MAGIC_SYSRQ=y ++CONFIG_DEBUG_FS=y ++CONFIG_DETECT_HUNG_TASK=y ++CONFIG_BOOTPARAM_HUNG_TASK_PANIC=y ++CONFIG_WQ_WATCHDOG=y ++# CONFIG_SCHED_DEBUG is not set ++# CONFIG_RCU_TRACE is not set ++# CONFIG_FTRACE is not set ++# CONFIG_RUNTIME_TESTING_MENU is not set +diff --git a/arch/arm/configs/pocketbook_touch_lux_3_defconfig b/arch/arm/configs/pocketbook_touch_lux_3_defconfig +new file mode 100644 +index 000000000000..a86ca9662199 +--- /dev/null ++++ b/arch/arm/configs/pocketbook_touch_lux_3_defconfig +@@ -0,0 +1,416 @@ ++CONFIG_KERNEL_LZ4=y ++CONFIG_SYSVIPC=y ++CONFIG_POSIX_MQUEUE=y ++CONFIG_HZ_PERIODIC=y ++CONFIG_NO_HZ=y ++CONFIG_HIGH_RES_TIMERS=y ++CONFIG_BPF_SYSCALL=y ++# CONFIG_BPF_UNPRIV_DEFAULT_OFF is not set ++CONFIG_BSD_PROCESS_ACCT=y ++CONFIG_BSD_PROCESS_ACCT_V3=y ++CONFIG_TASKSTATS=y ++CONFIG_TASK_DELAY_ACCT=y ++CONFIG_TASK_XACCT=y ++CONFIG_TASK_IO_ACCOUNTING=y ++CONFIG_MEMCG=y ++CONFIG_BLK_CGROUP=y ++CONFIG_RT_GROUP_SCHED=y ++CONFIG_CGROUP_PIDS=y ++CONFIG_CGROUP_DEVICE=y ++CONFIG_CGROUP_BPF=y ++CONFIG_USER_NS=y ++CONFIG_SCHED_AUTOGROUP=y ++# CONFIG_RD_BZIP2 is not set ++# CONFIG_RD_LZMA is not set ++# CONFIG_RD_XZ is not set ++# CONFIG_RD_LZO is not set ++CONFIG_BOOT_CONFIG=y ++CONFIG_PERF_EVENTS=y ++CONFIG_ARCH_SUNXI=y ++# CONFIG_MACH_SUN4I is not set ++# CONFIG_MACH_SUN6I is not set ++# CONFIG_MACH_SUN7I is not set ++# CONFIG_MACH_SUN8I is not set ++# CONFIG_MACH_SUN9I is not set ++CONFIG_ARM_ERRATA_430973=y ++CONFIG_ARM_ERRATA_814220=y ++CONFIG_HAVE_ARM_ARCH_TIMER=y ++CONFIG_ARM_PSCI=y ++CONFIG_HZ_1000=y ++# CONFIG_ARM_MODULE_PLTS is not set ++CONFIG_ARCH_FORCE_MAX_ORDER=11 ++CONFIG_ZBOOT_ROM_TEXT=0 ++CONFIG_ZBOOT_ROM_BSS=0 ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_STAT=y ++CONFIG_CPU_FREQ_GOV_POWERSAVE=y ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++CONFIG_CPU_FREQ_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y ++CONFIG_CPUFREQ_DT=y ++CONFIG_CPU_IDLE=y ++CONFIG_CPU_IDLE_GOV_LADDER=y ++CONFIG_ARM_CPUIDLE=y ++CONFIG_VFP=y ++CONFIG_NEON=y ++CONFIG_KERNEL_MODE_NEON=y ++CONFIG_PM_WAKELOCKS=y ++CONFIG_PM_DEBUG=y ++CONFIG_WQ_POWER_EFFICIENT_DEFAULT=y ++CONFIG_JUMP_LABEL=y ++# CONFIG_STACKPROTECTOR is not set ++CONFIG_MODULES=y ++CONFIG_MODULE_FORCE_LOAD=y ++CONFIG_MODULE_UNLOAD=y ++CONFIG_MODVERSIONS=y ++CONFIG_BLK_DEV_INTEGRITY=y ++CONFIG_BLK_DEV_THROTTLING=y ++CONFIG_BLK_WBT=y ++CONFIG_BLK_CGROUP_IOLATENCY=y ++# CONFIG_MQ_IOSCHED_DEADLINE is not set ++# CONFIG_MQ_IOSCHED_KYBER is not set ++CONFIG_CMA=y ++CONFIG_CMA_DEBUGFS=y ++CONFIG_CMA_AREAS=7 ++CONFIG_NET=y ++CONFIG_PACKET=y ++CONFIG_PACKET_DIAG=y ++CONFIG_UNIX=y ++CONFIG_UNIX_DIAG=y ++CONFIG_XFRM_USER=m ++CONFIG_XFRM_SUB_POLICY=y ++CONFIG_INET=y ++CONFIG_SYN_COOKIES=y ++# CONFIG_INET_DIAG is not set ++CONFIG_MPTCP=y ++CONFIG_NETFILTER=y ++CONFIG_NETFILTER_NETLINK_QUEUE=m ++CONFIG_NETFILTER_NETLINK_LOG=m ++CONFIG_NF_CONNTRACK=m ++CONFIG_NF_CONNTRACK_MARK=y ++# CONFIG_NF_CT_PROTO_DCCP is not set ++# CONFIG_NF_CT_PROTO_SCTP is not set ++# CONFIG_NF_CT_PROTO_UDPLITE is not set ++CONFIG_NF_CONNTRACK_FTP=m ++CONFIG_NF_CONNTRACK_H323=m ++CONFIG_NF_CONNTRACK_IRC=m ++CONFIG_NF_CONNTRACK_NETBIOS_NS=m ++CONFIG_NF_CONNTRACK_SNMP=m ++CONFIG_NF_CONNTRACK_PPTP=m ++CONFIG_NF_CONNTRACK_SIP=m ++CONFIG_NF_CONNTRACK_TFTP=m ++CONFIG_NF_CT_NETLINK=m ++CONFIG_NETFILTER_NETLINK_GLUE_CT=y ++CONFIG_NF_TABLES=m ++CONFIG_NF_TABLES_NETDEV=y ++CONFIG_NFT_NUMGEN=m ++CONFIG_NFT_CT=m ++CONFIG_NFT_CONNLIMIT=m ++CONFIG_NFT_LOG=m ++CONFIG_NFT_LIMIT=m ++CONFIG_NFT_MASQ=m ++CONFIG_NFT_REDIR=m ++CONFIG_NFT_NAT=m ++CONFIG_NFT_TUNNEL=m ++CONFIG_NFT_QUEUE=m ++CONFIG_NFT_QUOTA=m ++CONFIG_NFT_REJECT=m ++CONFIG_NFT_HASH=m ++CONFIG_NFT_SOCKET=m ++CONFIG_NFT_OSF=m ++CONFIG_NFT_DUP_NETDEV=m ++CONFIG_NFT_FWD_NETDEV=m ++CONFIG_NF_FLOW_TABLE_INET=m ++CONFIG_NF_FLOW_TABLE=m ++CONFIG_IP_SET=m ++CONFIG_IP_SET_BITMAP_IP=m ++CONFIG_IP_SET_BITMAP_IPMAC=m ++CONFIG_IP_SET_BITMAP_PORT=m ++CONFIG_IP_SET_HASH_IP=m ++CONFIG_IP_SET_HASH_IPMARK=m ++CONFIG_IP_SET_HASH_IPPORT=m ++CONFIG_IP_SET_HASH_IPPORTIP=m ++CONFIG_IP_SET_HASH_IPPORTNET=m ++CONFIG_IP_SET_HASH_IPMAC=m ++CONFIG_IP_SET_HASH_MAC=m ++CONFIG_IP_SET_HASH_NETPORTNET=m ++CONFIG_IP_SET_HASH_NET=m ++CONFIG_IP_SET_HASH_NETNET=m ++CONFIG_IP_SET_HASH_NETPORT=m ++CONFIG_IP_SET_HASH_NETIFACE=m ++CONFIG_IP_SET_LIST_SET=m ++CONFIG_NF_SOCKET_IPV4=y ++CONFIG_NF_TABLES_IPV4=y ++CONFIG_NFT_DUP_IPV4=m ++CONFIG_NFT_FIB_IPV4=m ++CONFIG_NF_TABLES_ARP=y ++CONFIG_NF_LOG_ARP=y ++CONFIG_NF_LOG_IPV4=y ++CONFIG_NF_REJECT_IPV4=y ++# CONFIG_NF_NAT_SNMP_BASIC is not set ++CONFIG_CFG80211=y ++CONFIG_CFG80211_DEBUGFS=y ++CONFIG_CFG80211_WEXT=y ++CONFIG_MAC80211=y ++CONFIG_MAC80211_LEDS=y ++CONFIG_MAC80211_DEBUGFS=y ++CONFIG_UEVENT_HELPER=y ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++#CONFIG_EXTRA_FIRMWARE="regulatory.db regulatory.db.p7s rtlwifi/rtl8188eufw.bin" ++#CONFIG_EXTRA_FIRMWARE_DIR="/workspace/megous.com/orangepi-pc/firmware" ++# CONFIG_FW_CACHE is not set ++CONFIG_SUNXI_RSB=y ++CONFIG_MTD=y ++# CONFIG_MTD_OF_PARTS is not set ++CONFIG_MTD_BLOCK_RO=y ++CONFIG_MTD_SPI_NOR=m ++CONFIG_OF_OVERLAY=y ++CONFIG_ZRAM=y ++CONFIG_ZRAM_WRITEBACK=y ++CONFIG_ZRAM_MEMORY_TRACKING=y ++CONFIG_BLK_DEV_LOOP=y ++CONFIG_BLK_DEV_NBD=y ++CONFIG_SCSI=y ++CONFIG_BLK_DEV_SD=y ++# CONFIG_SCSI_LOWLEVEL is not set ++CONFIG_NETDEVICES=y ++CONFIG_WIREGUARD=m ++CONFIG_TUN=y ++# CONFIG_ETHERNET is not set ++# CONFIG_USB_NET_DRIVERS is not set ++# CONFIG_WLAN_VENDOR_ADMTEK is not set ++# CONFIG_WLAN_VENDOR_ATH is not set ++# CONFIG_WLAN_VENDOR_ATMEL is not set ++# CONFIG_WLAN_VENDOR_BROADCOM is not set ++# CONFIG_WLAN_VENDOR_INTEL is not set ++# CONFIG_WLAN_VENDOR_INTERSIL is not set ++# CONFIG_WLAN_VENDOR_MARVELL is not set ++# CONFIG_WLAN_VENDOR_MEDIATEK is not set ++# CONFIG_WLAN_VENDOR_MICROCHIP is not set ++# CONFIG_WLAN_VENDOR_PURELIFI is not set ++# CONFIG_WLAN_VENDOR_RALINK is not set ++# CONFIG_RTL_CARDS is not set ++CONFIG_RTL8XXXU=m ++# CONFIG_WLAN_VENDOR_RSI is not set ++# CONFIG_WLAN_VENDOR_SILABS is not set ++# CONFIG_WLAN_VENDOR_ST is not set ++# CONFIG_WLAN_VENDOR_TI is not set ++# CONFIG_WLAN_VENDOR_ZYDAS is not set ++# CONFIG_WLAN_VENDOR_QUANTENNA is not set ++CONFIG_INPUT_EVDEV=y ++# CONFIG_KEYBOARD_ATKBD is not set ++CONFIG_KEYBOARD_GPIO=y ++CONFIG_KEYBOARD_GPIO_POLLED=y ++CONFIG_KEYBOARD_SUN4I_LRADC=y ++# CONFIG_INPUT_MOUSE is not set ++CONFIG_INPUT_TOUCHSCREEN=y ++CONFIG_TOUCHSCREEN_CYTTSP4_CORE=m ++CONFIG_TOUCHSCREEN_CYTTSP4_I2C=m ++CONFIG_INPUT_MISC=y ++CONFIG_INPUT_AXP20X_PEK=y ++# CONFIG_SERIO is not set ++# CONFIG_LEGACY_PTYS is not set ++CONFIG_SERIAL_8250=y ++# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set ++# CONFIG_SERIAL_8250_16550A_VARIANTS is not set ++CONFIG_SERIAL_8250_CONSOLE=y ++CONFIG_SERIAL_8250_NR_UARTS=8 ++CONFIG_SERIAL_8250_RUNTIME_UARTS=8 ++CONFIG_SERIAL_8250_DW=y ++CONFIG_SERIAL_OF_PLATFORM=y ++CONFIG_SERIAL_DEV_BUS=y ++# CONFIG_HW_RANDOM is not set ++CONFIG_I2C_CHARDEV=y ++CONFIG_I2C_MUX=y ++CONFIG_I2C_GPIO=y ++CONFIG_I2C_MV64XXX=y ++CONFIG_SPI=y ++CONFIG_SPI_SUN4I=y ++CONFIG_SPI_SPIDEV=y ++CONFIG_PINCTRL_AXP209=y ++CONFIG_POWER_SUPPLY=y ++CONFIG_CHARGER_AXP20X=y ++CONFIG_BATTERY_AXP20X=y ++CONFIG_AXP20X_POWER=y ++CONFIG_SENSORS_NTC_THERMISTOR=y ++CONFIG_THERMAL=y ++CONFIG_THERMAL_STATISTICS=y ++CONFIG_THERMAL_GOV_FAIR_SHARE=y ++CONFIG_THERMAL_GOV_BANG_BANG=y ++CONFIG_CPU_THERMAL=y ++CONFIG_WATCHDOG=y ++CONFIG_SUNXI_WATCHDOG=y ++CONFIG_MFD_SUN4I_GPADC=y ++CONFIG_MFD_AXP20X_I2C=y ++CONFIG_MFD_SUN6I_PRCM=y ++CONFIG_REGULATOR=y ++CONFIG_REGULATOR_FIXED_VOLTAGE=y ++CONFIG_REGULATOR_USERSPACE_CONSUMER_OF=y ++CONFIG_REGULATOR_AXP20X=y ++CONFIG_REGULATOR_GPIO=y ++CONFIG_REGULATOR_TP65185X=y ++CONFIG_DRM=y ++CONFIG_FB=y ++CONFIG_FB_SUN5I_EINK=m ++CONFIG_LCD_CLASS_DEVICE=y ++CONFIG_BACKLIGHT_CLASS_DEVICE=y ++CONFIG_BACKLIGHT_PWM=y ++CONFIG_FRAMEBUFFER_CONSOLE=y ++CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y ++# CONFIG_HID is not set ++# CONFIG_USB_HID is not set ++CONFIG_USB_LED_TRIG=y ++CONFIG_USB=m ++CONFIG_USB_ANNOUNCE_NEW_DEVICES=y ++CONFIG_USB_OTG=y ++CONFIG_USB_OTG_FSM=m ++CONFIG_USB_LEDS_TRIGGER_USBPORT=m ++CONFIG_USB_EHCI_HCD=m ++CONFIG_USB_EHCI_HCD_PLATFORM=m ++CONFIG_USB_OHCI_HCD=m ++CONFIG_USB_OHCI_HCD_PLATFORM=m ++CONFIG_USB_WDM=m ++CONFIG_USB_MUSB_HDRC=m ++CONFIG_USB_MUSB_SUNXI=m ++CONFIG_MUSB_PIO_ONLY=y ++CONFIG_NOP_USB_XCEIV=m ++CONFIG_USB_GADGET=m ++CONFIG_U_SERIAL_CONSOLE=y ++CONFIG_USB_CONFIGFS=m ++CONFIG_USB_CONFIGFS_SERIAL=y ++CONFIG_USB_CONFIGFS_ACM=y ++CONFIG_USB_CONFIGFS_OBEX=y ++CONFIG_USB_CONFIGFS_NCM=y ++CONFIG_USB_CONFIGFS_ECM=y ++CONFIG_USB_CONFIGFS_ECM_SUBSET=y ++CONFIG_USB_CONFIGFS_RNDIS=y ++CONFIG_USB_CONFIGFS_EEM=y ++CONFIG_USB_CONFIGFS_MASS_STORAGE=y ++CONFIG_USB_CONFIGFS_F_LB_SS=y ++CONFIG_USB_CONFIGFS_F_FS=y ++CONFIG_USB_CONFIGFS_F_HID=y ++CONFIG_USB_CONFIGFS_F_PRINTER=y ++CONFIG_USB_CDC_COMPOSITE=m ++CONFIG_MMC=y ++CONFIG_MMC_SUNXI=y ++CONFIG_NEW_LEDS=y ++CONFIG_LEDS_CLASS=y ++CONFIG_LEDS_GPIO=y ++CONFIG_LEDS_TRIGGER_TIMER=y ++CONFIG_LEDS_TRIGGER_ONESHOT=y ++CONFIG_LEDS_TRIGGER_HEARTBEAT=y ++CONFIG_LEDS_TRIGGER_CPU=y ++CONFIG_LEDS_TRIGGER_ACTIVITY=y ++CONFIG_LEDS_TRIGGER_DEFAULT_ON=y ++CONFIG_LEDS_TRIGGER_PANIC=y ++CONFIG_LEDS_TRIGGER_NETDEV=y ++CONFIG_LEDS_TRIGGER_PATTERN=y ++CONFIG_RTC_CLASS=y ++# CONFIG_RTC_INTF_SYSFS is not set ++# CONFIG_RTC_INTF_PROC is not set ++CONFIG_RTC_DRV_PCF8563=y ++CONFIG_DMADEVICES=y ++# CONFIG_VIRTIO_MENU is not set ++# CONFIG_VHOST_MENU is not set ++CONFIG_STAGING=y ++# CONFIG_CLK_SUNXI_PRCM_SUN6I is not set ++# CONFIG_CLK_SUNXI_PRCM_SUN8I is not set ++# CONFIG_CLK_SUNXI_PRCM_SUN9I is not set ++# CONFIG_IOMMU_SUPPORT is not set ++CONFIG_PM_DEVFREQ=y ++CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=y ++CONFIG_DEVFREQ_GOV_PERFORMANCE=y ++CONFIG_DEVFREQ_GOV_POWERSAVE=y ++CONFIG_DEVFREQ_GOV_USERSPACE=y ++CONFIG_DEVFREQ_GOV_PASSIVE=y ++CONFIG_PM_DEVFREQ_EVENT=y ++CONFIG_IIO=y ++CONFIG_IIO_CONFIGFS=y ++CONFIG_BMA180=y ++CONFIG_AXP20X_ADC=y ++CONFIG_SUN4I_GPADC=m ++CONFIG_LTR501=y ++CONFIG_AK8975=y ++CONFIG_PWM=y ++CONFIG_PWM_SUN4I=y ++CONFIG_PHY_SUN4I_USB=y ++CONFIG_ARM_CCI_PMU=y ++# CONFIG_ARM_CCI5xx_PMU is not set ++CONFIG_NVMEM_SUNXI_SID=y ++CONFIG_VALIDATE_FS_PARSER=y ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_FS_POSIX_ACL=y ++CONFIG_F2FS_FS=y ++CONFIG_F2FS_FS_SECURITY=y ++CONFIG_F2FS_CHECK_FS=y ++CONFIG_F2FS_FS_COMPRESSION=y ++CONFIG_FS_ENCRYPTION=y ++CONFIG_FANOTIFY=y ++CONFIG_AUTOFS_FS=y ++CONFIG_FUSE_FS=y ++CONFIG_CUSE=y ++CONFIG_VFAT_FS=y ++CONFIG_PROC_CHILDREN=y ++CONFIG_TMPFS=y ++CONFIG_TMPFS_POSIX_ACL=y ++# CONFIG_MISC_FILESYSTEMS is not set ++# CONFIG_NETWORK_FILESYSTEMS is not set ++CONFIG_NLS_CODEPAGE_437=y ++CONFIG_NLS_CODEPAGE_852=y ++CONFIG_NLS_ISO8859_1=y ++CONFIG_NLS_ISO8859_2=y ++CONFIG_NLS_UTF8=y ++CONFIG_KEYS_REQUEST_CACHE=y ++CONFIG_PERSISTENT_KEYRINGS=y ++CONFIG_BIG_KEYS=y ++CONFIG_ENCRYPTED_KEYS=y ++CONFIG_KEY_DH_OPERATIONS=y ++CONFIG_LSM="yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor" ++CONFIG_INIT_STACK_NONE=y ++CONFIG_CRYPTO_ECDH=y ++CONFIG_CRYPTO_CURVE25519=y ++CONFIG_CRYPTO_AES_TI=y ++CONFIG_CRYPTO_DES=y ++CONFIG_CRYPTO_ARC4=y ++CONFIG_CRYPTO_CHACHA20POLY1305=y ++CONFIG_CRYPTO_SEQIV=y ++CONFIG_CRYPTO_ECHAINIV=y ++CONFIG_CRYPTO_ESSIV=y ++CONFIG_CRYPTO_BLAKE2B=y ++CONFIG_CRYPTO_MD4=y ++CONFIG_CRYPTO_DEFLATE=y ++CONFIG_CRYPTO_LZO=y ++CONFIG_CRYPTO_LZ4=y ++CONFIG_CRYPTO_ZSTD=y ++CONFIG_CRYPTO_ANSI_CPRNG=y ++CONFIG_CRYPTO_USER_API_HASH=y ++CONFIG_CRYPTO_USER_API_SKCIPHER=y ++CONFIG_CRYPTO_USER_API_RNG=y ++CONFIG_CRYPTO_USER_API_AEAD=y ++CONFIG_CRYPTO_CURVE25519_NEON=y ++CONFIG_CRYPTO_GHASH_ARM_CE=y ++CONFIG_CRYPTO_POLY1305_ARM=y ++CONFIG_CRYPTO_SHA1_ARM_NEON=y ++CONFIG_CRYPTO_SHA1_ARM_CE=y ++CONFIG_CRYPTO_SHA2_ARM_CE=y ++CONFIG_CRYPTO_SHA512_ARM=y ++CONFIG_CRYPTO_AES_ARM_BS=y ++CONFIG_CRYPTO_AES_ARM_CE=y ++CONFIG_CRYPTO_CHACHA20_NEON=y ++CONFIG_CRYPTO_CRC32_ARM_CE=y ++CONFIG_CRYPTO_CRCT10DIF_ARM_CE=y ++CONFIG_CRYPTO_DEV_SUN4I_SS=y ++CONFIG_CRYPTO_DEV_SUN4I_SS_PRNG=y ++CONFIG_PKCS8_PRIVATE_KEY_PARSER=y ++CONFIG_CRYPTO_LIB_CURVE25519=y ++CONFIG_CRYPTO_LIB_CHACHA20POLY1305=y ++CONFIG_CRC_CCITT=y ++CONFIG_CRC_ITU_T=y ++CONFIG_LIBCRC32C=y ++CONFIG_DMA_CMA=y ++CONFIG_CMA_SIZE_MBYTES=128 ++CONFIG_CONSOLE_LOGLEVEL_DEFAULT=15 ++CONFIG_MESSAGE_LOGLEVEL_DEFAULT=7 ++CONFIG_DEBUG_FS=y ++# CONFIG_RUNTIME_TESTING_MENU is not set +diff --git a/arch/arm/configs/tbs_a711_defconfig b/arch/arm/configs/tbs_a711_defconfig +new file mode 100644 +index 000000000000..e29e2c7db357 +--- /dev/null ++++ b/arch/arm/configs/tbs_a711_defconfig +@@ -0,0 +1,524 @@ ++CONFIG_KERNEL_LZ4=y ++CONFIG_SYSVIPC=y ++CONFIG_POSIX_MQUEUE=y ++CONFIG_NO_HZ=y ++CONFIG_HIGH_RES_TIMERS=y ++CONFIG_BPF_SYSCALL=y ++# CONFIG_BPF_UNPRIV_DEFAULT_OFF is not set ++CONFIG_BSD_PROCESS_ACCT=y ++CONFIG_BSD_PROCESS_ACCT_V3=y ++CONFIG_TASKSTATS=y ++CONFIG_TASK_DELAY_ACCT=y ++CONFIG_TASK_XACCT=y ++CONFIG_TASK_IO_ACCOUNTING=y ++CONFIG_LOG_BUF_SHIFT=18 ++CONFIG_MEMCG=y ++CONFIG_BLK_CGROUP=y ++CONFIG_RT_GROUP_SCHED=y ++CONFIG_CGROUP_PIDS=y ++CONFIG_CPUSETS=y ++# CONFIG_PROC_PID_CPUSET is not set ++CONFIG_CGROUP_DEVICE=y ++CONFIG_CGROUP_BPF=y ++CONFIG_USER_NS=y ++CONFIG_SCHED_AUTOGROUP=y ++# CONFIG_RD_BZIP2 is not set ++# CONFIG_RD_LZMA is not set ++# CONFIG_RD_XZ is not set ++# CONFIG_RD_LZO is not set ++CONFIG_PERF_EVENTS=y ++CONFIG_ARCH_SUNXI=y ++# CONFIG_MACH_SUN9I is not set ++CONFIG_ARM_ERRATA_814220=y ++CONFIG_SMP=y ++CONFIG_SCHED_MC=y ++CONFIG_MCPM=y ++CONFIG_NR_CPUS=8 ++CONFIG_HZ_1000=y ++CONFIG_HIGHMEM=y ++# CONFIG_ARM_MODULE_PLTS is not set ++CONFIG_ARCH_FORCE_MAX_ORDER=11 ++CONFIG_ZBOOT_ROM_TEXT=0 ++CONFIG_ZBOOT_ROM_BSS=0 ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_STAT=y ++CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y ++CONFIG_CPU_FREQ_GOV_POWERSAVE=y ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++CONFIG_CPU_FREQ_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y ++CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y ++CONFIG_CPUFREQ_DT=y ++CONFIG_ARM_ALLWINNER_SUN50I_CPUFREQ_NVMEM=y ++CONFIG_CPU_IDLE=y ++CONFIG_CPU_IDLE_GOV_LADDER=y ++CONFIG_ARM_CPUIDLE=y ++CONFIG_VFP=y ++CONFIG_NEON=y ++CONFIG_KERNEL_MODE_NEON=y ++CONFIG_PM_WAKELOCKS=y ++CONFIG_PM_DEBUG=y ++CONFIG_PM_ADVANCED_DEBUG=y ++CONFIG_APM_EMULATION=y ++CONFIG_WQ_POWER_EFFICIENT_DEFAULT=y ++CONFIG_JUMP_LABEL=y ++# CONFIG_STACKPROTECTOR is not set ++CONFIG_MODULES=y ++CONFIG_MODULE_UNLOAD=y ++CONFIG_BLK_DEV_INTEGRITY=y ++CONFIG_BLK_DEV_THROTTLING=y ++CONFIG_BLK_WBT=y ++CONFIG_BLK_CGROUP_IOLATENCY=y ++# CONFIG_MQ_IOSCHED_DEADLINE is not set ++# CONFIG_MQ_IOSCHED_KYBER is not set ++# CONFIG_BFQ_GROUP_IOSCHED is not set ++CONFIG_CMA=y ++CONFIG_CMA_DEBUGFS=y ++CONFIG_CMA_AREAS=7 ++CONFIG_NET=y ++CONFIG_PACKET=y ++CONFIG_PACKET_DIAG=y ++CONFIG_UNIX=y ++CONFIG_UNIX_DIAG=y ++CONFIG_INET=y ++CONFIG_IP_ADVANCED_ROUTER=y ++CONFIG_SYN_COOKIES=y ++CONFIG_INET_DIAG=m ++CONFIG_INET_UDP_DIAG=m ++CONFIG_INET_RAW_DIAG=m ++# CONFIG_IPV6_SIT is not set ++CONFIG_MPTCP=y ++CONFIG_NETFILTER=y ++CONFIG_BRIDGE_NETFILTER=m ++CONFIG_NETFILTER_NETLINK_ACCT=m ++CONFIG_NETFILTER_NETLINK_QUEUE=m ++CONFIG_NETFILTER_NETLINK_LOG=m ++CONFIG_NF_CONNTRACK=m ++CONFIG_NF_CONNTRACK_MARK=y ++# CONFIG_NF_CT_PROTO_DCCP is not set ++# CONFIG_NF_CT_PROTO_SCTP is not set ++# CONFIG_NF_CT_PROTO_UDPLITE is not set ++CONFIG_NF_CONNTRACK_FTP=m ++CONFIG_NF_CONNTRACK_IRC=m ++CONFIG_NF_CONNTRACK_NETBIOS_NS=m ++CONFIG_NF_CONNTRACK_SIP=m ++CONFIG_NF_CT_NETLINK=m ++CONFIG_NETFILTER_NETLINK_GLUE_CT=y ++CONFIG_NF_TABLES=m ++CONFIG_NF_TABLES_INET=y ++CONFIG_NFT_NUMGEN=m ++CONFIG_NFT_CT=m ++CONFIG_NFT_CONNLIMIT=m ++CONFIG_NFT_LOG=m ++CONFIG_NFT_LIMIT=m ++CONFIG_NFT_MASQ=m ++CONFIG_NFT_REDIR=m ++CONFIG_NFT_NAT=m ++CONFIG_NFT_TUNNEL=m ++CONFIG_NFT_QUEUE=m ++CONFIG_NFT_QUOTA=m ++CONFIG_NFT_REJECT=m ++CONFIG_NFT_HASH=m ++CONFIG_NFT_FIB_INET=m ++CONFIG_NFT_SOCKET=m ++CONFIG_NFT_OSF=m ++CONFIG_NF_FLOW_TABLE_INET=m ++CONFIG_NF_FLOW_TABLE=m ++CONFIG_IP_SET=m ++CONFIG_IP_SET_BITMAP_IP=m ++CONFIG_IP_SET_BITMAP_IPMAC=m ++CONFIG_IP_SET_BITMAP_PORT=m ++CONFIG_IP_SET_HASH_IP=m ++CONFIG_IP_SET_HASH_IPMARK=m ++CONFIG_IP_SET_HASH_IPPORT=m ++CONFIG_IP_SET_HASH_IPPORTIP=m ++CONFIG_IP_SET_HASH_IPPORTNET=m ++CONFIG_IP_SET_HASH_IPMAC=m ++CONFIG_IP_SET_HASH_MAC=m ++CONFIG_IP_SET_HASH_NETPORTNET=m ++CONFIG_IP_SET_HASH_NET=m ++CONFIG_IP_SET_HASH_NETNET=m ++CONFIG_IP_SET_HASH_NETPORT=m ++CONFIG_IP_SET_HASH_NETIFACE=m ++CONFIG_IP_SET_LIST_SET=m ++CONFIG_NFT_DUP_IPV4=m ++CONFIG_NFT_FIB_IPV4=m ++CONFIG_NF_TABLES_ARP=y ++CONFIG_NF_LOG_ARP=m ++CONFIG_NF_LOG_IPV4=m ++CONFIG_NFT_DUP_IPV6=m ++CONFIG_NFT_FIB_IPV6=m ++CONFIG_NF_LOG_IPV6=m ++CONFIG_NF_TABLES_BRIDGE=m ++CONFIG_NFT_BRIDGE_REJECT=m ++CONFIG_BRIDGE=m ++CONFIG_NET_SCHED=y ++CONFIG_NET_SCH_HTB=y ++CONFIG_NET_CLS_BASIC=y ++CONFIG_NET_CLS_ROUTE4=y ++CONFIG_NET_CLS_FW=y ++CONFIG_NET_CLS_CGROUP=y ++CONFIG_NET_CLS_MATCHALL=y ++CONFIG_NET_CLS_ACT=y ++CONFIG_NET_ACT_POLICE=y ++CONFIG_NET_ACT_GACT=y ++CONFIG_BT=m ++CONFIG_BT_RFCOMM=m ++CONFIG_BT_RFCOMM_TTY=y ++CONFIG_BT_BNEP=m ++CONFIG_BT_BNEP_MC_FILTER=y ++CONFIG_BT_BNEP_PROTO_FILTER=y ++CONFIG_BT_HIDP=m ++CONFIG_BT_LEDS=y ++CONFIG_BT_HCIBTSDIO=m ++CONFIG_BT_HCIUART=m ++CONFIG_BT_HCIUART_BCM=y ++CONFIG_CFG80211=y ++# CONFIG_CFG80211_DEFAULT_PS is not set ++CONFIG_CFG80211_DEBUGFS=y ++CONFIG_MAC80211=y ++CONFIG_MAC80211_LEDS=y ++CONFIG_RFKILL=y ++CONFIG_RFKILL_GPIO=y ++CONFIG_NFC=m ++CONFIG_NFC_DIGITAL=m ++CONFIG_NFC_NCI=m ++CONFIG_NFC_NXP_NCI=m ++CONFIG_NFC_NXP_NCI_I2C=m ++CONFIG_PAGE_POOL_STATS=y ++CONFIG_UEVENT_HELPER=y ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++#CONFIG_EXTRA_FIRMWARE="regulatory.db regulatory.db.p7s brcm/brcmfmac43362-sdio.bin brcm/brcmfmac43362-sdio.txt brcm/BCM20702A1.hcd hm5065-af.bin hm5065-init.bin brcm/brcmfmac43362-sdio.tbs-biometrics,a711.txt" ++#CONFIG_EXTRA_FIRMWARE_DIR="/workspace/megous.com/orangepi-pc/firmware" ++# CONFIG_FW_CACHE is not set ++CONFIG_ARM_SCPI_PROTOCOL=m ++CONFIG_GNSS=y ++CONFIG_GNSS_SIRF_SERIAL=y ++CONFIG_GNSS_UBX_SERIAL=y ++CONFIG_OF_OVERLAY=y ++CONFIG_ZRAM=y ++CONFIG_ZRAM_WRITEBACK=y ++CONFIG_ZRAM_MEMORY_TRACKING=y ++CONFIG_BLK_DEV_LOOP=m ++CONFIG_BLK_DEV_NBD=m ++CONFIG_MODEM_POWER=m ++CONFIG_MD=y ++CONFIG_BLK_DEV_DM=y ++CONFIG_DM_CRYPT=y ++CONFIG_DM_INIT=y ++CONFIG_NETDEVICES=y ++CONFIG_WIREGUARD=m ++CONFIG_TUN=m ++CONFIG_VETH=m ++# CONFIG_ETHERNET is not set ++CONFIG_PPP=m ++CONFIG_PPP_BSDCOMP=m ++CONFIG_PPP_DEFLATE=m ++CONFIG_PPP_FILTER=y ++CONFIG_PPP_MPPE=m ++CONFIG_PPP_ASYNC=m ++CONFIG_PPP_SYNC_TTY=m ++# CONFIG_USB_NET_DRIVERS is not set ++# CONFIG_WLAN_VENDOR_ADMTEK is not set ++# CONFIG_WLAN_VENDOR_ATH is not set ++# CONFIG_WLAN_VENDOR_ATMEL is not set ++CONFIG_BRCMFMAC=m ++# CONFIG_WLAN_VENDOR_INTEL is not set ++# CONFIG_WLAN_VENDOR_INTERSIL is not set ++# CONFIG_WLAN_VENDOR_MARVELL is not set ++# CONFIG_WLAN_VENDOR_MEDIATEK is not set ++# CONFIG_WLAN_VENDOR_RALINK is not set ++# CONFIG_WLAN_VENDOR_REALTEK is not set ++# CONFIG_WLAN_VENDOR_RSI is not set ++# CONFIG_WLAN_VENDOR_ST is not set ++# CONFIG_WLAN_VENDOR_TI is not set ++# CONFIG_WLAN_VENDOR_ZYDAS is not set ++# CONFIG_WLAN_VENDOR_QUANTENNA is not set ++CONFIG_INPUT_EVDEV=y ++# CONFIG_KEYBOARD_ATKBD is not set ++CONFIG_KEYBOARD_GPIO=y ++CONFIG_KEYBOARD_GPIO_POLLED=y ++CONFIG_KEYBOARD_SUN4I_LRADC=y ++# CONFIG_INPUT_MOUSE is not set ++CONFIG_INPUT_TOUCHSCREEN=y ++CONFIG_TOUCHSCREEN_EDT_FT5X06=m ++CONFIG_INPUT_MISC=y ++CONFIG_INPUT_GPIO_VIBRA=y ++CONFIG_INPUT_AXP20X_PEK=y ++# CONFIG_SERIO is not set ++# CONFIG_LEGACY_PTYS is not set ++CONFIG_SERIAL_8250=y ++CONFIG_SERIAL_8250_CONSOLE=y ++CONFIG_SERIAL_8250_NR_UARTS=8 ++CONFIG_SERIAL_8250_RUNTIME_UARTS=8 ++CONFIG_SERIAL_8250_DW=y ++CONFIG_SERIAL_OF_PLATFORM=y ++CONFIG_SERIAL_DEV_BUS=y ++# CONFIG_HW_RANDOM is not set ++CONFIG_I2C_CHARDEV=y ++CONFIG_I2C_MUX=y ++CONFIG_I2C_GPIO=y ++CONFIG_I2C_MV64XXX=y ++CONFIG_SPI=y ++CONFIG_SPI_SUN4I=y ++CONFIG_SPI_SUN6I=y ++CONFIG_SPI_SPIDEV=y ++CONFIG_PINCTRL_AXP209=y ++CONFIG_POWER_SUPPLY=y ++CONFIG_CHARGER_AXP20X=y ++CONFIG_BATTERY_AXP20X=y ++CONFIG_AXP20X_POWER=y ++# CONFIG_HWMON is not set ++CONFIG_THERMAL=y ++CONFIG_THERMAL_STATISTICS=y ++CONFIG_THERMAL_GOV_FAIR_SHARE=y ++CONFIG_THERMAL_GOV_BANG_BANG=y ++CONFIG_CPU_THERMAL=y ++CONFIG_SUN8I_THERMAL=y ++CONFIG_WATCHDOG=y ++CONFIG_SUNXI_WATCHDOG=y ++CONFIG_MFD_SUN4I_GPADC=y ++CONFIG_MFD_AC100=y ++CONFIG_MFD_AXP20X_RSB=y ++CONFIG_REGULATOR=y ++CONFIG_REGULATOR_FIXED_VOLTAGE=y ++CONFIG_REGULATOR_USERSPACE_CONSUMER=y ++CONFIG_REGULATOR_AXP20X=y ++CONFIG_REGULATOR_GPIO=y ++CONFIG_REGULATOR_PWM=y ++CONFIG_MEDIA_SUPPORT=m ++# CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set ++CONFIG_MEDIA_CAMERA_SUPPORT=y ++CONFIG_MEDIA_PLATFORM_SUPPORT=y ++CONFIG_VIDEO_ADV_DEBUG=y ++CONFIG_V4L_PLATFORM_DRIVERS=y ++CONFIG_V4L_MEM2MEM_DRIVERS=y ++CONFIG_VIDEO_MEM2MEM_DEINTERLACE=m ++CONFIG_VIDEO_SUN6I_CSI=m ++CONFIG_VIDEO_SUN8I_DEINTERLACE=m ++CONFIG_VIDEO_SUN8I_ROTATE=m ++CONFIG_VIDEO_HM5065=m ++CONFIG_VIDEO_GC2145=m ++CONFIG_DRM=y ++CONFIG_DRM_SUN4I=y ++# CONFIG_DRM_SUN4I_HDMI is not set ++# CONFIG_DRM_SUN4I_BACKEND is not set ++# CONFIG_DRM_SUN6I_DSI is not set ++# CONFIG_DRM_SUN8I_DW_HDMI is not set ++CONFIG_DRM_PANEL_LVDS=y ++CONFIG_FB=y ++CONFIG_BACKLIGHT_CLASS_DEVICE=y ++CONFIG_BACKLIGHT_PWM=y ++CONFIG_SOUND=m ++CONFIG_SND=m ++CONFIG_SND_HRTIMER=m ++# CONFIG_SND_SUPPORT_OLD_API is not set ++CONFIG_SND_DEBUG=y ++CONFIG_SND_DEBUG_VERBOSE=y ++# CONFIG_SND_DRIVERS is not set ++# CONFIG_SND_ARM is not set ++# CONFIG_SND_SPI is not set ++# CONFIG_SND_USB is not set ++CONFIG_SND_SOC=m ++CONFIG_SND_SUN8I_CODEC=m ++CONFIG_SND_AC100_CODEC=m ++CONFIG_SND_SUN4I_I2S=m ++CONFIG_SND_SOC_BT_SCO=m ++CONFIG_SND_SOC_EC25=m ++CONFIG_SND_SOC_SIMPLE_AMPLIFIER=m ++CONFIG_SND_SIMPLE_CARD=m ++CONFIG_HIDRAW=y ++# CONFIG_HID_A4TECH is not set ++# CONFIG_HID_APPLE is not set ++# CONFIG_HID_BELKIN is not set ++# CONFIG_HID_CHERRY is not set ++# CONFIG_HID_CHICONY is not set ++# CONFIG_HID_CYPRESS is not set ++# CONFIG_HID_EZKEY is not set ++# CONFIG_HID_ITE is not set ++# CONFIG_HID_KENSINGTON is not set ++# CONFIG_HID_LOGITECH is not set ++# CONFIG_HID_REDRAGON is not set ++# CONFIG_HID_MICROSOFT is not set ++# CONFIG_HID_MONTEREY is not set ++CONFIG_USB_HIDDEV=y ++CONFIG_USB_LED_TRIG=y ++CONFIG_USB=m ++CONFIG_USB_ANNOUNCE_NEW_DEVICES=y ++CONFIG_USB_OTG=y ++CONFIG_USB_OTG_FSM=m ++CONFIG_USB_LEDS_TRIGGER_USBPORT=m ++CONFIG_USB_EHCI_HCD=m ++CONFIG_USB_EHCI_HCD_PLATFORM=m ++CONFIG_USB_OHCI_HCD=m ++CONFIG_USB_OHCI_HCD_PLATFORM=m ++CONFIG_USB_ACM=m ++CONFIG_USB_WDM=m ++CONFIG_USB_MUSB_HDRC=m ++CONFIG_USB_MUSB_SUNXI=m ++CONFIG_USB_SERIAL=m ++CONFIG_USB_SERIAL_GENERIC=y ++CONFIG_USB_SERIAL_OPTION=m ++CONFIG_NOP_USB_XCEIV=m ++CONFIG_USB_GADGET=m ++CONFIG_U_SERIAL_CONSOLE=y ++CONFIG_USB_CONFIGFS=m ++CONFIG_USB_CONFIGFS_SERIAL=y ++CONFIG_USB_CONFIGFS_ACM=y ++CONFIG_USB_CONFIGFS_OBEX=y ++CONFIG_USB_CONFIGFS_NCM=y ++CONFIG_USB_CONFIGFS_ECM=y ++CONFIG_USB_CONFIGFS_ECM_SUBSET=y ++CONFIG_USB_CONFIGFS_RNDIS=y ++CONFIG_USB_CONFIGFS_EEM=y ++CONFIG_USB_CONFIGFS_MASS_STORAGE=y ++CONFIG_USB_CONFIGFS_F_LB_SS=y ++CONFIG_USB_CONFIGFS_F_FS=y ++CONFIG_USB_CONFIGFS_F_UAC1=y ++CONFIG_USB_CONFIGFS_F_UAC2=y ++CONFIG_USB_CONFIGFS_F_MIDI=y ++CONFIG_USB_CONFIGFS_F_HID=y ++CONFIG_USB_CONFIGFS_F_UVC=y ++CONFIG_USB_CONFIGFS_F_PRINTER=y ++CONFIG_USB_ETH=m ++CONFIG_USB_ETH_EEM=y ++CONFIG_MMC=y ++CONFIG_MMC_SUNXI=y ++CONFIG_NEW_LEDS=y ++CONFIG_LEDS_CLASS=y ++CONFIG_LEDS_GPIO=y ++CONFIG_LEDS_AXP20X=y ++CONFIG_LEDS_TRIGGER_TIMER=y ++CONFIG_LEDS_TRIGGER_ONESHOT=y ++CONFIG_LEDS_TRIGGER_HEARTBEAT=y ++CONFIG_LEDS_TRIGGER_BACKLIGHT=y ++CONFIG_LEDS_TRIGGER_CPU=y ++CONFIG_LEDS_TRIGGER_ACTIVITY=y ++CONFIG_LEDS_TRIGGER_DEFAULT_ON=y ++CONFIG_LEDS_TRIGGER_PANIC=y ++CONFIG_LEDS_TRIGGER_NETDEV=y ++CONFIG_LEDS_TRIGGER_PATTERN=y ++CONFIG_RTC_CLASS=y ++# CONFIG_RTC_INTF_SYSFS is not set ++# CONFIG_RTC_INTF_PROC is not set ++CONFIG_RTC_DRV_AC100=y ++# CONFIG_RTC_DRV_SUN6I is not set ++CONFIG_DMADEVICES=y ++CONFIG_DMA_SUN6I=y ++CONFIG_DMABUF_HEAPS=y ++CONFIG_DMABUF_HEAPS_SYSTEM=y ++CONFIG_DMABUF_HEAPS_CMA=y ++# CONFIG_VIRTIO_MENU is not set ++# CONFIG_VHOST_MENU is not set ++CONFIG_STAGING=y ++CONFIG_STAGING_MEDIA=y ++CONFIG_VIDEO_SUNXI=y ++CONFIG_VIDEO_SUNXI_CEDRUS=m ++# CONFIG_CLK_SUNXI_PRCM_SUN9I is not set ++# CONFIG_SUN4I_A10_CCU is not set ++# CONFIG_SUN5I_CCU is not set ++# CONFIG_SUN6I_A31_CCU is not set ++# CONFIG_SUN8I_A23_CCU is not set ++# CONFIG_SUN8I_A33_CCU is not set ++# CONFIG_SUN8I_V3S_CCU is not set ++# CONFIG_SUN8I_R40_CCU is not set ++CONFIG_MAILBOX=y ++# CONFIG_IOMMU_SUPPORT is not set ++# CONFIG_ARM_SCPI_POWER_DOMAIN is not set ++CONFIG_IIO=y ++CONFIG_IIO_KFIFO_BUF=y ++CONFIG_IIO_CONFIGFS=y ++CONFIG_BMA180=m ++CONFIG_AXP20X_ADC=y ++CONFIG_PWM=y ++CONFIG_PWM_SUN4I=y ++CONFIG_PHY_SUN4I_USB=y ++CONFIG_ARM_CCI_PMU=y ++# CONFIG_ARM_CCI5xx_PMU is not set ++CONFIG_NVMEM_SUNXI_SID=y ++CONFIG_VALIDATE_FS_PARSER=y ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_FS_POSIX_ACL=y ++CONFIG_F2FS_FS=y ++CONFIG_F2FS_FS_SECURITY=y ++CONFIG_F2FS_CHECK_FS=y ++CONFIG_F2FS_FS_COMPRESSION=y ++CONFIG_FS_ENCRYPTION=y ++CONFIG_FANOTIFY=y ++CONFIG_AUTOFS_FS=y ++CONFIG_FUSE_FS=m ++CONFIG_VFAT_FS=y ++CONFIG_PROC_CHILDREN=y ++CONFIG_TMPFS=y ++CONFIG_TMPFS_POSIX_ACL=y ++# CONFIG_MISC_FILESYSTEMS is not set ++# CONFIG_NETWORK_FILESYSTEMS is not set ++CONFIG_NLS_CODEPAGE_437=y ++CONFIG_NLS_CODEPAGE_852=y ++CONFIG_NLS_ISO8859_1=y ++CONFIG_NLS_ISO8859_2=y ++CONFIG_NLS_UTF8=y ++CONFIG_KEYS_REQUEST_CACHE=y ++CONFIG_PERSISTENT_KEYRINGS=y ++CONFIG_ENCRYPTED_KEYS=y ++CONFIG_KEY_DH_OPERATIONS=y ++CONFIG_LSM="yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor" ++CONFIG_INIT_STACK_NONE=y ++CONFIG_CRYPTO_PCRYPT=y ++CONFIG_CRYPTO_ECDH=y ++CONFIG_CRYPTO_CURVE25519=y ++CONFIG_CRYPTO_AES_TI=y ++CONFIG_CRYPTO_DES=y ++CONFIG_CRYPTO_CHACHA20POLY1305=y ++CONFIG_CRYPTO_SEQIV=y ++CONFIG_CRYPTO_ECHAINIV=y ++CONFIG_CRYPTO_BLAKE2B=y ++CONFIG_CRYPTO_MD4=y ++CONFIG_CRYPTO_MD5=y ++CONFIG_CRYPTO_DEFLATE=m ++CONFIG_CRYPTO_LZO=y ++CONFIG_CRYPTO_LZ4=y ++CONFIG_CRYPTO_LZ4HC=y ++CONFIG_CRYPTO_ZSTD=y ++CONFIG_CRYPTO_ANSI_CPRNG=y ++CONFIG_CRYPTO_USER_API_HASH=m ++CONFIG_CRYPTO_USER_API_SKCIPHER=m ++CONFIG_CRYPTO_USER_API_RNG=m ++CONFIG_CRYPTO_USER_API_AEAD=m ++CONFIG_CRYPTO_CURVE25519_NEON=y ++CONFIG_CRYPTO_POLY1305_ARM=y ++CONFIG_CRYPTO_SHA1_ARM_NEON=y ++CONFIG_CRYPTO_SHA1_ARM_CE=y ++CONFIG_CRYPTO_SHA2_ARM_CE=y ++CONFIG_CRYPTO_SHA512_ARM=y ++CONFIG_CRYPTO_AES_ARM_BS=y ++CONFIG_CRYPTO_AES_ARM_CE=y ++CONFIG_CRYPTO_CRC32_ARM_CE=y ++CONFIG_CRYPTO_DEV_SUN4I_SS=m ++CONFIG_CRYPTO_DEV_SUN4I_SS_PRNG=y ++CONFIG_CRYPTO_DEV_SUN8I_CE=m ++CONFIG_CRYPTO_DEV_SUN8I_SS=m ++CONFIG_PKCS8_PRIVATE_KEY_PARSER=y ++CONFIG_CRC_CCITT=y ++CONFIG_CRC_ITU_T=y ++CONFIG_LIBCRC32C=y ++CONFIG_DMA_CMA=y ++CONFIG_CMA_SIZE_MBYTES=64 ++CONFIG_CONSOLE_LOGLEVEL_DEFAULT=15 ++CONFIG_MESSAGE_LOGLEVEL_DEFAULT=7 ++CONFIG_DEBUG_KERNEL=y ++CONFIG_MAGIC_SYSRQ=y ++CONFIG_DEBUG_FS=y ++CONFIG_DETECT_HUNG_TASK=y ++CONFIG_BOOTPARAM_HUNG_TASK_PANIC=y ++CONFIG_WQ_WATCHDOG=y ++# CONFIG_SCHED_DEBUG is not set ++# CONFIG_RCU_TRACE is not set ++CONFIG_BOOTTIME_TRACING=y ++CONFIG_FUNCTION_TRACER=y ++# CONFIG_UPROBE_EVENTS is not set ++CONFIG_UNWINDER_FRAME_POINTER=y ++CONFIG_DEBUG_LL=y ++CONFIG_EARLY_PRINTK=y ++# CONFIG_RUNTIME_TESTING_MENU is not set +diff --git a/arch/arm64/configs/orangepi_defconfig b/arch/arm64/configs/orangepi_defconfig +new file mode 100644 +index 000000000000..19cfe9bb4c6e +--- /dev/null ++++ b/arch/arm64/configs/orangepi_defconfig +@@ -0,0 +1,1037 @@ ++CONFIG_SYSVIPC=y ++CONFIG_POSIX_MQUEUE=y ++CONFIG_WATCH_QUEUE=y ++CONFIG_GENERIC_IRQ_DEBUGFS=y ++CONFIG_NO_HZ=y ++CONFIG_HIGH_RES_TIMERS=y ++CONFIG_BPF_SYSCALL=y ++CONFIG_BPF_JIT=y ++CONFIG_BPF_JIT_ALWAYS_ON=y ++# CONFIG_BPF_UNPRIV_DEFAULT_OFF is not set ++CONFIG_PREEMPT_VOLUNTARY=y ++CONFIG_BSD_PROCESS_ACCT=y ++CONFIG_BSD_PROCESS_ACCT_V3=y ++CONFIG_TASKSTATS=y ++CONFIG_TASK_DELAY_ACCT=y ++CONFIG_TASK_XACCT=y ++CONFIG_TASK_IO_ACCOUNTING=y ++CONFIG_PSI=y ++CONFIG_IKCONFIG=y ++CONFIG_IKCONFIG_PROC=y ++CONFIG_LOG_BUF_SHIFT=20 ++CONFIG_LOG_CPU_MAX_BUF_SHIFT=14 ++CONFIG_MEMCG=y ++CONFIG_BLK_CGROUP=y ++CONFIG_CFS_BANDWIDTH=y ++CONFIG_RT_GROUP_SCHED=y ++CONFIG_CGROUP_PIDS=y ++CONFIG_CGROUP_FREEZER=y ++CONFIG_CPUSETS=y ++# CONFIG_PROC_PID_CPUSET is not set ++CONFIG_CGROUP_DEVICE=y ++CONFIG_CGROUP_CPUACCT=y ++CONFIG_CGROUP_BPF=y ++CONFIG_CGROUP_MISC=y ++CONFIG_USER_NS=y ++CONFIG_SCHED_AUTOGROUP=y ++CONFIG_RELAY=y ++# CONFIG_RD_BZIP2 is not set ++# CONFIG_RD_LZMA is not set ++# CONFIG_RD_XZ is not set ++# CONFIG_RD_LZO is not set ++CONFIG_BOOT_CONFIG=y ++CONFIG_KALLSYMS_ALL=y ++CONFIG_PERF_EVENTS=y ++CONFIG_ARCH_SUNXI=y ++CONFIG_ARCH_ROCKCHIP=y ++# CONFIG_ARM64_ERRATUM_832075 is not set ++CONFIG_ARM64_ERRATUM_2441007=y ++CONFIG_ARM64_ERRATUM_1286807=y ++# CONFIG_ARM64_ERRATUM_1508412 is not set ++# CONFIG_ARM64_ERRATUM_2051678 is not set ++# CONFIG_ARM64_ERRATUM_2077057 is not set ++# CONFIG_ARM64_ERRATUM_2658417 is not set ++# CONFIG_ARM64_ERRATUM_2054223 is not set ++# CONFIG_ARM64_ERRATUM_2067961 is not set ++# CONFIG_ARM64_ERRATUM_2645198 is not set ++# CONFIG_ARM64_ERRATUM_2966298 is not set ++# CONFIG_CAVIUM_ERRATUM_22375 is not set ++# CONFIG_CAVIUM_ERRATUM_23144 is not set ++# CONFIG_CAVIUM_ERRATUM_23154 is not set ++# CONFIG_CAVIUM_ERRATUM_27456 is not set ++# CONFIG_CAVIUM_ERRATUM_30115 is not set ++# CONFIG_CAVIUM_TX2_ERRATUM_219 is not set ++# CONFIG_FUJITSU_ERRATUM_010001 is not set ++# CONFIG_HISILICON_ERRATUM_161600802 is not set ++# CONFIG_QCOM_FALKOR_ERRATUM_1003 is not set ++# CONFIG_QCOM_FALKOR_ERRATUM_1009 is not set ++# CONFIG_QCOM_QDF2400_ERRATUM_0065 is not set ++# CONFIG_QCOM_FALKOR_ERRATUM_E1041 is not set ++# CONFIG_NVIDIA_CARMEL_CNP_ERRATUM is not set ++CONFIG_ARM64_VA_BITS_48=y ++CONFIG_SCHED_MC=y ++CONFIG_SCHED_CLUSTER=y ++CONFIG_NR_CPUS=8 ++CONFIG_NUMA=y ++CONFIG_NODES_SHIFT=3 ++CONFIG_PARAVIRT=y ++CONFIG_COMPAT=y ++CONFIG_ARM64_PMEM=y ++# CONFIG_ARM64_PTR_AUTH is not set ++# CONFIG_ARM64_AMU_EXTN is not set ++# CONFIG_ARM64_TLB_RANGE is not set ++# CONFIG_ARM64_BTI is not set ++# CONFIG_ARM64_E0PD is not set ++# CONFIG_ARM64_MTE is not set ++# CONFIG_ARM64_EPAN is not set ++# CONFIG_ARM64_SVE is not set ++# CONFIG_EFI is not set ++CONFIG_HIBERNATION=y ++CONFIG_PM_AUTOSLEEP=y ++CONFIG_PM_WAKELOCKS=y ++CONFIG_PM_DEBUG=y ++CONFIG_WQ_POWER_EFFICIENT_DEFAULT=y ++CONFIG_ENERGY_MODEL=y ++CONFIG_CPU_IDLE=y ++CONFIG_CPU_IDLE_GOV_LADDER=y ++CONFIG_ARM_PSCI_CPUIDLE=y ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_STAT=y ++CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y ++CONFIG_CPU_FREQ_GOV_POWERSAVE=y ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++CONFIG_CPU_FREQ_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y ++CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y ++CONFIG_CPUFREQ_DT=y ++CONFIG_ARM_ALLWINNER_SUN50I_CPUFREQ_NVMEM=y ++CONFIG_ARM_SCPI_CPUFREQ=y ++CONFIG_ARM_SCMI_CPUFREQ=y ++CONFIG_VIRTUALIZATION=y ++CONFIG_KVM=y ++CONFIG_JUMP_LABEL=y ++# CONFIG_STACKPROTECTOR is not set ++CONFIG_MODULES=y ++CONFIG_MODULE_UNLOAD=y ++CONFIG_BLK_DEV_THROTTLING=y ++CONFIG_BLK_WBT=y ++CONFIG_BLK_CGROUP_IOLATENCY=y ++CONFIG_BLK_CGROUP_IOCOST=y ++CONFIG_BLK_CGROUP_IOPRIO=y ++CONFIG_BLK_INLINE_ENCRYPTION=y ++CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK=y ++CONFIG_PARTITION_ADVANCED=y ++CONFIG_BINFMT_MISC=y ++CONFIG_ZSWAP=y ++CONFIG_ZSWAP_DEFAULT_ON=y ++CONFIG_ZSWAP_COMPRESSOR_DEFAULT_LZ4=y ++CONFIG_ZSWAP_ZPOOL_DEFAULT_ZBUD=y ++CONFIG_ZSMALLOC_STAT=y ++CONFIG_TRANSPARENT_HUGEPAGE=y ++CONFIG_CMA=y ++CONFIG_CMA_DEBUGFS=y ++CONFIG_CMA_SYSFS=y ++CONFIG_CMA_AREAS=7 ++CONFIG_NET=y ++CONFIG_PACKET=y ++CONFIG_PACKET_DIAG=y ++CONFIG_UNIX=y ++CONFIG_UNIX_DIAG=y ++CONFIG_XFRM_USER=y ++CONFIG_NET_KEY=y ++CONFIG_INET=y ++CONFIG_IP_MULTICAST=y ++CONFIG_IP_ADVANCED_ROUTER=y ++CONFIG_IP_MULTIPLE_TABLES=y ++CONFIG_IP_ROUTE_MULTIPATH=y ++CONFIG_IP_ROUTE_VERBOSE=y ++CONFIG_IP_MROUTE=y ++CONFIG_IP_MROUTE_MULTIPLE_TABLES=y ++CONFIG_INET_UDP_DIAG=y ++CONFIG_INET_RAW_DIAG=y ++CONFIG_INET_DIAG_DESTROY=y ++CONFIG_IPV6_ROUTER_PREF=y ++CONFIG_IPV6_ROUTE_INFO=y ++CONFIG_IPV6_OPTIMISTIC_DAD=y ++CONFIG_IPV6_MIP6=y ++CONFIG_IPV6_TUNNEL=y ++CONFIG_IPV6_MULTIPLE_TABLES=y ++CONFIG_IPV6_SUBTREES=y ++CONFIG_IPV6_MROUTE=y ++CONFIG_IPV6_MROUTE_MULTIPLE_TABLES=y ++CONFIG_NETFILTER=y ++CONFIG_BRIDGE_NETFILTER=y ++CONFIG_NETFILTER_NETLINK_HOOK=y ++CONFIG_NETFILTER_NETLINK_ACCT=y ++CONFIG_NETFILTER_NETLINK_QUEUE=y ++CONFIG_NETFILTER_NETLINK_LOG=y ++CONFIG_NF_CONNTRACK=y ++CONFIG_NF_CONNTRACK_MARK=y ++CONFIG_NF_CONNTRACK_EVENTS=y ++CONFIG_NF_CONNTRACK_TIMEOUT=y ++CONFIG_NF_CONNTRACK_TIMESTAMP=y ++CONFIG_NF_CONNTRACK_LABELS=y ++CONFIG_NF_CONNTRACK_FTP=y ++CONFIG_NF_CONNTRACK_H323=y ++CONFIG_NF_CONNTRACK_IRC=y ++CONFIG_NF_CONNTRACK_NETBIOS_NS=y ++CONFIG_NF_CONNTRACK_SNMP=y ++CONFIG_NF_CONNTRACK_PPTP=y ++CONFIG_NF_CONNTRACK_SANE=y ++CONFIG_NF_CONNTRACK_SIP=y ++CONFIG_NF_CONNTRACK_TFTP=y ++CONFIG_NF_CT_NETLINK=y ++CONFIG_NETFILTER_NETLINK_GLUE_CT=y ++CONFIG_NF_TABLES=y ++CONFIG_NF_TABLES_INET=y ++CONFIG_NF_TABLES_NETDEV=y ++CONFIG_NFT_NUMGEN=y ++CONFIG_NFT_CT=y ++CONFIG_NFT_FLOW_OFFLOAD=y ++CONFIG_NFT_CONNLIMIT=y ++CONFIG_NFT_LOG=y ++CONFIG_NFT_LIMIT=y ++CONFIG_NFT_MASQ=y ++CONFIG_NFT_REDIR=y ++CONFIG_NFT_NAT=y ++CONFIG_NFT_TUNNEL=y ++CONFIG_NFT_QUEUE=y ++CONFIG_NFT_QUOTA=y ++CONFIG_NFT_REJECT=y ++CONFIG_NFT_HASH=y ++CONFIG_NFT_FIB_INET=y ++CONFIG_NFT_SOCKET=y ++CONFIG_NFT_OSF=y ++CONFIG_NFT_TPROXY=y ++CONFIG_NFT_SYNPROXY=y ++CONFIG_NFT_DUP_NETDEV=y ++CONFIG_NFT_FWD_NETDEV=y ++CONFIG_NFT_FIB_NETDEV=y ++CONFIG_NFT_REJECT_NETDEV=y ++CONFIG_NF_FLOW_TABLE_INET=y ++CONFIG_NF_FLOW_TABLE=y ++CONFIG_IP_SET=y ++CONFIG_IP_SET_BITMAP_IP=y ++CONFIG_IP_SET_BITMAP_IPMAC=y ++CONFIG_IP_SET_BITMAP_PORT=y ++CONFIG_IP_SET_HASH_IP=y ++CONFIG_IP_SET_HASH_IPMARK=y ++CONFIG_IP_SET_HASH_IPPORT=y ++CONFIG_IP_SET_HASH_IPPORTIP=y ++CONFIG_IP_SET_HASH_IPPORTNET=y ++CONFIG_IP_SET_HASH_IPMAC=y ++CONFIG_IP_SET_HASH_MAC=y ++CONFIG_IP_SET_HASH_NETPORTNET=y ++CONFIG_IP_SET_HASH_NET=y ++CONFIG_IP_SET_HASH_NETNET=y ++CONFIG_IP_SET_HASH_NETPORT=y ++CONFIG_IP_SET_HASH_NETIFACE=y ++CONFIG_IP_SET_LIST_SET=y ++CONFIG_NFT_DUP_IPV4=y ++CONFIG_NFT_FIB_IPV4=y ++CONFIG_NF_TABLES_ARP=y ++CONFIG_NF_LOG_ARP=y ++CONFIG_NF_LOG_IPV4=y ++# CONFIG_NF_NAT_SNMP_BASIC is not set ++CONFIG_NFT_DUP_IPV6=y ++CONFIG_NFT_FIB_IPV6=y ++CONFIG_NF_LOG_IPV6=y ++CONFIG_NF_TABLES_BRIDGE=y ++CONFIG_NFT_BRIDGE_META=y ++CONFIG_NFT_BRIDGE_REJECT=y ++CONFIG_NF_CONNTRACK_BRIDGE=y ++CONFIG_L2TP=y ++CONFIG_L2TP_V3=y ++CONFIG_L2TP_IP=y ++CONFIG_BRIDGE=y ++CONFIG_BRIDGE_VLAN_FILTERING=y ++CONFIG_BRIDGE_MRP=y ++CONFIG_BRIDGE_CFM=y ++CONFIG_VLAN_8021Q=y ++CONFIG_VLAN_8021Q_GVRP=y ++CONFIG_VLAN_8021Q_MVRP=y ++CONFIG_NET_SCHED=y ++CONFIG_NET_SCH_HTB=y ++CONFIG_NET_SCH_HFSC=y ++CONFIG_NET_SCH_PRIO=y ++CONFIG_NET_SCH_MULTIQ=y ++CONFIG_NET_SCH_RED=y ++CONFIG_NET_SCH_SFB=y ++CONFIG_NET_SCH_SFQ=y ++CONFIG_NET_SCH_TEQL=y ++CONFIG_NET_SCH_TBF=y ++CONFIG_NET_SCH_CBS=y ++CONFIG_NET_SCH_ETF=y ++CONFIG_NET_SCH_TAPRIO=y ++CONFIG_NET_SCH_GRED=y ++CONFIG_NET_SCH_NETEM=y ++CONFIG_NET_SCH_DRR=y ++CONFIG_NET_SCH_MQPRIO=y ++CONFIG_NET_SCH_SKBPRIO=y ++CONFIG_NET_SCH_CHOKE=y ++CONFIG_NET_SCH_QFQ=y ++CONFIG_NET_SCH_CODEL=y ++CONFIG_NET_SCH_FQ_CODEL=y ++CONFIG_NET_SCH_CAKE=y ++CONFIG_NET_SCH_FQ=y ++CONFIG_NET_SCH_HHF=y ++CONFIG_NET_SCH_PIE=y ++CONFIG_NET_SCH_FQ_PIE=y ++CONFIG_NET_SCH_INGRESS=y ++CONFIG_NET_SCH_PLUG=y ++CONFIG_NET_SCH_ETS=y ++CONFIG_NET_CLS_BASIC=y ++CONFIG_NET_CLS_ROUTE4=y ++CONFIG_NET_CLS_FW=y ++CONFIG_NET_CLS_U32=y ++CONFIG_CLS_U32_PERF=y ++CONFIG_CLS_U32_MARK=y ++CONFIG_NET_CLS_FLOW=y ++CONFIG_NET_CLS_CGROUP=y ++CONFIG_NET_CLS_BPF=y ++CONFIG_NET_CLS_FLOWER=y ++CONFIG_NET_CLS_MATCHALL=y ++CONFIG_NET_EMATCH=y ++CONFIG_NET_EMATCH_CMP=y ++CONFIG_NET_EMATCH_NBYTE=y ++CONFIG_NET_EMATCH_U32=y ++CONFIG_NET_EMATCH_META=y ++CONFIG_NET_EMATCH_TEXT=y ++CONFIG_NET_EMATCH_IPSET=y ++CONFIG_NET_CLS_ACT=y ++CONFIG_NET_ACT_POLICE=y ++CONFIG_NET_ACT_GACT=y ++CONFIG_GACT_PROB=y ++CONFIG_NET_ACT_CONNMARK=y ++CONFIG_NET_ACT_CTINFO=y ++CONFIG_NETLINK_DIAG=y ++CONFIG_CGROUP_NET_PRIO=y ++CONFIG_BT=y ++CONFIG_BT_RFCOMM=y ++CONFIG_BT_RFCOMM_TTY=y ++CONFIG_BT_BNEP=y ++CONFIG_BT_BNEP_MC_FILTER=y ++CONFIG_BT_BNEP_PROTO_FILTER=y ++CONFIG_BT_HIDP=y ++CONFIG_BT_LEDS=y ++CONFIG_BT_HCIBTUSB=y ++CONFIG_BT_HCIBTUSB_AUTOSUSPEND=y ++CONFIG_BT_HCIUART=y ++CONFIG_BT_HCIUART_BCM=y ++CONFIG_BT_HCIUART_RTL=y ++CONFIG_CFG80211=y ++CONFIG_CFG80211_DEBUGFS=y ++CONFIG_CFG80211_WEXT=y ++CONFIG_MAC80211=y ++CONFIG_MAC80211_MESH=y ++CONFIG_MAC80211_LEDS=y ++CONFIG_MAC80211_DEBUGFS=y ++CONFIG_RFKILL=y ++CONFIG_RFKILL_GPIO=y ++CONFIG_PAGE_POOL_STATS=y ++CONFIG_PCI=y ++CONFIG_PCIEPORTBUS=y ++CONFIG_HOTPLUG_PCI_PCIE=y ++CONFIG_PCIEAER=y ++CONFIG_PCIE_DPC=y ++CONFIG_PCIE_PTM=y ++CONFIG_PCI_DEBUG=y ++CONFIG_PCI_REALLOC_ENABLE_AUTO=y ++CONFIG_PCI_STUB=y ++CONFIG_PCI_PF_STUB=y ++CONFIG_PCI_IOV=y ++CONFIG_PCI_PRI=y ++CONFIG_PCI_PASID=y ++CONFIG_HOTPLUG_PCI=y ++CONFIG_PCIE_ROCKCHIP_HOST=y ++CONFIG_PCIE_ROCKCHIP_DW_HOST=y ++CONFIG_UEVENT_HELPER=y ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++#CONFIG_EXTRA_FIRMWARE="regulatory.db regulatory.db.p7s edid/asus.bin brcm/brcmfmac43456-sdio.bin brcm/brcmfmac43456-sdio.clm_blob brcm/brcmfmac43456-sdio.txt brcm/brcmfmac43456-sdio.pine64,pinebook-pro.txt brcm/BCM4345C5.hcd brcm/brcmfmac43456-sdio.xunlong,orangepi-3.txt anx7688-fw.bin rtl_bt/rtl8723cs_xx_config.bin rtl_bt/rtl8723cs_xx_fw.bin ov5640_af.bin brcm/brcmfmac43455-sdio.bin brcm/brcmfmac43455-sdio.pine64,pinephone-pro.txt brcm/brcmfmac43455-sdio.pine64,pinebook-pro.txt brcm/brcmfmac43455-sdio.clm_blob brcm/BCM4345C0.hcd rockchip/dptx.bin rt2870.bin rtw88/rtw8822c_fw.bin rtw88/rtw8822c_wow_fw.bin rtw89/rtw8852a_fw.bin rtl_nic/rtl8125a-3.fw rtl_nic/rtl8125b-2.fw rtl_nic/rtl8153a-4.fw rtl_nic/rtl8168h-2.fw brcm/brcmfmac43752-pcie.clm_blob brcm/brcmfmac43752-pcie.bin brcm/brcmfmac43752-pcie.txt bes2600/bes2600_factory.txt bes2600/best2002_fw_boot_sdio.bin bes2600/best2002_fw_sdio.bin bes2600/best2002_fw_sdio_btrf.bin bes2600/best2002_fw_sdio_nosignal.bin bes2600/sdd.bin bes2600/factory.bin rtl_bt/rtl8821c_fw.bin rtl_bt/rtl8821c_config.bin rtl_bt/rtl8822b_fw.bin rtl_bt/rtl8822b_config.bin rtw88/rtw8821c_fw.bin rtw88/rtw8822b_fw.bin arm/mali/arch10.8/mali_csffw.bin" ++#CONFIG_EXTRA_FIRMWARE_DIR="/workspace/megous.com/orangepi-pc/firmware" ++CONFIG_ARM_SCMI_PROTOCOL=y ++CONFIG_ARM_SCPI_PROTOCOL=y ++CONFIG_GNSS=y ++CONFIG_MTD=y ++CONFIG_MTD_BLOCK=y ++CONFIG_MTD_PARTITIONED_MASTER=y ++CONFIG_MTD_SPI_NOR=y ++# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set ++CONFIG_ZRAM=y ++CONFIG_ZRAM_WRITEBACK=y ++CONFIG_ZRAM_MEMORY_TRACKING=y ++CONFIG_BLK_DEV_LOOP=y ++CONFIG_BLK_DEV_NBD=y ++CONFIG_BLK_DEV_NVME=y ++CONFIG_NVME_HWMON=y ++CONFIG_SRAM=y ++CONFIG_PPKB_POWER_MANAGER=y ++CONFIG_MODEM_POWER=y ++CONFIG_BLK_DEV_SD=y ++# CONFIG_SCSI_LOWLEVEL is not set ++CONFIG_ATA=y ++CONFIG_SATA_AHCI=y ++CONFIG_SATA_MOBILE_LPM_POLICY=0 ++CONFIG_SATA_AHCI_PLATFORM=y ++CONFIG_AHCI_DWC=y ++# CONFIG_ATA_SFF is not set ++CONFIG_MD=y ++CONFIG_BLK_DEV_DM=y ++CONFIG_DM_CRYPT=y ++CONFIG_DM_INIT=y ++CONFIG_DM_UEVENT=y ++CONFIG_DM_INTEGRITY=y ++CONFIG_NETDEVICES=y ++CONFIG_WIREGUARD=y ++CONFIG_TUN=y ++CONFIG_VETH=y ++# CONFIG_NET_VENDOR_ALACRITECH is not set ++# CONFIG_NET_VENDOR_ALLWINNER is not set ++# CONFIG_NET_VENDOR_AMAZON is not set ++# CONFIG_NET_VENDOR_AMD is not set ++# CONFIG_NET_VENDOR_AQUANTIA is not set ++# CONFIG_NET_VENDOR_ARC is not set ++# CONFIG_NET_VENDOR_BROADCOM is not set ++# CONFIG_NET_VENDOR_CADENCE is not set ++# CONFIG_NET_VENDOR_CAVIUM is not set ++# CONFIG_NET_VENDOR_CORTINA is not set ++# CONFIG_NET_VENDOR_EZCHIP is not set ++# CONFIG_NET_VENDOR_GOOGLE is not set ++# CONFIG_NET_VENDOR_HISILICON is not set ++# CONFIG_NET_VENDOR_HUAWEI is not set ++CONFIG_IGB=y ++CONFIG_IGBVF=y ++# CONFIG_NET_VENDOR_MARVELL is not set ++# CONFIG_NET_VENDOR_MELLANOX is not set ++# CONFIG_NET_VENDOR_MICREL is not set ++# CONFIG_NET_VENDOR_MICROCHIP is not set ++# CONFIG_NET_VENDOR_MICROSEMI is not set ++# CONFIG_NET_VENDOR_NI is not set ++# CONFIG_NET_VENDOR_NATSEMI is not set ++# CONFIG_NET_VENDOR_NETRONOME is not set ++# CONFIG_NET_VENDOR_PENSANDO is not set ++# CONFIG_NET_VENDOR_QUALCOMM is not set ++CONFIG_R8169=y ++# CONFIG_NET_VENDOR_RENESAS is not set ++# CONFIG_NET_VENDOR_ROCKER is not set ++# CONFIG_NET_VENDOR_SAMSUNG is not set ++# CONFIG_NET_VENDOR_SEEQ is not set ++# CONFIG_NET_VENDOR_SOLARFLARE is not set ++# CONFIG_NET_VENDOR_SMSC is not set ++# CONFIG_NET_VENDOR_SOCIONEXT is not set ++CONFIG_STMMAC_ETH=y ++CONFIG_DWMAC_DWC_QOS_ETH=y ++# CONFIG_NET_VENDOR_SYNOPSYS is not set ++# CONFIG_NET_VENDOR_VIA is not set ++# CONFIG_NET_VENDOR_WIZNET is not set ++# CONFIG_NET_VENDOR_XILINX is not set ++CONFIG_LED_TRIGGER_PHY=y ++CONFIG_MOTORCOMM_PHY=y ++CONFIG_ROCKCHIP_PHY=y ++CONFIG_MDIO_SUN4I=y ++CONFIG_MDIO_BUS_MUX_MULTIPLEXER=y ++CONFIG_PPP=y ++CONFIG_PPP_BSDCOMP=y ++CONFIG_PPP_DEFLATE=y ++CONFIG_PPP_FILTER=y ++CONFIG_PPP_MPPE=y ++CONFIG_PPPOE=y ++CONFIG_PPPOL2TP=y ++CONFIG_PPP_ASYNC=y ++CONFIG_PPP_SYNC_TTY=y ++CONFIG_USB_RTL8152=y ++CONFIG_USB_USBNET=y ++# CONFIG_USB_NET_AX8817X is not set ++# CONFIG_USB_NET_AX88179_178A is not set ++CONFIG_USB_NET_CDC_EEM=y ++CONFIG_USB_NET_CDC_MBIM=y ++# CONFIG_USB_NET_NET1080 is not set ++# CONFIG_USB_NET_CDC_SUBSET is not set ++# CONFIG_USB_NET_ZAURUS is not set ++CONFIG_USB_NET_QMI_WWAN=y ++# CONFIG_WLAN_VENDOR_ADMTEK is not set ++# CONFIG_WLAN_VENDOR_ATH is not set ++# CONFIG_WLAN_VENDOR_ATMEL is not set ++CONFIG_BRCMFMAC=y ++CONFIG_BRCMFMAC_PCIE=y ++CONFIG_BRCMDBG=y ++# CONFIG_WLAN_VENDOR_INTEL is not set ++# CONFIG_WLAN_VENDOR_INTERSIL is not set ++# CONFIG_WLAN_VENDOR_MARVELL is not set ++# CONFIG_WLAN_VENDOR_MEDIATEK is not set ++# CONFIG_WLAN_VENDOR_MICROCHIP is not set ++# CONFIG_WLAN_VENDOR_PURELIFI is not set ++CONFIG_RT2X00=y ++CONFIG_RT2800USB=y ++# CONFIG_RT2800USB_RT35XX is not set ++CONFIG_RT2800USB_RT53XX=y ++# CONFIG_RTL_CARDS is not set ++CONFIG_RTW88=y ++CONFIG_RTW88_8822BU=y ++CONFIG_RTW88_8822CE=y ++CONFIG_RTW88_8821CU=y ++CONFIG_RTW88_DEBUG=y ++CONFIG_RTW88_DEBUGFS=y ++CONFIG_RTW89=y ++CONFIG_RTW89_8852AE=y ++CONFIG_RTW89_8852CE=y ++# CONFIG_WLAN_VENDOR_RSI is not set ++CONFIG_CW1200=m ++CONFIG_CW1200_WLAN_SDIO=m ++# CONFIG_WLAN_VENDOR_TI is not set ++# CONFIG_WLAN_VENDOR_ZYDAS is not set ++# CONFIG_WLAN_VENDOR_QUANTENNA is not set ++CONFIG_INPUT_MOUSEDEV=y ++CONFIG_INPUT_MOUSEDEV_PSAUX=y ++CONFIG_INPUT_EVDEV=y ++CONFIG_KEYBOARD_ADC=y ++# CONFIG_KEYBOARD_ATKBD is not set ++CONFIG_KEYBOARD_GPIO=y ++CONFIG_KEYBOARD_GPIO_POLLED=y ++CONFIG_KEYBOARD_PINEPHONE=y ++CONFIG_KEYBOARD_SUN4I_LRADC=y ++# CONFIG_INPUT_MOUSE is not set ++CONFIG_INPUT_TOUCHSCREEN=y ++CONFIG_TOUCHSCREEN_GOODIX=y ++CONFIG_INPUT_MISC=y ++CONFIG_INPUT_GPIO_VIBRA=y ++CONFIG_INPUT_AXP20X_PEK=y ++CONFIG_INPUT_UINPUT=y ++CONFIG_INPUT_RK805_PWRKEY=y ++# CONFIG_SERIO is not set ++# CONFIG_LEGACY_PTYS is not set ++CONFIG_SERIAL_8250=y ++CONFIG_SERIAL_8250_CONSOLE=y ++CONFIG_SERIAL_8250_NR_UARTS=8 ++CONFIG_SERIAL_8250_RUNTIME_UARTS=8 ++CONFIG_SERIAL_8250_PCI1XXXX=y ++CONFIG_SERIAL_8250_DW=y ++CONFIG_SERIAL_OF_PLATFORM=y ++CONFIG_SERIAL_AMBA_PL011=y ++CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++CONFIG_SERIAL_DEV_BUS=y ++CONFIG_HW_RANDOM_CN10K=y ++# CONFIG_HW_RANDOM_ROCKCHIP is not set ++CONFIG_I2C_CHARDEV=y ++CONFIG_I2C_GPIO=y ++CONFIG_I2C_MV64XXX=y ++CONFIG_I2C_RK3X=y ++CONFIG_SPI=y ++CONFIG_SPI_ROCKCHIP=y ++CONFIG_SPI_ROCKCHIP_SFC=y ++CONFIG_SPI_SUN4I=y ++CONFIG_SPI_SUN6I=y ++CONFIG_PINCTRL_AXP209=y ++CONFIG_PINCTRL_RK805=y ++CONFIG_PINCTRL_SINGLE=y ++# CONFIG_PINCTRL_SUN50I_H616 is not set ++# CONFIG_PINCTRL_SUN50I_H616_R is not set ++CONFIG_POWER_RESET_GPIO=y ++CONFIG_POWER_RESET_GPIO_RESTART=y ++CONFIG_SYSCON_REBOOT_MODE=y ++CONFIG_NVMEM_REBOOT_MODE=y ++CONFIG_IP5XXX_POWER=y ++CONFIG_BATTERY_CW2015=y ++CONFIG_CHARGER_AXP20X=y ++CONFIG_BATTERY_AXP20X=y ++CONFIG_AXP20X_POWER=y ++CONFIG_CHARGER_GPIO=y ++CONFIG_CHARGER_RK817=y ++CONFIG_CHARGER_RK818=y ++CONFIG_SENSORS_ARM_SCMI=y ++CONFIG_SENSORS_ARM_SCPI=y ++CONFIG_SENSORS_GPIO_FAN=y ++CONFIG_THERMAL=y ++CONFIG_THERMAL_STATISTICS=y ++CONFIG_THERMAL_GOV_FAIR_SHARE=y ++CONFIG_THERMAL_GOV_BANG_BANG=y ++CONFIG_CPU_THERMAL=y ++CONFIG_DEVFREQ_THERMAL=y ++CONFIG_SUN8I_THERMAL=y ++CONFIG_ROCKCHIP_THERMAL=y ++CONFIG_WATCHDOG=y ++CONFIG_DW_WATCHDOG=y ++CONFIG_SUNXI_WATCHDOG=y ++CONFIG_BCMA=y ++CONFIG_MFD_SUN4I_GPADC=y ++CONFIG_MFD_AXP20X_I2C=y ++CONFIG_MFD_AXP20X_RSB=y ++CONFIG_MFD_RK8XX_I2C=y ++CONFIG_MFD_RK8XX_SPI=y ++CONFIG_MFD_SUN6I_PRCM=y ++CONFIG_REGULATOR=y ++CONFIG_REGULATOR_FIXED_VOLTAGE=y ++CONFIG_REGULATOR_USERSPACE_CONSUMER=y ++CONFIG_REGULATOR_AXP20X=y ++CONFIG_REGULATOR_FAN53555=y ++CONFIG_REGULATOR_GPIO=y ++CONFIG_REGULATOR_PWM=y ++CONFIG_REGULATOR_RK808=y ++CONFIG_REGULATOR_SY8106A=y ++CONFIG_RC_CORE=y ++CONFIG_BPF_LIRC_MODE2=y ++CONFIG_LIRC=y ++CONFIG_RC_DECODERS=y ++CONFIG_IR_JVC_DECODER=y ++CONFIG_IR_MCE_KBD_DECODER=y ++CONFIG_IR_NEC_DECODER=y ++CONFIG_IR_RC5_DECODER=y ++CONFIG_IR_RC6_DECODER=y ++CONFIG_IR_SANYO_DECODER=y ++CONFIG_IR_SHARP_DECODER=y ++CONFIG_IR_SONY_DECODER=y ++CONFIG_IR_XMP_DECODER=y ++CONFIG_RC_DEVICES=y ++CONFIG_IR_GPIO_CIR=y ++CONFIG_IR_SUNXI=y ++CONFIG_MEDIA_CEC_RC=y ++CONFIG_MEDIA_CEC_SUPPORT=y ++CONFIG_MEDIA_SUPPORT=y ++# CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set ++CONFIG_MEDIA_CAMERA_SUPPORT=y ++CONFIG_MEDIA_PLATFORM_SUPPORT=y ++CONFIG_VIDEO_ADV_DEBUG=y ++CONFIG_V4L2_FLASH_LED_CLASS=y ++CONFIG_MEDIA_USB_SUPPORT=y ++CONFIG_USB_VIDEO_CLASS=y ++CONFIG_V4L_PLATFORM_DRIVERS=y ++CONFIG_V4L_MEM2MEM_DRIVERS=y ++CONFIG_VIDEO_MEM2MEM_DEINTERLACE=y ++CONFIG_VIDEO_ROCKCHIP_RGA=y ++CONFIG_VIDEO_ROCKCHIP_ISP1=y ++CONFIG_VIDEO_SUN6I_CSI=y ++CONFIG_VIDEO_SUN8I_DEINTERLACE=y ++CONFIG_VIDEO_SUN8I_ROTATE=y ++CONFIG_VIDEO_HANTRO=y ++CONFIG_VIDEO_IMX258=y ++CONFIG_VIDEO_OV5640=y ++CONFIG_VIDEO_OV5648=y ++CONFIG_VIDEO_OV8858=y ++CONFIG_VIDEO_GC2145=y ++CONFIG_VIDEO_DW9714=y ++CONFIG_DRM=y ++CONFIG_DRM_LOAD_EDID_FIRMWARE=y ++CONFIG_DRM_ROCKCHIP=y ++CONFIG_ROCKCHIP_VOP2=y ++CONFIG_ROCKCHIP_ANALOGIX_DP=y ++CONFIG_ROCKCHIP_CDN_DP=y ++CONFIG_ROCKCHIP_DW_HDMI=y ++CONFIG_ROCKCHIP_DW_HDMI_QP=y ++CONFIG_ROCKCHIP_DW_MIPI_DSI=y ++CONFIG_ROCKCHIP_INNO_HDMI=y ++CONFIG_ROCKCHIP_LVDS=y ++CONFIG_ROCKCHIP_RGB=y ++CONFIG_DRM_SUN4I=y ++CONFIG_DRM_PANEL_BOE_TH101MB31UIG002_28A=y ++CONFIG_DRM_PANEL_HIMAX_HX8394=y ++CONFIG_DRM_PANEL_ILITEK_ILI9881C=y ++CONFIG_DRM_PANEL_SITRONIX_ST7703=y ++CONFIG_DRM_PANEL_EDP=y ++CONFIG_DRM_PANEL_SIMPLE=y ++CONFIG_DRM_DISPLAY_CONNECTOR=y ++CONFIG_DRM_SIMPLE_BRIDGE=y ++CONFIG_DRM_CDNS_DSI=y ++CONFIG_DRM_DW_HDMI_AHB_AUDIO=y ++CONFIG_DRM_DW_HDMI_I2S_AUDIO=y ++CONFIG_DRM_DW_HDMI_GP_AUDIO=y ++CONFIG_DRM_DW_HDMI_CEC=y ++CONFIG_DRM_LIMA=y ++CONFIG_DRM_PANFROST=y ++CONFIG_DRM_PANTHOR=y ++CONFIG_FB=y ++CONFIG_FB_SIMPLE=y ++CONFIG_BACKLIGHT_CLASS_DEVICE=y ++CONFIG_BACKLIGHT_PWM=y ++CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y ++CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER=y ++CONFIG_LOGO=y ++CONFIG_SOUND=y ++CONFIG_SND=y ++CONFIG_SND_OSSEMUL=y ++CONFIG_SND_MIXER_OSS=y ++CONFIG_SND_PCM_OSS=y ++CONFIG_SND_HRTIMER=y ++CONFIG_SND_DUMMY=y ++CONFIG_SND_ALOOP=y ++# CONFIG_SND_SPI is not set ++CONFIG_SND_USB_AUDIO=y ++CONFIG_SND_SOC=y ++CONFIG_SND_SOC_ROCKCHIP=y ++CONFIG_SND_SOC_ROCKCHIP_I2S=y ++CONFIG_SND_SOC_ROCKCHIP_I2S_TDM=y ++CONFIG_SND_SOC_ROCKCHIP_PDM=y ++CONFIG_SND_SOC_ROCKCHIP_SPDIF=y ++CONFIG_SND_SUN4I_CODEC=y ++CONFIG_SND_SUN8I_CODEC=y ++CONFIG_SND_SUN8I_CODEC_ANALOG=y ++CONFIG_SND_SUN50I_CODEC_ANALOG=y ++CONFIG_SND_SUN4I_I2S=y ++CONFIG_SND_SUN4I_SPDIF=y ++CONFIG_SND_SUN9I_HDMI_AUDIO=y ++CONFIG_SND_SOC_BT_SCO=y ++CONFIG_SND_SOC_DMIC=y ++CONFIG_SND_SOC_EC25=y ++CONFIG_SND_SOC_ES8316=y ++CONFIG_SND_SOC_ES8328_I2C=y ++CONFIG_SND_SOC_MAX98357A=y ++CONFIG_SND_SOC_RK817=y ++CONFIG_SND_SOC_RT5640=y ++CONFIG_SND_SOC_SIMPLE_AMPLIFIER=y ++CONFIG_SND_SOC_SPDIF=y ++CONFIG_SND_SIMPLE_CARD=y ++CONFIG_SND_AUDIO_GRAPH_CARD=y ++CONFIG_HIDRAW=y ++CONFIG_UHID=y ++# CONFIG_HID_A4TECH is not set ++# CONFIG_HID_APPLE is not set ++# CONFIG_HID_BELKIN is not set ++# CONFIG_HID_CHERRY is not set ++# CONFIG_HID_CHICONY is not set ++# CONFIG_HID_CYPRESS is not set ++# CONFIG_HID_EZKEY is not set ++# CONFIG_HID_KENSINGTON is not set ++# CONFIG_HID_LOGITECH is not set ++# CONFIG_HID_REDRAGON is not set ++# CONFIG_HID_MICROSOFT is not set ++# CONFIG_HID_MONTEREY is not set ++CONFIG_HID_MULTITOUCH=y ++CONFIG_USB_HIDDEV=y ++CONFIG_USB_LED_TRIG=y ++CONFIG_USB_ULPI_BUS=y ++CONFIG_USB_CONN_GPIO=y ++CONFIG_USB=y ++CONFIG_USB_ANNOUNCE_NEW_DEVICES=y ++CONFIG_USB_OTG=y ++CONFIG_USB_OTG_FSM=y ++CONFIG_USB_LEDS_TRIGGER_USBPORT=y ++CONFIG_USB_MON=y ++CONFIG_USB_XHCI_HCD=y ++CONFIG_USB_EHCI_HCD=y ++CONFIG_USB_EHCI_HCD_PLATFORM=y ++CONFIG_USB_OHCI_HCD=y ++CONFIG_USB_OHCI_HCD_PLATFORM=y ++CONFIG_USB_ACM=y ++CONFIG_USB_PRINTER=y ++CONFIG_USB_STORAGE=y ++CONFIG_USB_UAS=y ++CONFIG_USBIP_CORE=y ++CONFIG_USBIP_VHCI_HCD=y ++CONFIG_USBIP_HOST=y ++CONFIG_USBIP_VUDC=y ++CONFIG_USB_MUSB_HDRC=y ++CONFIG_USB_MUSB_SUNXI=y ++CONFIG_MUSB_PIO_ONLY=y ++CONFIG_USB_DWC3=y ++CONFIG_USB_DWC3_ULPI=y ++CONFIG_USB_DWC2=y ++CONFIG_USB_SERIAL=y ++CONFIG_USB_SERIAL_CONSOLE=y ++CONFIG_USB_SERIAL_GENERIC=y ++CONFIG_USB_SERIAL_SIMPLE=y ++CONFIG_USB_SERIAL_CH341=y ++CONFIG_USB_SERIAL_CP210X=y ++CONFIG_USB_SERIAL_FTDI_SIO=y ++CONFIG_USB_SERIAL_PL2303=y ++CONFIG_USB_SERIAL_QCAUX=y ++CONFIG_USB_SERIAL_QUALCOMM=y ++CONFIG_USB_SERIAL_OPTION=y ++CONFIG_NOP_USB_XCEIV=y ++CONFIG_USB_GADGET=y ++CONFIG_USB_GADGET_VBUS_DRAW=500 ++CONFIG_U_SERIAL_CONSOLE=y ++CONFIG_USB_CONFIGFS=y ++CONFIG_USB_CONFIGFS_SERIAL=y ++CONFIG_USB_CONFIGFS_ACM=y ++CONFIG_USB_CONFIGFS_OBEX=y ++CONFIG_USB_CONFIGFS_NCM=y ++CONFIG_USB_CONFIGFS_ECM=y ++CONFIG_USB_CONFIGFS_ECM_SUBSET=y ++CONFIG_USB_CONFIGFS_RNDIS=y ++CONFIG_USB_CONFIGFS_EEM=y ++CONFIG_USB_CONFIGFS_MASS_STORAGE=y ++CONFIG_USB_CONFIGFS_F_LB_SS=y ++CONFIG_USB_CONFIGFS_F_FS=y ++CONFIG_USB_CONFIGFS_F_UAC1=y ++CONFIG_USB_CONFIGFS_F_UAC2=y ++CONFIG_USB_CONFIGFS_F_MIDI=y ++CONFIG_USB_CONFIGFS_F_HID=y ++CONFIG_USB_CONFIGFS_F_UVC=y ++CONFIG_USB_CONFIGFS_F_PRINTER=y ++CONFIG_TYPEC=y ++CONFIG_TYPEC_TCPM=y ++CONFIG_TYPEC_TCPCI=y ++CONFIG_TYPEC_HUSB311=y ++CONFIG_TYPEC_FUSB302=y ++CONFIG_TYPEC_UCSI=y ++CONFIG_TYPEC_ANX7688=y ++CONFIG_TYPEC_EXTCON=y ++CONFIG_TYPEC_DP_ALTMODE=y ++CONFIG_MMC=y ++CONFIG_MMC_SDHCI=y ++CONFIG_MMC_SDHCI_PLTFM=y ++CONFIG_MMC_SDHCI_OF_ARASAN=y ++CONFIG_MMC_SDHCI_OF_DWCMSHC=y ++CONFIG_MMC_DW=y ++CONFIG_MMC_DW_ROCKCHIP=y ++CONFIG_MMC_SUNXI=y ++CONFIG_NEW_LEDS=y ++CONFIG_LEDS_CLASS=y ++CONFIG_LEDS_CLASS_FLASH=y ++CONFIG_LEDS_GPIO=y ++CONFIG_LEDS_PWM=y ++CONFIG_LEDS_REGULATOR=y ++CONFIG_LEDS_SGM3140=y ++CONFIG_LEDS_TRIGGER_TIMER=y ++CONFIG_LEDS_TRIGGER_ONESHOT=y ++CONFIG_LEDS_TRIGGER_HEARTBEAT=y ++CONFIG_LEDS_TRIGGER_CPU=y ++CONFIG_LEDS_TRIGGER_ACTIVITY=y ++CONFIG_LEDS_TRIGGER_DEFAULT_ON=y ++CONFIG_LEDS_TRIGGER_TRANSIENT=y ++CONFIG_LEDS_TRIGGER_CAMERA=y ++CONFIG_LEDS_TRIGGER_PANIC=y ++CONFIG_LEDS_TRIGGER_NETDEV=y ++CONFIG_LEDS_TRIGGER_PATTERN=y ++CONFIG_RTC_CLASS=y ++# CONFIG_RTC_INTF_PROC is not set ++CONFIG_RTC_DRV_HYM8563=y ++CONFIG_RTC_DRV_RK808=y ++CONFIG_RTC_DRV_SUN6I=y ++CONFIG_DMADEVICES=y ++CONFIG_DMA_SUN6I=y ++CONFIG_PL330_DMA=y ++CONFIG_DMABUF_HEAPS=y ++CONFIG_DMABUF_HEAPS_SYSTEM=y ++CONFIG_DMABUF_HEAPS_CMA=y ++# CONFIG_VIRTIO_MENU is not set ++CONFIG_STAGING=y ++CONFIG_STAGING_MEDIA=y ++CONFIG_VIDEO_ROCKCHIP_VDEC=y ++CONFIG_VIDEO_SUNXI=y ++CONFIG_VIDEO_SUNXI_CEDRUS=y ++CONFIG_RTL8723CS=y ++CONFIG_COMMON_CLK_RK808=y ++CONFIG_COMMON_CLK_SCMI=y ++CONFIG_COMMON_CLK_SCPI=y ++CONFIG_COMMON_CLK_PWM=y ++# CONFIG_CLK_RK3308 is not set ++# CONFIG_CLK_RK3328 is not set ++# CONFIG_CLK_RK3368 is not set ++# CONFIG_SUN50I_A100_CCU is not set ++# CONFIG_SUN50I_A100_R_CCU is not set ++# CONFIG_SUN50I_H616_CCU is not set ++# CONFIG_SUN6I_RTC_CCU is not set ++# CONFIG_FSL_ERRATUM_A008585 is not set ++# CONFIG_HISILICON_ERRATUM_161010101 is not set ++CONFIG_MAILBOX=y ++CONFIG_ROCKCHIP_MBOX=y ++CONFIG_IOMMU_IO_PGTABLE_ARMV7S=y ++CONFIG_ROCKCHIP_IOMMU=y ++CONFIG_SUN50I_IOMMU=y ++CONFIG_ARM_SMMU=y ++CONFIG_ARM_SMMU_V3=y ++CONFIG_ROCKCHIP_IODOMAIN=y ++CONFIG_ROCKCHIP_PM_DOMAINS=y ++CONFIG_DEVFREQ_GOV_PERFORMANCE=y ++CONFIG_DEVFREQ_GOV_POWERSAVE=y ++CONFIG_DEVFREQ_GOV_USERSPACE=y ++CONFIG_DEVFREQ_GOV_PASSIVE=y ++CONFIG_ARM_RK3399_DMC_DEVFREQ=y ++CONFIG_ARM_SUN8I_A33_MBUS_DEVFREQ=y ++CONFIG_EXTCON_ADC_JACK=y ++CONFIG_EXTCON_GPIO=y ++CONFIG_EXTCON_USB_GPIO=y ++CONFIG_IIO=y ++CONFIG_IIO_BUFFER_CB=y ++CONFIG_IIO_BUFFER_HW_CONSUMER=y ++CONFIG_IIO_SW_DEVICE=y ++CONFIG_IIO_SW_TRIGGER=y ++CONFIG_IIO_ST_ACCEL_3AXIS=y ++# CONFIG_IIO_ST_ACCEL_SPI_3AXIS is not set ++CONFIG_AXP20X_ADC=y ++CONFIG_ROCKCHIP_SARADC=y ++CONFIG_INV_MPU6050_I2C=y ++CONFIG_STK3310=y ++CONFIG_AF8133J=y ++CONFIG_IIO_ST_MAGN_3AXIS=y ++CONFIG_IIO_HRTIMER_TRIGGER=y ++CONFIG_IIO_INTERRUPT_TRIGGER=y ++CONFIG_IIO_SYSFS_TRIGGER=y ++CONFIG_PWM=y ++CONFIG_PWM_ROCKCHIP=y ++CONFIG_PWM_SUN4I=y ++CONFIG_PHY_SUN4I_USB=y ++CONFIG_PHY_SUN50I_USB3=y ++CONFIG_PHY_ROCKCHIP_DP=y ++CONFIG_PHY_ROCKCHIP_DPHY_RX0=y ++CONFIG_PHY_ROCKCHIP_EMMC=y ++CONFIG_PHY_ROCKCHIP_INNO_HDMI=y ++CONFIG_PHY_ROCKCHIP_INNO_USB2=y ++CONFIG_PHY_ROCKCHIP_INNO_CSIDPHY=y ++CONFIG_PHY_ROCKCHIP_INNO_DSIDPHY=y ++CONFIG_PHY_ROCKCHIP_NANENG_COMBO_PHY=y ++CONFIG_PHY_ROCKCHIP_PCIE=y ++CONFIG_PHY_ROCKCHIP_SAMSUNG_HDPTX=y ++CONFIG_PHY_ROCKCHIP_SNPS_PCIE3=y ++CONFIG_PHY_ROCKCHIP_TYPEC=y ++CONFIG_PHY_ROCKCHIP_USB=y ++CONFIG_PHY_ROCKCHIP_USBDP=y ++CONFIG_ARM_CCI_PMU=y ++# CONFIG_ARM_CCI5xx_PMU is not set ++CONFIG_ANDROID_BINDER_IPC=y ++CONFIG_ANDROID_BINDERFS=y ++CONFIG_NVMEM_SUNXI_SID=y ++CONFIG_TEE=y ++CONFIG_OPTEE=y ++CONFIG_VALIDATE_FS_PARSER=y ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_FS_POSIX_ACL=y ++CONFIG_XFS_FS=y ++CONFIG_XFS_POSIX_ACL=y ++CONFIG_BTRFS_FS=y ++CONFIG_BTRFS_FS_POSIX_ACL=y ++CONFIG_F2FS_FS=y ++CONFIG_F2FS_FS_SECURITY=y ++CONFIG_F2FS_CHECK_FS=y ++CONFIG_F2FS_FS_COMPRESSION=y ++CONFIG_FS_ENCRYPTION=y ++CONFIG_FS_ENCRYPTION_INLINE_CRYPT=y ++CONFIG_FANOTIFY=y ++CONFIG_AUTOFS_FS=y ++CONFIG_FUSE_FS=y ++CONFIG_CUSE=y ++CONFIG_OVERLAY_FS=y ++CONFIG_OVERLAY_FS_INDEX=y ++CONFIG_OVERLAY_FS_XINO_AUTO=y ++CONFIG_OVERLAY_FS_METACOPY=y ++CONFIG_ISO9660_FS=y ++CONFIG_JOLIET=y ++CONFIG_ZISOFS=y ++CONFIG_UDF_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_UTF8=y ++CONFIG_EXFAT_FS=y ++CONFIG_NTFS3_FS=y ++CONFIG_NTFS3_64BIT_CLUSTER=y ++CONFIG_NTFS3_FS_POSIX_ACL=y ++CONFIG_PROC_CHILDREN=y ++CONFIG_TMPFS=y ++CONFIG_TMPFS_POSIX_ACL=y ++CONFIG_TMPFS_INODE64=y ++CONFIG_HUGETLBFS=y ++CONFIG_ECRYPT_FS=y ++CONFIG_ECRYPT_FS_MESSAGING=y ++CONFIG_SQUASHFS=y ++CONFIG_SQUASHFS_FILE_DIRECT=y ++CONFIG_SQUASHFS_CHOICE_DECOMP_BY_MOUNT=y ++CONFIG_SQUASHFS_XATTR=y ++CONFIG_SQUASHFS_LZ4=y ++CONFIG_SQUASHFS_LZO=y ++CONFIG_SQUASHFS_XZ=y ++CONFIG_SQUASHFS_ZSTD=y ++CONFIG_NFS_FS=y ++# CONFIG_NFS_V3 is not set ++CONFIG_NFS_V4=y ++CONFIG_NFS_V4_1=y ++CONFIG_NFS_V4_2=y ++CONFIG_NFS_FSCACHE=y ++CONFIG_NFSD=y ++CONFIG_NFSD_V3_ACL=y ++CONFIG_NFSD_V4=y ++# CONFIG_NFSD_LEGACY_CLIENT_TRACKING is not set ++CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_AES_SHA2=y ++CONFIG_CIFS=y ++# CONFIG_CIFS_STATS2 is not set ++CONFIG_CIFS_XATTR=y ++CONFIG_CIFS_POSIX=y ++# CONFIG_CIFS_DEBUG is not set ++CONFIG_CIFS_DFS_UPCALL=y ++CONFIG_CIFS_SWN_UPCALL=y ++CONFIG_CIFS_FSCACHE=y ++CONFIG_NLS_CODEPAGE_437=y ++CONFIG_NLS_CODEPAGE_852=y ++CONFIG_NLS_ISO8859_1=y ++CONFIG_NLS_ISO8859_2=y ++CONFIG_NLS_UTF8=y ++CONFIG_KEYS_REQUEST_CACHE=y ++CONFIG_PERSISTENT_KEYRINGS=y ++CONFIG_BIG_KEYS=y ++CONFIG_TRUSTED_KEYS=y ++CONFIG_ENCRYPTED_KEYS=y ++CONFIG_KEY_DH_OPERATIONS=y ++CONFIG_SECURITY=y ++# CONFIG_INTEGRITY is not set ++CONFIG_LSM="yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor" ++CONFIG_INIT_STACK_NONE=y ++CONFIG_CRYPTO_USER=y ++CONFIG_CRYPTO_PCRYPT=y ++CONFIG_CRYPTO_CRYPTD=y ++CONFIG_CRYPTO_ECDSA=y ++CONFIG_CRYPTO_CURVE25519=y ++CONFIG_CRYPTO_ARC4=y ++CONFIG_CRYPTO_CHACHA20POLY1305=y ++CONFIG_CRYPTO_SEQIV=y ++CONFIG_CRYPTO_ECHAINIV=y ++CONFIG_CRYPTO_MD4=y ++CONFIG_CRYPTO_DEFLATE=y ++CONFIG_CRYPTO_ZSTD=y ++CONFIG_CRYPTO_ANSI_CPRNG=y ++CONFIG_CRYPTO_USER_API_HASH=y ++CONFIG_CRYPTO_USER_API_SKCIPHER=y ++CONFIG_CRYPTO_USER_API_RNG=y ++CONFIG_CRYPTO_USER_API_AEAD=y ++CONFIG_CRYPTO_NHPOLY1305_NEON=y ++CONFIG_CRYPTO_GHASH_ARM64_CE=y ++CONFIG_CRYPTO_SHA1_ARM64_CE=y ++CONFIG_CRYPTO_SHA2_ARM64_CE=y ++CONFIG_CRYPTO_SHA512_ARM64_CE=y ++CONFIG_CRYPTO_SHA3_ARM64=y ++CONFIG_CRYPTO_SM3_ARM64_CE=y ++CONFIG_CRYPTO_AES_ARM64=y ++CONFIG_CRYPTO_AES_ARM64_BS=y ++CONFIG_CRYPTO_SM4_ARM64_CE=y ++CONFIG_CRYPTO_AES_ARM64_CE_CCM=y ++CONFIG_CRYPTO_CRCT10DIF_ARM64_CE=y ++CONFIG_CRYPTO_DEV_SUN4I_SS=y ++CONFIG_CRYPTO_DEV_SUN4I_SS_PRNG=y ++CONFIG_CRYPTO_DEV_SUN4I_SS_DEBUG=y ++CONFIG_CRYPTO_DEV_SUN8I_CE=y ++CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG=y ++CONFIG_CRYPTO_DEV_SUN8I_CE_HASH=y ++CONFIG_CRYPTO_DEV_SUN8I_CE_PRNG=y ++CONFIG_CRYPTO_DEV_SUN8I_CE_TRNG=y ++CONFIG_CRYPTO_DEV_SUN8I_SS=y ++CONFIG_CRYPTO_DEV_SUN8I_SS_DEBUG=y ++CONFIG_CRYPTO_DEV_SUN8I_SS_PRNG=y ++CONFIG_CRYPTO_DEV_SUN8I_SS_HASH=y ++CONFIG_CRYPTO_DEV_ROCKCHIP=y ++CONFIG_CRYPTO_DEV_ROCKCHIP_DEBUG=y ++CONFIG_PKCS8_PRIVATE_KEY_PARSER=y ++# CONFIG_RAID6_PQ_BENCHMARK is not set ++CONFIG_CORDIC=y ++CONFIG_DMA_CMA=y ++CONFIG_DMA_NUMA_CMA=y ++CONFIG_CMA_SIZE_MBYTES=64 ++CONFIG_FONTS=y ++CONFIG_FONT_8x8=y ++CONFIG_FONT_8x16=y ++CONFIG_FONT_SUN12x22=y ++CONFIG_FONT_TER16x32=y ++CONFIG_CONSOLE_LOGLEVEL_DEFAULT=15 ++CONFIG_MESSAGE_LOGLEVEL_DEFAULT=7 ++CONFIG_DYNAMIC_DEBUG=y ++CONFIG_DEBUG_KERNEL=y ++CONFIG_FRAME_WARN=1024 ++CONFIG_MAGIC_SYSRQ=y ++CONFIG_PER_VMA_LOCK_STATS=y ++CONFIG_PANIC_ON_OOPS=y ++CONFIG_SOFTLOCKUP_DETECTOR=y ++CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y ++CONFIG_BOOTPARAM_HUNG_TASK_PANIC=y ++CONFIG_WQ_WATCHDOG=y ++CONFIG_RCU_EXP_CPU_STALL_TIMEOUT=20 ++# CONFIG_RCU_TRACE is not set ++# CONFIG_FTRACE is not set ++# CONFIG_STRICT_DEVMEM is not set ++# CONFIG_RUNTIME_TESTING_MENU is not set +diff --git a/arch/arm64/configs/pinephone_defconfig b/arch/arm64/configs/pinephone_defconfig +new file mode 100644 +index 000000000000..51c8740d56e7 +--- /dev/null ++++ b/arch/arm64/configs/pinephone_defconfig +@@ -0,0 +1,650 @@ ++CONFIG_SYSVIPC=y ++CONFIG_POSIX_MQUEUE=y ++CONFIG_NO_HZ=y ++CONFIG_HIGH_RES_TIMERS=y ++CONFIG_BPF_SYSCALL=y ++CONFIG_BPF_JIT=y ++CONFIG_BPF_JIT_ALWAYS_ON=y ++# CONFIG_BPF_UNPRIV_DEFAULT_OFF is not set ++CONFIG_PREEMPT=y ++CONFIG_BSD_PROCESS_ACCT=y ++CONFIG_BSD_PROCESS_ACCT_V3=y ++CONFIG_TASKSTATS=y ++CONFIG_TASK_DELAY_ACCT=y ++CONFIG_TASK_XACCT=y ++CONFIG_TASK_IO_ACCOUNTING=y ++CONFIG_PSI=y ++CONFIG_LOG_BUF_SHIFT=19 ++CONFIG_MEMCG=y ++CONFIG_BLK_CGROUP=y ++CONFIG_CFS_BANDWIDTH=y ++CONFIG_RT_GROUP_SCHED=y ++CONFIG_CGROUP_PIDS=y ++CONFIG_CGROUP_FREEZER=y ++CONFIG_CPUSETS=y ++# CONFIG_PROC_PID_CPUSET is not set ++CONFIG_CGROUP_DEVICE=y ++CONFIG_CGROUP_CPUACCT=y ++CONFIG_CGROUP_BPF=y ++CONFIG_USER_NS=y ++CONFIG_SCHED_AUTOGROUP=y ++# CONFIG_RD_GZIP is not set ++# CONFIG_RD_BZIP2 is not set ++# CONFIG_RD_LZMA is not set ++# CONFIG_RD_XZ is not set ++# CONFIG_RD_LZO is not set ++# CONFIG_RD_ZSTD is not set ++CONFIG_PERF_EVENTS=y ++CONFIG_ARCH_SUNXI=y ++# CONFIG_ARM64_ERRATUM_832075 is not set ++# CONFIG_ARM64_ERRATUM_1024718 is not set ++# CONFIG_ARM64_ERRATUM_1418040 is not set ++# CONFIG_ARM64_ERRATUM_1165522 is not set ++# CONFIG_ARM64_ERRATUM_1319367 is not set ++# CONFIG_ARM64_ERRATUM_1530923 is not set ++CONFIG_ARM64_ERRATUM_2441007=y ++# CONFIG_ARM64_ERRATUM_1463225 is not set ++# CONFIG_ARM64_ERRATUM_2051678 is not set ++# CONFIG_ARM64_ERRATUM_2077057 is not set ++CONFIG_ARM64_ERRATUM_2441009=y ++# CONFIG_CAVIUM_ERRATUM_22375 is not set ++# CONFIG_CAVIUM_ERRATUM_23154 is not set ++# CONFIG_CAVIUM_ERRATUM_27456 is not set ++# CONFIG_CAVIUM_ERRATUM_30115 is not set ++# CONFIG_CAVIUM_TX2_ERRATUM_219 is not set ++# CONFIG_FUJITSU_ERRATUM_010001 is not set ++# CONFIG_HISILICON_ERRATUM_161600802 is not set ++# CONFIG_QCOM_FALKOR_ERRATUM_1003 is not set ++# CONFIG_QCOM_FALKOR_ERRATUM_1009 is not set ++# CONFIG_QCOM_QDF2400_ERRATUM_0065 is not set ++# CONFIG_QCOM_FALKOR_ERRATUM_E1041 is not set ++# CONFIG_SOCIONEXT_SYNQUACER_PREITS is not set ++CONFIG_ARM64_VA_BITS_39=y ++CONFIG_SCHED_MC=y ++CONFIG_NR_CPUS=4 ++CONFIG_HZ_100=y ++CONFIG_COMPAT=y ++# CONFIG_ARM64_HW_AFDBM is not set ++# CONFIG_ARM64_PAN is not set ++# CONFIG_ARM64_USE_LSE_ATOMICS is not set ++# CONFIG_ARM64_RAS_EXTN is not set ++# CONFIG_ARM64_CNP is not set ++# CONFIG_ARM64_PTR_AUTH is not set ++# CONFIG_ARM64_AMU_EXTN is not set ++# CONFIG_ARM64_TLB_RANGE is not set ++# CONFIG_ARM64_BTI is not set ++# CONFIG_ARM64_E0PD is not set ++# CONFIG_ARM64_SVE is not set ++# CONFIG_EFI is not set ++CONFIG_PM_AUTOSLEEP=y ++CONFIG_PM_WAKELOCKS=y ++CONFIG_PM_DEBUG=y ++CONFIG_WQ_POWER_EFFICIENT_DEFAULT=y ++CONFIG_ENERGY_MODEL=y ++CONFIG_CPU_IDLE=y ++CONFIG_CPU_IDLE_GOV_LADDER=y ++CONFIG_CPU_IDLE_GOV_MENU=y ++CONFIG_CPU_IDLE_GOV_TEO=y ++CONFIG_ARM_PSCI_CPUIDLE=y ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_STAT=y ++CONFIG_CPU_FREQ_GOV_POWERSAVE=y ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++CONFIG_CPU_FREQ_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y ++CONFIG_CPUFREQ_DT=y ++CONFIG_JUMP_LABEL=y ++# CONFIG_STACKPROTECTOR is not set ++# CONFIG_GCC_PLUGINS is not set ++CONFIG_MODULES=y ++CONFIG_MODULE_UNLOAD=y ++CONFIG_MODVERSIONS=y ++CONFIG_BLK_DEV_THROTTLING=y ++CONFIG_BLK_WBT=y ++CONFIG_BLK_CGROUP_IOLATENCY=y ++CONFIG_BLK_CGROUP_IOCOST=y ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_MQ_IOSCHED_DEADLINE is not set ++# CONFIG_MQ_IOSCHED_KYBER is not set ++CONFIG_CMA=y ++CONFIG_CMA_DEBUGFS=y ++CONFIG_CMA_AREAS=7 ++CONFIG_NET=y ++CONFIG_PACKET=y ++CONFIG_PACKET_DIAG=y ++CONFIG_UNIX=y ++CONFIG_UNIX_DIAG=y ++CONFIG_XFRM_USER=y ++CONFIG_NET_KEY=m ++CONFIG_INET=y ++CONFIG_IP_MULTICAST=y ++CONFIG_IP_ADVANCED_ROUTER=y ++CONFIG_SYN_COOKIES=y ++# CONFIG_INET_DIAG is not set ++# CONFIG_IPV6_SIT is not set ++CONFIG_MPTCP=y ++CONFIG_NETFILTER=y ++CONFIG_NETFILTER_NETLINK_QUEUE=m ++CONFIG_NETFILTER_NETLINK_LOG=m ++CONFIG_NF_CONNTRACK=m ++CONFIG_NF_CONNTRACK_MARK=y ++# CONFIG_NF_CT_PROTO_DCCP is not set ++# CONFIG_NF_CT_PROTO_SCTP is not set ++# CONFIG_NF_CT_PROTO_UDPLITE is not set ++CONFIG_NF_CONNTRACK_FTP=m ++CONFIG_NF_CONNTRACK_H323=m ++CONFIG_NF_CONNTRACK_IRC=m ++CONFIG_NF_CONNTRACK_NETBIOS_NS=m ++CONFIG_NF_CONNTRACK_SNMP=m ++CONFIG_NF_CONNTRACK_PPTP=m ++CONFIG_NF_CONNTRACK_SIP=m ++CONFIG_NF_CONNTRACK_TFTP=m ++CONFIG_NF_CT_NETLINK=m ++CONFIG_NETFILTER_NETLINK_GLUE_CT=y ++CONFIG_NF_TABLES=m ++CONFIG_NF_TABLES_INET=y ++CONFIG_NF_TABLES_NETDEV=y ++CONFIG_NFT_NUMGEN=m ++CONFIG_NFT_CT=m ++CONFIG_NFT_CONNLIMIT=m ++CONFIG_NFT_LOG=m ++CONFIG_NFT_LIMIT=m ++CONFIG_NFT_MASQ=m ++CONFIG_NFT_REDIR=m ++CONFIG_NFT_NAT=m ++CONFIG_NFT_TUNNEL=m ++CONFIG_NFT_QUEUE=m ++CONFIG_NFT_QUOTA=m ++CONFIG_NFT_REJECT=m ++CONFIG_NFT_HASH=m ++CONFIG_NFT_FIB_INET=m ++CONFIG_NFT_SOCKET=m ++CONFIG_NFT_OSF=m ++CONFIG_NFT_DUP_NETDEV=m ++CONFIG_NFT_FWD_NETDEV=m ++CONFIG_NF_FLOW_TABLE_INET=m ++CONFIG_NF_FLOW_TABLE=m ++CONFIG_IP_SET=m ++CONFIG_IP_SET_BITMAP_IP=m ++CONFIG_IP_SET_BITMAP_IPMAC=m ++CONFIG_IP_SET_BITMAP_PORT=m ++CONFIG_IP_SET_HASH_IP=m ++CONFIG_IP_SET_HASH_IPMARK=m ++CONFIG_IP_SET_HASH_IPPORT=m ++CONFIG_IP_SET_HASH_IPPORTIP=m ++CONFIG_IP_SET_HASH_IPPORTNET=m ++CONFIG_IP_SET_HASH_IPMAC=m ++CONFIG_IP_SET_HASH_MAC=m ++CONFIG_IP_SET_HASH_NETPORTNET=m ++CONFIG_IP_SET_HASH_NET=m ++CONFIG_IP_SET_HASH_NETNET=m ++CONFIG_IP_SET_HASH_NETPORT=m ++CONFIG_IP_SET_HASH_NETIFACE=m ++CONFIG_IP_SET_LIST_SET=m ++CONFIG_NFT_FIB_IPV4=m ++CONFIG_NF_TABLES_ARP=y ++CONFIG_NF_LOG_ARP=m ++CONFIG_NF_LOG_IPV4=m ++# CONFIG_NF_NAT_SNMP_BASIC is not set ++CONFIG_NFT_DUP_IPV6=m ++CONFIG_NFT_FIB_IPV6=m ++CONFIG_NF_LOG_IPV6=m ++CONFIG_BRIDGE=y ++CONFIG_NET_SCHED=y ++CONFIG_NET_SCH_HTB=m ++CONFIG_NET_CLS_BASIC=m ++CONFIG_NET_CLS_ROUTE4=m ++CONFIG_NET_CLS_FW=m ++CONFIG_NET_CLS_CGROUP=m ++CONFIG_NET_CLS_ACT=y ++CONFIG_NET_ACT_POLICE=m ++CONFIG_NET_ACT_GACT=m ++CONFIG_NET_ACT_MIRRED=m ++CONFIG_NETLINK_DIAG=m ++CONFIG_BT=m ++CONFIG_BT_RFCOMM=m ++CONFIG_BT_RFCOMM_TTY=y ++CONFIG_BT_BNEP=m ++CONFIG_BT_BNEP_MC_FILTER=y ++CONFIG_BT_BNEP_PROTO_FILTER=y ++CONFIG_BT_HIDP=m ++CONFIG_BT_LEDS=y ++CONFIG_BT_HCIUART=m ++CONFIG_BT_HCIUART_RTL=y ++CONFIG_CFG80211=m ++CONFIG_CFG80211_DEBUGFS=y ++CONFIG_CFG80211_WEXT=y ++CONFIG_MAC80211=m ++CONFIG_MAC80211_LEDS=y ++CONFIG_RFKILL=m ++CONFIG_RFKILL_GPIO=m ++CONFIG_PAGE_POOL_STATS=y ++CONFIG_UEVENT_HELPER=y ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++#CONFIG_EXTRA_FIRMWARE="regulatory.db regulatory.db.p7s anx7688-fw.bin rtl_bt/rtl8723cs_xx_config.bin rtl_bt/rtl8723cs_xx_fw.bin ov5640_af.bin rtw88/rtw8703b_wow_fw.bin rtw88/rtw8703b_fw.bin" ++#CONFIG_EXTRA_FIRMWARE_DIR="/workspace/megous.com/orangepi-pc/firmware" ++CONFIG_ARM_SCPI_PROTOCOL=y ++# CONFIG_ARM_SMCCC_SOC_ID is not set ++CONFIG_ZRAM=m ++CONFIG_ZRAM_WRITEBACK=y ++CONFIG_ZRAM_MEMORY_TRACKING=y ++CONFIG_BLK_DEV_LOOP=m ++CONFIG_BLK_DEV_NBD=m ++CONFIG_PPKB_POWER_MANAGER=y ++CONFIG_MODEM_POWER=m ++CONFIG_SCSI=m ++CONFIG_BLK_DEV_SD=m ++# CONFIG_BLK_DEV_BSG is not set ++# CONFIG_SCSI_LOWLEVEL is not set ++CONFIG_MD=y ++CONFIG_BLK_DEV_DM=y ++CONFIG_DM_CRYPT=y ++CONFIG_DM_INIT=y ++CONFIG_DM_UEVENT=y ++CONFIG_NETDEVICES=y ++CONFIG_WIREGUARD=m ++CONFIG_TUN=m ++CONFIG_VETH=m ++# CONFIG_ETHERNET is not set ++CONFIG_PPP=m ++CONFIG_PPP_BSDCOMP=m ++CONFIG_PPP_DEFLATE=m ++CONFIG_PPP_FILTER=y ++CONFIG_PPP_MPPE=m ++CONFIG_USB_RTL8152=m ++CONFIG_USB_USBNET=m ++# CONFIG_USB_NET_AX8817X is not set ++# CONFIG_USB_NET_AX88179_178A is not set ++CONFIG_USB_NET_CDC_EEM=m ++CONFIG_USB_NET_CDC_MBIM=m ++# CONFIG_USB_NET_NET1080 is not set ++# CONFIG_USB_NET_CDC_SUBSET is not set ++# CONFIG_USB_NET_ZAURUS is not set ++CONFIG_USB_NET_QMI_WWAN=m ++# CONFIG_WLAN_VENDOR_ADMTEK is not set ++# CONFIG_WLAN_VENDOR_ATH is not set ++# CONFIG_WLAN_VENDOR_ATMEL is not set ++# CONFIG_WLAN_VENDOR_BROADCOM is not set ++# CONFIG_WLAN_VENDOR_INTEL is not set ++# CONFIG_WLAN_VENDOR_INTERSIL is not set ++# CONFIG_WLAN_VENDOR_MARVELL is not set ++# CONFIG_WLAN_VENDOR_MEDIATEK is not set ++# CONFIG_WLAN_VENDOR_MICROCHIP is not set ++# CONFIG_WLAN_VENDOR_PURELIFI is not set ++# CONFIG_WLAN_VENDOR_RALINK is not set ++# CONFIG_WLAN_VENDOR_REALTEK is not set ++# CONFIG_WLAN_VENDOR_RSI is not set ++# CONFIG_WLAN_VENDOR_SILABS is not set ++# CONFIG_WLAN_VENDOR_ST is not set ++# CONFIG_WLAN_VENDOR_TI is not set ++# CONFIG_WLAN_VENDOR_ZYDAS is not set ++# CONFIG_WLAN_VENDOR_QUANTENNA is not set ++CONFIG_INPUT_MOUSEDEV=m ++CONFIG_INPUT_MOUSEDEV_PSAUX=y ++CONFIG_INPUT_EVDEV=y ++# CONFIG_KEYBOARD_ATKBD is not set ++CONFIG_KEYBOARD_GPIO=y ++CONFIG_KEYBOARD_GPIO_POLLED=y ++CONFIG_KEYBOARD_PINEPHONE=y ++CONFIG_KEYBOARD_SUN4I_LRADC=y ++# CONFIG_INPUT_MOUSE is not set ++CONFIG_INPUT_TOUCHSCREEN=y ++CONFIG_TOUCHSCREEN_GOODIX=m ++CONFIG_INPUT_MISC=y ++CONFIG_INPUT_GPIO_VIBRA=y ++CONFIG_INPUT_AXP20X_PEK=y ++CONFIG_INPUT_UINPUT=y ++# CONFIG_SERIO is not set ++# CONFIG_LEGACY_PTYS is not set ++CONFIG_SERIAL_8250=y ++# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set ++CONFIG_SERIAL_8250_CONSOLE=y ++CONFIG_SERIAL_8250_NR_UARTS=8 ++CONFIG_SERIAL_8250_RUNTIME_UARTS=8 ++CONFIG_SERIAL_8250_DW=y ++CONFIG_SERIAL_OF_PLATFORM=y ++CONFIG_SERIAL_DEV_BUS=y ++# CONFIG_HW_RANDOM is not set ++CONFIG_I2C_CHARDEV=y ++CONFIG_I2C_MUX=y ++CONFIG_I2C_GPIO=y ++CONFIG_I2C_MV64XXX=y ++CONFIG_PINCTRL_AXP209=y ++CONFIG_PINCTRL_SINGLE=y ++# CONFIG_PINCTRL_SUN8I_H3_R is not set ++# CONFIG_PINCTRL_SUN50I_H5 is not set ++# CONFIG_PINCTRL_SUN50I_H6 is not set ++# CONFIG_PINCTRL_SUN50I_H6_R is not set ++# CONFIG_PINCTRL_SUN50I_H616 is not set ++# CONFIG_PINCTRL_SUN50I_H616_R is not set ++CONFIG_NVMEM_REBOOT_MODE=y ++CONFIG_IP5XXX_POWER=y ++CONFIG_CHARGER_AXP20X=y ++CONFIG_BATTERY_AXP20X=y ++CONFIG_AXP20X_POWER=y ++# CONFIG_HWMON is not set ++CONFIG_THERMAL=y ++CONFIG_THERMAL_STATISTICS=y ++CONFIG_THERMAL_GOV_FAIR_SHARE=y ++CONFIG_THERMAL_GOV_BANG_BANG=y ++CONFIG_CPU_THERMAL=y ++CONFIG_SUN8I_THERMAL=y ++CONFIG_MFD_SUN4I_GPADC=y ++CONFIG_MFD_AXP20X_RSB=y ++CONFIG_MFD_SUN6I_PRCM=y ++CONFIG_REGULATOR=y ++CONFIG_REGULATOR_FIXED_VOLTAGE=y ++CONFIG_REGULATOR_AXP20X=y ++CONFIG_REGULATOR_GPIO=y ++CONFIG_MEDIA_SUPPORT=m ++# CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set ++CONFIG_MEDIA_CAMERA_SUPPORT=y ++CONFIG_MEDIA_PLATFORM_SUPPORT=y ++CONFIG_V4L2_FLASH_LED_CLASS=m ++CONFIG_V4L_PLATFORM_DRIVERS=y ++CONFIG_V4L_MEM2MEM_DRIVERS=y ++CONFIG_VIDEO_MEM2MEM_DEINTERLACE=m ++CONFIG_VIDEO_SUN6I_CSI=m ++CONFIG_VIDEO_SUN8I_DEINTERLACE=m ++CONFIG_VIDEO_SUN8I_ROTATE=m ++CONFIG_VIDEO_OV5640=m ++CONFIG_VIDEO_GC2145=m ++CONFIG_DRM=y ++CONFIG_DRM_LOAD_EDID_FIRMWARE=y ++CONFIG_DRM_SUN4I=y ++CONFIG_DRM_PANEL_ILITEK_ILI9881C=y ++CONFIG_DRM_PANEL_SITRONIX_ST7703=y ++CONFIG_DRM_DW_HDMI_AHB_AUDIO=m ++CONFIG_DRM_DW_HDMI_I2S_AUDIO=m ++CONFIG_DRM_DW_HDMI_CEC=y ++CONFIG_DRM_LIMA=m ++CONFIG_FB=y ++CONFIG_FB_SIMPLE=y ++CONFIG_BACKLIGHT_CLASS_DEVICE=y ++CONFIG_BACKLIGHT_PWM=y ++CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y ++CONFIG_SOUND=m ++CONFIG_SND=m ++CONFIG_SND_OSSEMUL=y ++CONFIG_SND_MIXER_OSS=m ++CONFIG_SND_PCM_OSS=m ++CONFIG_SND_HRTIMER=m ++# CONFIG_SND_SUPPORT_OLD_API is not set ++CONFIG_SND_SEQUENCER=m ++CONFIG_SND_SEQ_DUMMY=m ++CONFIG_SND_SEQUENCER_OSS=m ++CONFIG_SND_ALOOP=m ++CONFIG_SND_USB_AUDIO=m ++CONFIG_SND_SOC=m ++CONFIG_SND_SUN4I_CODEC=m ++CONFIG_SND_SUN8I_CODEC=m ++CONFIG_SND_SUN8I_CODEC_ANALOG=m ++CONFIG_SND_SUN50I_CODEC_ANALOG=m ++CONFIG_SND_SUN4I_I2S=m ++CONFIG_SND_SUN4I_SPDIF=m ++CONFIG_SND_SUN9I_HDMI_AUDIO=m ++CONFIG_SND_SOC_BT_SCO=m ++CONFIG_SND_SOC_EC25=m ++CONFIG_SND_SOC_SIMPLE_AMPLIFIER=m ++CONFIG_SND_SIMPLE_CARD=m ++CONFIG_SND_AUDIO_GRAPH_CARD=m ++CONFIG_HID=m ++# CONFIG_HID_A4TECH is not set ++# CONFIG_HID_APPLE is not set ++# CONFIG_HID_BELKIN is not set ++# CONFIG_HID_CHERRY is not set ++# CONFIG_HID_CHICONY is not set ++# CONFIG_HID_CYPRESS is not set ++# CONFIG_HID_EZKEY is not set ++# CONFIG_HID_ITE is not set ++# CONFIG_HID_KENSINGTON is not set ++# CONFIG_HID_LOGITECH is not set ++# CONFIG_HID_REDRAGON is not set ++# CONFIG_HID_MICROSOFT is not set ++# CONFIG_HID_MONTEREY is not set ++CONFIG_USB_HIDDEV=y ++CONFIG_USB_LED_TRIG=y ++CONFIG_USB=m ++CONFIG_USB_ANNOUNCE_NEW_DEVICES=y ++CONFIG_USB_OTG=y ++CONFIG_USB_OTG_FSM=m ++CONFIG_USB_LEDS_TRIGGER_USBPORT=m ++CONFIG_USB_MON=m ++CONFIG_USB_EHCI_HCD=m ++CONFIG_USB_EHCI_HCD_PLATFORM=m ++CONFIG_USB_OHCI_HCD=m ++CONFIG_USB_OHCI_HCD_PLATFORM=m ++CONFIG_USB_ACM=m ++CONFIG_USB_STORAGE=m ++CONFIG_USB_UAS=m ++CONFIG_USBIP_CORE=m ++CONFIG_USBIP_VHCI_HCD=m ++CONFIG_USBIP_HOST=m ++CONFIG_USBIP_VUDC=m ++CONFIG_USB_MUSB_HDRC=m ++CONFIG_USB_MUSB_SUNXI=m ++CONFIG_MUSB_PIO_ONLY=y ++CONFIG_USB_SERIAL=m ++CONFIG_USB_SERIAL_SIMPLE=m ++CONFIG_USB_SERIAL_CH341=m ++CONFIG_USB_SERIAL_CP210X=m ++CONFIG_USB_SERIAL_FTDI_SIO=m ++CONFIG_USB_SERIAL_QCAUX=m ++CONFIG_USB_SERIAL_QUALCOMM=m ++CONFIG_USB_SERIAL_OPTION=m ++CONFIG_NOP_USB_XCEIV=m ++CONFIG_USB_GADGET=m ++CONFIG_USB_GADGET_VBUS_DRAW=500 ++CONFIG_U_SERIAL_CONSOLE=y ++CONFIG_USB_CONFIGFS=m ++CONFIG_USB_CONFIGFS_SERIAL=y ++CONFIG_USB_CONFIGFS_ACM=y ++CONFIG_USB_CONFIGFS_OBEX=y ++CONFIG_USB_CONFIGFS_NCM=y ++CONFIG_USB_CONFIGFS_ECM=y ++CONFIG_USB_CONFIGFS_ECM_SUBSET=y ++CONFIG_USB_CONFIGFS_RNDIS=y ++CONFIG_USB_CONFIGFS_EEM=y ++CONFIG_USB_CONFIGFS_MASS_STORAGE=y ++CONFIG_USB_CONFIGFS_F_LB_SS=y ++CONFIG_USB_CONFIGFS_F_FS=y ++CONFIG_USB_CONFIGFS_F_UAC1=y ++CONFIG_USB_CONFIGFS_F_UAC2=y ++CONFIG_USB_CONFIGFS_F_MIDI=y ++CONFIG_USB_CONFIGFS_F_HID=y ++CONFIG_USB_CONFIGFS_F_UVC=y ++CONFIG_USB_CONFIGFS_F_PRINTER=y ++CONFIG_USB_AUDIO=m ++CONFIG_USB_ETH=m ++CONFIG_USB_ETH_EEM=y ++CONFIG_USB_G_NCM=m ++CONFIG_USB_GADGETFS=m ++CONFIG_USB_FUNCTIONFS=m ++CONFIG_USB_FUNCTIONFS_ETH=y ++CONFIG_USB_FUNCTIONFS_RNDIS=y ++CONFIG_USB_FUNCTIONFS_GENERIC=y ++CONFIG_USB_MASS_STORAGE=m ++CONFIG_USB_G_SERIAL=m ++CONFIG_USB_MIDI_GADGET=m ++CONFIG_USB_G_PRINTER=m ++CONFIG_USB_CDC_COMPOSITE=m ++CONFIG_USB_G_ACM_MS=m ++CONFIG_USB_G_MULTI=m ++CONFIG_USB_G_MULTI_CDC=y ++CONFIG_USB_G_HID=m ++CONFIG_USB_G_WEBCAM=m ++CONFIG_USB_RAW_GADGET=m ++CONFIG_TYPEC=y ++CONFIG_TYPEC_ANX7688=y ++CONFIG_TYPEC_DP_ALTMODE=y ++CONFIG_MMC=y ++CONFIG_MMC_SUNXI=y ++CONFIG_NEW_LEDS=y ++CONFIG_LEDS_CLASS=y ++CONFIG_LEDS_CLASS_FLASH=y ++CONFIG_LEDS_CLASS_MULTICOLOR=y ++CONFIG_LEDS_GPIO=y ++CONFIG_LEDS_SGM3140=m ++CONFIG_LEDS_GROUP_MULTICOLOR=m ++CONFIG_LEDS_TRIGGER_TIMER=y ++CONFIG_LEDS_TRIGGER_ONESHOT=y ++CONFIG_LEDS_TRIGGER_HEARTBEAT=y ++CONFIG_LEDS_TRIGGER_CPU=y ++CONFIG_LEDS_TRIGGER_ACTIVITY=y ++CONFIG_LEDS_TRIGGER_DEFAULT_ON=y ++CONFIG_LEDS_TRIGGER_TRANSIENT=m ++CONFIG_LEDS_TRIGGER_CAMERA=m ++CONFIG_LEDS_TRIGGER_PANIC=y ++CONFIG_LEDS_TRIGGER_NETDEV=y ++CONFIG_LEDS_TRIGGER_PATTERN=y ++CONFIG_RTC_CLASS=y ++# CONFIG_RTC_INTF_PROC is not set ++CONFIG_RTC_DRV_SUN6I=y ++CONFIG_DMADEVICES=y ++CONFIG_DMA_SUN6I=y ++CONFIG_DMABUF_HEAPS=y ++CONFIG_DMABUF_HEAPS_SYSTEM=y ++CONFIG_DMABUF_HEAPS_CMA=y ++# CONFIG_VIRTIO_MENU is not set ++# CONFIG_VHOST_MENU is not set ++CONFIG_STAGING=y ++CONFIG_STAGING_MEDIA=y ++CONFIG_VIDEO_SUNXI=y ++CONFIG_VIDEO_SUNXI_CEDRUS=m ++CONFIG_RTL8723CS=m ++# CONFIG_SUN50I_H6_CCU is not set ++# CONFIG_SUN50I_H616_CCU is not set ++# CONFIG_SUN50I_H6_R_CCU is not set ++# CONFIG_SUN8I_H3_CCU is not set ++# CONFIG_HISILICON_ERRATUM_161010101 is not set ++# CONFIG_ARM64_ERRATUM_858921 is not set ++CONFIG_MAILBOX=y ++CONFIG_IOMMU_IO_PGTABLE_LPAE=y ++CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=y ++CONFIG_DEVFREQ_GOV_PERFORMANCE=m ++CONFIG_DEVFREQ_GOV_POWERSAVE=m ++CONFIG_DEVFREQ_GOV_USERSPACE=m ++CONFIG_DEVFREQ_GOV_PASSIVE=m ++CONFIG_ARM_SUN8I_A33_MBUS_DEVFREQ=m ++CONFIG_PM_DEVFREQ_EVENT=y ++CONFIG_IIO=y ++CONFIG_IIO_BUFFER_CB=m ++CONFIG_IIO_BUFFER_HW_CONSUMER=m ++CONFIG_IIO_SW_DEVICE=m ++CONFIG_IIO_SW_TRIGGER=m ++CONFIG_AXP20X_ADC=y ++CONFIG_INV_MPU6050_I2C=m ++CONFIG_STK3310=m ++CONFIG_AF8133J=m ++CONFIG_IIO_ST_MAGN_3AXIS=m ++CONFIG_IIO_HRTIMER_TRIGGER=m ++CONFIG_IIO_INTERRUPT_TRIGGER=m ++CONFIG_IIO_SYSFS_TRIGGER=m ++CONFIG_PWM=y ++CONFIG_PWM_SUN4I=y ++CONFIG_PHY_SUN4I_USB=y ++CONFIG_ARM_CCI_PMU=y ++# CONFIG_ARM_CCI5xx_PMU is not set ++CONFIG_ANDROID_BINDER_IPC=y ++CONFIG_ANDROID_BINDERFS=y ++CONFIG_NVMEM_SUNXI_SID=y ++CONFIG_VALIDATE_FS_PARSER=y ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_FS_POSIX_ACL=y ++CONFIG_XFS_FS=m ++CONFIG_XFS_POSIX_ACL=y ++CONFIG_BTRFS_FS=y ++CONFIG_BTRFS_FS_POSIX_ACL=y ++CONFIG_NILFS2_FS=m ++CONFIG_F2FS_FS=y ++CONFIG_F2FS_FS_SECURITY=y ++CONFIG_F2FS_CHECK_FS=y ++CONFIG_F2FS_FS_COMPRESSION=y ++CONFIG_FS_ENCRYPTION=y ++CONFIG_FANOTIFY=y ++CONFIG_AUTOFS_FS=m ++CONFIG_FUSE_FS=m ++CONFIG_CUSE=m ++CONFIG_OVERLAY_FS=m ++CONFIG_OVERLAY_FS_INDEX=y ++CONFIG_OVERLAY_FS_METACOPY=y ++CONFIG_VFAT_FS=y ++CONFIG_EXFAT_FS=m ++CONFIG_PROC_CHILDREN=y ++CONFIG_TMPFS=y ++CONFIG_TMPFS_POSIX_ACL=y ++CONFIG_CONFIGFS_FS=y ++CONFIG_ECRYPT_FS=m ++CONFIG_ECRYPT_FS_MESSAGING=y ++CONFIG_SQUASHFS=m ++CONFIG_SQUASHFS_FILE_DIRECT=y ++CONFIG_SQUASHFS_XATTR=y ++CONFIG_SQUASHFS_LZ4=y ++CONFIG_SQUASHFS_LZO=y ++CONFIG_SQUASHFS_XZ=y ++CONFIG_SQUASHFS_ZSTD=y ++# CONFIG_NETWORK_FILESYSTEMS is not set ++CONFIG_NLS_CODEPAGE_437=y ++CONFIG_NLS_CODEPAGE_852=y ++CONFIG_NLS_ISO8859_1=y ++CONFIG_NLS_ISO8859_2=y ++CONFIG_NLS_UTF8=y ++CONFIG_KEYS_REQUEST_CACHE=y ++CONFIG_PERSISTENT_KEYRINGS=y ++CONFIG_ENCRYPTED_KEYS=y ++CONFIG_KEY_DH_OPERATIONS=y ++CONFIG_LSM="yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor" ++CONFIG_INIT_STACK_NONE=y ++CONFIG_CRYPTO_USER=m ++CONFIG_CRYPTO_PCRYPT=m ++CONFIG_CRYPTO_CRYPTD=y ++CONFIG_CRYPTO_CURVE25519=m ++CONFIG_CRYPTO_DES=y ++CONFIG_CRYPTO_ARC4=m ++CONFIG_CRYPTO_CHACHA20POLY1305=m ++CONFIG_CRYPTO_SEQIV=m ++CONFIG_CRYPTO_ECHAINIV=m ++CONFIG_CRYPTO_MD4=m ++CONFIG_CRYPTO_SHA1=y ++CONFIG_CRYPTO_SHA3=y ++CONFIG_CRYPTO_DEFLATE=y ++CONFIG_CRYPTO_LZO=y ++CONFIG_CRYPTO_LZ4=y ++CONFIG_CRYPTO_ZSTD=y ++CONFIG_CRYPTO_ANSI_CPRNG=m ++CONFIG_CRYPTO_USER_API_HASH=m ++CONFIG_CRYPTO_USER_API_SKCIPHER=m ++CONFIG_CRYPTO_USER_API_RNG=m ++CONFIG_CRYPTO_USER_API_AEAD=m ++CONFIG_CRYPTO_GHASH_ARM64_CE=m ++CONFIG_CRYPTO_SHA1_ARM64_CE=m ++CONFIG_CRYPTO_SHA2_ARM64_CE=m ++CONFIG_CRYPTO_SHA512_ARM64_CE=m ++CONFIG_CRYPTO_SHA3_ARM64=m ++CONFIG_CRYPTO_SM3_ARM64_CE=m ++CONFIG_CRYPTO_AES_ARM64=m ++CONFIG_CRYPTO_AES_ARM64_BS=m ++CONFIG_CRYPTO_SM4_ARM64_CE=m ++CONFIG_CRYPTO_AES_ARM64_CE_CCM=m ++CONFIG_CRYPTO_CRCT10DIF_ARM64_CE=m ++CONFIG_CRYPTO_DEV_SUN8I_CE=m ++CONFIG_PKCS8_PRIVATE_KEY_PARSER=m ++# CONFIG_RAID6_PQ_BENCHMARK is not set ++CONFIG_CRC_T10DIF=y ++CONFIG_DMA_CMA=y ++CONFIG_CMA_SIZE_MBYTES=64 ++CONFIG_MESSAGE_LOGLEVEL_DEFAULT=7 ++CONFIG_DYNAMIC_DEBUG=y ++CONFIG_FRAME_WARN=1024 ++CONFIG_MAGIC_SYSRQ=y ++CONFIG_DEBUG_FS=y ++CONFIG_PER_VMA_LOCK_STATS=y ++CONFIG_RCU_EXP_CPU_STALL_TIMEOUT=20 ++CONFIG_FTRACE=y ++CONFIG_BOOTTIME_TRACING=y ++CONFIG_FUNCTION_TRACER=y ++CONFIG_FTRACE_SYSCALLS=y ++CONFIG_TRACER_SNAPSHOT=y ++# CONFIG_STRICT_DEVMEM is not set ++# CONFIG_RUNTIME_TESTING_MENU is not set +diff --git a/arch/arm64/configs/pinephone_multidist_defconfig b/arch/arm64/configs/pinephone_multidist_defconfig +new file mode 100644 +index 000000000000..633924e11882 +--- /dev/null ++++ b/arch/arm64/configs/pinephone_multidist_defconfig +@@ -0,0 +1,600 @@ ++CONFIG_SYSVIPC=y ++CONFIG_POSIX_MQUEUE=y ++CONFIG_NO_HZ=y ++CONFIG_HIGH_RES_TIMERS=y ++CONFIG_BPF_SYSCALL=y ++CONFIG_BPF_JIT=y ++CONFIG_BPF_JIT_ALWAYS_ON=y ++# CONFIG_BPF_UNPRIV_DEFAULT_OFF is not set ++CONFIG_BSD_PROCESS_ACCT=y ++CONFIG_BSD_PROCESS_ACCT_V3=y ++CONFIG_TASKSTATS=y ++CONFIG_TASK_DELAY_ACCT=y ++CONFIG_TASK_XACCT=y ++CONFIG_TASK_IO_ACCOUNTING=y ++CONFIG_PSI=y ++CONFIG_MEMCG=y ++CONFIG_BLK_CGROUP=y ++CONFIG_CFS_BANDWIDTH=y ++CONFIG_RT_GROUP_SCHED=y ++CONFIG_CGROUP_PIDS=y ++CONFIG_CGROUP_FREEZER=y ++CONFIG_CPUSETS=y ++# CONFIG_PROC_PID_CPUSET is not set ++CONFIG_CGROUP_DEVICE=y ++CONFIG_CGROUP_CPUACCT=y ++CONFIG_CGROUP_BPF=y ++CONFIG_NAMESPACES=y ++CONFIG_USER_NS=y ++CONFIG_SCHED_AUTOGROUP=y ++# CONFIG_RD_GZIP is not set ++# CONFIG_RD_BZIP2 is not set ++# CONFIG_RD_LZMA is not set ++# CONFIG_RD_XZ is not set ++# CONFIG_RD_LZO is not set ++# CONFIG_RD_ZSTD is not set ++CONFIG_EXPERT=y ++CONFIG_PERF_EVENTS=y ++CONFIG_ARCH_SUNXI=y ++# CONFIG_ARM64_ERRATUM_832075 is not set ++# CONFIG_ARM64_ERRATUM_1024718 is not set ++# CONFIG_ARM64_ERRATUM_1418040 is not set ++# CONFIG_ARM64_ERRATUM_1165522 is not set ++# CONFIG_ARM64_ERRATUM_1319367 is not set ++# CONFIG_ARM64_ERRATUM_1530923 is not set ++CONFIG_ARM64_ERRATUM_2441007=y ++# CONFIG_ARM64_ERRATUM_1463225 is not set ++# CONFIG_ARM64_ERRATUM_2051678 is not set ++# CONFIG_ARM64_ERRATUM_2077057 is not set ++CONFIG_ARM64_ERRATUM_2441009=y ++# CONFIG_CAVIUM_ERRATUM_22375 is not set ++# CONFIG_CAVIUM_ERRATUM_23154 is not set ++# CONFIG_CAVIUM_ERRATUM_27456 is not set ++# CONFIG_CAVIUM_ERRATUM_30115 is not set ++# CONFIG_CAVIUM_TX2_ERRATUM_219 is not set ++# CONFIG_FUJITSU_ERRATUM_010001 is not set ++# CONFIG_HISILICON_ERRATUM_161600802 is not set ++# CONFIG_QCOM_FALKOR_ERRATUM_1003 is not set ++# CONFIG_QCOM_FALKOR_ERRATUM_1009 is not set ++# CONFIG_QCOM_QDF2400_ERRATUM_0065 is not set ++# CONFIG_QCOM_FALKOR_ERRATUM_E1041 is not set ++# CONFIG_SOCIONEXT_SYNQUACER_PREITS is not set ++CONFIG_ARM64_VA_BITS_39=y ++CONFIG_SCHED_MC=y ++CONFIG_NR_CPUS=4 ++CONFIG_COMPAT=y ++# CONFIG_ARM64_HW_AFDBM is not set ++# CONFIG_ARM64_PAN is not set ++# CONFIG_ARM64_USE_LSE_ATOMICS is not set ++# CONFIG_ARM64_RAS_EXTN is not set ++# CONFIG_ARM64_CNP is not set ++# CONFIG_ARM64_PTR_AUTH is not set ++# CONFIG_ARM64_AMU_EXTN is not set ++# CONFIG_ARM64_TLB_RANGE is not set ++# CONFIG_ARM64_BTI is not set ++# CONFIG_ARM64_E0PD is not set ++# CONFIG_ARM64_SVE is not set ++# CONFIG_EFI is not set ++CONFIG_PM_AUTOSLEEP=y ++CONFIG_PM_WAKELOCKS=y ++CONFIG_PM_DEBUG=y ++CONFIG_WQ_POWER_EFFICIENT_DEFAULT=y ++CONFIG_ENERGY_MODEL=y ++CONFIG_CPU_IDLE=y ++CONFIG_CPU_IDLE_GOV_LADDER=y ++CONFIG_ARM_PSCI_CPUIDLE=y ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_STAT=y ++CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y ++CONFIG_CPUFREQ_DT=y ++CONFIG_JUMP_LABEL=y ++# CONFIG_STACKPROTECTOR is not set ++# CONFIG_GCC_PLUGINS is not set ++CONFIG_MODULES=y ++CONFIG_MODULE_UNLOAD=y ++CONFIG_MODVERSIONS=y ++CONFIG_BLK_DEV_THROTTLING=y ++CONFIG_BLK_WBT=y ++CONFIG_BLK_CGROUP_IOLATENCY=y ++CONFIG_BLK_CGROUP_IOCOST=y ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_MQ_IOSCHED_DEADLINE is not set ++# CONFIG_MQ_IOSCHED_KYBER is not set ++CONFIG_CMA=y ++CONFIG_CMA_DEBUGFS=y ++CONFIG_CMA_AREAS=7 ++CONFIG_NET=y ++CONFIG_PACKET=y ++CONFIG_PACKET_DIAG=y ++CONFIG_UNIX=y ++CONFIG_UNIX_DIAG=y ++CONFIG_XFRM_USER=y ++CONFIG_NET_KEY=y ++CONFIG_INET=y ++CONFIG_IP_MULTICAST=y ++CONFIG_IP_ADVANCED_ROUTER=y ++CONFIG_SYN_COOKIES=y ++# CONFIG_INET_DIAG is not set ++# CONFIG_IPV6_SIT is not set ++CONFIG_MPTCP=y ++CONFIG_NETFILTER=y ++CONFIG_NETFILTER_NETLINK_QUEUE=y ++CONFIG_NETFILTER_NETLINK_LOG=y ++CONFIG_NF_CONNTRACK=y ++CONFIG_NF_CONNTRACK_MARK=y ++# CONFIG_NF_CT_PROTO_DCCP is not set ++# CONFIG_NF_CT_PROTO_SCTP is not set ++# CONFIG_NF_CT_PROTO_UDPLITE is not set ++CONFIG_NF_CONNTRACK_FTP=y ++CONFIG_NF_CONNTRACK_H323=y ++CONFIG_NF_CONNTRACK_IRC=y ++CONFIG_NF_CONNTRACK_NETBIOS_NS=y ++CONFIG_NF_CONNTRACK_SNMP=y ++CONFIG_NF_CONNTRACK_PPTP=y ++CONFIG_NF_CONNTRACK_SIP=y ++CONFIG_NF_CONNTRACK_TFTP=y ++CONFIG_NF_CT_NETLINK=y ++CONFIG_NETFILTER_NETLINK_GLUE_CT=y ++CONFIG_NF_TABLES=y ++CONFIG_NF_TABLES_INET=y ++CONFIG_NF_TABLES_NETDEV=y ++CONFIG_NFT_NUMGEN=y ++CONFIG_NFT_CT=y ++CONFIG_NFT_CONNLIMIT=y ++CONFIG_NFT_LOG=y ++CONFIG_NFT_LIMIT=y ++CONFIG_NFT_MASQ=y ++CONFIG_NFT_REDIR=y ++CONFIG_NFT_NAT=y ++CONFIG_NFT_TUNNEL=y ++CONFIG_NFT_QUEUE=y ++CONFIG_NFT_QUOTA=y ++CONFIG_NFT_REJECT=y ++CONFIG_NFT_HASH=y ++CONFIG_NFT_FIB_INET=y ++CONFIG_NFT_SOCKET=y ++CONFIG_NFT_OSF=y ++CONFIG_NFT_DUP_NETDEV=y ++CONFIG_NFT_FWD_NETDEV=y ++CONFIG_NF_FLOW_TABLE_INET=y ++CONFIG_NF_FLOW_TABLE=y ++CONFIG_IP_SET=y ++CONFIG_IP_SET_BITMAP_IP=y ++CONFIG_IP_SET_BITMAP_IPMAC=y ++CONFIG_IP_SET_BITMAP_PORT=y ++CONFIG_IP_SET_HASH_IP=y ++CONFIG_IP_SET_HASH_IPMARK=y ++CONFIG_IP_SET_HASH_IPPORT=y ++CONFIG_IP_SET_HASH_IPPORTIP=y ++CONFIG_IP_SET_HASH_IPPORTNET=y ++CONFIG_IP_SET_HASH_IPMAC=y ++CONFIG_IP_SET_HASH_MAC=y ++CONFIG_IP_SET_HASH_NETPORTNET=y ++CONFIG_IP_SET_HASH_NET=y ++CONFIG_IP_SET_HASH_NETNET=y ++CONFIG_IP_SET_HASH_NETPORT=y ++CONFIG_IP_SET_HASH_NETIFACE=y ++CONFIG_IP_SET_LIST_SET=y ++CONFIG_NFT_FIB_IPV4=y ++CONFIG_NF_TABLES_ARP=y ++CONFIG_NF_LOG_ARP=y ++CONFIG_NF_LOG_IPV4=y ++# CONFIG_NF_NAT_SNMP_BASIC is not set ++CONFIG_NFT_DUP_IPV6=y ++CONFIG_NFT_FIB_IPV6=y ++CONFIG_NF_LOG_IPV6=y ++CONFIG_BRIDGE=y ++CONFIG_NET_SCHED=y ++CONFIG_NET_SCH_HTB=y ++CONFIG_NET_CLS_BASIC=y ++CONFIG_NET_CLS_ROUTE4=y ++CONFIG_NET_CLS_FW=y ++CONFIG_NET_CLS_CGROUP=y ++CONFIG_NET_CLS_ACT=y ++CONFIG_NET_ACT_POLICE=y ++CONFIG_NET_ACT_GACT=y ++CONFIG_NET_ACT_MIRRED=y ++CONFIG_NETLINK_DIAG=y ++CONFIG_BT=y ++CONFIG_BT_RFCOMM=y ++CONFIG_BT_RFCOMM_TTY=y ++CONFIG_BT_BNEP=y ++CONFIG_BT_BNEP_MC_FILTER=y ++CONFIG_BT_BNEP_PROTO_FILTER=y ++CONFIG_BT_HIDP=y ++CONFIG_BT_LEDS=y ++CONFIG_BT_HCIUART=y ++CONFIG_BT_HCIUART_RTL=y ++CONFIG_CFG80211=y ++CONFIG_CFG80211_DEBUGFS=y ++CONFIG_CFG80211_WEXT=y ++CONFIG_MAC80211=y ++CONFIG_MAC80211_LEDS=y ++CONFIG_RFKILL=y ++CONFIG_RFKILL_GPIO=y ++CONFIG_PAGE_POOL_STATS=y ++CONFIG_UEVENT_HELPER=y ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++CONFIG_EXTRA_FIRMWARE="regulatory.db regulatory.db.p7s anx7688-fw.bin rtl_bt/rtl8723cs_xx_config.bin rtl_bt/rtl8723cs_xx_fw.bin ov5640_af.bin rtw88/rtw8703b_wow_fw.bin rtw88/rtw8703b_fw.bin" ++CONFIG_EXTRA_FIRMWARE_DIR="/workspace/megous.com/orangepi-pc/firmware" ++CONFIG_ARM_SCPI_PROTOCOL=y ++# CONFIG_ARM_SMCCC_SOC_ID is not set ++CONFIG_ZRAM=y ++CONFIG_ZRAM_WRITEBACK=y ++CONFIG_ZRAM_MEMORY_TRACKING=y ++CONFIG_BLK_DEV_LOOP=y ++CONFIG_BLK_DEV_NBD=y ++CONFIG_PPKB_POWER_MANAGER=y ++CONFIG_MODEM_POWER=y ++CONFIG_SCSI=y ++CONFIG_BLK_DEV_SD=y ++# CONFIG_BLK_DEV_BSG is not set ++# CONFIG_SCSI_LOWLEVEL is not set ++CONFIG_MD=y ++CONFIG_BLK_DEV_DM=y ++CONFIG_DM_CRYPT=y ++CONFIG_DM_INIT=y ++CONFIG_DM_UEVENT=y ++CONFIG_NETDEVICES=y ++CONFIG_WIREGUARD=y ++CONFIG_TUN=y ++CONFIG_VETH=y ++# CONFIG_ETHERNET is not set ++CONFIG_PPP=y ++CONFIG_PPP_BSDCOMP=y ++CONFIG_PPP_DEFLATE=y ++CONFIG_PPP_FILTER=y ++CONFIG_PPP_MPPE=y ++CONFIG_USB_RTL8152=y ++CONFIG_USB_USBNET=y ++# CONFIG_USB_NET_AX8817X is not set ++# CONFIG_USB_NET_AX88179_178A is not set ++CONFIG_USB_NET_CDC_EEM=y ++CONFIG_USB_NET_CDC_MBIM=y ++# CONFIG_USB_NET_NET1080 is not set ++# CONFIG_USB_NET_CDC_SUBSET is not set ++# CONFIG_USB_NET_ZAURUS is not set ++CONFIG_USB_NET_QMI_WWAN=y ++# CONFIG_WLAN_VENDOR_ADMTEK is not set ++# CONFIG_WLAN_VENDOR_ATH is not set ++# CONFIG_WLAN_VENDOR_ATMEL is not set ++# CONFIG_WLAN_VENDOR_BROADCOM is not set ++# CONFIG_WLAN_VENDOR_INTEL is not set ++# CONFIG_WLAN_VENDOR_INTERSIL is not set ++# CONFIG_WLAN_VENDOR_MARVELL is not set ++# CONFIG_WLAN_VENDOR_MEDIATEK is not set ++# CONFIG_WLAN_VENDOR_MICROCHIP is not set ++# CONFIG_WLAN_VENDOR_PURELIFI is not set ++# CONFIG_WLAN_VENDOR_RALINK is not set ++# CONFIG_RTL_CARDS is not set ++CONFIG_RTW88=m ++# CONFIG_WLAN_VENDOR_RSI is not set ++# CONFIG_WLAN_VENDOR_SILABS is not set ++# CONFIG_WLAN_VENDOR_ST is not set ++# CONFIG_WLAN_VENDOR_TI is not set ++# CONFIG_WLAN_VENDOR_ZYDAS is not set ++# CONFIG_WLAN_VENDOR_QUANTENNA is not set ++CONFIG_INPUT_MOUSEDEV=y ++CONFIG_INPUT_MOUSEDEV_PSAUX=y ++CONFIG_INPUT_EVDEV=y ++# CONFIG_KEYBOARD_ATKBD is not set ++CONFIG_KEYBOARD_GPIO=y ++CONFIG_KEYBOARD_GPIO_POLLED=y ++CONFIG_KEYBOARD_PINEPHONE=y ++CONFIG_KEYBOARD_SUN4I_LRADC=y ++# CONFIG_INPUT_MOUSE is not set ++CONFIG_INPUT_TOUCHSCREEN=y ++CONFIG_TOUCHSCREEN_GOODIX=y ++CONFIG_INPUT_MISC=y ++CONFIG_INPUT_GPIO_VIBRA=y ++CONFIG_INPUT_AXP20X_PEK=y ++CONFIG_INPUT_UINPUT=y ++# CONFIG_SERIO is not set ++# CONFIG_LEGACY_PTYS is not set ++CONFIG_SERIAL_8250=y ++# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set ++CONFIG_SERIAL_8250_CONSOLE=y ++CONFIG_SERIAL_8250_NR_UARTS=8 ++CONFIG_SERIAL_8250_RUNTIME_UARTS=8 ++CONFIG_SERIAL_8250_DW=y ++CONFIG_SERIAL_OF_PLATFORM=y ++CONFIG_SERIAL_DEV_BUS=y ++# CONFIG_HW_RANDOM is not set ++CONFIG_I2C_CHARDEV=y ++CONFIG_I2C_GPIO=y ++CONFIG_I2C_MV64XXX=y ++CONFIG_PINCTRL_AXP209=y ++CONFIG_PINCTRL_SINGLE=y ++# CONFIG_PINCTRL_SUN8I_H3_R is not set ++# CONFIG_PINCTRL_SUN50I_H5 is not set ++# CONFIG_PINCTRL_SUN50I_H6 is not set ++# CONFIG_PINCTRL_SUN50I_H6_R is not set ++# CONFIG_PINCTRL_SUN50I_H616 is not set ++# CONFIG_PINCTRL_SUN50I_H616_R is not set ++CONFIG_GPIO_SYSFS=y ++CONFIG_NVMEM_REBOOT_MODE=y ++CONFIG_IP5XXX_POWER=y ++CONFIG_CHARGER_AXP20X=y ++CONFIG_BATTERY_AXP20X=y ++CONFIG_AXP20X_POWER=y ++# CONFIG_HWMON is not set ++CONFIG_THERMAL=y ++CONFIG_THERMAL_STATISTICS=y ++CONFIG_THERMAL_GOV_FAIR_SHARE=y ++CONFIG_THERMAL_GOV_BANG_BANG=y ++CONFIG_CPU_THERMAL=y ++CONFIG_SUN8I_THERMAL=y ++CONFIG_WATCHDOG=y ++CONFIG_SUNXI_WATCHDOG=y ++CONFIG_MFD_SUN4I_GPADC=y ++CONFIG_MFD_AXP20X_RSB=y ++CONFIG_MFD_SUN6I_PRCM=y ++CONFIG_REGULATOR=y ++CONFIG_REGULATOR_FIXED_VOLTAGE=y ++CONFIG_REGULATOR_AXP20X=y ++CONFIG_REGULATOR_GPIO=y ++CONFIG_MEDIA_SUPPORT=y ++CONFIG_MEDIA_SUPPORT_FILTER=y ++# CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set ++CONFIG_MEDIA_CAMERA_SUPPORT=y ++CONFIG_MEDIA_PLATFORM_SUPPORT=y ++CONFIG_V4L2_FLASH_LED_CLASS=y ++CONFIG_V4L_PLATFORM_DRIVERS=y ++CONFIG_V4L_MEM2MEM_DRIVERS=y ++CONFIG_VIDEO_MEM2MEM_DEINTERLACE=y ++CONFIG_VIDEO_SUN6I_CSI=y ++CONFIG_VIDEO_SUN8I_DEINTERLACE=y ++CONFIG_VIDEO_SUN8I_ROTATE=y ++CONFIG_VIDEO_OV5640=y ++CONFIG_VIDEO_GC2145=y ++CONFIG_DRM=y ++CONFIG_DRM_LOAD_EDID_FIRMWARE=y ++CONFIG_DRM_SUN4I=y ++CONFIG_DRM_PANEL_ILITEK_ILI9881C=y ++CONFIG_DRM_PANEL_SITRONIX_ST7703=y ++CONFIG_DRM_DW_HDMI_AHB_AUDIO=y ++CONFIG_DRM_DW_HDMI_I2S_AUDIO=y ++CONFIG_DRM_DW_HDMI_CEC=y ++CONFIG_DRM_LIMA=y ++CONFIG_FB=y ++CONFIG_FB_SIMPLE=y ++CONFIG_BACKLIGHT_CLASS_DEVICE=y ++CONFIG_BACKLIGHT_PWM=y ++CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y ++CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER=y ++CONFIG_SOUND=y ++CONFIG_SND=y ++CONFIG_SND_OSSEMUL=y ++CONFIG_SND_MIXER_OSS=y ++CONFIG_SND_PCM_OSS=y ++CONFIG_SND_HRTIMER=y ++# CONFIG_SND_SUPPORT_OLD_API is not set ++# CONFIG_SND_DRIVERS is not set ++CONFIG_SND_USB_AUDIO=y ++CONFIG_SND_SOC=y ++CONFIG_SND_SUN4I_CODEC=y ++CONFIG_SND_SUN8I_CODEC=y ++CONFIG_SND_SUN8I_CODEC_ANALOG=y ++CONFIG_SND_SUN50I_CODEC_ANALOG=y ++CONFIG_SND_SUN4I_I2S=y ++CONFIG_SND_SUN4I_SPDIF=y ++CONFIG_SND_SUN9I_HDMI_AUDIO=y ++CONFIG_SND_SOC_BT_SCO=y ++CONFIG_SND_SOC_EC25=y ++CONFIG_SND_SOC_SIMPLE_AMPLIFIER=y ++CONFIG_SND_SIMPLE_CARD=y ++CONFIG_USB_HIDDEV=y ++CONFIG_USB_LED_TRIG=y ++CONFIG_USB=y ++CONFIG_USB_ANNOUNCE_NEW_DEVICES=y ++CONFIG_USB_OTG=y ++CONFIG_USB_OTG_FSM=y ++CONFIG_USB_LEDS_TRIGGER_USBPORT=y ++CONFIG_USB_MON=y ++CONFIG_USB_EHCI_HCD=y ++CONFIG_USB_EHCI_HCD_PLATFORM=y ++CONFIG_USB_OHCI_HCD=y ++CONFIG_USB_OHCI_HCD_PLATFORM=y ++CONFIG_USB_ACM=y ++CONFIG_USB_STORAGE=y ++CONFIG_USB_UAS=y ++CONFIG_USB_MUSB_HDRC=y ++CONFIG_USB_MUSB_SUNXI=y ++CONFIG_MUSB_PIO_ONLY=y ++CONFIG_USB_SERIAL=y ++CONFIG_USB_SERIAL_CONSOLE=y ++CONFIG_USB_SERIAL_GENERIC=y ++CONFIG_USB_SERIAL_SIMPLE=y ++CONFIG_USB_SERIAL_CH341=y ++CONFIG_USB_SERIAL_CP210X=y ++CONFIG_USB_SERIAL_FTDI_SIO=y ++CONFIG_USB_SERIAL_QCAUX=y ++CONFIG_USB_SERIAL_OPTION=y ++CONFIG_NOP_USB_XCEIV=y ++CONFIG_USB_GADGET=y ++CONFIG_USB_GADGET_VBUS_DRAW=500 ++CONFIG_U_SERIAL_CONSOLE=y ++CONFIG_USB_CONFIGFS=y ++CONFIG_USB_CONFIGFS_SERIAL=y ++CONFIG_USB_CONFIGFS_ACM=y ++CONFIG_USB_CONFIGFS_OBEX=y ++CONFIG_USB_CONFIGFS_NCM=y ++CONFIG_USB_CONFIGFS_ECM=y ++CONFIG_USB_CONFIGFS_ECM_SUBSET=y ++CONFIG_USB_CONFIGFS_RNDIS=y ++CONFIG_USB_CONFIGFS_EEM=y ++CONFIG_USB_CONFIGFS_MASS_STORAGE=y ++CONFIG_USB_CONFIGFS_F_FS=y ++CONFIG_USB_CONFIGFS_F_UAC1=y ++CONFIG_USB_CONFIGFS_F_UAC2=y ++CONFIG_USB_CONFIGFS_F_HID=y ++CONFIG_USB_CONFIGFS_F_UVC=y ++CONFIG_USB_CONFIGFS_F_PRINTER=y ++CONFIG_TYPEC=y ++CONFIG_TYPEC_TCPM=y ++CONFIG_TYPEC_TCPCI=y ++CONFIG_TYPEC_UCSI=y ++CONFIG_TYPEC_ANX7688=y ++CONFIG_TYPEC_DP_ALTMODE=y ++CONFIG_MMC=y ++CONFIG_MMC_SUNXI=y ++CONFIG_NEW_LEDS=y ++CONFIG_LEDS_CLASS=y ++CONFIG_LEDS_CLASS_FLASH=y ++CONFIG_LEDS_GPIO=y ++CONFIG_LEDS_SGM3140=y ++CONFIG_LEDS_TRIGGER_TIMER=y ++CONFIG_LEDS_TRIGGER_ONESHOT=y ++CONFIG_LEDS_TRIGGER_HEARTBEAT=y ++CONFIG_LEDS_TRIGGER_CPU=y ++CONFIG_LEDS_TRIGGER_ACTIVITY=y ++CONFIG_LEDS_TRIGGER_DEFAULT_ON=y ++CONFIG_LEDS_TRIGGER_TRANSIENT=y ++CONFIG_LEDS_TRIGGER_CAMERA=y ++CONFIG_LEDS_TRIGGER_PANIC=y ++CONFIG_LEDS_TRIGGER_NETDEV=y ++CONFIG_LEDS_TRIGGER_PATTERN=y ++CONFIG_RTC_CLASS=y ++# CONFIG_RTC_INTF_PROC is not set ++CONFIG_RTC_DRV_SUN6I=y ++CONFIG_DMADEVICES=y ++CONFIG_DMA_SUN6I=y ++CONFIG_DMABUF_HEAPS=y ++CONFIG_DMABUF_HEAPS_SYSTEM=y ++CONFIG_DMABUF_HEAPS_CMA=y ++# CONFIG_VIRTIO_MENU is not set ++# CONFIG_VHOST_MENU is not set ++CONFIG_STAGING=y ++CONFIG_STAGING_MEDIA=y ++CONFIG_VIDEO_SUNXI=y ++CONFIG_VIDEO_SUNXI_CEDRUS=y ++CONFIG_RTL8723CS=m ++# CONFIG_SUN50I_H6_CCU is not set ++# CONFIG_SUN50I_H616_CCU is not set ++# CONFIG_SUN50I_H6_R_CCU is not set ++# CONFIG_SUN8I_H3_CCU is not set ++# CONFIG_HISILICON_ERRATUM_161010101 is not set ++# CONFIG_ARM64_ERRATUM_858921 is not set ++CONFIG_MAILBOX=y ++CONFIG_IOMMU_IO_PGTABLE_LPAE=y ++CONFIG_DEVFREQ_GOV_PERFORMANCE=y ++CONFIG_DEVFREQ_GOV_POWERSAVE=y ++CONFIG_DEVFREQ_GOV_USERSPACE=y ++CONFIG_DEVFREQ_GOV_PASSIVE=y ++CONFIG_ARM_SUN8I_A33_MBUS_DEVFREQ=y ++CONFIG_PM_DEVFREQ_EVENT=y ++CONFIG_IIO=y ++CONFIG_IIO_BUFFER_CB=y ++CONFIG_IIO_BUFFER_HW_CONSUMER=y ++CONFIG_IIO_SW_DEVICE=y ++CONFIG_IIO_SW_TRIGGER=y ++CONFIG_AXP20X_ADC=y ++CONFIG_INV_MPU6050_I2C=y ++CONFIG_STK3310=y ++CONFIG_AF8133J=y ++CONFIG_IIO_ST_MAGN_3AXIS=y ++CONFIG_IIO_HRTIMER_TRIGGER=y ++CONFIG_IIO_INTERRUPT_TRIGGER=y ++CONFIG_IIO_SYSFS_TRIGGER=y ++CONFIG_PWM=y ++CONFIG_PWM_SUN4I=y ++CONFIG_PHY_SUN4I_USB=y ++CONFIG_ARM_CCI_PMU=y ++# CONFIG_ARM_CCI5xx_PMU is not set ++CONFIG_ANDROID_BINDER_IPC=y ++CONFIG_ANDROID_BINDERFS=y ++CONFIG_NVMEM_SUNXI_SID=y ++CONFIG_VALIDATE_FS_PARSER=y ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_FS_POSIX_ACL=y ++CONFIG_BTRFS_FS=y ++CONFIG_BTRFS_FS_POSIX_ACL=y ++CONFIG_F2FS_FS=y ++CONFIG_F2FS_FS_SECURITY=y ++CONFIG_F2FS_CHECK_FS=y ++CONFIG_F2FS_FS_COMPRESSION=y ++CONFIG_FS_ENCRYPTION=y ++CONFIG_FANOTIFY=y ++CONFIG_AUTOFS_FS=y ++CONFIG_FUSE_FS=y ++CONFIG_CUSE=y ++CONFIG_OVERLAY_FS=y ++CONFIG_OVERLAY_FS_INDEX=y ++CONFIG_OVERLAY_FS_METACOPY=y ++CONFIG_VFAT_FS=y ++CONFIG_EXFAT_FS=y ++CONFIG_PROC_CHILDREN=y ++CONFIG_TMPFS=y ++CONFIG_TMPFS_POSIX_ACL=y ++CONFIG_ECRYPT_FS=y ++CONFIG_ECRYPT_FS_MESSAGING=y ++CONFIG_SQUASHFS=y ++CONFIG_SQUASHFS_FILE_DIRECT=y ++CONFIG_SQUASHFS_XATTR=y ++CONFIG_SQUASHFS_LZ4=y ++CONFIG_SQUASHFS_LZO=y ++CONFIG_SQUASHFS_XZ=y ++CONFIG_SQUASHFS_ZSTD=y ++# CONFIG_NETWORK_FILESYSTEMS is not set ++CONFIG_NLS_CODEPAGE_437=y ++CONFIG_NLS_CODEPAGE_852=y ++CONFIG_NLS_ISO8859_1=y ++CONFIG_NLS_ISO8859_2=y ++CONFIG_NLS_UTF8=y ++CONFIG_KEYS_REQUEST_CACHE=y ++CONFIG_PERSISTENT_KEYRINGS=y ++CONFIG_BIG_KEYS=y ++CONFIG_ENCRYPTED_KEYS=y ++CONFIG_KEY_DH_OPERATIONS=y ++CONFIG_LSM="yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor" ++CONFIG_INIT_STACK_NONE=y ++CONFIG_CRYPTO_USER=y ++CONFIG_CRYPTO_PCRYPT=y ++CONFIG_CRYPTO_CRYPTD=y ++CONFIG_CRYPTO_CURVE25519=y ++CONFIG_CRYPTO_ARC4=y ++CONFIG_CRYPTO_CHACHA20POLY1305=y ++CONFIG_CRYPTO_SEQIV=y ++CONFIG_CRYPTO_ECHAINIV=y ++CONFIG_CRYPTO_MD4=y ++CONFIG_CRYPTO_DEFLATE=y ++CONFIG_CRYPTO_LZO=y ++CONFIG_CRYPTO_LZ4=y ++CONFIG_CRYPTO_ZSTD=y ++CONFIG_CRYPTO_ANSI_CPRNG=y ++CONFIG_CRYPTO_USER_API_HASH=y ++CONFIG_CRYPTO_USER_API_SKCIPHER=y ++CONFIG_CRYPTO_USER_API_RNG=y ++CONFIG_CRYPTO_USER_API_AEAD=y ++CONFIG_CRYPTO_GHASH_ARM64_CE=y ++CONFIG_CRYPTO_SHA1_ARM64_CE=y ++CONFIG_CRYPTO_SHA2_ARM64_CE=y ++CONFIG_CRYPTO_SHA512_ARM64_CE=y ++CONFIG_CRYPTO_SHA3_ARM64=y ++CONFIG_CRYPTO_SM3_ARM64_CE=y ++CONFIG_CRYPTO_AES_ARM64=y ++CONFIG_CRYPTO_AES_ARM64_BS=y ++CONFIG_CRYPTO_SM4_ARM64_CE=y ++CONFIG_CRYPTO_AES_ARM64_CE_CCM=y ++CONFIG_CRYPTO_CRCT10DIF_ARM64_CE=y ++CONFIG_CRYPTO_DEV_SUN8I_CE=y ++CONFIG_PKCS8_PRIVATE_KEY_PARSER=y ++# CONFIG_RAID6_PQ_BENCHMARK is not set ++CONFIG_CRC_T10DIF=y ++CONFIG_DMA_CMA=y ++CONFIG_CMA_SIZE_MBYTES=64 ++CONFIG_MESSAGE_LOGLEVEL_DEFAULT=7 ++CONFIG_DYNAMIC_DEBUG=y ++CONFIG_FRAME_WARN=1024 ++CONFIG_MAGIC_SYSRQ=y ++CONFIG_DEBUG_FS=y ++# CONFIG_SLUB_DEBUG is not set ++CONFIG_PER_VMA_LOCK_STATS=y ++CONFIG_RCU_EXP_CPU_STALL_TIMEOUT=20 ++CONFIG_BOOTTIME_TRACING=y ++CONFIG_FUNCTION_TRACER=y ++CONFIG_FTRACE_SYSCALLS=y ++CONFIG_TRACER_SNAPSHOT=y ++# CONFIG_STRICT_DEVMEM is not set ++# CONFIG_RUNTIME_TESTING_MENU is not set +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/Fix-broken-allwinner-sram-dependency-on-h616-h618.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/Fix-broken-allwinner-sram-dependency-on-h616-h618.patch new file mode 100644 index 000000000000..02c47fe97a7c --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/Fix-broken-allwinner-sram-dependency-on-h616-h618.patch @@ -0,0 +1,53 @@ +From c9248297e040af7071212003c78140c1e383535d Mon Sep 17 00:00:00 2001 +From: "Steinar H. Gunderson" +Date: Mon, 4 Nov 2024 15:35:38 +0000 +Subject: Fix broken allwinner,sram dependency on h616, h618 + +On BigTreeTech CB1, the thermal sensor has an allwinner,sram +property pointing to <&syscon>. However, Armbian has an out-of-tree +kernel patch that creates dependencies based on allwinner,sram +properties, which assumes that they point to sram nodes exactly +two levels below the syscon node (instead of the syscon itself). +This manifests itself as the thermal sensor refusing to load with +a nonsensical error message: + + [ 23.775976] platform 5070400.thermal-sensor: deferred probe pending: platform: wait for supplier + +Note that it does not say _which_ supplier it is waiting for +(the message ends in a space and then no supplier). The patch +was unproblematic in the 5.6 megous patch set, where it was +introduced, and in 6.6, which is current for Armbian, but in +6.8, the sun8i-thermal driver got mainlined, with this extra +property compared to the out-of-tree version we used before +(since it wants to clear a special bit at 0x300000 instead of +relying on the firmware to do so before kernel boot). + +Fix by being a bit more flexible when we walk up the tree, +so that we always stop at the syscon node. + +Tested on a Sovol SV08, which is CB1-based. + +Signed-off-by: Steinar H. Gunderson +--- + drivers/of/property.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/drivers/of/property.c b/drivers/of/property.c +index 8c9f3ea663a6..074fbf9d27f8 100644 +--- a/drivers/of/property.c ++++ b/drivers/of/property.c +@@ -1470,8 +1470,9 @@ static struct device_node *parse_allwinner_sram(struct device_node *np, + return NULL; + + sram_node = of_parse_phandle(np, prop_name, 0); +- sram_node = of_get_parent(sram_node); +- sram_node = of_get_parent(sram_node); ++ while (sram_node && !of_node_is_type(sram_node, "syscon")) { ++ sram_node = of_get_parent(sram_node); ++ } + + return sram_node; + } +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/Fix-intptr_t-typedef.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/Fix-intptr_t-typedef.patch new file mode 100644 index 000000000000..e17baeb09b22 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/Fix-intptr_t-typedef.patch @@ -0,0 +1,32 @@ +From fb28d52e3bbfa61ae3a900d5a42972628c2e9ea2 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sat, 26 Aug 2023 10:55:24 +0200 +Subject: Fix intptr_t typedef + +GCC includes the same typedef, but on some 32bit platforms it's defined +as int, leading to conflicts. + +Signed-off-by: Ondrej Jirman +--- + include/linux/types.h | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/include/linux/types.h b/include/linux/types.h +index 1c509ce8f7f6..46f7b982a7ec 100644 +--- a/include/linux/types.h ++++ b/include/linux/types.h +@@ -40,7 +40,11 @@ typedef __kernel_uid16_t uid16_t; + typedef __kernel_gid16_t gid16_t; + + typedef unsigned long uintptr_t; ++#if __SIZEOF_POINTER__ == __SIZEOF_INT__ ++typedef int intptr_t; ++#else + typedef long intptr_t; ++#endif + + #ifdef CONFIG_HAVE_UID16 + /* This is defined by arch/{arch}/include/asm/posix_types.h */ +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/MAINTAINERS-Add-entry-for-Himax-HM5065.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/MAINTAINERS-Add-entry-for-Himax-HM5065.patch new file mode 100644 index 000000000000..8b378ab73241 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/MAINTAINERS-Add-entry-for-Himax-HM5065.patch @@ -0,0 +1,30 @@ +From 8b84ffb671ccc349736193252b38ecedb32503dd Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sat, 30 Sep 2017 21:31:35 +0200 +Subject: MAINTAINERS: Add entry for Himax HM5065 + +Signed-off-by: Ondrej Jirman +--- + MAINTAINERS | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/MAINTAINERS b/MAINTAINERS +index 00e94bec401e..7d07a2608cb2 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -10375,6 +10375,12 @@ L: linux-kernel@vger.kernel.org + S: Maintained + F: drivers/misc/hisi_hikey_usb.c + ++HIMAX HM5065 SENSOR DRIVER ++M: Ondrej Jirman ++L: linux-media@vger.kernel.org ++S: Supported ++F: drivers/media/i2c/hm5065.c ++ + HIMAX HX83112B TOUCHSCREEN SUPPORT + M: Job Noorman + L: linux-input@vger.kernel.org +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/Make-microbuttons-on-Orange-Pi-PC-and-PC-2-work-as-power-off-bu.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/Make-microbuttons-on-Orange-Pi-PC-and-PC-2-work-as-power-off-bu.patch new file mode 100644 index 000000000000..14982acd1091 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/Make-microbuttons-on-Orange-Pi-PC-and-PC-2-work-as-power-off-bu.patch @@ -0,0 +1,39 @@ +From 6d2930ee9c0b2c64a42d5559a91858b9b8e6926c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Fri, 18 Aug 2017 13:55:48 +0200 +Subject: Make microbuttons on Orange Pi PC and PC 2 work as power off buttons + +--- + arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-one.dts | 2 +- + arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-pc2.dts | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-one.dts b/arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-one.dts +index 927fd1bab07d..f5476cdab8a4 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-one.dts ++++ b/arch/arm/boot/dts/allwinner/sun8i-h3-orangepi-one.dts +@@ -91,7 +91,7 @@ gpio-keys { + + switch-4 { + label = "sw4"; +- linux,code = ; ++ linux,code = ; + gpios = <&r_pio 0 3 GPIO_ACTIVE_LOW>; + }; + }; +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-pc2.dts b/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-pc2.dts +index f420acc6f49f..3d16a6efa2a7 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-pc2.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-pc2.dts +@@ -60,7 +60,7 @@ gpio-keys { + + key-sw4 { + label = "sw4"; +- linux,code = ; ++ linux,code = ; + gpios = <&r_pio 0 3 GPIO_ACTIVE_LOW>; + wakeup-source; + }; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/Mark-some-slow-drivers-for-async-probe-with-PROBE_PREFER_ASYNCH.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/Mark-some-slow-drivers-for-async-probe-with-PROBE_PREFER_ASYNCH.patch new file mode 100644 index 000000000000..95bc4a97592c --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/Mark-some-slow-drivers-for-async-probe-with-PROBE_PREFER_ASYNCH.patch @@ -0,0 +1,40 @@ +From 3bcea65acc9d9f6227d539e7db848caa149e8ed6 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Mon, 10 Feb 2020 01:00:12 +0100 +Subject: Mark some slow drivers for async probe with PROBE_PREFER_ASYNCHRONOUS + +This makes it faster to boot TBS A711 tablet. + +Signed-off-by: Ondrej Jirman +--- + drivers/iio/accel/bma180.c | 1 + + drivers/nfc/nxp-nci/i2c.c | 1 + + 2 files changed, 2 insertions(+) + +diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c +index 128db14ba726..9881cb1de9ef 100644 +--- a/drivers/iio/accel/bma180.c ++++ b/drivers/iio/accel/bma180.c +@@ -1127,6 +1127,7 @@ static struct i2c_driver bma180_driver = { + .name = "bma180", + .pm = pm_sleep_ptr(&bma180_pm_ops), + .of_match_table = bma180_of_match, ++ .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, + .probe = bma180_probe, + .remove = bma180_remove, +diff --git a/drivers/nfc/nxp-nci/i2c.c b/drivers/nfc/nxp-nci/i2c.c +index 049662ffdf97..35986be81ee3 100644 +--- a/drivers/nfc/nxp-nci/i2c.c ++++ b/drivers/nfc/nxp-nci/i2c.c +@@ -348,6 +348,7 @@ static struct i2c_driver nxp_nci_i2c_driver = { + .name = NXP_NCI_I2C_DRIVER_NAME, + .acpi_match_table = ACPI_PTR(acpi_id), + .of_match_table = of_nxp_nci_i2c_match, ++ .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, + .probe = nxp_nci_i2c_probe, + .id_table = nxp_nci_i2c_id_table, +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/Move-a-node-to-avoid-merge-conflict.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/Move-a-node-to-avoid-merge-conflict.patch new file mode 100644 index 000000000000..02cab02c5ec3 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/Move-a-node-to-avoid-merge-conflict.patch @@ -0,0 +1,115 @@ +From 275a68cd0bf4414c886f9f4a5e39f7f166fe4286 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sun, 9 May 2021 23:43:21 +0200 +Subject: Move a node to avoid merge conflict + +--- + arch/arm/boot/dts/allwinner/sunxi-h3-h5.dtsi | 12 ++++----- + .../dts/allwinner/sun50i-h6-orangepi-3.dts | 8 +++--- + arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi | 27 +++++++++---------- + 3 files changed, 23 insertions(+), 24 deletions(-) + +diff --git a/arch/arm/boot/dts/allwinner/sunxi-h3-h5.dtsi b/arch/arm/boot/dts/allwinner/sunxi-h3-h5.dtsi +index 0f19e7c00c50..a872fc586ab6 100644 +--- a/arch/arm/boot/dts/allwinner/sunxi-h3-h5.dtsi ++++ b/arch/arm/boot/dts/allwinner/sunxi-h3-h5.dtsi +@@ -100,12 +100,6 @@ osc32k: osc32k-clk { + }; + }; + +- de: display-engine { +- compatible = "allwinner,sun8i-h3-display-engine"; +- allwinner,pipelines = <&mixer0>; +- status = "disabled"; +- }; +- + sound_hdmi: sound_hdmi { + compatible = "allwinner,sun9i-a80-hdmi-audio", + "allwinner,sun8i-h3-hdmi-audio"; +@@ -120,6 +114,12 @@ cpu { + }; + }; + ++ de: display-engine { ++ compatible = "allwinner,sun8i-h3-display-engine"; ++ allwinner,pipelines = <&mixer0>; ++ status = "disabled"; ++ }; ++ + soc { + compatible = "simple-bus"; + #address-cells = <1>; +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3.dts b/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3.dts +index 7e11286bf6d1..d1f5daf63484 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3.dts +@@ -128,6 +128,10 @@ &gpu { + status = "okay"; + }; + ++&i2s1 { ++ status = "okay"; ++}; ++ + &hdmi { + status = "okay"; + }; +@@ -167,10 +171,6 @@ ext_rgmii_phy: ethernet-phy@1 { + }; + }; + +-&i2s1 { +- status = "okay"; +-}; +- + &mmc0 { + vmmc-supply = <®_cldo1>; + cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi +index 13fe21b00711..c4e35a255643 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi +@@ -126,20 +126,6 @@ psci { + method = "smc"; + }; + +- sound_hdmi: sound_hdmi { +- compatible = "allwinner,sun9i-a80-hdmi-audio", +- "allwinner,sun50i-h6-hdmi-audio"; +- status = "disabled"; +- +- codec { +- sound-dai = <&hdmi>; +- }; +- +- cpu { +- sound-dai = <&i2s1>; +- }; +- }; +- + timer { + compatible = "arm,armv8-timer"; + arm,no-tick-in-suspend; +@@ -153,6 +139,19 @@ timer { + (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>; + }; + ++ sound_hdmi: sound_hdmi { ++ compatible = "allwinner,sun9i-a80-hdmi-audio", ++ "allwinner,sun50i-h6-hdmi-audio"; ++ status = "disabled"; ++ ++ codec { ++ sound-dai = <&hdmi>; ++ }; ++ ++ cpu { ++ sound-dai = <&i2s1>; ++ }; ++ }; + + soc { + compatible = "simple-bus"; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/Revert-Input-cyttsp4-remove-driver.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/Revert-Input-cyttsp4-remove-driver.patch new file mode 100644 index 000000000000..40050fc15bc4 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/Revert-Input-cyttsp4-remove-driver.patch @@ -0,0 +1,3232 @@ +From afe7cc88dc1c603d849bfac407867231ce8ccdff Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Thu, 26 Sep 2024 23:00:02 +0200 +Subject: Revert "Input: cyttsp4 - remove driver" + +This reverts commit 25162a4f64f8ba0065f300977589fe1f6af332f0. +--- + drivers/input/touchscreen/Kconfig | 30 + + drivers/input/touchscreen/Makefile | 5 +- + drivers/input/touchscreen/cyttsp4_core.c | 2174 +++++++++++++++++ + drivers/input/touchscreen/cyttsp4_core.h | 448 ++++ + drivers/input/touchscreen/cyttsp4_i2c.c | 72 + + drivers/input/touchscreen/cyttsp4_spi.c | 187 ++ + drivers/input/touchscreen/cyttsp_core.h | 4 + + drivers/input/touchscreen/cyttsp_i2c.c | 55 - + drivers/input/touchscreen/cyttsp_i2c_common.c | 86 + + include/linux/platform_data/cyttsp4.h | 62 + + 10 files changed, 3067 insertions(+), 56 deletions(-) + create mode 100644 drivers/input/touchscreen/cyttsp4_core.c + create mode 100644 drivers/input/touchscreen/cyttsp4_core.h + create mode 100644 drivers/input/touchscreen/cyttsp4_i2c.c + create mode 100644 drivers/input/touchscreen/cyttsp4_spi.c + create mode 100644 drivers/input/touchscreen/cyttsp_i2c_common.c + create mode 100644 include/linux/platform_data/cyttsp4.h + +diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig +index 1a03de7fcfa6..3a8a3ad64cdc 100644 +--- a/drivers/input/touchscreen/Kconfig ++++ b/drivers/input/touchscreen/Kconfig +@@ -254,6 +254,36 @@ config TOUCHSCREEN_CYTTSP_SPI + To compile this driver as a module, choose M here: the + module will be called cyttsp_spi. + ++config TOUCHSCREEN_CYTTSP4_CORE ++ tristate "Cypress TrueTouch Gen4 Touchscreen Driver" ++ help ++ Core driver for Cypress TrueTouch(tm) Standard Product ++ Generation4 touchscreen controllers. ++ ++ Say Y here if you have a Cypress Gen4 touchscreen. ++ ++ If unsure, say N. ++ ++ To compile this driver as a module, choose M here. ++ ++config TOUCHSCREEN_CYTTSP4_I2C ++ tristate "support I2C bus connection" ++ depends on TOUCHSCREEN_CYTTSP4_CORE && I2C ++ help ++ Say Y here if the touchscreen is connected via I2C bus. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called cyttsp4_i2c. ++ ++config TOUCHSCREEN_CYTTSP4_SPI ++ tristate "support SPI bus connection" ++ depends on TOUCHSCREEN_CYTTSP4_CORE && SPI_MASTER ++ help ++ Say Y here if the touchscreen is connected via SPI bus. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called cyttsp4_spi. ++ + config TOUCHSCREEN_CYTTSP5 + tristate "Cypress TrueTouch Gen5 Touchscreen Driver" + depends on I2C +diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile +index 82bc837ca01e..04dc8039341b 100644 +--- a/drivers/input/touchscreen/Makefile ++++ b/drivers/input/touchscreen/Makefile +@@ -25,8 +25,11 @@ obj-$(CONFIG_TOUCHSCREEN_CHIPONE_ICN8505) += chipone_icn8505.o + obj-$(CONFIG_TOUCHSCREEN_CY8CTMA140) += cy8ctma140.o + obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o + obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o +-obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o ++obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o cyttsp_i2c_common.o + obj-$(CONFIG_TOUCHSCREEN_CYTTSP_SPI) += cyttsp_spi.o ++obj-$(CONFIG_TOUCHSCREEN_CYTTSP4_CORE) += cyttsp4_core.o ++obj-$(CONFIG_TOUCHSCREEN_CYTTSP4_I2C) += cyttsp4_i2c.o cyttsp_i2c_common.o ++obj-$(CONFIG_TOUCHSCREEN_CYTTSP4_SPI) += cyttsp4_spi.o + obj-$(CONFIG_TOUCHSCREEN_CYTTSP5) += cyttsp5.o + obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o + obj-$(CONFIG_TOUCHSCREEN_DA9052) += da9052_tsi.o +diff --git a/drivers/input/touchscreen/cyttsp4_core.c b/drivers/input/touchscreen/cyttsp4_core.c +new file mode 100644 +index 000000000000..7cb26929dc73 +--- /dev/null ++++ b/drivers/input/touchscreen/cyttsp4_core.c +@@ -0,0 +1,2174 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * cyttsp4_core.c ++ * Cypress TrueTouch(TM) Standard Product V4 Core driver module. ++ * For use with Cypress Txx4xx parts. ++ * Supported parts include: ++ * TMA4XX ++ * TMA1036 ++ * ++ * Copyright (C) 2012 Cypress Semiconductor ++ * ++ * Contact Cypress Semiconductor at www.cypress.com ++ */ ++ ++#include "cyttsp4_core.h" ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Timeout in ms. */ ++#define CY_CORE_REQUEST_EXCLUSIVE_TIMEOUT 500 ++#define CY_CORE_SLEEP_REQUEST_EXCLUSIVE_TIMEOUT 5000 ++#define CY_CORE_MODE_CHANGE_TIMEOUT 1000 ++#define CY_CORE_RESET_AND_WAIT_TIMEOUT 500 ++#define CY_CORE_WAKEUP_TIMEOUT 500 ++ ++#define CY_CORE_STARTUP_RETRY_COUNT 3 ++ ++static const char * const cyttsp4_tch_abs_string[] = { ++ [CY_TCH_X] = "X", ++ [CY_TCH_Y] = "Y", ++ [CY_TCH_P] = "P", ++ [CY_TCH_T] = "T", ++ [CY_TCH_E] = "E", ++ [CY_TCH_O] = "O", ++ [CY_TCH_W] = "W", ++ [CY_TCH_MAJ] = "MAJ", ++ [CY_TCH_MIN] = "MIN", ++ [CY_TCH_OR] = "OR", ++ [CY_TCH_NUM_ABS] = "INVALID" ++}; ++ ++static const u8 ldr_exit[] = { ++ 0xFF, 0x01, 0x3B, 0x00, 0x00, 0x4F, 0x6D, 0x17 ++}; ++ ++static const u8 ldr_err_app[] = { ++ 0x01, 0x02, 0x00, 0x00, 0x55, 0xDD, 0x17 ++}; ++ ++static inline size_t merge_bytes(u8 high, u8 low) ++{ ++ return (high << 8) + low; ++} ++ ++#ifdef VERBOSE_DEBUG ++static void cyttsp4_pr_buf(struct device *dev, u8 *pr_buf, u8 *dptr, int size, ++ const char *data_name) ++{ ++ int i, k; ++ const char fmt[] = "%02X "; ++ int max; ++ ++ if (!size) ++ return; ++ ++ max = (CY_MAX_PRBUF_SIZE - 1) - sizeof(CY_PR_TRUNCATED); ++ ++ pr_buf[0] = 0; ++ for (i = k = 0; i < size && k < max; i++, k += 3) ++ scnprintf(pr_buf + k, CY_MAX_PRBUF_SIZE, fmt, dptr[i]); ++ ++ dev_vdbg(dev, "%s: %s[0..%d]=%s%s\n", __func__, data_name, size - 1, ++ pr_buf, size <= max ? "" : CY_PR_TRUNCATED); ++} ++#else ++#define cyttsp4_pr_buf(dev, pr_buf, dptr, size, data_name) do { } while (0) ++#endif ++ ++static int cyttsp4_load_status_regs(struct cyttsp4 *cd) ++{ ++ struct cyttsp4_sysinfo *si = &cd->sysinfo; ++ struct device *dev = cd->dev; ++ int rc; ++ ++ rc = cyttsp4_adap_read(cd, CY_REG_BASE, si->si_ofs.mode_size, ++ si->xy_mode); ++ if (rc < 0) ++ dev_err(dev, "%s: fail read mode regs r=%d\n", ++ __func__, rc); ++ else ++ cyttsp4_pr_buf(dev, cd->pr_buf, si->xy_mode, ++ si->si_ofs.mode_size, "xy_mode"); ++ ++ return rc; ++} ++ ++static int cyttsp4_handshake(struct cyttsp4 *cd, u8 mode) ++{ ++ u8 cmd = mode ^ CY_HST_TOGGLE; ++ int rc; ++ ++ /* ++ * Mode change issued, handshaking now will cause endless mode change ++ * requests, for sync mode modechange will do same with handshake ++ * */ ++ if (mode & CY_HST_MODE_CHANGE) ++ return 0; ++ ++ rc = cyttsp4_adap_write(cd, CY_REG_BASE, sizeof(cmd), &cmd); ++ if (rc < 0) ++ dev_err(cd->dev, "%s: bus write fail on handshake (ret=%d)\n", ++ __func__, rc); ++ ++ return rc; ++} ++ ++static int cyttsp4_hw_soft_reset(struct cyttsp4 *cd) ++{ ++ u8 cmd = CY_HST_RESET; ++ int rc = cyttsp4_adap_write(cd, CY_REG_BASE, sizeof(cmd), &cmd); ++ if (rc < 0) { ++ dev_err(cd->dev, "%s: FAILED to execute SOFT reset\n", ++ __func__); ++ return rc; ++ } ++ return 0; ++} ++ ++static int cyttsp4_hw_hard_reset(struct cyttsp4 *cd) ++{ ++ if (cd->cpdata->xres) { ++ cd->cpdata->xres(cd->cpdata, cd->dev); ++ dev_dbg(cd->dev, "%s: execute HARD reset\n", __func__); ++ return 0; ++ } ++ dev_err(cd->dev, "%s: FAILED to execute HARD reset\n", __func__); ++ return -ENOSYS; ++} ++ ++static int cyttsp4_hw_reset(struct cyttsp4 *cd) ++{ ++ int rc = cyttsp4_hw_hard_reset(cd); ++ if (rc == -ENOSYS) ++ rc = cyttsp4_hw_soft_reset(cd); ++ return rc; ++} ++ ++/* ++ * Gets number of bits for a touch filed as parameter, ++ * sets maximum value for field which is used as bit mask ++ * and returns number of bytes required for that field ++ */ ++static int cyttsp4_bits_2_bytes(unsigned int nbits, size_t *max) ++{ ++ *max = 1UL << nbits; ++ return (nbits + 7) / 8; ++} ++ ++static int cyttsp4_si_data_offsets(struct cyttsp4 *cd) ++{ ++ struct cyttsp4_sysinfo *si = &cd->sysinfo; ++ int rc = cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(si->si_data), ++ &si->si_data); ++ if (rc < 0) { ++ dev_err(cd->dev, "%s: fail read sysinfo data offsets r=%d\n", ++ __func__, rc); ++ return rc; ++ } ++ ++ /* Print sysinfo data offsets */ ++ cyttsp4_pr_buf(cd->dev, cd->pr_buf, (u8 *)&si->si_data, ++ sizeof(si->si_data), "sysinfo_data_offsets"); ++ ++ /* convert sysinfo data offset bytes into integers */ ++ ++ si->si_ofs.map_sz = merge_bytes(si->si_data.map_szh, ++ si->si_data.map_szl); ++ si->si_ofs.map_sz = merge_bytes(si->si_data.map_szh, ++ si->si_data.map_szl); ++ si->si_ofs.cydata_ofs = merge_bytes(si->si_data.cydata_ofsh, ++ si->si_data.cydata_ofsl); ++ si->si_ofs.test_ofs = merge_bytes(si->si_data.test_ofsh, ++ si->si_data.test_ofsl); ++ si->si_ofs.pcfg_ofs = merge_bytes(si->si_data.pcfg_ofsh, ++ si->si_data.pcfg_ofsl); ++ si->si_ofs.opcfg_ofs = merge_bytes(si->si_data.opcfg_ofsh, ++ si->si_data.opcfg_ofsl); ++ si->si_ofs.ddata_ofs = merge_bytes(si->si_data.ddata_ofsh, ++ si->si_data.ddata_ofsl); ++ si->si_ofs.mdata_ofs = merge_bytes(si->si_data.mdata_ofsh, ++ si->si_data.mdata_ofsl); ++ return rc; ++} ++ ++static int cyttsp4_si_get_cydata(struct cyttsp4 *cd) ++{ ++ struct cyttsp4_sysinfo *si = &cd->sysinfo; ++ int read_offset; ++ int mfgid_sz, calc_mfgid_sz; ++ void *p; ++ int rc; ++ ++ if (si->si_ofs.test_ofs <= si->si_ofs.cydata_ofs) { ++ dev_err(cd->dev, ++ "%s: invalid offset test_ofs: %zu, cydata_ofs: %zu\n", ++ __func__, si->si_ofs.test_ofs, si->si_ofs.cydata_ofs); ++ return -EINVAL; ++ } ++ ++ si->si_ofs.cydata_size = si->si_ofs.test_ofs - si->si_ofs.cydata_ofs; ++ dev_dbg(cd->dev, "%s: cydata size: %zd\n", __func__, ++ si->si_ofs.cydata_size); ++ ++ p = krealloc(si->si_ptrs.cydata, si->si_ofs.cydata_size, GFP_KERNEL); ++ if (p == NULL) { ++ dev_err(cd->dev, "%s: failed to allocate cydata memory\n", ++ __func__); ++ return -ENOMEM; ++ } ++ si->si_ptrs.cydata = p; ++ ++ read_offset = si->si_ofs.cydata_ofs; ++ ++ /* Read the CYDA registers up to MFGID field */ ++ rc = cyttsp4_adap_read(cd, read_offset, ++ offsetof(struct cyttsp4_cydata, mfgid_sz) ++ + sizeof(si->si_ptrs.cydata->mfgid_sz), ++ si->si_ptrs.cydata); ++ if (rc < 0) { ++ dev_err(cd->dev, "%s: fail read cydata r=%d\n", ++ __func__, rc); ++ return rc; ++ } ++ ++ /* Check MFGID size */ ++ mfgid_sz = si->si_ptrs.cydata->mfgid_sz; ++ calc_mfgid_sz = si->si_ofs.cydata_size - sizeof(struct cyttsp4_cydata); ++ if (mfgid_sz != calc_mfgid_sz) { ++ dev_err(cd->dev, "%s: mismatch in MFGID size, reported:%d calculated:%d\n", ++ __func__, mfgid_sz, calc_mfgid_sz); ++ return -EINVAL; ++ } ++ ++ read_offset += offsetof(struct cyttsp4_cydata, mfgid_sz) ++ + sizeof(si->si_ptrs.cydata->mfgid_sz); ++ ++ /* Read the CYDA registers for MFGID field */ ++ rc = cyttsp4_adap_read(cd, read_offset, si->si_ptrs.cydata->mfgid_sz, ++ si->si_ptrs.cydata->mfg_id); ++ if (rc < 0) { ++ dev_err(cd->dev, "%s: fail read cydata r=%d\n", ++ __func__, rc); ++ return rc; ++ } ++ ++ read_offset += si->si_ptrs.cydata->mfgid_sz; ++ ++ /* Read the rest of the CYDA registers */ ++ rc = cyttsp4_adap_read(cd, read_offset, ++ sizeof(struct cyttsp4_cydata) ++ - offsetof(struct cyttsp4_cydata, cyito_idh), ++ &si->si_ptrs.cydata->cyito_idh); ++ if (rc < 0) { ++ dev_err(cd->dev, "%s: fail read cydata r=%d\n", ++ __func__, rc); ++ return rc; ++ } ++ ++ cyttsp4_pr_buf(cd->dev, cd->pr_buf, (u8 *)si->si_ptrs.cydata, ++ si->si_ofs.cydata_size, "sysinfo_cydata"); ++ return rc; ++} ++ ++static int cyttsp4_si_get_test_data(struct cyttsp4 *cd) ++{ ++ struct cyttsp4_sysinfo *si = &cd->sysinfo; ++ void *p; ++ int rc; ++ ++ if (si->si_ofs.pcfg_ofs <= si->si_ofs.test_ofs) { ++ dev_err(cd->dev, ++ "%s: invalid offset pcfg_ofs: %zu, test_ofs: %zu\n", ++ __func__, si->si_ofs.pcfg_ofs, si->si_ofs.test_ofs); ++ return -EINVAL; ++ } ++ ++ si->si_ofs.test_size = si->si_ofs.pcfg_ofs - si->si_ofs.test_ofs; ++ ++ p = krealloc(si->si_ptrs.test, si->si_ofs.test_size, GFP_KERNEL); ++ if (p == NULL) { ++ dev_err(cd->dev, "%s: failed to allocate test memory\n", ++ __func__); ++ return -ENOMEM; ++ } ++ si->si_ptrs.test = p; ++ ++ rc = cyttsp4_adap_read(cd, si->si_ofs.test_ofs, si->si_ofs.test_size, ++ si->si_ptrs.test); ++ if (rc < 0) { ++ dev_err(cd->dev, "%s: fail read test data r=%d\n", ++ __func__, rc); ++ return rc; ++ } ++ ++ cyttsp4_pr_buf(cd->dev, cd->pr_buf, ++ (u8 *)si->si_ptrs.test, si->si_ofs.test_size, ++ "sysinfo_test_data"); ++ if (si->si_ptrs.test->post_codel & ++ CY_POST_CODEL_WDG_RST) ++ dev_info(cd->dev, "%s: %s codel=%02X\n", ++ __func__, "Reset was a WATCHDOG RESET", ++ si->si_ptrs.test->post_codel); ++ ++ if (!(si->si_ptrs.test->post_codel & ++ CY_POST_CODEL_CFG_DATA_CRC_FAIL)) ++ dev_info(cd->dev, "%s: %s codel=%02X\n", __func__, ++ "Config Data CRC FAIL", ++ si->si_ptrs.test->post_codel); ++ ++ if (!(si->si_ptrs.test->post_codel & ++ CY_POST_CODEL_PANEL_TEST_FAIL)) ++ dev_info(cd->dev, "%s: %s codel=%02X\n", ++ __func__, "PANEL TEST FAIL", ++ si->si_ptrs.test->post_codel); ++ ++ dev_info(cd->dev, "%s: SCANNING is %s codel=%02X\n", ++ __func__, si->si_ptrs.test->post_codel & 0x08 ? ++ "ENABLED" : "DISABLED", ++ si->si_ptrs.test->post_codel); ++ return rc; ++} ++ ++static int cyttsp4_si_get_pcfg_data(struct cyttsp4 *cd) ++{ ++ struct cyttsp4_sysinfo *si = &cd->sysinfo; ++ void *p; ++ int rc; ++ ++ if (si->si_ofs.opcfg_ofs <= si->si_ofs.pcfg_ofs) { ++ dev_err(cd->dev, ++ "%s: invalid offset opcfg_ofs: %zu, pcfg_ofs: %zu\n", ++ __func__, si->si_ofs.opcfg_ofs, si->si_ofs.pcfg_ofs); ++ return -EINVAL; ++ } ++ ++ si->si_ofs.pcfg_size = si->si_ofs.opcfg_ofs - si->si_ofs.pcfg_ofs; ++ ++ p = krealloc(si->si_ptrs.pcfg, si->si_ofs.pcfg_size, GFP_KERNEL); ++ if (p == NULL) { ++ dev_err(cd->dev, "%s: failed to allocate pcfg memory\n", ++ __func__); ++ return -ENOMEM; ++ } ++ si->si_ptrs.pcfg = p; ++ ++ rc = cyttsp4_adap_read(cd, si->si_ofs.pcfg_ofs, si->si_ofs.pcfg_size, ++ si->si_ptrs.pcfg); ++ if (rc < 0) { ++ dev_err(cd->dev, "%s: fail read pcfg data r=%d\n", ++ __func__, rc); ++ return rc; ++ } ++ ++ si->si_ofs.max_x = merge_bytes((si->si_ptrs.pcfg->res_xh ++ & CY_PCFG_RESOLUTION_X_MASK), si->si_ptrs.pcfg->res_xl); ++ si->si_ofs.x_origin = !!(si->si_ptrs.pcfg->res_xh ++ & CY_PCFG_ORIGIN_X_MASK); ++ si->si_ofs.max_y = merge_bytes((si->si_ptrs.pcfg->res_yh ++ & CY_PCFG_RESOLUTION_Y_MASK), si->si_ptrs.pcfg->res_yl); ++ si->si_ofs.y_origin = !!(si->si_ptrs.pcfg->res_yh ++ & CY_PCFG_ORIGIN_Y_MASK); ++ si->si_ofs.max_p = merge_bytes(si->si_ptrs.pcfg->max_zh, ++ si->si_ptrs.pcfg->max_zl); ++ ++ cyttsp4_pr_buf(cd->dev, cd->pr_buf, ++ (u8 *)si->si_ptrs.pcfg, ++ si->si_ofs.pcfg_size, "sysinfo_pcfg_data"); ++ return rc; ++} ++ ++static int cyttsp4_si_get_opcfg_data(struct cyttsp4 *cd) ++{ ++ struct cyttsp4_sysinfo *si = &cd->sysinfo; ++ struct cyttsp4_tch_abs_params *tch; ++ struct cyttsp4_tch_rec_params *tch_old, *tch_new; ++ enum cyttsp4_tch_abs abs; ++ int i; ++ void *p; ++ int rc; ++ ++ if (si->si_ofs.ddata_ofs <= si->si_ofs.opcfg_ofs) { ++ dev_err(cd->dev, ++ "%s: invalid offset ddata_ofs: %zu, opcfg_ofs: %zu\n", ++ __func__, si->si_ofs.ddata_ofs, si->si_ofs.opcfg_ofs); ++ return -EINVAL; ++ } ++ ++ si->si_ofs.opcfg_size = si->si_ofs.ddata_ofs - si->si_ofs.opcfg_ofs; ++ ++ p = krealloc(si->si_ptrs.opcfg, si->si_ofs.opcfg_size, GFP_KERNEL); ++ if (p == NULL) { ++ dev_err(cd->dev, "%s: failed to allocate opcfg memory\n", ++ __func__); ++ return -ENOMEM; ++ } ++ si->si_ptrs.opcfg = p; ++ ++ rc = cyttsp4_adap_read(cd, si->si_ofs.opcfg_ofs, si->si_ofs.opcfg_size, ++ si->si_ptrs.opcfg); ++ if (rc < 0) { ++ dev_err(cd->dev, "%s: fail read opcfg data r=%d\n", ++ __func__, rc); ++ return rc; ++ } ++ si->si_ofs.cmd_ofs = si->si_ptrs.opcfg->cmd_ofs; ++ si->si_ofs.rep_ofs = si->si_ptrs.opcfg->rep_ofs; ++ si->si_ofs.rep_sz = (si->si_ptrs.opcfg->rep_szh * 256) + ++ si->si_ptrs.opcfg->rep_szl; ++ si->si_ofs.num_btns = si->si_ptrs.opcfg->num_btns; ++ si->si_ofs.num_btn_regs = (si->si_ofs.num_btns + ++ CY_NUM_BTN_PER_REG - 1) / CY_NUM_BTN_PER_REG; ++ si->si_ofs.tt_stat_ofs = si->si_ptrs.opcfg->tt_stat_ofs; ++ si->si_ofs.obj_cfg0 = si->si_ptrs.opcfg->obj_cfg0; ++ si->si_ofs.max_tchs = si->si_ptrs.opcfg->max_tchs & ++ CY_BYTE_OFS_MASK; ++ si->si_ofs.tch_rec_size = si->si_ptrs.opcfg->tch_rec_size & ++ CY_BYTE_OFS_MASK; ++ ++ /* Get the old touch fields */ ++ for (abs = CY_TCH_X; abs < CY_NUM_TCH_FIELDS; abs++) { ++ tch = &si->si_ofs.tch_abs[abs]; ++ tch_old = &si->si_ptrs.opcfg->tch_rec_old[abs]; ++ ++ tch->ofs = tch_old->loc & CY_BYTE_OFS_MASK; ++ tch->size = cyttsp4_bits_2_bytes(tch_old->size, ++ &tch->max); ++ tch->bofs = (tch_old->loc & CY_BOFS_MASK) >> CY_BOFS_SHIFT; ++ } ++ ++ /* button fields */ ++ si->si_ofs.btn_rec_size = si->si_ptrs.opcfg->btn_rec_size; ++ si->si_ofs.btn_diff_ofs = si->si_ptrs.opcfg->btn_diff_ofs; ++ si->si_ofs.btn_diff_size = si->si_ptrs.opcfg->btn_diff_size; ++ ++ if (si->si_ofs.tch_rec_size > CY_TMA1036_TCH_REC_SIZE) { ++ /* Get the extended touch fields */ ++ for (i = 0; i < CY_NUM_EXT_TCH_FIELDS; abs++, i++) { ++ tch = &si->si_ofs.tch_abs[abs]; ++ tch_new = &si->si_ptrs.opcfg->tch_rec_new[i]; ++ ++ tch->ofs = tch_new->loc & CY_BYTE_OFS_MASK; ++ tch->size = cyttsp4_bits_2_bytes(tch_new->size, ++ &tch->max); ++ tch->bofs = (tch_new->loc & CY_BOFS_MASK) >> CY_BOFS_SHIFT; ++ } ++ } ++ ++ for (abs = 0; abs < CY_TCH_NUM_ABS; abs++) { ++ dev_dbg(cd->dev, "%s: tch_rec_%s\n", __func__, ++ cyttsp4_tch_abs_string[abs]); ++ dev_dbg(cd->dev, "%s: ofs =%2zd\n", __func__, ++ si->si_ofs.tch_abs[abs].ofs); ++ dev_dbg(cd->dev, "%s: siz =%2zd\n", __func__, ++ si->si_ofs.tch_abs[abs].size); ++ dev_dbg(cd->dev, "%s: max =%2zd\n", __func__, ++ si->si_ofs.tch_abs[abs].max); ++ dev_dbg(cd->dev, "%s: bofs=%2zd\n", __func__, ++ si->si_ofs.tch_abs[abs].bofs); ++ } ++ ++ si->si_ofs.mode_size = si->si_ofs.tt_stat_ofs + 1; ++ si->si_ofs.data_size = si->si_ofs.max_tchs * ++ si->si_ptrs.opcfg->tch_rec_size; ++ ++ cyttsp4_pr_buf(cd->dev, cd->pr_buf, (u8 *)si->si_ptrs.opcfg, ++ si->si_ofs.opcfg_size, "sysinfo_opcfg_data"); ++ ++ return 0; ++} ++ ++static int cyttsp4_si_get_ddata(struct cyttsp4 *cd) ++{ ++ struct cyttsp4_sysinfo *si = &cd->sysinfo; ++ void *p; ++ int rc; ++ ++ si->si_ofs.ddata_size = si->si_ofs.mdata_ofs - si->si_ofs.ddata_ofs; ++ ++ p = krealloc(si->si_ptrs.ddata, si->si_ofs.ddata_size, GFP_KERNEL); ++ if (p == NULL) { ++ dev_err(cd->dev, "%s: fail alloc ddata memory\n", __func__); ++ return -ENOMEM; ++ } ++ si->si_ptrs.ddata = p; ++ ++ rc = cyttsp4_adap_read(cd, si->si_ofs.ddata_ofs, si->si_ofs.ddata_size, ++ si->si_ptrs.ddata); ++ if (rc < 0) ++ dev_err(cd->dev, "%s: fail read ddata data r=%d\n", ++ __func__, rc); ++ else ++ cyttsp4_pr_buf(cd->dev, cd->pr_buf, ++ (u8 *)si->si_ptrs.ddata, ++ si->si_ofs.ddata_size, "sysinfo_ddata"); ++ return rc; ++} ++ ++static int cyttsp4_si_get_mdata(struct cyttsp4 *cd) ++{ ++ struct cyttsp4_sysinfo *si = &cd->sysinfo; ++ void *p; ++ int rc; ++ ++ si->si_ofs.mdata_size = si->si_ofs.map_sz - si->si_ofs.mdata_ofs; ++ ++ p = krealloc(si->si_ptrs.mdata, si->si_ofs.mdata_size, GFP_KERNEL); ++ if (p == NULL) { ++ dev_err(cd->dev, "%s: fail alloc mdata memory\n", __func__); ++ return -ENOMEM; ++ } ++ si->si_ptrs.mdata = p; ++ ++ rc = cyttsp4_adap_read(cd, si->si_ofs.mdata_ofs, si->si_ofs.mdata_size, ++ si->si_ptrs.mdata); ++ if (rc < 0) ++ dev_err(cd->dev, "%s: fail read mdata data r=%d\n", ++ __func__, rc); ++ else ++ cyttsp4_pr_buf(cd->dev, cd->pr_buf, ++ (u8 *)si->si_ptrs.mdata, ++ si->si_ofs.mdata_size, "sysinfo_mdata"); ++ return rc; ++} ++ ++static int cyttsp4_si_get_btn_data(struct cyttsp4 *cd) ++{ ++ struct cyttsp4_sysinfo *si = &cd->sysinfo; ++ int btn; ++ int num_defined_keys; ++ u16 *key_table; ++ void *p; ++ int rc = 0; ++ ++ if (si->si_ofs.num_btns) { ++ si->si_ofs.btn_keys_size = si->si_ofs.num_btns * ++ sizeof(struct cyttsp4_btn); ++ ++ p = krealloc(si->btn, si->si_ofs.btn_keys_size, ++ GFP_KERNEL|__GFP_ZERO); ++ if (p == NULL) { ++ dev_err(cd->dev, "%s: %s\n", __func__, ++ "fail alloc btn_keys memory"); ++ return -ENOMEM; ++ } ++ si->btn = p; ++ ++ if (cd->cpdata->sett[CY_IC_GRPNUM_BTN_KEYS] == NULL) ++ num_defined_keys = 0; ++ else if (cd->cpdata->sett[CY_IC_GRPNUM_BTN_KEYS]->data == NULL) ++ num_defined_keys = 0; ++ else ++ num_defined_keys = cd->cpdata->sett ++ [CY_IC_GRPNUM_BTN_KEYS]->size; ++ ++ for (btn = 0; btn < si->si_ofs.num_btns && ++ btn < num_defined_keys; btn++) { ++ key_table = (u16 *)cd->cpdata->sett ++ [CY_IC_GRPNUM_BTN_KEYS]->data; ++ si->btn[btn].key_code = key_table[btn]; ++ si->btn[btn].state = CY_BTN_RELEASED; ++ si->btn[btn].enabled = true; ++ } ++ for (; btn < si->si_ofs.num_btns; btn++) { ++ si->btn[btn].key_code = KEY_RESERVED; ++ si->btn[btn].state = CY_BTN_RELEASED; ++ si->btn[btn].enabled = true; ++ } ++ ++ return rc; ++ } ++ ++ si->si_ofs.btn_keys_size = 0; ++ kfree(si->btn); ++ si->btn = NULL; ++ return rc; ++} ++ ++static int cyttsp4_si_get_op_data_ptrs(struct cyttsp4 *cd) ++{ ++ struct cyttsp4_sysinfo *si = &cd->sysinfo; ++ void *p; ++ ++ p = krealloc(si->xy_mode, si->si_ofs.mode_size, GFP_KERNEL|__GFP_ZERO); ++ if (p == NULL) ++ return -ENOMEM; ++ si->xy_mode = p; ++ ++ p = krealloc(si->xy_data, si->si_ofs.data_size, GFP_KERNEL|__GFP_ZERO); ++ if (p == NULL) ++ return -ENOMEM; ++ si->xy_data = p; ++ ++ p = krealloc(si->btn_rec_data, ++ si->si_ofs.btn_rec_size * si->si_ofs.num_btns, ++ GFP_KERNEL|__GFP_ZERO); ++ if (p == NULL) ++ return -ENOMEM; ++ si->btn_rec_data = p; ++ ++ return 0; ++} ++ ++static void cyttsp4_si_put_log_data(struct cyttsp4 *cd) ++{ ++ struct cyttsp4_sysinfo *si = &cd->sysinfo; ++ dev_dbg(cd->dev, "%s: cydata_ofs =%4zd siz=%4zd\n", __func__, ++ si->si_ofs.cydata_ofs, si->si_ofs.cydata_size); ++ dev_dbg(cd->dev, "%s: test_ofs =%4zd siz=%4zd\n", __func__, ++ si->si_ofs.test_ofs, si->si_ofs.test_size); ++ dev_dbg(cd->dev, "%s: pcfg_ofs =%4zd siz=%4zd\n", __func__, ++ si->si_ofs.pcfg_ofs, si->si_ofs.pcfg_size); ++ dev_dbg(cd->dev, "%s: opcfg_ofs =%4zd siz=%4zd\n", __func__, ++ si->si_ofs.opcfg_ofs, si->si_ofs.opcfg_size); ++ dev_dbg(cd->dev, "%s: ddata_ofs =%4zd siz=%4zd\n", __func__, ++ si->si_ofs.ddata_ofs, si->si_ofs.ddata_size); ++ dev_dbg(cd->dev, "%s: mdata_ofs =%4zd siz=%4zd\n", __func__, ++ si->si_ofs.mdata_ofs, si->si_ofs.mdata_size); ++ ++ dev_dbg(cd->dev, "%s: cmd_ofs =%4zd\n", __func__, ++ si->si_ofs.cmd_ofs); ++ dev_dbg(cd->dev, "%s: rep_ofs =%4zd\n", __func__, ++ si->si_ofs.rep_ofs); ++ dev_dbg(cd->dev, "%s: rep_sz =%4zd\n", __func__, ++ si->si_ofs.rep_sz); ++ dev_dbg(cd->dev, "%s: num_btns =%4zd\n", __func__, ++ si->si_ofs.num_btns); ++ dev_dbg(cd->dev, "%s: num_btn_regs =%4zd\n", __func__, ++ si->si_ofs.num_btn_regs); ++ dev_dbg(cd->dev, "%s: tt_stat_ofs =%4zd\n", __func__, ++ si->si_ofs.tt_stat_ofs); ++ dev_dbg(cd->dev, "%s: tch_rec_size =%4zd\n", __func__, ++ si->si_ofs.tch_rec_size); ++ dev_dbg(cd->dev, "%s: max_tchs =%4zd\n", __func__, ++ si->si_ofs.max_tchs); ++ dev_dbg(cd->dev, "%s: mode_size =%4zd\n", __func__, ++ si->si_ofs.mode_size); ++ dev_dbg(cd->dev, "%s: data_size =%4zd\n", __func__, ++ si->si_ofs.data_size); ++ dev_dbg(cd->dev, "%s: map_sz =%4zd\n", __func__, ++ si->si_ofs.map_sz); ++ ++ dev_dbg(cd->dev, "%s: btn_rec_size =%2zd\n", __func__, ++ si->si_ofs.btn_rec_size); ++ dev_dbg(cd->dev, "%s: btn_diff_ofs =%2zd\n", __func__, ++ si->si_ofs.btn_diff_ofs); ++ dev_dbg(cd->dev, "%s: btn_diff_size =%2zd\n", __func__, ++ si->si_ofs.btn_diff_size); ++ ++ dev_dbg(cd->dev, "%s: max_x = 0x%04zX (%zd)\n", __func__, ++ si->si_ofs.max_x, si->si_ofs.max_x); ++ dev_dbg(cd->dev, "%s: x_origin = %zd (%s)\n", __func__, ++ si->si_ofs.x_origin, ++ si->si_ofs.x_origin == CY_NORMAL_ORIGIN ? ++ "left corner" : "right corner"); ++ dev_dbg(cd->dev, "%s: max_y = 0x%04zX (%zd)\n", __func__, ++ si->si_ofs.max_y, si->si_ofs.max_y); ++ dev_dbg(cd->dev, "%s: y_origin = %zd (%s)\n", __func__, ++ si->si_ofs.y_origin, ++ si->si_ofs.y_origin == CY_NORMAL_ORIGIN ? ++ "upper corner" : "lower corner"); ++ dev_dbg(cd->dev, "%s: max_p = 0x%04zX (%zd)\n", __func__, ++ si->si_ofs.max_p, si->si_ofs.max_p); ++ ++ dev_dbg(cd->dev, "%s: xy_mode=%p xy_data=%p\n", __func__, ++ si->xy_mode, si->xy_data); ++} ++ ++static int cyttsp4_get_sysinfo_regs(struct cyttsp4 *cd) ++{ ++ struct cyttsp4_sysinfo *si = &cd->sysinfo; ++ int rc; ++ ++ rc = cyttsp4_si_data_offsets(cd); ++ if (rc < 0) ++ return rc; ++ ++ rc = cyttsp4_si_get_cydata(cd); ++ if (rc < 0) ++ return rc; ++ ++ rc = cyttsp4_si_get_test_data(cd); ++ if (rc < 0) ++ return rc; ++ ++ rc = cyttsp4_si_get_pcfg_data(cd); ++ if (rc < 0) ++ return rc; ++ ++ rc = cyttsp4_si_get_opcfg_data(cd); ++ if (rc < 0) ++ return rc; ++ ++ rc = cyttsp4_si_get_ddata(cd); ++ if (rc < 0) ++ return rc; ++ ++ rc = cyttsp4_si_get_mdata(cd); ++ if (rc < 0) ++ return rc; ++ ++ rc = cyttsp4_si_get_btn_data(cd); ++ if (rc < 0) ++ return rc; ++ ++ rc = cyttsp4_si_get_op_data_ptrs(cd); ++ if (rc < 0) { ++ dev_err(cd->dev, "%s: failed to get_op_data\n", ++ __func__); ++ return rc; ++ } ++ ++ cyttsp4_si_put_log_data(cd); ++ ++ /* provide flow control handshake */ ++ rc = cyttsp4_handshake(cd, si->si_data.hst_mode); ++ if (rc < 0) ++ dev_err(cd->dev, "%s: handshake fail on sysinfo reg\n", ++ __func__); ++ ++ si->ready = true; ++ return rc; ++} ++ ++static void cyttsp4_queue_startup_(struct cyttsp4 *cd) ++{ ++ if (cd->startup_state == STARTUP_NONE) { ++ cd->startup_state = STARTUP_QUEUED; ++ schedule_work(&cd->startup_work); ++ dev_dbg(cd->dev, "%s: cyttsp4_startup queued\n", __func__); ++ } else { ++ dev_dbg(cd->dev, "%s: startup_state = %d\n", __func__, ++ cd->startup_state); ++ } ++} ++ ++static void cyttsp4_report_slot_liftoff(struct cyttsp4_mt_data *md, ++ int max_slots) ++{ ++ int t; ++ ++ if (md->num_prv_tch == 0) ++ return; ++ ++ for (t = 0; t < max_slots; t++) { ++ input_mt_slot(md->input, t); ++ input_mt_report_slot_inactive(md->input); ++ } ++} ++ ++static void cyttsp4_lift_all(struct cyttsp4_mt_data *md) ++{ ++ if (!md->si) ++ return; ++ ++ if (md->num_prv_tch != 0) { ++ cyttsp4_report_slot_liftoff(md, ++ md->si->si_ofs.tch_abs[CY_TCH_T].max); ++ input_sync(md->input); ++ md->num_prv_tch = 0; ++ } ++} ++ ++static void cyttsp4_get_touch_axis(struct cyttsp4_mt_data *md, ++ int *axis, int size, int max, u8 *xy_data, int bofs) ++{ ++ int nbyte; ++ int next; ++ ++ for (nbyte = 0, *axis = 0, next = 0; nbyte < size; nbyte++) { ++ dev_vdbg(&md->input->dev, ++ "%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p" ++ " xy_data[%d]=%02X(%d) bofs=%d\n", ++ __func__, *axis, *axis, size, max, xy_data, next, ++ xy_data[next], xy_data[next], bofs); ++ *axis = (*axis * 256) + (xy_data[next] >> bofs); ++ next++; ++ } ++ ++ *axis &= max - 1; ++ ++ dev_vdbg(&md->input->dev, ++ "%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p" ++ " xy_data[%d]=%02X(%d)\n", ++ __func__, *axis, *axis, size, max, xy_data, next, ++ xy_data[next], xy_data[next]); ++} ++ ++static void cyttsp4_get_touch(struct cyttsp4_mt_data *md, ++ struct cyttsp4_touch *touch, u8 *xy_data) ++{ ++ struct device *dev = &md->input->dev; ++ struct cyttsp4_sysinfo *si = md->si; ++ enum cyttsp4_tch_abs abs; ++ bool flipped; ++ ++ for (abs = CY_TCH_X; abs < CY_TCH_NUM_ABS; abs++) { ++ cyttsp4_get_touch_axis(md, &touch->abs[abs], ++ si->si_ofs.tch_abs[abs].size, ++ si->si_ofs.tch_abs[abs].max, ++ xy_data + si->si_ofs.tch_abs[abs].ofs, ++ si->si_ofs.tch_abs[abs].bofs); ++ dev_vdbg(dev, "%s: get %s=%04X(%d)\n", __func__, ++ cyttsp4_tch_abs_string[abs], ++ touch->abs[abs], touch->abs[abs]); ++ } ++ ++ if (md->pdata->flags & CY_FLAG_FLIP) { ++ swap(touch->abs[CY_TCH_X], touch->abs[CY_TCH_Y]); ++ flipped = true; ++ } else ++ flipped = false; ++ ++ if (md->pdata->flags & CY_FLAG_INV_X) { ++ if (flipped) ++ touch->abs[CY_TCH_X] = md->si->si_ofs.max_y - ++ touch->abs[CY_TCH_X]; ++ else ++ touch->abs[CY_TCH_X] = md->si->si_ofs.max_x - ++ touch->abs[CY_TCH_X]; ++ } ++ if (md->pdata->flags & CY_FLAG_INV_Y) { ++ if (flipped) ++ touch->abs[CY_TCH_Y] = md->si->si_ofs.max_x - ++ touch->abs[CY_TCH_Y]; ++ else ++ touch->abs[CY_TCH_Y] = md->si->si_ofs.max_y - ++ touch->abs[CY_TCH_Y]; ++ } ++ ++ dev_vdbg(dev, "%s: flip=%s inv-x=%s inv-y=%s x=%04X(%d) y=%04X(%d)\n", ++ __func__, flipped ? "true" : "false", ++ md->pdata->flags & CY_FLAG_INV_X ? "true" : "false", ++ md->pdata->flags & CY_FLAG_INV_Y ? "true" : "false", ++ touch->abs[CY_TCH_X], touch->abs[CY_TCH_X], ++ touch->abs[CY_TCH_Y], touch->abs[CY_TCH_Y]); ++} ++ ++static void cyttsp4_final_sync(struct input_dev *input, int max_slots, int *ids) ++{ ++ int t; ++ ++ for (t = 0; t < max_slots; t++) { ++ if (ids[t]) ++ continue; ++ input_mt_slot(input, t); ++ input_mt_report_slot_inactive(input); ++ } ++ ++ input_sync(input); ++} ++ ++static void cyttsp4_get_mt_touches(struct cyttsp4_mt_data *md, int num_cur_tch) ++{ ++ struct device *dev = &md->input->dev; ++ struct cyttsp4_sysinfo *si = md->si; ++ struct cyttsp4_touch tch; ++ int sig; ++ int i, j, t = 0; ++ int ids[max(CY_TMA1036_MAX_TCH, CY_TMA4XX_MAX_TCH)]; ++ ++ memset(ids, 0, si->si_ofs.tch_abs[CY_TCH_T].max * sizeof(int)); ++ for (i = 0; i < num_cur_tch; i++) { ++ cyttsp4_get_touch(md, &tch, si->xy_data + ++ (i * si->si_ofs.tch_rec_size)); ++ if ((tch.abs[CY_TCH_T] < md->pdata->frmwrk->abs ++ [(CY_ABS_ID_OST * CY_NUM_ABS_SET) + CY_MIN_OST]) || ++ (tch.abs[CY_TCH_T] > md->pdata->frmwrk->abs ++ [(CY_ABS_ID_OST * CY_NUM_ABS_SET) + CY_MAX_OST])) { ++ dev_err(dev, "%s: tch=%d -> bad trk_id=%d max_id=%d\n", ++ __func__, i, tch.abs[CY_TCH_T], ++ md->pdata->frmwrk->abs[(CY_ABS_ID_OST * ++ CY_NUM_ABS_SET) + CY_MAX_OST]); ++ continue; ++ } ++ ++ /* use 0 based track id's */ ++ sig = md->pdata->frmwrk->abs ++ [(CY_ABS_ID_OST * CY_NUM_ABS_SET) + 0]; ++ if (sig != CY_IGNORE_VALUE) { ++ t = tch.abs[CY_TCH_T] - md->pdata->frmwrk->abs ++ [(CY_ABS_ID_OST * CY_NUM_ABS_SET) + CY_MIN_OST]; ++ if (tch.abs[CY_TCH_E] == CY_EV_LIFTOFF) { ++ dev_dbg(dev, "%s: t=%d e=%d lift-off\n", ++ __func__, t, tch.abs[CY_TCH_E]); ++ goto cyttsp4_get_mt_touches_pr_tch; ++ } ++ input_mt_slot(md->input, t); ++ input_mt_report_slot_state(md->input, MT_TOOL_FINGER, ++ true); ++ ids[t] = true; ++ } ++ ++ /* all devices: position and pressure fields */ ++ for (j = 0; j <= CY_ABS_W_OST; j++) { ++ sig = md->pdata->frmwrk->abs[((CY_ABS_X_OST + j) * ++ CY_NUM_ABS_SET) + 0]; ++ if (sig != CY_IGNORE_VALUE) ++ input_report_abs(md->input, sig, ++ tch.abs[CY_TCH_X + j]); ++ } ++ if (si->si_ofs.tch_rec_size > CY_TMA1036_TCH_REC_SIZE) { ++ /* ++ * TMA400 size and orientation fields: ++ * if pressure is non-zero and major touch ++ * signal is zero, then set major and minor touch ++ * signals to minimum non-zero value ++ */ ++ if (tch.abs[CY_TCH_P] > 0 && tch.abs[CY_TCH_MAJ] == 0) ++ tch.abs[CY_TCH_MAJ] = tch.abs[CY_TCH_MIN] = 1; ++ ++ /* Get the extended touch fields */ ++ for (j = 0; j < CY_NUM_EXT_TCH_FIELDS; j++) { ++ sig = md->pdata->frmwrk->abs ++ [((CY_ABS_MAJ_OST + j) * ++ CY_NUM_ABS_SET) + 0]; ++ if (sig != CY_IGNORE_VALUE) ++ input_report_abs(md->input, sig, ++ tch.abs[CY_TCH_MAJ + j]); ++ } ++ } ++ ++cyttsp4_get_mt_touches_pr_tch: ++ if (si->si_ofs.tch_rec_size > CY_TMA1036_TCH_REC_SIZE) ++ dev_dbg(dev, ++ "%s: t=%d x=%d y=%d z=%d M=%d m=%d o=%d e=%d\n", ++ __func__, t, ++ tch.abs[CY_TCH_X], ++ tch.abs[CY_TCH_Y], ++ tch.abs[CY_TCH_P], ++ tch.abs[CY_TCH_MAJ], ++ tch.abs[CY_TCH_MIN], ++ tch.abs[CY_TCH_OR], ++ tch.abs[CY_TCH_E]); ++ else ++ dev_dbg(dev, ++ "%s: t=%d x=%d y=%d z=%d e=%d\n", __func__, ++ t, ++ tch.abs[CY_TCH_X], ++ tch.abs[CY_TCH_Y], ++ tch.abs[CY_TCH_P], ++ tch.abs[CY_TCH_E]); ++ } ++ ++ cyttsp4_final_sync(md->input, si->si_ofs.tch_abs[CY_TCH_T].max, ids); ++ ++ md->num_prv_tch = num_cur_tch; ++ ++ return; ++} ++ ++/* read xy_data for all current touches */ ++static int cyttsp4_xy_worker(struct cyttsp4 *cd) ++{ ++ struct cyttsp4_mt_data *md = &cd->md; ++ struct device *dev = &md->input->dev; ++ struct cyttsp4_sysinfo *si = md->si; ++ u8 num_cur_tch; ++ u8 hst_mode; ++ u8 rep_len; ++ u8 rep_stat; ++ u8 tt_stat; ++ int rc = 0; ++ ++ /* ++ * Get event data from cyttsp4 device. ++ * The event data includes all data ++ * for all active touches. ++ * Event data also includes button data ++ */ ++ /* ++ * Use 2 reads: ++ * 1st read to get mode + button bytes + touch count (core) ++ * 2nd read (optional) to get touch 1 - touch n data ++ */ ++ hst_mode = si->xy_mode[CY_REG_BASE]; ++ rep_len = si->xy_mode[si->si_ofs.rep_ofs]; ++ rep_stat = si->xy_mode[si->si_ofs.rep_ofs + 1]; ++ tt_stat = si->xy_mode[si->si_ofs.tt_stat_ofs]; ++ dev_vdbg(dev, "%s: %s%02X %s%d %s%02X %s%02X\n", __func__, ++ "hst_mode=", hst_mode, "rep_len=", rep_len, ++ "rep_stat=", rep_stat, "tt_stat=", tt_stat); ++ ++ num_cur_tch = GET_NUM_TOUCHES(tt_stat); ++ dev_vdbg(dev, "%s: num_cur_tch=%d\n", __func__, num_cur_tch); ++ ++ if (rep_len == 0 && num_cur_tch > 0) { ++ dev_err(dev, "%s: report length error rep_len=%d num_tch=%d\n", ++ __func__, rep_len, num_cur_tch); ++ goto cyttsp4_xy_worker_exit; ++ } ++ ++ /* read touches */ ++ if (num_cur_tch > 0) { ++ rc = cyttsp4_adap_read(cd, si->si_ofs.tt_stat_ofs + 1, ++ num_cur_tch * si->si_ofs.tch_rec_size, ++ si->xy_data); ++ if (rc < 0) { ++ dev_err(dev, "%s: read fail on touch regs r=%d\n", ++ __func__, rc); ++ goto cyttsp4_xy_worker_exit; ++ } ++ } ++ ++ /* print xy data */ ++ cyttsp4_pr_buf(dev, cd->pr_buf, si->xy_data, num_cur_tch * ++ si->si_ofs.tch_rec_size, "xy_data"); ++ ++ /* check any error conditions */ ++ if (IS_BAD_PKT(rep_stat)) { ++ dev_dbg(dev, "%s: Invalid buffer detected\n", __func__); ++ rc = 0; ++ goto cyttsp4_xy_worker_exit; ++ } ++ ++ if (IS_LARGE_AREA(tt_stat)) ++ dev_dbg(dev, "%s: Large area detected\n", __func__); ++ ++ if (num_cur_tch > si->si_ofs.max_tchs) { ++ dev_err(dev, "%s: too many tch; set to max tch (n=%d c=%zd)\n", ++ __func__, num_cur_tch, si->si_ofs.max_tchs); ++ num_cur_tch = si->si_ofs.max_tchs; ++ } ++ ++ /* extract xy_data for all currently reported touches */ ++ dev_vdbg(dev, "%s: extract data num_cur_tch=%d\n", __func__, ++ num_cur_tch); ++ if (num_cur_tch) ++ cyttsp4_get_mt_touches(md, num_cur_tch); ++ else ++ cyttsp4_lift_all(md); ++ ++ rc = 0; ++ ++cyttsp4_xy_worker_exit: ++ return rc; ++} ++ ++static int cyttsp4_mt_attention(struct cyttsp4 *cd) ++{ ++ struct device *dev = cd->dev; ++ struct cyttsp4_mt_data *md = &cd->md; ++ int rc = 0; ++ ++ if (!md->si) ++ return 0; ++ ++ mutex_lock(&md->report_lock); ++ if (!md->is_suspended) { ++ /* core handles handshake */ ++ rc = cyttsp4_xy_worker(cd); ++ } else { ++ dev_vdbg(dev, "%s: Ignoring report while suspended\n", ++ __func__); ++ } ++ mutex_unlock(&md->report_lock); ++ if (rc < 0) ++ dev_err(dev, "%s: xy_worker error r=%d\n", __func__, rc); ++ ++ return rc; ++} ++ ++static irqreturn_t cyttsp4_irq(int irq, void *handle) ++{ ++ struct cyttsp4 *cd = handle; ++ struct device *dev = cd->dev; ++ enum cyttsp4_mode cur_mode; ++ u8 cmd_ofs = cd->sysinfo.si_ofs.cmd_ofs; ++ u8 mode[3]; ++ int rc; ++ ++ /* ++ * Check whether this IRQ should be ignored (external) ++ * This should be the very first thing to check since ++ * ignore_irq may be set for a very short period of time ++ */ ++ if (atomic_read(&cd->ignore_irq)) { ++ dev_vdbg(dev, "%s: Ignoring IRQ\n", __func__); ++ return IRQ_HANDLED; ++ } ++ ++ dev_dbg(dev, "%s int:0x%x\n", __func__, cd->int_status); ++ ++ mutex_lock(&cd->system_lock); ++ ++ /* Just to debug */ ++ if (cd->sleep_state == SS_SLEEP_ON || cd->sleep_state == SS_SLEEPING) ++ dev_vdbg(dev, "%s: Received IRQ while in sleep\n", __func__); ++ ++ rc = cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(mode), mode); ++ if (rc) { ++ dev_err(cd->dev, "%s: Fail read adapter r=%d\n", __func__, rc); ++ goto cyttsp4_irq_exit; ++ } ++ dev_vdbg(dev, "%s mode[0-2]:0x%X 0x%X 0x%X\n", __func__, ++ mode[0], mode[1], mode[2]); ++ ++ if (IS_BOOTLOADER(mode[0], mode[1])) { ++ cur_mode = CY_MODE_BOOTLOADER; ++ dev_vdbg(dev, "%s: bl running\n", __func__); ++ if (cd->mode == CY_MODE_BOOTLOADER) { ++ /* Signal bootloader heartbeat heard */ ++ wake_up(&cd->wait_q); ++ goto cyttsp4_irq_exit; ++ } ++ ++ /* switch to bootloader */ ++ dev_dbg(dev, "%s: restart switch to bl m=%d -> m=%d\n", ++ __func__, cd->mode, cur_mode); ++ ++ /* catch operation->bl glitch */ ++ if (cd->mode != CY_MODE_UNKNOWN) { ++ /* Incase startup_state do not let startup_() */ ++ cd->mode = CY_MODE_UNKNOWN; ++ cyttsp4_queue_startup_(cd); ++ goto cyttsp4_irq_exit; ++ } ++ ++ /* ++ * do not wake thread on this switch since ++ * it is possible to get an early heartbeat ++ * prior to performing the reset ++ */ ++ cd->mode = cur_mode; ++ ++ goto cyttsp4_irq_exit; ++ } ++ ++ switch (mode[0] & CY_HST_MODE) { ++ case CY_HST_OPERATE: ++ cur_mode = CY_MODE_OPERATIONAL; ++ dev_vdbg(dev, "%s: operational\n", __func__); ++ break; ++ case CY_HST_CAT: ++ cur_mode = CY_MODE_CAT; ++ dev_vdbg(dev, "%s: CaT\n", __func__); ++ break; ++ case CY_HST_SYSINFO: ++ cur_mode = CY_MODE_SYSINFO; ++ dev_vdbg(dev, "%s: sysinfo\n", __func__); ++ break; ++ default: ++ cur_mode = CY_MODE_UNKNOWN; ++ dev_err(dev, "%s: unknown HST mode 0x%02X\n", __func__, ++ mode[0]); ++ break; ++ } ++ ++ /* Check whether this IRQ should be ignored (internal) */ ++ if (cd->int_status & CY_INT_IGNORE) { ++ dev_vdbg(dev, "%s: Ignoring IRQ\n", __func__); ++ goto cyttsp4_irq_exit; ++ } ++ ++ /* Check for wake up interrupt */ ++ if (cd->int_status & CY_INT_AWAKE) { ++ cd->int_status &= ~CY_INT_AWAKE; ++ wake_up(&cd->wait_q); ++ dev_vdbg(dev, "%s: Received wake up interrupt\n", __func__); ++ goto cyttsp4_irq_handshake; ++ } ++ ++ /* Expecting mode change interrupt */ ++ if ((cd->int_status & CY_INT_MODE_CHANGE) ++ && (mode[0] & CY_HST_MODE_CHANGE) == 0) { ++ cd->int_status &= ~CY_INT_MODE_CHANGE; ++ dev_dbg(dev, "%s: finish mode switch m=%d -> m=%d\n", ++ __func__, cd->mode, cur_mode); ++ cd->mode = cur_mode; ++ wake_up(&cd->wait_q); ++ goto cyttsp4_irq_handshake; ++ } ++ ++ /* compare current core mode to current device mode */ ++ dev_vdbg(dev, "%s: cd->mode=%d cur_mode=%d\n", ++ __func__, cd->mode, cur_mode); ++ if ((mode[0] & CY_HST_MODE_CHANGE) == 0 && cd->mode != cur_mode) { ++ /* Unexpected mode change occurred */ ++ dev_err(dev, "%s %d->%d 0x%x\n", __func__, cd->mode, ++ cur_mode, cd->int_status); ++ dev_dbg(dev, "%s: Unexpected mode change, startup\n", ++ __func__); ++ cyttsp4_queue_startup_(cd); ++ goto cyttsp4_irq_exit; ++ } ++ ++ /* Expecting command complete interrupt */ ++ dev_vdbg(dev, "%s: command byte:0x%x\n", __func__, mode[cmd_ofs]); ++ if ((cd->int_status & CY_INT_EXEC_CMD) ++ && mode[cmd_ofs] & CY_CMD_COMPLETE) { ++ cd->int_status &= ~CY_INT_EXEC_CMD; ++ dev_vdbg(dev, "%s: Received command complete interrupt\n", ++ __func__); ++ wake_up(&cd->wait_q); ++ /* ++ * It is possible to receive a single interrupt for ++ * command complete and touch/button status report. ++ * Continue processing for a possible status report. ++ */ ++ } ++ ++ /* This should be status report, read status regs */ ++ if (cd->mode == CY_MODE_OPERATIONAL) { ++ dev_vdbg(dev, "%s: Read status registers\n", __func__); ++ rc = cyttsp4_load_status_regs(cd); ++ if (rc < 0) ++ dev_err(dev, "%s: fail read mode regs r=%d\n", ++ __func__, rc); ++ } ++ ++ cyttsp4_mt_attention(cd); ++ ++cyttsp4_irq_handshake: ++ /* handshake the event */ ++ dev_vdbg(dev, "%s: Handshake mode=0x%02X r=%d\n", ++ __func__, mode[0], rc); ++ rc = cyttsp4_handshake(cd, mode[0]); ++ if (rc < 0) ++ dev_err(dev, "%s: Fail handshake mode=0x%02X r=%d\n", ++ __func__, mode[0], rc); ++ ++ /* ++ * a non-zero udelay period is required for using ++ * IRQF_TRIGGER_LOW in order to delay until the ++ * device completes isr deassert ++ */ ++ udelay(cd->cpdata->level_irq_udelay); ++ ++cyttsp4_irq_exit: ++ mutex_unlock(&cd->system_lock); ++ return IRQ_HANDLED; ++} ++ ++static void cyttsp4_start_wd_timer(struct cyttsp4 *cd) ++{ ++ if (!CY_WATCHDOG_TIMEOUT) ++ return; ++ ++ mod_timer(&cd->watchdog_timer, jiffies + ++ msecs_to_jiffies(CY_WATCHDOG_TIMEOUT)); ++} ++ ++static void cyttsp4_stop_wd_timer(struct cyttsp4 *cd) ++{ ++ if (!CY_WATCHDOG_TIMEOUT) ++ return; ++ ++ /* ++ * Ensure we wait until the watchdog timer ++ * running on a different CPU finishes ++ */ ++ timer_shutdown_sync(&cd->watchdog_timer); ++ cancel_work_sync(&cd->watchdog_work); ++} ++ ++static void cyttsp4_watchdog_timer(struct timer_list *t) ++{ ++ struct cyttsp4 *cd = from_timer(cd, t, watchdog_timer); ++ ++ dev_vdbg(cd->dev, "%s: Watchdog timer triggered\n", __func__); ++ ++ schedule_work(&cd->watchdog_work); ++ ++ return; ++} ++ ++static int cyttsp4_request_exclusive(struct cyttsp4 *cd, void *ownptr, ++ int timeout_ms) ++{ ++ int t = msecs_to_jiffies(timeout_ms); ++ bool with_timeout = (timeout_ms != 0); ++ ++ mutex_lock(&cd->system_lock); ++ if (!cd->exclusive_dev && cd->exclusive_waits == 0) { ++ cd->exclusive_dev = ownptr; ++ goto exit; ++ } ++ ++ cd->exclusive_waits++; ++wait: ++ mutex_unlock(&cd->system_lock); ++ if (with_timeout) { ++ t = wait_event_timeout(cd->wait_q, !cd->exclusive_dev, t); ++ if (IS_TMO(t)) { ++ dev_err(cd->dev, "%s: tmo waiting exclusive access\n", ++ __func__); ++ mutex_lock(&cd->system_lock); ++ cd->exclusive_waits--; ++ mutex_unlock(&cd->system_lock); ++ return -ETIME; ++ } ++ } else { ++ wait_event(cd->wait_q, !cd->exclusive_dev); ++ } ++ mutex_lock(&cd->system_lock); ++ if (cd->exclusive_dev) ++ goto wait; ++ cd->exclusive_dev = ownptr; ++ cd->exclusive_waits--; ++exit: ++ mutex_unlock(&cd->system_lock); ++ ++ return 0; ++} ++ ++/* ++ * returns error if was not owned ++ */ ++static int cyttsp4_release_exclusive(struct cyttsp4 *cd, void *ownptr) ++{ ++ mutex_lock(&cd->system_lock); ++ if (cd->exclusive_dev != ownptr) { ++ mutex_unlock(&cd->system_lock); ++ return -EINVAL; ++ } ++ ++ dev_vdbg(cd->dev, "%s: exclusive_dev %p freed\n", ++ __func__, cd->exclusive_dev); ++ cd->exclusive_dev = NULL; ++ wake_up(&cd->wait_q); ++ mutex_unlock(&cd->system_lock); ++ return 0; ++} ++ ++static int cyttsp4_wait_bl_heartbeat(struct cyttsp4 *cd) ++{ ++ long t; ++ int rc = 0; ++ ++ /* wait heartbeat */ ++ dev_vdbg(cd->dev, "%s: wait heartbeat...\n", __func__); ++ t = wait_event_timeout(cd->wait_q, cd->mode == CY_MODE_BOOTLOADER, ++ msecs_to_jiffies(CY_CORE_RESET_AND_WAIT_TIMEOUT)); ++ if (IS_TMO(t)) { ++ dev_err(cd->dev, "%s: tmo waiting bl heartbeat cd->mode=%d\n", ++ __func__, cd->mode); ++ rc = -ETIME; ++ } ++ ++ return rc; ++} ++ ++static int cyttsp4_wait_sysinfo_mode(struct cyttsp4 *cd) ++{ ++ long t; ++ ++ dev_vdbg(cd->dev, "%s: wait sysinfo...\n", __func__); ++ ++ t = wait_event_timeout(cd->wait_q, cd->mode == CY_MODE_SYSINFO, ++ msecs_to_jiffies(CY_CORE_MODE_CHANGE_TIMEOUT)); ++ if (IS_TMO(t)) { ++ dev_err(cd->dev, "%s: tmo waiting exit bl cd->mode=%d\n", ++ __func__, cd->mode); ++ mutex_lock(&cd->system_lock); ++ cd->int_status &= ~CY_INT_MODE_CHANGE; ++ mutex_unlock(&cd->system_lock); ++ return -ETIME; ++ } ++ ++ return 0; ++} ++ ++static int cyttsp4_reset_and_wait(struct cyttsp4 *cd) ++{ ++ int rc; ++ ++ /* reset hardware */ ++ mutex_lock(&cd->system_lock); ++ dev_dbg(cd->dev, "%s: reset hw...\n", __func__); ++ rc = cyttsp4_hw_reset(cd); ++ cd->mode = CY_MODE_UNKNOWN; ++ mutex_unlock(&cd->system_lock); ++ if (rc < 0) { ++ dev_err(cd->dev, "%s:Fail hw reset r=%d\n", __func__, rc); ++ return rc; ++ } ++ ++ return cyttsp4_wait_bl_heartbeat(cd); ++} ++ ++/* ++ * returns err if refused or timeout; block until mode change complete ++ * bit is set (mode change interrupt) ++ */ ++static int cyttsp4_set_mode(struct cyttsp4 *cd, int new_mode) ++{ ++ u8 new_dev_mode; ++ u8 mode; ++ long t; ++ int rc; ++ ++ switch (new_mode) { ++ case CY_MODE_OPERATIONAL: ++ new_dev_mode = CY_HST_OPERATE; ++ break; ++ case CY_MODE_SYSINFO: ++ new_dev_mode = CY_HST_SYSINFO; ++ break; ++ case CY_MODE_CAT: ++ new_dev_mode = CY_HST_CAT; ++ break; ++ default: ++ dev_err(cd->dev, "%s: invalid mode: %02X(%d)\n", ++ __func__, new_mode, new_mode); ++ return -EINVAL; ++ } ++ ++ /* change mode */ ++ dev_dbg(cd->dev, "%s: %s=%p new_dev_mode=%02X new_mode=%d\n", ++ __func__, "have exclusive", cd->exclusive_dev, ++ new_dev_mode, new_mode); ++ ++ mutex_lock(&cd->system_lock); ++ rc = cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(mode), &mode); ++ if (rc < 0) { ++ mutex_unlock(&cd->system_lock); ++ dev_err(cd->dev, "%s: Fail read mode r=%d\n", ++ __func__, rc); ++ goto exit; ++ } ++ ++ /* Clear device mode bits and set to new mode */ ++ mode &= ~CY_HST_MODE; ++ mode |= new_dev_mode | CY_HST_MODE_CHANGE; ++ ++ cd->int_status |= CY_INT_MODE_CHANGE; ++ rc = cyttsp4_adap_write(cd, CY_REG_BASE, sizeof(mode), &mode); ++ mutex_unlock(&cd->system_lock); ++ if (rc < 0) { ++ dev_err(cd->dev, "%s: Fail write mode change r=%d\n", ++ __func__, rc); ++ goto exit; ++ } ++ ++ /* wait for mode change done interrupt */ ++ t = wait_event_timeout(cd->wait_q, ++ (cd->int_status & CY_INT_MODE_CHANGE) == 0, ++ msecs_to_jiffies(CY_CORE_MODE_CHANGE_TIMEOUT)); ++ dev_dbg(cd->dev, "%s: back from wait t=%ld cd->mode=%d\n", ++ __func__, t, cd->mode); ++ ++ if (IS_TMO(t)) { ++ dev_err(cd->dev, "%s: %s\n", __func__, ++ "tmo waiting mode change"); ++ mutex_lock(&cd->system_lock); ++ cd->int_status &= ~CY_INT_MODE_CHANGE; ++ mutex_unlock(&cd->system_lock); ++ rc = -EINVAL; ++ } ++ ++exit: ++ return rc; ++} ++ ++static void cyttsp4_watchdog_work(struct work_struct *work) ++{ ++ struct cyttsp4 *cd = ++ container_of(work, struct cyttsp4, watchdog_work); ++ u8 *mode; ++ int retval; ++ ++ mutex_lock(&cd->system_lock); ++ retval = cyttsp4_load_status_regs(cd); ++ if (retval < 0) { ++ dev_err(cd->dev, ++ "%s: failed to access device in watchdog timer r=%d\n", ++ __func__, retval); ++ cyttsp4_queue_startup_(cd); ++ goto cyttsp4_timer_watchdog_exit_error; ++ } ++ mode = &cd->sysinfo.xy_mode[CY_REG_BASE]; ++ if (IS_BOOTLOADER(mode[0], mode[1])) { ++ dev_err(cd->dev, ++ "%s: device found in bootloader mode when operational mode\n", ++ __func__); ++ cyttsp4_queue_startup_(cd); ++ goto cyttsp4_timer_watchdog_exit_error; ++ } ++ ++ cyttsp4_start_wd_timer(cd); ++cyttsp4_timer_watchdog_exit_error: ++ mutex_unlock(&cd->system_lock); ++ return; ++} ++ ++static int cyttsp4_core_sleep_(struct cyttsp4 *cd) ++{ ++ enum cyttsp4_sleep_state ss = SS_SLEEP_ON; ++ enum cyttsp4_int_state int_status = CY_INT_IGNORE; ++ int rc = 0; ++ u8 mode[2]; ++ ++ /* Already in sleep mode? */ ++ mutex_lock(&cd->system_lock); ++ if (cd->sleep_state == SS_SLEEP_ON) { ++ mutex_unlock(&cd->system_lock); ++ return 0; ++ } ++ cd->sleep_state = SS_SLEEPING; ++ mutex_unlock(&cd->system_lock); ++ ++ cyttsp4_stop_wd_timer(cd); ++ ++ /* Wait until currently running IRQ handler exits and disable IRQ */ ++ disable_irq(cd->irq); ++ ++ dev_vdbg(cd->dev, "%s: write DEEP SLEEP...\n", __func__); ++ mutex_lock(&cd->system_lock); ++ rc = cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(mode), &mode); ++ if (rc) { ++ mutex_unlock(&cd->system_lock); ++ dev_err(cd->dev, "%s: Fail read adapter r=%d\n", __func__, rc); ++ goto error; ++ } ++ ++ if (IS_BOOTLOADER(mode[0], mode[1])) { ++ mutex_unlock(&cd->system_lock); ++ dev_err(cd->dev, "%s: Device in BOOTLOADER mode.\n", __func__); ++ rc = -EINVAL; ++ goto error; ++ } ++ ++ mode[0] |= CY_HST_SLEEP; ++ rc = cyttsp4_adap_write(cd, CY_REG_BASE, sizeof(mode[0]), &mode[0]); ++ mutex_unlock(&cd->system_lock); ++ if (rc) { ++ dev_err(cd->dev, "%s: Fail write adapter r=%d\n", __func__, rc); ++ goto error; ++ } ++ dev_vdbg(cd->dev, "%s: write DEEP SLEEP succeeded\n", __func__); ++ ++ if (cd->cpdata->power) { ++ dev_dbg(cd->dev, "%s: Power down HW\n", __func__); ++ rc = cd->cpdata->power(cd->cpdata, 0, cd->dev, &cd->ignore_irq); ++ } else { ++ dev_dbg(cd->dev, "%s: No power function\n", __func__); ++ rc = 0; ++ } ++ if (rc < 0) { ++ dev_err(cd->dev, "%s: HW Power down fails r=%d\n", ++ __func__, rc); ++ goto error; ++ } ++ ++ /* Give time to FW to sleep */ ++ msleep(50); ++ ++ goto exit; ++ ++error: ++ ss = SS_SLEEP_OFF; ++ int_status = CY_INT_NONE; ++ cyttsp4_start_wd_timer(cd); ++ ++exit: ++ mutex_lock(&cd->system_lock); ++ cd->sleep_state = ss; ++ cd->int_status |= int_status; ++ mutex_unlock(&cd->system_lock); ++ enable_irq(cd->irq); ++ return rc; ++} ++ ++static int cyttsp4_startup_(struct cyttsp4 *cd) ++{ ++ int retry = CY_CORE_STARTUP_RETRY_COUNT; ++ int rc; ++ ++ cyttsp4_stop_wd_timer(cd); ++ ++reset: ++ if (retry != CY_CORE_STARTUP_RETRY_COUNT) ++ dev_dbg(cd->dev, "%s: Retry %d\n", __func__, ++ CY_CORE_STARTUP_RETRY_COUNT - retry); ++ ++ /* reset hardware and wait for heartbeat */ ++ rc = cyttsp4_reset_and_wait(cd); ++ if (rc < 0) { ++ dev_err(cd->dev, "%s: Error on h/w reset r=%d\n", __func__, rc); ++ if (retry--) ++ goto reset; ++ goto exit; ++ } ++ ++ /* exit bl into sysinfo mode */ ++ dev_vdbg(cd->dev, "%s: write exit ldr...\n", __func__); ++ mutex_lock(&cd->system_lock); ++ cd->int_status &= ~CY_INT_IGNORE; ++ cd->int_status |= CY_INT_MODE_CHANGE; ++ ++ rc = cyttsp4_adap_write(cd, CY_REG_BASE, sizeof(ldr_exit), ++ (u8 *)ldr_exit); ++ mutex_unlock(&cd->system_lock); ++ if (rc < 0) { ++ dev_err(cd->dev, "%s: Fail write r=%d\n", __func__, rc); ++ if (retry--) ++ goto reset; ++ goto exit; ++ } ++ ++ rc = cyttsp4_wait_sysinfo_mode(cd); ++ if (rc < 0) { ++ u8 buf[sizeof(ldr_err_app)]; ++ int rc1; ++ ++ /* Check for invalid/corrupted touch application */ ++ rc1 = cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(ldr_err_app), ++ buf); ++ if (rc1) { ++ dev_err(cd->dev, "%s: Fail read r=%d\n", __func__, rc1); ++ } else if (!memcmp(buf, ldr_err_app, sizeof(ldr_err_app))) { ++ dev_err(cd->dev, "%s: Error launching touch application\n", ++ __func__); ++ mutex_lock(&cd->system_lock); ++ cd->invalid_touch_app = true; ++ mutex_unlock(&cd->system_lock); ++ goto exit_no_wd; ++ } ++ ++ if (retry--) ++ goto reset; ++ goto exit; ++ } ++ ++ mutex_lock(&cd->system_lock); ++ cd->invalid_touch_app = false; ++ mutex_unlock(&cd->system_lock); ++ ++ /* read sysinfo data */ ++ dev_vdbg(cd->dev, "%s: get sysinfo regs..\n", __func__); ++ rc = cyttsp4_get_sysinfo_regs(cd); ++ if (rc < 0) { ++ dev_err(cd->dev, "%s: failed to get sysinfo regs rc=%d\n", ++ __func__, rc); ++ if (retry--) ++ goto reset; ++ goto exit; ++ } ++ ++ rc = cyttsp4_set_mode(cd, CY_MODE_OPERATIONAL); ++ if (rc < 0) { ++ dev_err(cd->dev, "%s: failed to set mode to operational rc=%d\n", ++ __func__, rc); ++ if (retry--) ++ goto reset; ++ goto exit; ++ } ++ ++ cyttsp4_lift_all(&cd->md); ++ ++ /* restore to sleep if was suspended */ ++ mutex_lock(&cd->system_lock); ++ if (cd->sleep_state == SS_SLEEP_ON) { ++ cd->sleep_state = SS_SLEEP_OFF; ++ mutex_unlock(&cd->system_lock); ++ cyttsp4_core_sleep_(cd); ++ goto exit_no_wd; ++ } ++ mutex_unlock(&cd->system_lock); ++ ++exit: ++ cyttsp4_start_wd_timer(cd); ++exit_no_wd: ++ return rc; ++} ++ ++static int cyttsp4_startup(struct cyttsp4 *cd) ++{ ++ int rc; ++ ++ mutex_lock(&cd->system_lock); ++ cd->startup_state = STARTUP_RUNNING; ++ mutex_unlock(&cd->system_lock); ++ ++ rc = cyttsp4_request_exclusive(cd, cd->dev, ++ CY_CORE_REQUEST_EXCLUSIVE_TIMEOUT); ++ if (rc < 0) { ++ dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", ++ __func__, cd->exclusive_dev, cd->dev); ++ goto exit; ++ } ++ ++ rc = cyttsp4_startup_(cd); ++ ++ if (cyttsp4_release_exclusive(cd, cd->dev) < 0) ++ /* Don't return fail code, mode is already changed. */ ++ dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); ++ else ++ dev_vdbg(cd->dev, "%s: pass release exclusive\n", __func__); ++ ++exit: ++ mutex_lock(&cd->system_lock); ++ cd->startup_state = STARTUP_NONE; ++ mutex_unlock(&cd->system_lock); ++ ++ /* Wake the waiters for end of startup */ ++ wake_up(&cd->wait_q); ++ ++ return rc; ++} ++ ++static void cyttsp4_startup_work_function(struct work_struct *work) ++{ ++ struct cyttsp4 *cd = container_of(work, struct cyttsp4, startup_work); ++ int rc; ++ ++ rc = cyttsp4_startup(cd); ++ if (rc < 0) ++ dev_err(cd->dev, "%s: Fail queued startup r=%d\n", ++ __func__, rc); ++} ++ ++static void cyttsp4_free_si_ptrs(struct cyttsp4 *cd) ++{ ++ struct cyttsp4_sysinfo *si = &cd->sysinfo; ++ ++ if (!si) ++ return; ++ ++ kfree(si->si_ptrs.cydata); ++ kfree(si->si_ptrs.test); ++ kfree(si->si_ptrs.pcfg); ++ kfree(si->si_ptrs.opcfg); ++ kfree(si->si_ptrs.ddata); ++ kfree(si->si_ptrs.mdata); ++ kfree(si->btn); ++ kfree(si->xy_mode); ++ kfree(si->xy_data); ++ kfree(si->btn_rec_data); ++} ++ ++static int cyttsp4_core_sleep(struct cyttsp4 *cd) ++{ ++ int rc; ++ ++ rc = cyttsp4_request_exclusive(cd, cd->dev, ++ CY_CORE_SLEEP_REQUEST_EXCLUSIVE_TIMEOUT); ++ if (rc < 0) { ++ dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", ++ __func__, cd->exclusive_dev, cd->dev); ++ return 0; ++ } ++ ++ rc = cyttsp4_core_sleep_(cd); ++ ++ if (cyttsp4_release_exclusive(cd, cd->dev) < 0) ++ dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); ++ else ++ dev_vdbg(cd->dev, "%s: pass release exclusive\n", __func__); ++ ++ return rc; ++} ++ ++static int cyttsp4_core_wake_(struct cyttsp4 *cd) ++{ ++ struct device *dev = cd->dev; ++ int rc; ++ u8 mode; ++ int t; ++ ++ /* Already woken? */ ++ mutex_lock(&cd->system_lock); ++ if (cd->sleep_state == SS_SLEEP_OFF) { ++ mutex_unlock(&cd->system_lock); ++ return 0; ++ } ++ cd->int_status &= ~CY_INT_IGNORE; ++ cd->int_status |= CY_INT_AWAKE; ++ cd->sleep_state = SS_WAKING; ++ ++ if (cd->cpdata->power) { ++ dev_dbg(dev, "%s: Power up HW\n", __func__); ++ rc = cd->cpdata->power(cd->cpdata, 1, dev, &cd->ignore_irq); ++ } else { ++ dev_dbg(dev, "%s: No power function\n", __func__); ++ rc = -ENOSYS; ++ } ++ if (rc < 0) { ++ dev_err(dev, "%s: HW Power up fails r=%d\n", ++ __func__, rc); ++ ++ /* Initiate a read transaction to wake up */ ++ cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(mode), &mode); ++ } else ++ dev_vdbg(cd->dev, "%s: HW power up succeeds\n", ++ __func__); ++ mutex_unlock(&cd->system_lock); ++ ++ t = wait_event_timeout(cd->wait_q, ++ (cd->int_status & CY_INT_AWAKE) == 0, ++ msecs_to_jiffies(CY_CORE_WAKEUP_TIMEOUT)); ++ if (IS_TMO(t)) { ++ dev_err(dev, "%s: TMO waiting for wakeup\n", __func__); ++ mutex_lock(&cd->system_lock); ++ cd->int_status &= ~CY_INT_AWAKE; ++ /* Try starting up */ ++ cyttsp4_queue_startup_(cd); ++ mutex_unlock(&cd->system_lock); ++ } ++ ++ mutex_lock(&cd->system_lock); ++ cd->sleep_state = SS_SLEEP_OFF; ++ mutex_unlock(&cd->system_lock); ++ ++ cyttsp4_start_wd_timer(cd); ++ ++ return 0; ++} ++ ++static int cyttsp4_core_wake(struct cyttsp4 *cd) ++{ ++ int rc; ++ ++ rc = cyttsp4_request_exclusive(cd, cd->dev, ++ CY_CORE_REQUEST_EXCLUSIVE_TIMEOUT); ++ if (rc < 0) { ++ dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", ++ __func__, cd->exclusive_dev, cd->dev); ++ return 0; ++ } ++ ++ rc = cyttsp4_core_wake_(cd); ++ ++ if (cyttsp4_release_exclusive(cd, cd->dev) < 0) ++ dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); ++ else ++ dev_vdbg(cd->dev, "%s: pass release exclusive\n", __func__); ++ ++ return rc; ++} ++ ++static int cyttsp4_core_suspend(struct device *dev) ++{ ++ struct cyttsp4 *cd = dev_get_drvdata(dev); ++ struct cyttsp4_mt_data *md = &cd->md; ++ int rc; ++ ++ md->is_suspended = true; ++ ++ rc = cyttsp4_core_sleep(cd); ++ if (rc < 0) { ++ dev_err(dev, "%s: Error on sleep\n", __func__); ++ return -EAGAIN; ++ } ++ return 0; ++} ++ ++static int cyttsp4_core_resume(struct device *dev) ++{ ++ struct cyttsp4 *cd = dev_get_drvdata(dev); ++ struct cyttsp4_mt_data *md = &cd->md; ++ int rc; ++ ++ md->is_suspended = false; ++ ++ rc = cyttsp4_core_wake(cd); ++ if (rc < 0) { ++ dev_err(dev, "%s: Error on wake\n", __func__); ++ return -EAGAIN; ++ } ++ ++ return 0; ++} ++ ++EXPORT_GPL_RUNTIME_DEV_PM_OPS(cyttsp4_pm_ops, ++ cyttsp4_core_suspend, cyttsp4_core_resume, NULL); ++ ++static int cyttsp4_mt_open(struct input_dev *input) ++{ ++ pm_runtime_get(input->dev.parent); ++ return 0; ++} ++ ++static void cyttsp4_mt_close(struct input_dev *input) ++{ ++ struct cyttsp4_mt_data *md = input_get_drvdata(input); ++ mutex_lock(&md->report_lock); ++ if (!md->is_suspended) ++ pm_runtime_put(input->dev.parent); ++ mutex_unlock(&md->report_lock); ++} ++ ++ ++static int cyttsp4_setup_input_device(struct cyttsp4 *cd) ++{ ++ struct device *dev = cd->dev; ++ struct cyttsp4_mt_data *md = &cd->md; ++ int signal = CY_IGNORE_VALUE; ++ int max_x, max_y, max_p, min, max; ++ int max_x_tmp, max_y_tmp; ++ int i; ++ int rc; ++ ++ dev_vdbg(dev, "%s: Initialize event signals\n", __func__); ++ __set_bit(EV_ABS, md->input->evbit); ++ __set_bit(EV_REL, md->input->evbit); ++ __set_bit(EV_KEY, md->input->evbit); ++ ++ max_x_tmp = md->si->si_ofs.max_x; ++ max_y_tmp = md->si->si_ofs.max_y; ++ ++ /* get maximum values from the sysinfo data */ ++ if (md->pdata->flags & CY_FLAG_FLIP) { ++ max_x = max_y_tmp - 1; ++ max_y = max_x_tmp - 1; ++ } else { ++ max_x = max_x_tmp - 1; ++ max_y = max_y_tmp - 1; ++ } ++ max_p = md->si->si_ofs.max_p; ++ ++ /* set event signal capabilities */ ++ for (i = 0; i < (md->pdata->frmwrk->size / CY_NUM_ABS_SET); i++) { ++ signal = md->pdata->frmwrk->abs ++ [(i * CY_NUM_ABS_SET) + CY_SIGNAL_OST]; ++ if (signal != CY_IGNORE_VALUE) { ++ __set_bit(signal, md->input->absbit); ++ min = md->pdata->frmwrk->abs ++ [(i * CY_NUM_ABS_SET) + CY_MIN_OST]; ++ max = md->pdata->frmwrk->abs ++ [(i * CY_NUM_ABS_SET) + CY_MAX_OST]; ++ if (i == CY_ABS_ID_OST) { ++ /* shift track ids down to start at 0 */ ++ max = max - min; ++ min = min - min; ++ } else if (i == CY_ABS_X_OST) ++ max = max_x; ++ else if (i == CY_ABS_Y_OST) ++ max = max_y; ++ else if (i == CY_ABS_P_OST) ++ max = max_p; ++ input_set_abs_params(md->input, signal, min, max, ++ md->pdata->frmwrk->abs ++ [(i * CY_NUM_ABS_SET) + CY_FUZZ_OST], ++ md->pdata->frmwrk->abs ++ [(i * CY_NUM_ABS_SET) + CY_FLAT_OST]); ++ dev_dbg(dev, "%s: register signal=%02X min=%d max=%d\n", ++ __func__, signal, min, max); ++ if ((i == CY_ABS_ID_OST) && ++ (md->si->si_ofs.tch_rec_size < ++ CY_TMA4XX_TCH_REC_SIZE)) ++ break; ++ } ++ } ++ ++ input_mt_init_slots(md->input, md->si->si_ofs.tch_abs[CY_TCH_T].max, ++ INPUT_MT_DIRECT); ++ rc = input_register_device(md->input); ++ if (rc < 0) ++ dev_err(dev, "%s: Error, failed register input device r=%d\n", ++ __func__, rc); ++ return rc; ++} ++ ++static int cyttsp4_mt_probe(struct cyttsp4 *cd) ++{ ++ struct device *dev = cd->dev; ++ struct cyttsp4_mt_data *md = &cd->md; ++ struct cyttsp4_mt_platform_data *pdata = cd->pdata->mt_pdata; ++ int rc = 0; ++ ++ mutex_init(&md->report_lock); ++ md->pdata = pdata; ++ /* Create the input device and register it. */ ++ dev_vdbg(dev, "%s: Create the input device and register it\n", ++ __func__); ++ md->input = input_allocate_device(); ++ if (md->input == NULL) { ++ dev_err(dev, "%s: Error, failed to allocate input device\n", ++ __func__); ++ rc = -ENOSYS; ++ goto error_alloc_failed; ++ } ++ ++ md->input->name = pdata->inp_dev_name; ++ scnprintf(md->phys, sizeof(md->phys)-1, "%s", dev_name(dev)); ++ md->input->phys = md->phys; ++ md->input->id.bustype = cd->bus_ops->bustype; ++ md->input->dev.parent = dev; ++ md->input->open = cyttsp4_mt_open; ++ md->input->close = cyttsp4_mt_close; ++ input_set_drvdata(md->input, md); ++ ++ /* get sysinfo */ ++ md->si = &cd->sysinfo; ++ ++ rc = cyttsp4_setup_input_device(cd); ++ if (rc) ++ goto error_init_input; ++ ++ return 0; ++ ++error_init_input: ++ input_free_device(md->input); ++error_alloc_failed: ++ dev_err(dev, "%s failed.\n", __func__); ++ return rc; ++} ++ ++struct cyttsp4 *cyttsp4_probe(const struct cyttsp4_bus_ops *ops, ++ struct device *dev, u16 irq, size_t xfer_buf_size) ++{ ++ struct cyttsp4 *cd; ++ struct cyttsp4_platform_data *pdata = dev_get_platdata(dev); ++ unsigned long irq_flags; ++ int rc = 0; ++ ++ if (!pdata || !pdata->core_pdata || !pdata->mt_pdata) { ++ dev_err(dev, "%s: Missing platform data\n", __func__); ++ rc = -ENODEV; ++ goto error_no_pdata; ++ } ++ ++ cd = kzalloc(sizeof(*cd), GFP_KERNEL); ++ if (!cd) { ++ dev_err(dev, "%s: Error, kzalloc\n", __func__); ++ rc = -ENOMEM; ++ goto error_alloc_data; ++ } ++ ++ cd->xfer_buf = kzalloc(xfer_buf_size, GFP_KERNEL); ++ if (!cd->xfer_buf) { ++ dev_err(dev, "%s: Error, kzalloc\n", __func__); ++ rc = -ENOMEM; ++ goto error_free_cd; ++ } ++ ++ /* Initialize device info */ ++ cd->dev = dev; ++ cd->pdata = pdata; ++ cd->cpdata = pdata->core_pdata; ++ cd->bus_ops = ops; ++ ++ /* Initialize mutexes and spinlocks */ ++ mutex_init(&cd->system_lock); ++ mutex_init(&cd->adap_lock); ++ ++ /* Initialize wait queue */ ++ init_waitqueue_head(&cd->wait_q); ++ ++ /* Initialize works */ ++ INIT_WORK(&cd->startup_work, cyttsp4_startup_work_function); ++ INIT_WORK(&cd->watchdog_work, cyttsp4_watchdog_work); ++ ++ /* Initialize IRQ */ ++ cd->irq = gpio_to_irq(cd->cpdata->irq_gpio); ++ if (cd->irq < 0) { ++ rc = -EINVAL; ++ goto error_free_xfer; ++ } ++ ++ dev_set_drvdata(dev, cd); ++ ++ /* Call platform init function */ ++ if (cd->cpdata->init) { ++ dev_dbg(cd->dev, "%s: Init HW\n", __func__); ++ rc = cd->cpdata->init(cd->cpdata, 1, cd->dev); ++ } else { ++ dev_dbg(cd->dev, "%s: No HW INIT function\n", __func__); ++ rc = 0; ++ } ++ if (rc < 0) ++ dev_err(cd->dev, "%s: HW Init fail r=%d\n", __func__, rc); ++ ++ dev_dbg(dev, "%s: initialize threaded irq=%d\n", __func__, cd->irq); ++ if (cd->cpdata->level_irq_udelay > 0) ++ /* use level triggered interrupts */ ++ irq_flags = IRQF_TRIGGER_LOW | IRQF_ONESHOT; ++ else ++ /* use edge triggered interrupts */ ++ irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; ++ ++ rc = request_threaded_irq(cd->irq, NULL, cyttsp4_irq, irq_flags, ++ dev_name(dev), cd); ++ if (rc < 0) { ++ dev_err(dev, "%s: Error, could not request irq\n", __func__); ++ goto error_request_irq; ++ } ++ ++ /* Setup watchdog timer */ ++ timer_setup(&cd->watchdog_timer, cyttsp4_watchdog_timer, 0); ++ ++ /* ++ * call startup directly to ensure that the device ++ * is tested before leaving the probe ++ */ ++ rc = cyttsp4_startup(cd); ++ ++ /* Do not fail probe if startup fails but the device is detected */ ++ if (rc < 0 && cd->mode == CY_MODE_UNKNOWN) { ++ dev_err(cd->dev, "%s: Fail initial startup r=%d\n", ++ __func__, rc); ++ goto error_startup; ++ } ++ ++ rc = cyttsp4_mt_probe(cd); ++ if (rc < 0) { ++ dev_err(dev, "%s: Error, fail mt probe\n", __func__); ++ goto error_startup; ++ } ++ ++ pm_runtime_enable(dev); ++ ++ return cd; ++ ++error_startup: ++ cancel_work_sync(&cd->startup_work); ++ cyttsp4_stop_wd_timer(cd); ++ pm_runtime_disable(dev); ++ cyttsp4_free_si_ptrs(cd); ++ free_irq(cd->irq, cd); ++error_request_irq: ++ if (cd->cpdata->init) ++ cd->cpdata->init(cd->cpdata, 0, dev); ++error_free_xfer: ++ kfree(cd->xfer_buf); ++error_free_cd: ++ kfree(cd); ++error_alloc_data: ++error_no_pdata: ++ dev_err(dev, "%s failed.\n", __func__); ++ return ERR_PTR(rc); ++} ++EXPORT_SYMBOL_GPL(cyttsp4_probe); ++ ++static void cyttsp4_mt_release(struct cyttsp4_mt_data *md) ++{ ++ input_unregister_device(md->input); ++ input_set_drvdata(md->input, NULL); ++} ++ ++int cyttsp4_remove(struct cyttsp4 *cd) ++{ ++ struct device *dev = cd->dev; ++ ++ cyttsp4_mt_release(&cd->md); ++ ++ /* ++ * Suspend the device before freeing the startup_work and stopping ++ * the watchdog since sleep function restarts watchdog on failure ++ */ ++ pm_runtime_suspend(dev); ++ pm_runtime_disable(dev); ++ ++ cancel_work_sync(&cd->startup_work); ++ ++ cyttsp4_stop_wd_timer(cd); ++ ++ free_irq(cd->irq, cd); ++ if (cd->cpdata->init) ++ cd->cpdata->init(cd->cpdata, 0, dev); ++ cyttsp4_free_si_ptrs(cd); ++ kfree(cd); ++ return 0; ++} ++EXPORT_SYMBOL_GPL(cyttsp4_remove); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard touchscreen core driver"); ++MODULE_AUTHOR("Cypress"); +diff --git a/drivers/input/touchscreen/cyttsp4_core.h b/drivers/input/touchscreen/cyttsp4_core.h +new file mode 100644 +index 000000000000..6262f6e45075 +--- /dev/null ++++ b/drivers/input/touchscreen/cyttsp4_core.h +@@ -0,0 +1,448 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++/* ++ * cyttsp4_core.h ++ * Cypress TrueTouch(TM) Standard Product V4 Core driver module. ++ * For use with Cypress Txx4xx parts. ++ * Supported parts include: ++ * TMA4XX ++ * TMA1036 ++ * ++ * Copyright (C) 2012 Cypress Semiconductor ++ * ++ * Contact Cypress Semiconductor at www.cypress.com ++ */ ++ ++#ifndef _LINUX_CYTTSP4_CORE_H ++#define _LINUX_CYTTSP4_CORE_H ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define CY_REG_BASE 0x00 ++ ++#define CY_POST_CODEL_WDG_RST 0x01 ++#define CY_POST_CODEL_CFG_DATA_CRC_FAIL 0x02 ++#define CY_POST_CODEL_PANEL_TEST_FAIL 0x04 ++ ++#define CY_NUM_BTN_PER_REG 4 ++ ++/* touch record system information offset masks and shifts */ ++#define CY_BYTE_OFS_MASK 0x1F ++#define CY_BOFS_MASK 0xE0 ++#define CY_BOFS_SHIFT 5 ++ ++#define CY_TMA1036_TCH_REC_SIZE 6 ++#define CY_TMA4XX_TCH_REC_SIZE 9 ++#define CY_TMA1036_MAX_TCH 0x0E ++#define CY_TMA4XX_MAX_TCH 0x1E ++ ++#define CY_NORMAL_ORIGIN 0 /* upper, left corner */ ++#define CY_INVERT_ORIGIN 1 /* lower, right corner */ ++ ++/* helpers */ ++#define GET_NUM_TOUCHES(x) ((x) & 0x1F) ++#define IS_LARGE_AREA(x) ((x) & 0x20) ++#define IS_BAD_PKT(x) ((x) & 0x20) ++#define IS_BOOTLOADER(hst_mode, reset_detect) \ ++ ((hst_mode) & 0x01 || (reset_detect) != 0) ++#define IS_TMO(t) ((t) == 0) ++ ++ ++enum cyttsp_cmd_bits { ++ CY_CMD_COMPLETE = (1 << 6), ++}; ++ ++/* Timeout in ms. */ ++#define CY_WATCHDOG_TIMEOUT 1000 ++ ++#define CY_MAX_PRINT_SIZE 512 ++#ifdef VERBOSE_DEBUG ++#define CY_MAX_PRBUF_SIZE PIPE_BUF ++#define CY_PR_TRUNCATED " truncated..." ++#endif ++ ++enum cyttsp4_ic_grpnum { ++ CY_IC_GRPNUM_RESERVED, ++ CY_IC_GRPNUM_CMD_REGS, ++ CY_IC_GRPNUM_TCH_REP, ++ CY_IC_GRPNUM_DATA_REC, ++ CY_IC_GRPNUM_TEST_REC, ++ CY_IC_GRPNUM_PCFG_REC, ++ CY_IC_GRPNUM_TCH_PARM_VAL, ++ CY_IC_GRPNUM_TCH_PARM_SIZE, ++ CY_IC_GRPNUM_RESERVED1, ++ CY_IC_GRPNUM_RESERVED2, ++ CY_IC_GRPNUM_OPCFG_REC, ++ CY_IC_GRPNUM_DDATA_REC, ++ CY_IC_GRPNUM_MDATA_REC, ++ CY_IC_GRPNUM_TEST_REGS, ++ CY_IC_GRPNUM_BTN_KEYS, ++ CY_IC_GRPNUM_TTHE_REGS, ++ CY_IC_GRPNUM_NUM ++}; ++ ++enum cyttsp4_int_state { ++ CY_INT_NONE, ++ CY_INT_IGNORE = (1 << 0), ++ CY_INT_MODE_CHANGE = (1 << 1), ++ CY_INT_EXEC_CMD = (1 << 2), ++ CY_INT_AWAKE = (1 << 3), ++}; ++ ++enum cyttsp4_mode { ++ CY_MODE_UNKNOWN, ++ CY_MODE_BOOTLOADER = (1 << 1), ++ CY_MODE_OPERATIONAL = (1 << 2), ++ CY_MODE_SYSINFO = (1 << 3), ++ CY_MODE_CAT = (1 << 4), ++ CY_MODE_STARTUP = (1 << 5), ++ CY_MODE_LOADER = (1 << 6), ++ CY_MODE_CHANGE_MODE = (1 << 7), ++ CY_MODE_CHANGED = (1 << 8), ++ CY_MODE_CMD_COMPLETE = (1 << 9), ++}; ++ ++enum cyttsp4_sleep_state { ++ SS_SLEEP_OFF, ++ SS_SLEEP_ON, ++ SS_SLEEPING, ++ SS_WAKING, ++}; ++ ++enum cyttsp4_startup_state { ++ STARTUP_NONE, ++ STARTUP_QUEUED, ++ STARTUP_RUNNING, ++}; ++ ++#define CY_NUM_REVCTRL 8 ++struct cyttsp4_cydata { ++ u8 ttpidh; ++ u8 ttpidl; ++ u8 fw_ver_major; ++ u8 fw_ver_minor; ++ u8 revctrl[CY_NUM_REVCTRL]; ++ u8 blver_major; ++ u8 blver_minor; ++ u8 jtag_si_id3; ++ u8 jtag_si_id2; ++ u8 jtag_si_id1; ++ u8 jtag_si_id0; ++ u8 mfgid_sz; ++ u8 cyito_idh; ++ u8 cyito_idl; ++ u8 cyito_verh; ++ u8 cyito_verl; ++ u8 ttsp_ver_major; ++ u8 ttsp_ver_minor; ++ u8 device_info; ++ u8 mfg_id[]; ++} __packed; ++ ++struct cyttsp4_test { ++ u8 post_codeh; ++ u8 post_codel; ++} __packed; ++ ++struct cyttsp4_pcfg { ++ u8 electrodes_x; ++ u8 electrodes_y; ++ u8 len_xh; ++ u8 len_xl; ++ u8 len_yh; ++ u8 len_yl; ++ u8 res_xh; ++ u8 res_xl; ++ u8 res_yh; ++ u8 res_yl; ++ u8 max_zh; ++ u8 max_zl; ++ u8 panel_info0; ++} __packed; ++ ++struct cyttsp4_tch_rec_params { ++ u8 loc; ++ u8 size; ++} __packed; ++ ++#define CY_NUM_TCH_FIELDS 7 ++#define CY_NUM_EXT_TCH_FIELDS 3 ++struct cyttsp4_opcfg { ++ u8 cmd_ofs; ++ u8 rep_ofs; ++ u8 rep_szh; ++ u8 rep_szl; ++ u8 num_btns; ++ u8 tt_stat_ofs; ++ u8 obj_cfg0; ++ u8 max_tchs; ++ u8 tch_rec_size; ++ struct cyttsp4_tch_rec_params tch_rec_old[CY_NUM_TCH_FIELDS]; ++ u8 btn_rec_size; /* btn record size (in bytes) */ ++ u8 btn_diff_ofs; /* btn data loc, diff counts */ ++ u8 btn_diff_size; /* btn size of diff counts (in bits) */ ++ struct cyttsp4_tch_rec_params tch_rec_new[CY_NUM_EXT_TCH_FIELDS]; ++} __packed; ++ ++struct cyttsp4_sysinfo_ptr { ++ struct cyttsp4_cydata *cydata; ++ struct cyttsp4_test *test; ++ struct cyttsp4_pcfg *pcfg; ++ struct cyttsp4_opcfg *opcfg; ++ struct cyttsp4_ddata *ddata; ++ struct cyttsp4_mdata *mdata; ++} __packed; ++ ++struct cyttsp4_sysinfo_data { ++ u8 hst_mode; ++ u8 reserved; ++ u8 map_szh; ++ u8 map_szl; ++ u8 cydata_ofsh; ++ u8 cydata_ofsl; ++ u8 test_ofsh; ++ u8 test_ofsl; ++ u8 pcfg_ofsh; ++ u8 pcfg_ofsl; ++ u8 opcfg_ofsh; ++ u8 opcfg_ofsl; ++ u8 ddata_ofsh; ++ u8 ddata_ofsl; ++ u8 mdata_ofsh; ++ u8 mdata_ofsl; ++} __packed; ++ ++enum cyttsp4_tch_abs { /* for ordering within the extracted touch data array */ ++ CY_TCH_X, /* X */ ++ CY_TCH_Y, /* Y */ ++ CY_TCH_P, /* P (Z) */ ++ CY_TCH_T, /* TOUCH ID */ ++ CY_TCH_E, /* EVENT ID */ ++ CY_TCH_O, /* OBJECT ID */ ++ CY_TCH_W, /* SIZE */ ++ CY_TCH_MAJ, /* TOUCH_MAJOR */ ++ CY_TCH_MIN, /* TOUCH_MINOR */ ++ CY_TCH_OR, /* ORIENTATION */ ++ CY_TCH_NUM_ABS ++}; ++ ++struct cyttsp4_touch { ++ int abs[CY_TCH_NUM_ABS]; ++}; ++ ++struct cyttsp4_tch_abs_params { ++ size_t ofs; /* abs byte offset */ ++ size_t size; /* size in bits */ ++ size_t max; /* max value */ ++ size_t bofs; /* bit offset */ ++}; ++ ++struct cyttsp4_sysinfo_ofs { ++ size_t chip_type; ++ size_t cmd_ofs; ++ size_t rep_ofs; ++ size_t rep_sz; ++ size_t num_btns; ++ size_t num_btn_regs; /* ceil(num_btns/4) */ ++ size_t tt_stat_ofs; ++ size_t tch_rec_size; ++ size_t obj_cfg0; ++ size_t max_tchs; ++ size_t mode_size; ++ size_t data_size; ++ size_t map_sz; ++ size_t max_x; ++ size_t x_origin; /* left or right corner */ ++ size_t max_y; ++ size_t y_origin; /* upper or lower corner */ ++ size_t max_p; ++ size_t cydata_ofs; ++ size_t test_ofs; ++ size_t pcfg_ofs; ++ size_t opcfg_ofs; ++ size_t ddata_ofs; ++ size_t mdata_ofs; ++ size_t cydata_size; ++ size_t test_size; ++ size_t pcfg_size; ++ size_t opcfg_size; ++ size_t ddata_size; ++ size_t mdata_size; ++ size_t btn_keys_size; ++ struct cyttsp4_tch_abs_params tch_abs[CY_TCH_NUM_ABS]; ++ size_t btn_rec_size; /* btn record size (in bytes) */ ++ size_t btn_diff_ofs;/* btn data loc ,diff counts, (Op-Mode byte ofs) */ ++ size_t btn_diff_size;/* btn size of diff counts (in bits) */ ++}; ++ ++enum cyttsp4_btn_state { ++ CY_BTN_RELEASED, ++ CY_BTN_PRESSED, ++ CY_BTN_NUM_STATE ++}; ++ ++struct cyttsp4_btn { ++ bool enabled; ++ int state; /* CY_BTN_PRESSED, CY_BTN_RELEASED */ ++ int key_code; ++}; ++ ++struct cyttsp4_sysinfo { ++ bool ready; ++ struct cyttsp4_sysinfo_data si_data; ++ struct cyttsp4_sysinfo_ptr si_ptrs; ++ struct cyttsp4_sysinfo_ofs si_ofs; ++ struct cyttsp4_btn *btn; /* button states */ ++ u8 *btn_rec_data; /* button diff count data */ ++ u8 *xy_mode; /* operational mode and status regs */ ++ u8 *xy_data; /* operational touch regs */ ++}; ++ ++struct cyttsp4_mt_data { ++ struct cyttsp4_mt_platform_data *pdata; ++ struct cyttsp4_sysinfo *si; ++ struct input_dev *input; ++ struct mutex report_lock; ++ bool is_suspended; ++ char phys[NAME_MAX]; ++ int num_prv_tch; ++}; ++ ++struct cyttsp4 { ++ struct device *dev; ++ struct mutex system_lock; ++ struct mutex adap_lock; ++ enum cyttsp4_mode mode; ++ enum cyttsp4_sleep_state sleep_state; ++ enum cyttsp4_startup_state startup_state; ++ int int_status; ++ wait_queue_head_t wait_q; ++ int irq; ++ struct work_struct startup_work; ++ struct work_struct watchdog_work; ++ struct timer_list watchdog_timer; ++ struct cyttsp4_sysinfo sysinfo; ++ void *exclusive_dev; ++ int exclusive_waits; ++ atomic_t ignore_irq; ++ bool invalid_touch_app; ++ struct cyttsp4_mt_data md; ++ struct cyttsp4_platform_data *pdata; ++ struct cyttsp4_core_platform_data *cpdata; ++ const struct cyttsp4_bus_ops *bus_ops; ++ u8 *xfer_buf; ++#ifdef VERBOSE_DEBUG ++ u8 pr_buf[CY_MAX_PRBUF_SIZE]; ++#endif ++}; ++ ++struct cyttsp4_bus_ops { ++ u16 bustype; ++ int (*write)(struct device *dev, u8 *xfer_buf, u16 addr, u8 length, ++ const void *values); ++ int (*read)(struct device *dev, u8 *xfer_buf, u16 addr, u8 length, ++ void *values); ++}; ++ ++enum cyttsp4_hst_mode_bits { ++ CY_HST_TOGGLE = (1 << 7), ++ CY_HST_MODE_CHANGE = (1 << 3), ++ CY_HST_MODE = (7 << 4), ++ CY_HST_OPERATE = (0 << 4), ++ CY_HST_SYSINFO = (1 << 4), ++ CY_HST_CAT = (2 << 4), ++ CY_HST_LOWPOW = (1 << 2), ++ CY_HST_SLEEP = (1 << 1), ++ CY_HST_RESET = (1 << 0), ++}; ++ ++/* abs settings */ ++#define CY_IGNORE_VALUE 0xFFFF ++ ++/* abs signal capabilities offsets in the frameworks array */ ++enum cyttsp4_sig_caps { ++ CY_SIGNAL_OST, ++ CY_MIN_OST, ++ CY_MAX_OST, ++ CY_FUZZ_OST, ++ CY_FLAT_OST, ++ CY_NUM_ABS_SET /* number of signal capability fields */ ++}; ++ ++/* abs axis signal offsets in the framworks array */ ++enum cyttsp4_sig_ost { ++ CY_ABS_X_OST, ++ CY_ABS_Y_OST, ++ CY_ABS_P_OST, ++ CY_ABS_W_OST, ++ CY_ABS_ID_OST, ++ CY_ABS_MAJ_OST, ++ CY_ABS_MIN_OST, ++ CY_ABS_OR_OST, ++ CY_NUM_ABS_OST /* number of abs signals */ ++}; ++ ++enum cyttsp4_flags { ++ CY_FLAG_NONE = 0x00, ++ CY_FLAG_HOVER = 0x04, ++ CY_FLAG_FLIP = 0x08, ++ CY_FLAG_INV_X = 0x10, ++ CY_FLAG_INV_Y = 0x20, ++ CY_FLAG_VKEYS = 0x40, ++}; ++ ++enum cyttsp4_object_id { ++ CY_OBJ_STANDARD_FINGER, ++ CY_OBJ_LARGE_OBJECT, ++ CY_OBJ_STYLUS, ++ CY_OBJ_HOVER, ++}; ++ ++enum cyttsp4_event_id { ++ CY_EV_NO_EVENT, ++ CY_EV_TOUCHDOWN, ++ CY_EV_MOVE, /* significant displacement (> act dist) */ ++ CY_EV_LIFTOFF, /* record reports last position */ ++}; ++ ++/* x-axis resolution of panel in pixels */ ++#define CY_PCFG_RESOLUTION_X_MASK 0x7F ++ ++/* y-axis resolution of panel in pixels */ ++#define CY_PCFG_RESOLUTION_Y_MASK 0x7F ++ ++/* x-axis, 0:origin is on left side of panel, 1: right */ ++#define CY_PCFG_ORIGIN_X_MASK 0x80 ++ ++/* y-axis, 0:origin is on top side of panel, 1: bottom */ ++#define CY_PCFG_ORIGIN_Y_MASK 0x80 ++ ++static inline int cyttsp4_adap_read(struct cyttsp4 *ts, u16 addr, int size, ++ void *buf) ++{ ++ return ts->bus_ops->read(ts->dev, ts->xfer_buf, addr, size, buf); ++} ++ ++static inline int cyttsp4_adap_write(struct cyttsp4 *ts, u16 addr, int size, ++ const void *buf) ++{ ++ return ts->bus_ops->write(ts->dev, ts->xfer_buf, addr, size, buf); ++} ++ ++extern struct cyttsp4 *cyttsp4_probe(const struct cyttsp4_bus_ops *ops, ++ struct device *dev, u16 irq, size_t xfer_buf_size); ++extern int cyttsp4_remove(struct cyttsp4 *ts); ++int cyttsp_i2c_write_block_data(struct device *dev, u8 *xfer_buf, u16 addr, ++ u8 length, const void *values); ++int cyttsp_i2c_read_block_data(struct device *dev, u8 *xfer_buf, u16 addr, ++ u8 length, void *values); ++extern const struct dev_pm_ops cyttsp4_pm_ops; ++ ++#endif /* _LINUX_CYTTSP4_CORE_H */ +diff --git a/drivers/input/touchscreen/cyttsp4_i2c.c b/drivers/input/touchscreen/cyttsp4_i2c.c +new file mode 100644 +index 000000000000..da32c151def5 +--- /dev/null ++++ b/drivers/input/touchscreen/cyttsp4_i2c.c +@@ -0,0 +1,72 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * cyttsp_i2c.c ++ * Cypress TrueTouch(TM) Standard Product (TTSP) I2C touchscreen driver. ++ * For use with Cypress Txx4xx parts. ++ * Supported parts include: ++ * TMA4XX ++ * TMA1036 ++ * ++ * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. ++ * Copyright (C) 2012 Javier Martinez Canillas ++ * Copyright (C) 2013 Cypress Semiconductor ++ * ++ * Contact Cypress Semiconductor at www.cypress.com ++ */ ++ ++#include "cyttsp4_core.h" ++ ++#include ++#include ++ ++#define CYTTSP4_I2C_DATA_SIZE (3 * 256) ++ ++static const struct cyttsp4_bus_ops cyttsp4_i2c_bus_ops = { ++ .bustype = BUS_I2C, ++ .write = cyttsp_i2c_write_block_data, ++ .read = cyttsp_i2c_read_block_data, ++}; ++ ++static int cyttsp4_i2c_probe(struct i2c_client *client) ++{ ++ struct cyttsp4 *ts; ++ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { ++ dev_err(&client->dev, "I2C functionality not Supported\n"); ++ return -EIO; ++ } ++ ++ ts = cyttsp4_probe(&cyttsp4_i2c_bus_ops, &client->dev, client->irq, ++ CYTTSP4_I2C_DATA_SIZE); ++ ++ return PTR_ERR_OR_ZERO(ts); ++} ++ ++static void cyttsp4_i2c_remove(struct i2c_client *client) ++{ ++ struct cyttsp4 *ts = i2c_get_clientdata(client); ++ ++ cyttsp4_remove(ts); ++} ++ ++static const struct i2c_device_id cyttsp4_i2c_id[] = { ++ { CYTTSP4_I2C_NAME }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, cyttsp4_i2c_id); ++ ++static struct i2c_driver cyttsp4_i2c_driver = { ++ .driver = { ++ .name = CYTTSP4_I2C_NAME, ++ .pm = pm_ptr(&cyttsp4_pm_ops), ++ }, ++ .probe = cyttsp4_i2c_probe, ++ .remove = cyttsp4_i2c_remove, ++ .id_table = cyttsp4_i2c_id, ++}; ++ ++module_i2c_driver(cyttsp4_i2c_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) I2C driver"); ++MODULE_AUTHOR("Cypress"); +diff --git a/drivers/input/touchscreen/cyttsp4_spi.c b/drivers/input/touchscreen/cyttsp4_spi.c +new file mode 100644 +index 000000000000..944fbbe9113e +--- /dev/null ++++ b/drivers/input/touchscreen/cyttsp4_spi.c +@@ -0,0 +1,187 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Source for: ++ * Cypress TrueTouch(TM) Standard Product (TTSP) SPI touchscreen driver. ++ * For use with Cypress Txx4xx parts. ++ * Supported parts include: ++ * TMA4XX ++ * TMA1036 ++ * ++ * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. ++ * Copyright (C) 2012 Javier Martinez Canillas ++ * Copyright (C) 2013 Cypress Semiconductor ++ * ++ * Contact Cypress Semiconductor at www.cypress.com ++ */ ++ ++#include "cyttsp4_core.h" ++ ++#include ++#include ++#include ++ ++#define CY_SPI_WR_OP 0x00 /* r/~w */ ++#define CY_SPI_RD_OP 0x01 ++#define CY_SPI_BITS_PER_WORD 8 ++#define CY_SPI_A8_BIT 0x02 ++#define CY_SPI_WR_HEADER_BYTES 2 ++#define CY_SPI_RD_HEADER_BYTES 1 ++#define CY_SPI_CMD_BYTES 2 ++#define CY_SPI_SYNC_BYTE 0 ++#define CY_SPI_SYNC_ACK 0x62 /* from TRM *A protocol */ ++#define CY_SPI_DATA_SIZE (2 * 256) ++ ++#define CY_SPI_DATA_BUF_SIZE (CY_SPI_CMD_BYTES + CY_SPI_DATA_SIZE) ++ ++static int cyttsp_spi_xfer(struct device *dev, u8 *xfer_buf, ++ u8 op, u16 reg, u8 *buf, int length) ++{ ++ struct spi_device *spi = to_spi_device(dev); ++ struct spi_message msg; ++ struct spi_transfer xfer[2]; ++ u8 *wr_buf = &xfer_buf[0]; ++ u8 rd_buf[CY_SPI_CMD_BYTES]; ++ int retval; ++ int i; ++ ++ if (length > CY_SPI_DATA_SIZE) { ++ dev_err(dev, "%s: length %d is too big.\n", ++ __func__, length); ++ return -EINVAL; ++ } ++ ++ memset(wr_buf, 0, CY_SPI_DATA_BUF_SIZE); ++ memset(rd_buf, 0, CY_SPI_CMD_BYTES); ++ ++ wr_buf[0] = op + (((reg >> 8) & 0x1) ? CY_SPI_A8_BIT : 0); ++ if (op == CY_SPI_WR_OP) { ++ wr_buf[1] = reg & 0xFF; ++ if (length > 0) ++ memcpy(wr_buf + CY_SPI_CMD_BYTES, buf, length); ++ } ++ ++ memset(xfer, 0, sizeof(xfer)); ++ spi_message_init(&msg); ++ ++ /* ++ We set both TX and RX buffers because Cypress TTSP ++ requires full duplex operation. ++ */ ++ xfer[0].tx_buf = wr_buf; ++ xfer[0].rx_buf = rd_buf; ++ switch (op) { ++ case CY_SPI_WR_OP: ++ xfer[0].len = length + CY_SPI_CMD_BYTES; ++ spi_message_add_tail(&xfer[0], &msg); ++ break; ++ ++ case CY_SPI_RD_OP: ++ xfer[0].len = CY_SPI_RD_HEADER_BYTES; ++ spi_message_add_tail(&xfer[0], &msg); ++ ++ xfer[1].rx_buf = buf; ++ xfer[1].len = length; ++ spi_message_add_tail(&xfer[1], &msg); ++ break; ++ ++ default: ++ dev_err(dev, "%s: bad operation code=%d\n", __func__, op); ++ return -EINVAL; ++ } ++ ++ retval = spi_sync(spi, &msg); ++ if (retval < 0) { ++ dev_dbg(dev, "%s: spi_sync() error %d, len=%d, op=%d\n", ++ __func__, retval, xfer[1].len, op); ++ ++ /* ++ * do not return here since was a bad ACK sequence ++ * let the following ACK check handle any errors and ++ * allow silent retries ++ */ ++ } ++ ++ if (rd_buf[CY_SPI_SYNC_BYTE] != CY_SPI_SYNC_ACK) { ++ dev_dbg(dev, "%s: operation %d failed\n", __func__, op); ++ ++ for (i = 0; i < CY_SPI_CMD_BYTES; i++) ++ dev_dbg(dev, "%s: test rd_buf[%d]:0x%02x\n", ++ __func__, i, rd_buf[i]); ++ for (i = 0; i < length; i++) ++ dev_dbg(dev, "%s: test buf[%d]:0x%02x\n", ++ __func__, i, buf[i]); ++ ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++static int cyttsp_spi_read_block_data(struct device *dev, u8 *xfer_buf, ++ u16 addr, u8 length, void *data) ++{ ++ int rc; ++ ++ rc = cyttsp_spi_xfer(dev, xfer_buf, CY_SPI_WR_OP, addr, NULL, 0); ++ if (rc) ++ return rc; ++ else ++ return cyttsp_spi_xfer(dev, xfer_buf, CY_SPI_RD_OP, addr, data, ++ length); ++} ++ ++static int cyttsp_spi_write_block_data(struct device *dev, u8 *xfer_buf, ++ u16 addr, u8 length, const void *data) ++{ ++ return cyttsp_spi_xfer(dev, xfer_buf, CY_SPI_WR_OP, addr, (void *)data, ++ length); ++} ++ ++static const struct cyttsp4_bus_ops cyttsp_spi_bus_ops = { ++ .bustype = BUS_SPI, ++ .write = cyttsp_spi_write_block_data, ++ .read = cyttsp_spi_read_block_data, ++}; ++ ++static int cyttsp4_spi_probe(struct spi_device *spi) ++{ ++ struct cyttsp4 *ts; ++ int error; ++ ++ /* Set up SPI*/ ++ spi->bits_per_word = CY_SPI_BITS_PER_WORD; ++ spi->mode = SPI_MODE_0; ++ error = spi_setup(spi); ++ if (error < 0) { ++ dev_err(&spi->dev, "%s: SPI setup error %d\n", ++ __func__, error); ++ return error; ++ } ++ ++ ts = cyttsp4_probe(&cyttsp_spi_bus_ops, &spi->dev, spi->irq, ++ CY_SPI_DATA_BUF_SIZE); ++ ++ return PTR_ERR_OR_ZERO(ts); ++} ++ ++static void cyttsp4_spi_remove(struct spi_device *spi) ++{ ++ struct cyttsp4 *ts = spi_get_drvdata(spi); ++ cyttsp4_remove(ts); ++} ++ ++static struct spi_driver cyttsp4_spi_driver = { ++ .driver = { ++ .name = CYTTSP4_SPI_NAME, ++ .pm = pm_ptr(&cyttsp4_pm_ops), ++ }, ++ .probe = cyttsp4_spi_probe, ++ .remove = cyttsp4_spi_remove, ++}; ++ ++module_spi_driver(cyttsp4_spi_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) SPI driver"); ++MODULE_AUTHOR("Cypress"); ++MODULE_ALIAS("spi:cyttsp4"); +diff --git a/drivers/input/touchscreen/cyttsp_core.h b/drivers/input/touchscreen/cyttsp_core.h +index 40a605d20285..cd3b80401970 100644 +--- a/drivers/input/touchscreen/cyttsp_core.h ++++ b/drivers/input/touchscreen/cyttsp_core.h +@@ -136,6 +136,10 @@ struct cyttsp { + struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops, + struct device *dev, int irq, size_t xfer_buf_size); + ++int cyttsp_i2c_write_block_data(struct device *dev, u8 *xfer_buf, u16 addr, ++ u8 length, const void *values); ++int cyttsp_i2c_read_block_data(struct device *dev, u8 *xfer_buf, u16 addr, ++ u8 length, void *values); + extern const struct dev_pm_ops cyttsp_pm_ops; + + #endif /* __CYTTSP_CORE_H__ */ +diff --git a/drivers/input/touchscreen/cyttsp_i2c.c b/drivers/input/touchscreen/cyttsp_i2c.c +index cb15600549cd..bf13b3448a6b 100644 +--- a/drivers/input/touchscreen/cyttsp_i2c.c ++++ b/drivers/input/touchscreen/cyttsp_i2c.c +@@ -22,61 +22,6 @@ + + #define CY_I2C_DATA_SIZE 128 + +-static int cyttsp_i2c_read_block_data(struct device *dev, u8 *xfer_buf, +- u16 addr, u8 length, void *values) +-{ +- struct i2c_client *client = to_i2c_client(dev); +- u8 client_addr = client->addr | ((addr >> 8) & 0x1); +- u8 addr_lo = addr & 0xFF; +- struct i2c_msg msgs[] = { +- { +- .addr = client_addr, +- .flags = 0, +- .len = 1, +- .buf = &addr_lo, +- }, +- { +- .addr = client_addr, +- .flags = I2C_M_RD, +- .len = length, +- .buf = values, +- }, +- }; +- int retval; +- +- retval = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); +- if (retval < 0) +- return retval; +- +- return retval != ARRAY_SIZE(msgs) ? -EIO : 0; +-} +- +-static int cyttsp_i2c_write_block_data(struct device *dev, u8 *xfer_buf, +- u16 addr, u8 length, const void *values) +-{ +- struct i2c_client *client = to_i2c_client(dev); +- u8 client_addr = client->addr | ((addr >> 8) & 0x1); +- u8 addr_lo = addr & 0xFF; +- struct i2c_msg msgs[] = { +- { +- .addr = client_addr, +- .flags = 0, +- .len = length + 1, +- .buf = xfer_buf, +- }, +- }; +- int retval; +- +- xfer_buf[0] = addr_lo; +- memcpy(&xfer_buf[1], values, length); +- +- retval = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); +- if (retval < 0) +- return retval; +- +- return retval != ARRAY_SIZE(msgs) ? -EIO : 0; +-} +- + static const struct cyttsp_bus_ops cyttsp_i2c_bus_ops = { + .bustype = BUS_I2C, + .write = cyttsp_i2c_write_block_data, +diff --git a/drivers/input/touchscreen/cyttsp_i2c_common.c b/drivers/input/touchscreen/cyttsp_i2c_common.c +new file mode 100644 +index 000000000000..7e752fb9fad7 +--- /dev/null ++++ b/drivers/input/touchscreen/cyttsp_i2c_common.c +@@ -0,0 +1,86 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * cyttsp_i2c_common.c ++ * Cypress TrueTouch(TM) Standard Product (TTSP) I2C touchscreen driver. ++ * For use with Cypress Txx3xx and Txx4xx parts. ++ * Supported parts include: ++ * CY8CTST341 ++ * CY8CTMA340 ++ * TMA4XX ++ * TMA1036 ++ * ++ * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. ++ * Copyright (C) 2012 Javier Martinez Canillas ++ * ++ * Contact Cypress Semiconductor at www.cypress.com ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "cyttsp4_core.h" ++ ++int cyttsp_i2c_read_block_data(struct device *dev, u8 *xfer_buf, ++ u16 addr, u8 length, void *values) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ u8 client_addr = client->addr | ((addr >> 8) & 0x1); ++ u8 addr_lo = addr & 0xFF; ++ struct i2c_msg msgs[] = { ++ { ++ .addr = client_addr, ++ .flags = 0, ++ .len = 1, ++ .buf = &addr_lo, ++ }, ++ { ++ .addr = client_addr, ++ .flags = I2C_M_RD, ++ .len = length, ++ .buf = values, ++ }, ++ }; ++ int retval; ++ ++ retval = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); ++ if (retval < 0) ++ return retval; ++ ++ return retval != ARRAY_SIZE(msgs) ? -EIO : 0; ++} ++EXPORT_SYMBOL_GPL(cyttsp_i2c_read_block_data); ++ ++int cyttsp_i2c_write_block_data(struct device *dev, u8 *xfer_buf, ++ u16 addr, u8 length, const void *values) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ u8 client_addr = client->addr | ((addr >> 8) & 0x1); ++ u8 addr_lo = addr & 0xFF; ++ struct i2c_msg msgs[] = { ++ { ++ .addr = client_addr, ++ .flags = 0, ++ .len = length + 1, ++ .buf = xfer_buf, ++ }, ++ }; ++ int retval; ++ ++ xfer_buf[0] = addr_lo; ++ memcpy(&xfer_buf[1], values, length); ++ ++ retval = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); ++ if (retval < 0) ++ return retval; ++ ++ return retval != ARRAY_SIZE(msgs) ? -EIO : 0; ++} ++EXPORT_SYMBOL_GPL(cyttsp_i2c_write_block_data); ++ ++ ++MODULE_DESCRIPTION("Cypress TrueTouch(TM) Standard Product (TTSP) I2C touchscreen driver"); ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Cypress"); +diff --git a/include/linux/platform_data/cyttsp4.h b/include/linux/platform_data/cyttsp4.h +new file mode 100644 +index 000000000000..5dc9d2be384b +--- /dev/null ++++ b/include/linux/platform_data/cyttsp4.h +@@ -0,0 +1,62 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++/* ++ * Header file for: ++ * Cypress TrueTouch(TM) Standard Product (TTSP) touchscreen drivers. ++ * For use with Cypress Txx3xx parts. ++ * Supported parts include: ++ * CY8CTST341 ++ * CY8CTMA340 ++ * ++ * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. ++ * Copyright (C) 2012 Javier Martinez Canillas ++ * ++ * Contact Cypress Semiconductor at www.cypress.com (kev@cypress.com) ++ */ ++#ifndef _CYTTSP4_H_ ++#define _CYTTSP4_H_ ++ ++#define CYTTSP4_MT_NAME "cyttsp4_mt" ++#define CYTTSP4_I2C_NAME "cyttsp4_i2c_adapter" ++#define CYTTSP4_SPI_NAME "cyttsp4_spi_adapter" ++ ++#define CY_TOUCH_SETTINGS_MAX 32 ++ ++struct touch_framework { ++ const uint16_t *abs; ++ uint8_t size; ++ uint8_t enable_vkeys; ++} __packed; ++ ++struct cyttsp4_mt_platform_data { ++ struct touch_framework *frmwrk; ++ unsigned short flags; ++ char const *inp_dev_name; ++}; ++ ++struct touch_settings { ++ const uint8_t *data; ++ uint32_t size; ++ uint8_t tag; ++} __packed; ++ ++struct cyttsp4_core_platform_data { ++ int irq_gpio; ++ int rst_gpio; ++ int level_irq_udelay; ++ int (*xres)(struct cyttsp4_core_platform_data *pdata, ++ struct device *dev); ++ int (*init)(struct cyttsp4_core_platform_data *pdata, ++ int on, struct device *dev); ++ int (*power)(struct cyttsp4_core_platform_data *pdata, ++ int on, struct device *dev, atomic_t *ignore_irq); ++ int (*irq_stat)(struct cyttsp4_core_platform_data *pdata, ++ struct device *dev); ++ struct touch_settings *sett[CY_TOUCH_SETTINGS_MAX]; ++}; ++ ++struct cyttsp4_platform_data { ++ struct cyttsp4_core_platform_data *core_pdata; ++ struct cyttsp4_mt_platform_data *mt_pdata; ++}; ++ ++#endif /* _CYTTSP4_H_ */ +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/Revert-drm-sun4i-lvds-Invert-the-LVDS-polarity.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/Revert-drm-sun4i-lvds-Invert-the-LVDS-polarity.patch new file mode 100644 index 000000000000..4c49a1575cec --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/Revert-drm-sun4i-lvds-Invert-the-LVDS-polarity.patch @@ -0,0 +1,28 @@ +From 150ddcb209012261b0a63d80ae9481134105e04a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sun, 1 Nov 2020 02:57:38 +0100 +Subject: Revert "drm/sun4i: lvds: Invert the LVDS polarity" + +This reverts commit 3bc46a083fa9f475dd2e0ab8717d9839b070b8d9. +--- + drivers/gpu/drm/sun4i/sun4i_tcon.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c +index 960e83c8291d..fca95b76e258 100644 +--- a/drivers/gpu/drm/sun4i/sun4i_tcon.c ++++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c +@@ -476,7 +476,9 @@ static void sun4i_tcon0_mode_set_lvds(struct sun4i_tcon *tcon, + SUN4I_TCON0_BASIC2_V_TOTAL(mode->crtc_vtotal * 2) | + SUN4I_TCON0_BASIC2_V_BACKPORCH(bp)); + +- reg = SUN4I_TCON0_LVDS_IF_CLK_SEL_TCON0; ++ reg = SUN4I_TCON0_LVDS_IF_CLK_SEL_TCON0 | ++ SUN4I_TCON0_LVDS_IF_DATA_POL_NORMAL | ++ SUN4I_TCON0_LVDS_IF_CLK_POL_NORMAL; + if (sun4i_tcon_get_pixel_depth(encoder) == 24) + reg |= SUN4I_TCON0_LVDS_IF_BITWIDTH_24BITS; + else +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/Revert-usb-typec-tcpm-unregister-existing-source-caps-before-re.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/Revert-usb-typec-tcpm-unregister-existing-source-caps-before-re.patch new file mode 100644 index 000000000000..cfbff1c58939 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/Revert-usb-typec-tcpm-unregister-existing-source-caps-before-re.patch @@ -0,0 +1,39 @@ +From 4328071664e452dc5ceff356343a04b4f399c118 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Tue, 7 May 2024 18:39:12 +0200 +Subject: Revert "usb: typec: tcpm: unregister existing source caps before + re-registration" + +This reverts commit 230ecdf71a644c9c73e0e6735b33173074ae3f94. +--- + drivers/usb/typec/tcpm/tcpm.c | 7 +------ + 1 file changed, 1 insertion(+), 6 deletions(-) + +diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c +index 62ca4a0ec55b..2c92996954b2 100644 +--- a/drivers/usb/typec/tcpm/tcpm.c ++++ b/drivers/usb/typec/tcpm/tcpm.c +@@ -3053,7 +3053,7 @@ static int tcpm_register_source_caps(struct tcpm_port *port) + { + struct usb_power_delivery_desc desc = { port->negotiated_rev }; + struct usb_power_delivery_capabilities_desc caps = { }; +- struct usb_power_delivery_capabilities *cap = port->partner_source_caps; ++ struct usb_power_delivery_capabilities *cap; + + if (!port->partner_pd) + port->partner_pd = usb_power_delivery_register(NULL, &desc); +@@ -3063,11 +3063,6 @@ static int tcpm_register_source_caps(struct tcpm_port *port) + memcpy(caps.pdo, port->source_caps, sizeof(u32) * port->nr_source_caps); + caps.role = TYPEC_SOURCE; + +- if (cap) { +- usb_power_delivery_unregister_capabilities(cap); +- port->partner_source_caps = NULL; +- } +- + cap = usb_power_delivery_register_capabilities(port->partner_pd, &caps); + if (IS_ERR(cap)) + return PTR_ERR(cap); +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-allwinner-dts-a64-enable-K101-IM2BYL02-panel-for-PineTab.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-allwinner-dts-a64-enable-K101-IM2BYL02-panel-for-PineTab.patch new file mode 100644 index 000000000000..7c22387b4693 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-allwinner-dts-a64-enable-K101-IM2BYL02-panel-for-PineTab.patch @@ -0,0 +1,35 @@ +From eff7988fc46688c551cb1d18bef171cf3243905d Mon Sep 17 00:00:00 2001 +From: Icenowy Zheng +Date: Mon, 20 Jul 2020 01:11:34 +0800 +Subject: arm64: allwinner: dts: a64: enable K101-IM2BYL02 panel for PineTab + +Newer PineTab may switch to K101-IM2BYL02. + +Signed-off-by: Icenowy Zheng +--- + arch/arm64/boot/dts/allwinner/sun50i-a64-pinetab.dts | 8 +++----- + 1 file changed, 3 insertions(+), 5 deletions(-) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinetab.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinetab.dts +index 50a0e1916743..6056c965baf6 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinetab.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinetab.dts +@@ -200,12 +200,10 @@ &dsi { + status = "okay"; + + panel@0 { +- compatible = "feixin,k101-im2ba02"; ++ compatible = "feixin,k101-im2byl02"; + reg = <0>; +- avdd-supply = <®_dc1sw>; +- dvdd-supply = <®_dc1sw>; +- cvdd-supply = <®_ldo_io1>; +- reset-gpios = <&pio 3 24 GPIO_ACTIVE_HIGH>; /* PD24 */ ++ power-supply = <®_dc1sw>; ++ reset-gpios = <&pio 3 24 GPIO_ACTIVE_LOW>; /* PD24 */ + backlight = <&backlight>; + }; + }; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-allwinner-Enforce-consistent-MMC-numbering.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-allwinner-Enforce-consistent-MMC-numbering.patch new file mode 100644 index 000000000000..e15c1e45cda1 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-allwinner-Enforce-consistent-MMC-numbering.patch @@ -0,0 +1,66 @@ +From add1775ff68e073feb7cc5e3ada49826633c256c Mon Sep 17 00:00:00 2001 +From: Samuel Holland +Date: Sun, 6 Dec 2020 11:15:34 -0600 +Subject: arm64: dts: allwinner: Enforce consistent MMC numbering + +Signed-off-by: Samuel Holland +--- + arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 6 ++++++ + arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi | 6 ++++++ + arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi | 6 ++++++ + 3 files changed, 18 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi +index c061387abe41..c4272550580c 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi +@@ -18,6 +18,12 @@ / { + #address-cells = <1>; + #size-cells = <1>; + ++ aliases { ++ mmc0 = &mmc0; ++ mmc1 = &mmc1; ++ mmc2 = &mmc2; ++ }; ++ + chosen { + #address-cells = <1>; + #size-cells = <1>; +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi +index a6a2e2a19e75..080c3d10fdfa 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi +@@ -6,6 +6,12 @@ + #include + + / { ++ aliases { ++ mmc0 = &mmc0; ++ mmc1 = &mmc1; ++ mmc2 = &mmc2; ++ }; ++ + cpus { + #address-cells = <1>; + #size-cells = <0>; +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi +index c4e35a255643..1596ff1edce4 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi +@@ -17,6 +17,12 @@ / { + #address-cells = <1>; + #size-cells = <1>; + ++ aliases { ++ mmc0 = &mmc0; ++ mmc1 = &mmc1; ++ mmc2 = &mmc2; ++ }; ++ + cpus { + #address-cells = <1>; + #size-cells = <0>; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-allwinner-a64-Add-hdmi-sound-card.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-allwinner-a64-Add-hdmi-sound-card.patch new file mode 100644 index 000000000000..a26e4b20dbd0 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-allwinner-a64-Add-hdmi-sound-card.patch @@ -0,0 +1,48 @@ +From 6129debfdc7530a3ac08ad6486d4008b7aa7c3ab Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sun, 9 May 2021 10:39:56 +0200 +Subject: arm64: dts: allwinner: a64: Add hdmi sound card + +A64 supports HDMI audio. Add a sound card node for it. + +Signed-off-by: Jernej Skrabec +--- + arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi +index 0fecf0abb204..9a43bcc2f489 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi +@@ -196,6 +196,20 @@ link0_codec: codec { + }; + }; + ++ sound_hdmi: sound_hdmi { ++ compatible = "allwinner,sun9i-a80-hdmi-audio", ++ "allwinner,sun50i-a64-hdmi-audio"; ++ status = "disabled"; ++ ++ codec { ++ sound-dai = <&hdmi>; ++ }; ++ ++ cpu { ++ sound-dai = <&i2s2>; ++ }; ++ }; ++ + timer { + compatible = "arm,armv8-timer"; + allwinner,erratum-unknown1; +@@ -1261,6 +1275,7 @@ deinterlace: deinterlace@1e00000 { + }; + + hdmi: hdmi@1ee0000 { ++ #sound-dai-cells = <0>; + compatible = "allwinner,sun50i-a64-dw-hdmi", + "allwinner,sun8i-a83t-dw-hdmi"; + reg = <0x01ee0000 0x10000>; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-allwinner-a64-Enable-hdmi-sound-card-on-boards-with-h.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-allwinner-a64-Enable-hdmi-sound-card-on-boards-with-h.patch new file mode 100644 index 000000000000..3cd1913aa740 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-allwinner-a64-Enable-hdmi-sound-card-on-boards-with-h.patch @@ -0,0 +1,204 @@ +From 12b313d4d8ec2709665fd56a97d28fee9dbd5207 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sun, 9 May 2021 10:42:16 +0200 +Subject: arm64: dts: allwinner: a64: Enable hdmi sound card on boards with + hdmi + +Each board that has HDMI connector can also transmit audio through it. +Enable HDMI sound card on all A64 boards with HDMI connector. + +Signed-off-by: Jernej Skrabec +--- + arch/arm64/boot/dts/allwinner/sun50i-a64-bananapi-m64.dts | 8 ++++++++ + arch/arm64/boot/dts/allwinner/sun50i-a64-nanopi-a64.dts | 8 ++++++++ + arch/arm64/boot/dts/allwinner/sun50i-a64-olinuxino.dts | 7 +++++++ + arch/arm64/boot/dts/allwinner/sun50i-a64-orangepi-win.dts | 8 ++++++++ + arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts | 8 ++++++++ + .../boot/dts/allwinner/sun50i-a64-sopine-baseboard.dts | 8 ++++++++ + arch/arm64/boot/dts/allwinner/sun50i-a64-teres-i.dts | 8 ++++++++ + 7 files changed, 55 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-bananapi-m64.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-bananapi-m64.dts +index d1f415acd7b5..0b68888c9eef 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-bananapi-m64.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-bananapi-m64.dts +@@ -130,6 +130,10 @@ &i2c1_pins { + bias-pull-up; + }; + ++&i2s2 { ++ status = "okay"; ++}; ++ + &mdio { + ext_rgmii_phy: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; +@@ -341,6 +345,10 @@ &sound { + "MIC1", "Onboard Microphone"; + }; + ++&sound_hdmi { ++ status = "okay"; ++}; ++ + &uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_pb_pins>; +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-nanopi-a64.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-nanopi-a64.dts +index dec9960a7440..b6142224c329 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-nanopi-a64.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-nanopi-a64.dts +@@ -102,6 +102,10 @@ &i2c1_pins { + bias-pull-up; + }; + ++&i2s2 { ++ status = "okay"; ++}; ++ + &mdio { + ext_rgmii_phy: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; +@@ -252,6 +256,10 @@ &simplefb_hdmi { + vcc-hdmi-supply = <®_dldo1>; + }; + ++&sound_hdmi { ++ status = "okay"; ++}; ++ + &uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_pb_pins>; +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-olinuxino.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-olinuxino.dts +index fd3794678c33..0cc2e11dcb92 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-olinuxino.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-olinuxino.dts +@@ -120,6 +120,10 @@ hdmi_out_con: endpoint { + }; + }; + ++&i2s2 { ++ status = "okay"; ++}; ++ + &mdio { + ext_rgmii_phy: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; +@@ -355,6 +359,9 @@ &sound { + "MIC1", "Microphone Jack Left", + "Microphone Jack Right", "MBIAS", + "MIC2", "Microphone Jack Right"; ++}; ++ ++&sound_hdmi { + status = "okay"; + }; + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-orangepi-win.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-orangepi-win.dts +index c8303a66438d..9194ca4b1e19 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-orangepi-win.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-orangepi-win.dts +@@ -137,6 +137,10 @@ hdmi_out_con: endpoint { + }; + }; + ++&i2s2 { ++ status = "okay"; ++}; ++ + &mdio { + ext_rgmii_phy: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; +@@ -340,6 +344,10 @@ &sound { + "MIC1", "Onboard Microphone"; + }; + ++&sound_hdmi { ++ status = "okay"; ++}; ++ + &spi0 { + status = "okay"; + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts +index 09e71fd60785..51bedd34fc25 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts +@@ -107,6 +107,10 @@ &i2c1_pins { + bias-pull-up; + }; + ++&i2s2 { ++ status = "okay"; ++}; ++ + &mdio { + ext_rmii_phy1: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; +@@ -270,6 +274,10 @@ &sound { + status = "okay"; + }; + ++&sound_hdmi { ++ status = "okay"; ++}; ++ + /* On Euler connector */ + &spdif { + status = "disabled"; +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-sopine-baseboard.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-sopine-baseboard.dts +index be2347c8f267..70c7f8ff49ad 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-sopine-baseboard.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-sopine-baseboard.dts +@@ -96,6 +96,10 @@ hdmi_out_con: endpoint { + }; + }; + ++&i2s2 { ++ status = "okay"; ++}; ++ + &mdio { + ext_rgmii_phy: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; +@@ -169,6 +173,10 @@ &sound { + status = "okay"; + }; + ++&sound_hdmi { ++ status = "okay"; ++}; ++ + &uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_pb_pins>; +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-teres-i.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-teres-i.dts +index ec055510af8b..992dc947551d 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-teres-i.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-teres-i.dts +@@ -150,6 +150,10 @@ anx6345_in: endpoint { + }; + }; + ++&i2s2 { ++ status = "okay"; ++}; ++ + &mixer0 { + status = "okay"; + }; +@@ -366,6 +370,10 @@ &sound { + status = "okay"; + }; + ++&sound_hdmi { ++ status = "okay"; ++}; ++ + &tcon0 { + pinctrl-names = "default"; + pinctrl-0 = <&lcd_rgb666_pins>; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-allwinner-a64-Fix-LRADC-compatible.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-allwinner-a64-Fix-LRADC-compatible.patch new file mode 100644 index 000000000000..dbc1ee3adebe --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-allwinner-a64-Fix-LRADC-compatible.patch @@ -0,0 +1,32 @@ +From c675093587794fbf1de91fa2de45aea0c117b901 Mon Sep 17 00:00:00 2001 +From: Icenowy Zheng +Date: Sun, 30 Jun 2019 22:45:34 +0800 +Subject: arm64: dts: allwinner: a64: Fix LRADC compatible + +The LRADC reference voltage of A64 is 2.0V, and AVCC is 3.0V. So the +internal voltage divider is 2/3 (the A10 value), not 3/4 (the A83T +value). + +Fix the compatible string of the A64 LRADC. + +Signed-off-by: Icenowy Zheng +--- + arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi +index d53a6f7e086a..c061387abe41 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi +@@ -921,7 +921,7 @@ spdif: spdif@1c21000 { + + lradc: lradc@1c21800 { + compatible = "allwinner,sun50i-a64-lradc", +- "allwinner,sun8i-a83t-r-lradc"; ++ "allwinner,sun4i-a10-lradc-keys"; + reg = <0x01c21800 0x400>; + interrupt-parent = <&r_intc>; + interrupts = ; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-allwinner-a64-pinetab-add-front-camera.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-allwinner-a64-pinetab-add-front-camera.patch new file mode 100644 index 000000000000..fc01a5a392fd --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-allwinner-a64-pinetab-add-front-camera.patch @@ -0,0 +1,99 @@ +From 572548e1235cc21b5be242b267fdc66984d6d3f8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Tue, 17 Nov 2020 01:06:30 +0100 +Subject: arm64: dts: allwinner: a64: pinetab: add front camera + +--- + .../boot/dts/allwinner/sun50i-a64-pinetab.dts | 50 ++++++++++++++++--- + 1 file changed, 44 insertions(+), 6 deletions(-) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinetab.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinetab.dts +index f5fb1ee32dad..50a0e1916743 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinetab.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinetab.dts +@@ -55,12 +55,35 @@ i2c-csi { + #address-cells = <1>; + #size-cells = <0>; + ++ /* Front camera */ ++ gc2145: front-camera@3c { ++ compatible = "galaxycore,gc2145"; ++ reg = <0x3c>; ++ clocks = <&ccu CLK_CSI_MCLK>; ++ clock-names = "xclk"; ++ ++ AVDD-supply = <®_dldo3>; ++ DVDD-supply = <®_aldo1>; ++ IOVDD-supply = <®_eldo3>; ++ reset-gpios = <&pio 4 16 (GPIO_ACTIVE_LOW | GPIO_OPEN_DRAIN)>; /* PE16 */ ++ enable-gpios = <&pio 4 17 (GPIO_ACTIVE_LOW | GPIO_OPEN_DRAIN)>; /* PE17 */ ++ ++ port { ++ gc2145_ep: endpoint { ++ remote-endpoint = <&csi_gc2145_ep>; ++ bus-width = <8>; ++ hsync-active = <1>; ++ vsync-active = <1>; ++ data-active = <1>; ++ pclk-sample = <1>; ++ }; ++ }; ++ }; ++ + /* Rear camera */ +- ov5640: camera@3c { ++ ov5640: rear-camera@4c { + compatible = "ovti,ov5640"; +- reg = <0x3c>; +- pinctrl-names = "default"; +- pinctrl-0 = <&csi_mclk_pin>; ++ reg = <0x4c>; + clocks = <&ccu CLK_CSI_MCLK>; + clock-names = "xclk"; + +@@ -72,7 +95,7 @@ ov5640: camera@3c { + + port { + ov5640_ep: endpoint { +- remote-endpoint = <&csi_ep>; ++ remote-endpoint = <&csi_ov5640_ep>; + bus-width = <8>; + hsync-active = <1>; /* Active high */ + vsync-active = <0>; /* Active low */ +@@ -131,10 +154,15 @@ &cpu3 { + }; + + &csi { ++ pinctrl-0 = <&csi_pins>, <&csi_mclk_pin>; + status = "okay"; + + port { +- csi_ep: endpoint { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ csi_ov5640_ep: endpoint@0 { ++ reg = <0>; + remote-endpoint = <&ov5640_ep>; + bus-width = <8>; + hsync-active = <1>; /* Active high */ +@@ -142,6 +170,16 @@ csi_ep: endpoint { + data-active = <1>; /* Active high */ + pclk-sample = <1>; /* Rising */ + }; ++ ++ csi_gc2145_ep: endpoint@1 { ++ reg = <1>; ++ remote-endpoint = <&gc2145_ep>; ++ bus-width=<8>; ++ hsync-active = <1>; /* Active high */ ++ vsync-active = <1>; /* Active high */ ++ data-active = <1>; /* Active high */ ++ pclk-sample = <1>; /* Rising */ ++ }; + }; + }; + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-allwinner-h5-Enable-hdmi-sound-card-on-boards-with-hd.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-allwinner-h5-Enable-hdmi-sound-card-on-boards-with-hd.patch new file mode 100644 index 000000000000..b085458fe29f --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-allwinner-h5-Enable-hdmi-sound-card-on-boards-with-hd.patch @@ -0,0 +1,113 @@ +From 7835fbd016daedfa548cdb6b9c4b9d663bc67c73 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sun, 9 May 2021 12:40:31 +0200 +Subject: arm64: dts: allwinner: h5: Enable hdmi sound card on boards with hdmi + +Each board that has HDMI connector can also transmit audio through it. +Enable HDMI sound card on all H5 boards with HDMI connector. + +Signed-off-by: Jernej Skrabec +--- + .../dts/allwinner/sun50i-h5-emlid-neutis-n5-devboard.dts | 8 ++++++++ + arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-pc2.dts | 8 ++++++++ + .../arm64/boot/dts/allwinner/sun50i-h5-orangepi-prime.dts | 8 ++++++++ + .../boot/dts/allwinner/sun50i-h5-orangepi-zero-plus2.dts | 8 ++++++++ + 4 files changed, 32 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h5-emlid-neutis-n5-devboard.dts b/arch/arm64/boot/dts/allwinner/sun50i-h5-emlid-neutis-n5-devboard.dts +index 076a0b983101..5882b62097a9 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-h5-emlid-neutis-n5-devboard.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-h5-emlid-neutis-n5-devboard.dts +@@ -69,3 +69,11 @@ hdmi_out_con: endpoint { + &i2c1 { + status = "okay"; + }; ++ ++&i2s2 { ++ status = "okay"; ++}; ++ ++&sound_hdmi { ++ status = "okay"; ++}; +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-pc2.dts b/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-pc2.dts +index 0f29da7d51e6..f420acc6f49f 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-pc2.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-pc2.dts +@@ -145,6 +145,10 @@ hdmi_out_con: endpoint { + }; + }; + ++&i2s2 { ++ status = "okay"; ++}; ++ + &ir { + pinctrl-names = "default"; + pinctrl-0 = <&r_ir_rx_pin>; +@@ -191,6 +195,10 @@ reg_vdd_cpux: regulator@65 { + }; + }; + ++&sound_hdmi { ++ status = "okay"; ++}; ++ + &spi0 { + status = "okay"; + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-prime.dts b/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-prime.dts +index d4fc4e60e4e7..42d0ffa4a864 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-prime.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-prime.dts +@@ -145,6 +145,10 @@ hdmi_out_con: endpoint { + }; + }; + ++&i2s2 { ++ status = "okay"; ++}; ++ + &ir { + pinctrl-names = "default"; + pinctrl-0 = <&r_ir_rx_pin>; +@@ -183,6 +187,10 @@ &ohci3 { + status = "okay"; + }; + ++&sound_hdmi { ++ status = "okay"; ++}; ++ + &uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_pa_pins>; +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-zero-plus2.dts b/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-zero-plus2.dts +index 3eb986c354a9..281d37d46d43 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-zero-plus2.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-zero-plus2.dts +@@ -77,6 +77,10 @@ hdmi_out_con: endpoint { + }; + }; + ++&i2s2 { ++ status = "okay"; ++}; ++ + &mmc0 { + vmmc-supply = <®_vcc3v3>; + disable-wp; +@@ -116,6 +120,10 @@ &ohci0 { + status = "okay"; + }; + ++&sound_hdmi { ++ status = "okay"; ++}; ++ + &uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_pa_pins>; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-allwinner-h6-Add-hdmi-sound-card.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-allwinner-h6-Add-hdmi-sound-card.patch new file mode 100644 index 000000000000..2589bd609691 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-allwinner-h6-Add-hdmi-sound-card.patch @@ -0,0 +1,56 @@ +From eedcdc30e7c22e62ce01296f6fefc63b1143e810 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Tue, 10 Nov 2020 20:43:28 +0100 +Subject: arm64: dts: allwinner: h6: Add hdmi sound card + +H6 supports HDMI audio. Add a sound card node for it. + +Signed-off-by: Jernej Skrabec +--- + arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi +index 2301c59b41b1..13fe21b00711 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi +@@ -126,6 +126,20 @@ psci { + method = "smc"; + }; + ++ sound_hdmi: sound_hdmi { ++ compatible = "allwinner,sun9i-a80-hdmi-audio", ++ "allwinner,sun50i-h6-hdmi-audio"; ++ status = "disabled"; ++ ++ codec { ++ sound-dai = <&hdmi>; ++ }; ++ ++ cpu { ++ sound-dai = <&i2s1>; ++ }; ++ }; ++ + timer { + compatible = "arm,armv8-timer"; + arm,no-tick-in-suspend; +@@ -139,6 +153,7 @@ timer { + (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>; + }; + ++ + soc { + compatible = "simple-bus"; + #address-cells = <1>; +@@ -817,6 +832,7 @@ ohci3: usb@5311400 { + }; + + hdmi: hdmi@6000000 { ++ #sound-dai-cells = <0>; + compatible = "allwinner,sun50i-h6-dw-hdmi"; + reg = <0x06000000 0x10000>; + reg-io-width = <1>; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-allwinner-h6-Enable-hdmi-sound-card-on-boards-with-hd.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-allwinner-h6-Enable-hdmi-sound-card-on-boards-with-hd.patch new file mode 100644 index 000000000000..109c6a2cd839 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-allwinner-h6-Enable-hdmi-sound-card-on-boards-with-hd.patch @@ -0,0 +1,123 @@ +From dead2c32ebf5c0c69df98a4b72e35e11753991b2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Tue, 11 Jan 2022 13:08:47 +0100 +Subject: arm64: dts: allwinner: h6: Enable hdmi sound card on boards with hdmi + +Each board that has HDMI connector can also transmit audio through it. +Enable HDMI sound card on all H6 boards with HDMI connector. + +Signed-off-by: Jernej Skrabec +--- + arch/arm64/boot/dts/allwinner/sun50i-h6-beelink-gs1.dts | 8 ++++++++ + arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3.dts | 8 ++++++++ + arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi.dtsi | 8 ++++++++ + arch/arm64/boot/dts/allwinner/sun50i-h6-pine-h64.dts | 8 ++++++++ + 4 files changed, 32 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6-beelink-gs1.dts b/arch/arm64/boot/dts/allwinner/sun50i-h6-beelink-gs1.dts +index 13a0e63afeaf..4044c33c92f2 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-h6-beelink-gs1.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-h6-beelink-gs1.dts +@@ -119,6 +119,10 @@ hdmi_out_con: endpoint { + }; + }; + ++&i2s1 { ++ status = "okay"; ++}; ++ + &mdio { + ext_rgmii_phy: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; +@@ -291,6 +295,10 @@ sw { + }; + }; + ++&sound_hdmi { ++ status = "okay"; ++}; ++ + &spdif { + pinctrl-names = "default"; + pinctrl-0 = <&spdif_tx_pin>; +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3.dts b/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3.dts +index 48d720e20334..7e11286bf6d1 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3.dts +@@ -167,6 +167,10 @@ ext_rgmii_phy: ethernet-phy@1 { + }; + }; + ++&i2s1 { ++ status = "okay"; ++}; ++ + &mmc0 { + vmmc-supply = <®_cldo1>; + cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ +@@ -340,6 +344,10 @@ &rtc { + clocks = <&ext_osc32k>; + }; + ++&sound_hdmi { ++ status = "okay"; ++}; ++ + &uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_ph_pins>; +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi.dtsi +index d05dc5d6e6b9..ed4d4672f4c9 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi.dtsi +@@ -91,6 +91,10 @@ hdmi_out_con: endpoint { + }; + }; + ++&i2s1 { ++ status = "okay"; ++}; ++ + &mmc0 { + vmmc-supply = <®_cldo1>; + cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; +@@ -245,6 +249,10 @@ &rtc { + clocks = <&ext_osc32k>; + }; + ++&sound_hdmi { ++ status = "okay"; ++}; ++ + &uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_ph_pins>; +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6-pine-h64.dts b/arch/arm64/boot/dts/allwinner/sun50i-h6-pine-h64.dts +index fa7a765ee828..95aea02bce40 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-h6-pine-h64.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-h6-pine-h64.dts +@@ -123,6 +123,10 @@ hdmi_out_con: endpoint { + }; + }; + ++&i2s1 { ++ status = "okay"; ++}; ++ + &mdio { + ext_rgmii_phy: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; +@@ -299,6 +303,10 @@ &rtc { + clocks = <&ext_osc32k>; + }; + ++&sound_hdmi { ++ status = "okay"; ++}; ++ + /* + * The CS pin is shared with the MMC2 CMD pin, so we cannot have the SPI + * flash and eMMC at the same time, as one of them would fail probing. +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-allwinner-orange-pi-3-Enable-ethernet.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-allwinner-orange-pi-3-Enable-ethernet.patch new file mode 100644 index 000000000000..89e454ee1ff7 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-allwinner-orange-pi-3-Enable-ethernet.patch @@ -0,0 +1,105 @@ +From 4e0844ed190a4de7091fac2fcb3f1cfb7b6893dd Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Tue, 20 Aug 2019 14:54:48 +0200 +Subject: arm64: dts: allwinner: orange-pi-3: Enable ethernet + +Orange Pi 3 has two regulators that power the Realtek RTL8211E +PHY. According to the datasheet, both regulators need to be enabled +at the same time, or that "phy-io" should be enabled slightly earlier +than "phy" regulator. + +RTL8211E/RTL8211EG datasheet says: + + Note 4: 2.5V (or 1.8/1.5V) RGMII power should be risen simultaneously + or slightly earlier than 3.3V power. Rising 2.5V (or 1.8/1.5V) power + later than 3.3V power may lead to errors. + +The driver ensures the regulator enable ordering. The timing is set +in DT via startup-delay-us. + +We also need to wait at least 30ms after power-up/reset, before +accessing the PHY registers. + +All values of RX/TX delay were tested exhaustively and a middle one +of the range of working values was chosen. + +Signed-off-by: Ondrej Jirman +--- + .../dts/allwinner/sun50i-h6-orangepi-3.dts | 40 +++++++++++++++++++ + 1 file changed, 40 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3.dts b/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3.dts +index ab87c3447cd7..48d720e20334 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3.dts +@@ -15,6 +15,7 @@ / { + aliases { + serial0 = &uart0; + serial1 = &uart1; ++ ethernet0 = &emac; + }; + + chosen { +@@ -64,6 +65,15 @@ reg_vcc5v: vcc5v { + regulator-always-on; + }; + ++ reg_gmac_2v5: gmac-2v5 { ++ compatible = "regulator-fixed"; ++ regulator-name = "gmac-2v5"; ++ regulator-min-microvolt = <2500000>; ++ regulator-max-microvolt = <2500000>; ++ enable-active-high; ++ gpio = <&pio 3 6 GPIO_ACTIVE_HIGH>; /* PD6 */ ++ }; ++ + reg_vcc33_wifi: vcc33-wifi { + /* Always on 3.3V regulator for WiFi and BT */ + compatible = "regulator-fixed"; +@@ -128,6 +138,35 @@ hdmi_out_con: endpoint { + }; + }; + ++&emac { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&ext_rgmii_pins>; ++ phy-mode = "rgmii-id"; ++ phy-handle = <&ext_rgmii_phy>; ++ /* ++ * The board uses 2.5V RGMII signalling. Power sequence to enable ++ * the phy is to enable GMAC-2V5 and GMAC-3V (aldo2) power rails ++ * at the same time and to wait 100ms. The driver enables phy-io ++ * first. Delay is achieved with enable-ramp-delay on reg_aldo2. ++ */ ++ phy-supply = <®_aldo2>; ++ phy-io-supply = <®_gmac_2v5>; ++ allwinner,rx-delay-ps = <200>; ++ allwinner,tx-delay-ps = <200>; ++ status = "okay"; ++}; ++ ++&mdio { ++ ext_rgmii_phy: ethernet-phy@1 { ++ compatible = "ethernet-phy-ieee802.3-c22"; ++ reg = <1>; ++ ++ reset-gpios = <&pio 3 14 GPIO_ACTIVE_LOW>; /* PD14 */ ++ reset-assert-us = <15000>; ++ reset-deassert-us = <40000>; ++ }; ++}; ++ + &mmc0 { + vmmc-supply = <®_cldo1>; + cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ +@@ -212,6 +251,7 @@ reg_aldo2: aldo2 { + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vcc33-audio-tv-ephy-mac"; ++ regulator-enable-ramp-delay = <100000>; + }; + + /* ALDO3 is shorted to CLDO1 */ +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-rk3399-Add-dmc_opp_table.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-rk3399-Add-dmc_opp_table.patch new file mode 100644 index 000000000000..686684e5868f --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-rk3399-Add-dmc_opp_table.patch @@ -0,0 +1,62 @@ +From 90c0c047de85bdde3752b7556d4abf735df72242 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Fri, 22 Nov 2024 23:41:48 +0100 +Subject: arm64: dts: rk3399: Add dmc_opp_table + +This was removed in v6.12. Re-add it with original values. + +Signed-off-by: Ondrej Jirman +--- + arch/arm64/boot/dts/rockchip/rk3399.dtsi | 29 ++++++++++++++++++++++++ + 1 file changed, 29 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi +index 6bc1249d99e6..bb9205bebf9f 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi +@@ -104,6 +104,31 @@ opp05 { + opp-microvolt = <1100000 1100000 1150000>; + }; + }; ++ ++ dmc_opp_table: opp-table-3 { ++ compatible = "operating-points-v2"; ++ ++ opp00 { ++ opp-hz = /bits/ 64 <328000000>; ++ opp-microvolt = <900000>; ++ }; ++ opp01 { ++ opp-hz = /bits/ 64 <416000000>; ++ opp-microvolt = <900000>; ++ }; ++ opp02 { ++ opp-hz = /bits/ 64 <666000000>; ++ opp-microvolt = <900000>; ++ }; ++ opp03 { ++ opp-hz = /bits/ 64 <856000000>; ++ opp-microvolt = <900000>; ++ }; ++ opp04 { ++ opp-hz = /bits/ 64 <928000000>; ++ opp-microvolt = <925000>; ++ }; ++ }; + }; + + &cpu_l0 { +@@ -130,6 +155,10 @@ &cpu_b1 { + operating-points-v2 = <&cluster1_opp>; + }; + ++&dmc { ++ operating-points-v2 = <&dmc_opp_table>; ++}; ++ + &gpu { + operating-points-v2 = <&gpu_opp_table>; + }; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-rockchip-rk3399-s-Add-DMC-table.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-rockchip-rk3399-s-Add-DMC-table.patch new file mode 100644 index 000000000000..8bcf9982009d --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-rockchip-rk3399-s-Add-DMC-table.patch @@ -0,0 +1,62 @@ +From ea6833fd7294f14e4e84d6dde5fa11f82243f1a8 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Thu, 28 Nov 2024 11:16:31 +0100 +Subject: arm64: dts: rockchip: rk3399-s: Add DMC table + +This is used by Pinephone Pro. + +Signed-off-by: Ondrej Jirman +--- + arch/arm64/boot/dts/rockchip/rk3399-s.dtsi | 29 ++++++++++++++++++++++ + 1 file changed, 29 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3399-s.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-s.dtsi +index e54f451af9f3..82b941720294 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3399-s.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk3399-s.dtsi +@@ -92,6 +92,31 @@ opp05 { + opp-microvolt = <1100000 1100000 1150000>; + }; + }; ++ ++ dmc_opp_table: opp-table-3 { ++ compatible = "operating-points-v2"; ++ ++ opp00 { ++ opp-hz = /bits/ 64 <328000000>; ++ opp-microvolt = <900000>; ++ }; ++ opp01 { ++ opp-hz = /bits/ 64 <416000000>; ++ opp-microvolt = <900000>; ++ }; ++ opp02 { ++ opp-hz = /bits/ 64 <666000000>; ++ opp-microvolt = <900000>; ++ }; ++ opp03 { ++ opp-hz = /bits/ 64 <856000000>; ++ opp-microvolt = <900000>; ++ }; ++ opp04 { ++ opp-hz = /bits/ 64 <928000000>; ++ opp-microvolt = <925000>; ++ }; ++ }; + }; + + &cpu_l0 { +@@ -118,6 +143,10 @@ &cpu_b1 { + operating-points-v2 = <&cluster1_opp>; + }; + ++&dmc { ++ operating-points-v2 = <&dmc_opp_table>; ++}; ++ + &gpu { + operating-points-v2 = <&gpu_opp_table>; + }; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-rockchip-rk356x-Fix-PCIe-register-map-and-ranges.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-rockchip-rk356x-Fix-PCIe-register-map-and-ranges.patch new file mode 100644 index 000000000000..4ba8d341bbe6 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-rockchip-rk356x-Fix-PCIe-register-map-and-ranges.patch @@ -0,0 +1,47 @@ +From e35cba7b314073d85e8229ce603d291c0f720a68 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sat, 24 Sep 2022 21:59:07 +0200 +Subject: arm64: dts: rockchip: rk356x: Fix PCIe register map and ranges + +I have two Realtek PCIe wifi cards connected over the 4 port PCIe bridge +to Quartz64-A. The cards fail to work, when nvme SSD is connected at the +same time to the bridge. Without nvme connected, cards work fine. The +issue seems to be related to mixed use of devices which make use of I/O +ranges and memory ranges. + +This patch changes I/O, MEM and config mappings so that config and I/O +mappings use the 0xf4000000 outbound address space, and MEM range uses +the whole 0x300000000 outbound space. + +These values were suggested by pgwipeout: + + https://lore.kernel.org/lkml/875ygbsrf3.fsf@bloch.sibelius.xs4all.nl/T/#m84b5f6992cc26dffe0d3783c0d8c9c86e5e10c10 + +This is identical to how BSP does the mappings. + +This change to the regs/ranges makes the issue go away and both nvme and +wifi cards work when connected at the same time to the bridge. I tested +the nvme with large amount of reads/writes, both behind the PCIe bridge +and when directly connected to Quartz64-A board. + +Signed-off-by: Ondrej Jirman +--- + arch/arm64/boot/dts/rockchip/rk356x-base.dtsi | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/arm64/boot/dts/rockchip/rk356x-base.dtsi b/arch/arm64/boot/dts/rockchip/rk356x-base.dtsi +index 8421d4b8c771..3dd63161bbf1 100644 +--- a/arch/arm64/boot/dts/rockchip/rk356x-base.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk356x-base.dtsi +@@ -954,7 +954,7 @@ pcie2x1: pcie@fe260000 { + <0 0 0 4 &pcie_intc 3>; + linux,pci-domain = <0>; + num-ib-windows = <6>; +- num-ob-windows = <2>; ++ num-ob-windows = <8>; + max-link-speed = <2>; + msi-map = <0x0 &gic 0x0 0x1000>; + num-lanes = <1>; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50-a64-pinephone-Define-jack-pins-in-DT.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50-a64-pinephone-Define-jack-pins-in-DT.patch new file mode 100644 index 000000000000..48a2b34a1280 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50-a64-pinephone-Define-jack-pins-in-DT.patch @@ -0,0 +1,30 @@ +From ed8e79073bb141a1f5a5b5f2b891e3c9be8079c8 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Tue, 14 May 2024 10:47:05 +0200 +Subject: arm64: dts: sun50-a64-pinephone: Define jack pins in DT + +To restore pre v6.8 behavior of the sound card. + +Signed-off-by: Ondrej Jirman +--- + arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi +index f0f1367c5868..d502c1392b8e 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi +@@ -999,6 +999,10 @@ &sound { + "MIC1", "Internal Microphone", + "Headset Microphone", "HBIAS", + "MIC2", "Headset Microphone"; ++ simple-audio-card,jack-pins = ++ "1c22e00.codec", "Headphone Jack", ++ "1c22e00.codec", "Headset Microphone"; ++ simple-audio-card,jack-pins-mask = <1>, <2>; + + simple-audio-card,dai-link@1 { + format = "dsp_a"; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-Define-orientation-and-rotation-for-PinePhone-.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-Define-orientation-and-rotation-for-PinePhone-.patch new file mode 100644 index 000000000000..2e608e06d9d9 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-Define-orientation-and-rotation-for-PinePhone-.patch @@ -0,0 +1,31 @@ +From b0ce326666eaf6803bcc5723af14c513eff895d5 Mon Sep 17 00:00:00 2001 +From: Arnav Singh +Date: Mon, 6 May 2024 16:36:04 -0700 +Subject: arm64: dts: sun50i: Define orientation and rotation for PinePhone + rear camera + +This enables libcamera to detect the correct orientation from +the device tree and propagate it further to e.g. Pipewire. + +Signed-off-by: Arnav Singh +--- + arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi +index e7e90e5baff4..1a57720c25ef 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi +@@ -617,6 +617,9 @@ ov5640: rear-camera@4c { + reset-gpios = <&pio 3 3 (GPIO_ACTIVE_LOW | GPIO_OPEN_DRAIN)>; /* PD3 */ + powerdown-gpios = <&pio 2 0 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; /* PC0 */ + ++ orientation = <1>; ++ rotation = <270>; ++ + port { + ov5640_ep: endpoint { + remote-endpoint = <&csi_ov5640_ep>; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-Set-fifo-size-for-uarts.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-Set-fifo-size-for-uarts.patch new file mode 100644 index 000000000000..5c5658df204d --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-Set-fifo-size-for-uarts.patch @@ -0,0 +1,59 @@ +From d742191977aca3b6ba39e760ff7c307728e10eb2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sat, 1 Feb 2020 23:41:18 +0100 +Subject: arm64: dts: sun50i-a64: Set fifo-size for uarts + +Boot time optimization. + +Signed-off-by: Ondrej Jirman +--- + arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi +index 9a43bcc2f489..d53a6f7e086a 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi +@@ -1012,6 +1012,7 @@ uart0: serial@1c28000 { + interrupts = ; + reg-shift = <2>; + reg-io-width = <4>; ++ fifo-size = <64>; + clocks = <&ccu CLK_BUS_UART0>; + resets = <&ccu RST_BUS_UART0>; + status = "disabled"; +@@ -1023,6 +1024,7 @@ uart1: serial@1c28400 { + interrupts = ; + reg-shift = <2>; + reg-io-width = <4>; ++ fifo-size = <64>; + clocks = <&ccu CLK_BUS_UART1>; + resets = <&ccu RST_BUS_UART1>; + status = "disabled"; +@@ -1034,6 +1036,7 @@ uart2: serial@1c28800 { + interrupts = ; + reg-shift = <2>; + reg-io-width = <4>; ++ fifo-size = <64>; + clocks = <&ccu CLK_BUS_UART2>; + resets = <&ccu RST_BUS_UART2>; + status = "disabled"; +@@ -1045,6 +1048,7 @@ uart3: serial@1c28c00 { + interrupts = ; + reg-shift = <2>; + reg-io-width = <4>; ++ fifo-size = <64>; + clocks = <&ccu CLK_BUS_UART3>; + resets = <&ccu RST_BUS_UART3>; + status = "disabled"; +@@ -1056,6 +1060,7 @@ uart4: serial@1c29000 { + interrupts = ; + reg-shift = <2>; + reg-io-width = <4>; ++ fifo-size = <64>; + clocks = <&ccu CLK_BUS_UART4>; + resets = <&ccu RST_BUS_UART4>; + status = "disabled"; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-Type-C-support-for-all-PP-va.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-Type-C-support-for-all-PP-va.patch new file mode 100644 index 000000000000..54ebfbfc6e2a --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-Type-C-support-for-all-PP-va.patch @@ -0,0 +1,416 @@ +From dbf95776199d6ae835f22bcb28af833821d3e2b2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Wed, 8 Jul 2020 00:58:16 +0200 +Subject: arm64: dts: sun50i-a64-pinephone: Add Type-C support for all PP + variants 1.0-1.2 + +There are differences in GPIO and how VBUS is handled. Describe them +in DTS. Thankfully we can support both unmodded and modded variants +of 1.0 and 1.1 boards with one DTS. No need for more subvariants. + +Signed-off-by: Ondrej Jirman +--- + .../allwinner/sun50i-a64-pinephone-1.0.dts | 86 +++++++++++++++++++ + .../allwinner/sun50i-a64-pinephone-1.1.dts | 86 +++++++++++++++++++ + .../allwinner/sun50i-a64-pinephone-1.2.dts | 60 +++++++++++++ + .../dts/allwinner/sun50i-a64-pinephone.dtsi | 68 ++++++++++++++- + 4 files changed, 299 insertions(+), 1 deletion(-) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.0.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.0.dts +index 219f720b8b7d..498ae493d176 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.0.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.0.dts +@@ -8,6 +8,92 @@ + / { + model = "Pine64 PinePhone Developer Batch (1.0)"; + compatible = "pine64,pinephone-1.0", "pine64,pinephone", "allwinner,sun50i-a64"; ++ ++ reg_vbus: usb0-vbus { ++ compatible = "regulator-fixed"; ++ regulator-name = "usb0-vbus"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ gpio = <&pio 3 6 GPIO_ACTIVE_HIGH>; /* PD6 */ ++ enable-active-high; ++ vin-supply = <®_drivevbus>; ++ }; ++}; ++ ++&axp803 { ++ x-powers,drive-vbus-en; ++}; ++ ++&i2c0 { ++ hdmi-bridge@28 { ++ compatible = "analogix,anx7688"; ++ reg = <0x28>; ++ ++ interrupt-parent = <&r_pio>; ++ interrupts = <0 11 IRQ_TYPE_EDGE_FALLING>; /* PL11 */ ++ ++ enable-gpios = <&pio 3 10 GPIO_ACTIVE_LOW>; /* PD10 */ ++ reset-gpios = <&r_pio 0 9 GPIO_ACTIVE_HIGH>; /* PL9 */ ++ ++ cabledet-gpios = <&r_pio 0 8 GPIO_ACTIVE_HIGH>; /* PL8 */ ++ ++ avdd10-supply = <®_anx1v0>; ++ dvdd10-supply = <®_anx1v0>; ++ /* This is for the HW hack variant, but will also work for non-modified version. */ ++ avdd18-supply = <®_ldo_io1>; ++ dvdd18-supply = <®_ldo_io1>; ++ avdd33-supply = <®_dldo1>; ++ vconn-supply = <®_vconn5v0>; ++ hdmi_vt-supply = <®_dldo1>; ++ ++ source-caps = < ++ PDO_FIXED(5000, 500, PDO_FIXED_DATA_SWAP | PDO_FIXED_USB_COMM | PDO_FIXED_DUAL_ROLE) ++ >; ++ ++ sink-caps = < ++ PDO_FIXED(5000, 3000, PDO_FIXED_DATA_SWAP | PDO_FIXED_USB_COMM | PDO_FIXED_DUAL_ROLE) ++ >; ++ ++ /* ++ * ANX7688 can't enable/disable USB-5V <-> DCIN switch by ++ * itself, and PMIC's N_VBUSEN is not connected anywhere either, ++ * so we need to configure everything in software: ++ * ++ * - get the VBUS status from ANX7688 via I2C ++ * - configure PMIC to let it know vbus drive status (via ++ * reg_drivevbus) ++ * - enable VBUS switch and USB-5V regulator (via reg_vbus ++ * and reg_usb_5v) ++ */ ++ vbus-supply = <®_vbus>; ++ vbus_in-supply = <&usb_power_supply>; ++ ++ port { ++ typec0_dr_sw: endpoint { ++ remote-endpoint = <&usb0_drd_sw>; ++ }; ++ }; ++ }; ++}; ++ ++/* ++ * The N_VBUSEN pin is disconnected, but we need to inform the PMIC about ++ * the VBUS status anyway. To avoid the pin from floating and to inform ++ * the PMIC, about VBUS status, we couple reg_drivevbus with reg_vbus. ++ */ ++®_drivevbus { ++ vin-supply = <®_usb_5v>; ++ status = "okay"; ++}; ++ ++&usbphy { ++ usb-role-switch; ++ ++ port { ++ usb0_drd_sw: endpoint { ++ remote-endpoint = <&typec0_dr_sw>; ++ }; ++ }; + }; + + &codec_analog { +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.1.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.1.dts +index 723af64a9cee..73275308f484 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.1.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.1.dts +@@ -8,6 +8,20 @@ + / { + model = "Pine64 PinePhone Braveheart (1.1)"; + compatible = "pine64,pinephone-1.1", "pine64,pinephone", "allwinner,sun50i-a64"; ++ ++ reg_vbus: usb0-vbus { ++ compatible = "regulator-fixed"; ++ regulator-name = "usb0-vbus"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ gpio = <&pio 3 6 GPIO_ACTIVE_HIGH>; /* PD6 */ ++ enable-active-high; ++ vin-supply = <®_drivevbus>; ++ }; ++}; ++ ++&axp803 { ++ x-powers,drive-vbus-en; + }; + + &backlight { +@@ -33,7 +47,79 @@ &codec_analog { + allwinner,internal-bias-resistor; + }; + ++&i2c0 { ++ hdmi-bridge@28 { ++ compatible = "analogix,anx7688"; ++ reg = <0x28>; ++ ++ interrupt-parent = <&r_pio>; ++ interrupts = <0 11 IRQ_TYPE_EDGE_FALLING>; /* PL11 */ ++ ++ enable-gpios = <&pio 3 10 GPIO_ACTIVE_LOW>; /* PD10 */ ++ reset-gpios = <&r_pio 0 9 GPIO_ACTIVE_HIGH>; /* PL9 */ ++ ++ cabledet-gpios = <&r_pio 0 8 GPIO_ACTIVE_HIGH>; /* PL8 */ ++ ++ avdd10-supply = <®_anx1v0>; ++ dvdd10-supply = <®_anx1v0>; ++ /* This is for the HW hack variant, but will also work for non-modified version. */ ++ avdd18-supply = <®_ldo_io1>; ++ dvdd18-supply = <®_ldo_io1>; ++ avdd33-supply = <®_dldo1>; ++ vconn-supply = <®_vconn5v0>; ++ hdmi_vt-supply = <®_dldo1>; ++ ++ source-caps = < ++ PDO_FIXED(5000, 500, PDO_FIXED_DATA_SWAP | PDO_FIXED_USB_COMM | PDO_FIXED_DUAL_ROLE) ++ >; ++ ++ sink-caps = < ++ PDO_FIXED(5000, 3000, PDO_FIXED_DATA_SWAP | PDO_FIXED_USB_COMM | PDO_FIXED_DUAL_ROLE) ++ >; ++ ++ /* ++ * ANX7688 can't enable/disable USB-5V <-> DCIN switch by ++ * itself, and PMIC's N_VBUSEN is not connected anywhere either, ++ * so we need to configure everything in software: ++ * ++ * - get the VBUS status from ANX7688 via I2C ++ * - configure PMIC to let it know vbus drive status (via ++ * reg_drivevbus) ++ * - enable VBUS switch and USB-5V regulator (via reg_vbus ++ * and reg_usb_5v) ++ */ ++ vbus-supply = <®_vbus>; ++ vbus_in-supply = <&usb_power_supply>; ++ ++ port { ++ typec0_dr_sw: endpoint { ++ remote-endpoint = <&usb0_drd_sw>; ++ }; ++ }; ++ }; ++}; ++ ++/* ++ * The N_VBUSEN pin is disconnected, but we need to inform the PMIC about ++ * the VBUS status anyway. To avoid the pin from floating and to inform ++ * the PMIC, about VBUS status, we couple reg_drivevbus with reg_vbus. ++ */ ++®_drivevbus { ++ vin-supply = <®_usb_5v>; ++ status = "okay"; ++}; ++ + &sgm3140 { + enable-gpios = <&pio 3 24 GPIO_ACTIVE_HIGH>; /* PD24 */ + flash-gpios = <&pio 2 3 GPIO_ACTIVE_HIGH>; /* PC3 */ + }; ++ ++&usbphy { ++ usb-role-switch; ++ ++ port { ++ usb0_drd_sw: endpoint { ++ remote-endpoint = <&typec0_dr_sw>; ++ }; ++ }; ++}; +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.2.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.2.dts +index 4e7e237cb46a..40127186606f 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.2.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.2.dts +@@ -48,7 +48,67 @@ &mmc1 { + mmc-pwrseq = <&wifi_pwrseq>; + }; + ++&i2c0 { ++ hdmi-bridge@28 { ++ compatible = "analogix,anx7688"; ++ reg = <0x28>; ++ ++ interrupt-parent = <&r_pio>; ++ interrupts = <0 11 IRQ_TYPE_EDGE_FALLING>; /* PL11 */ ++ ++ enable-gpios = <&pio 3 10 GPIO_ACTIVE_LOW>; /* PD10 */ ++ reset-gpios = <&pio 3 6 GPIO_ACTIVE_HIGH>; /* PD6 */ ++ ++ cabledet-gpios = <&r_pio 0 8 GPIO_ACTIVE_HIGH>; /* PL8 */ ++ ++ avdd10-supply = <®_anx1v0>; ++ dvdd10-supply = <®_anx1v0>; ++ avdd18-supply = <®_ldo_io1>; ++ dvdd18-supply = <®_ldo_io1>; ++ avdd33-supply = <®_dcdc1>; ++ vconn-supply = <®_vconn5v0>; ++ hdmi_vt-supply = <®_dldo1>; ++ ++ source-caps = < ++ PDO_FIXED(5000, 500, PDO_FIXED_DATA_SWAP | PDO_FIXED_USB_COMM | PDO_FIXED_DUAL_ROLE) ++ >; ++ ++ sink-caps = < ++ PDO_FIXED(5000, 3000, PDO_FIXED_DATA_SWAP | PDO_FIXED_USB_COMM | PDO_FIXED_DUAL_ROLE) ++ >; ++ ++ /* ++ * ANX7688 will enable/disable USB-5V <-> DCIN switch by itself ++ * via VBUS_CTRL pin. The driver just has to enable the USB-5V ++ * so that the switch has power. ++ */ ++ vbus-supply = <®_usb_5v>; ++ vbus_in-supply = <&usb_power_supply>; ++ ++ port { ++ typec0_dr_sw: endpoint { ++ remote-endpoint = <&usb0_drd_sw>; ++ }; ++ }; ++ }; ++}; ++ ++®_anx1v0 { ++ gpio = <&pio 3 11 GPIO_ACTIVE_HIGH>; /* PD11 */ ++ enable-active-high; ++}; ++ + &sgm3140 { + enable-gpios = <&pio 3 24 GPIO_ACTIVE_HIGH>; /* PD24 */ + flash-gpios = <&pio 2 3 GPIO_ACTIVE_HIGH>; /* PC3 */ + }; ++ ++&usbphy { ++ usb-role-switch; ++ ++ port { ++ usb0_drd_sw: endpoint { ++ remote-endpoint = <&typec0_dr_sw>; ++ }; ++ }; ++}; +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi +index 5d55c6f82352..e8f6c016e1b3 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi +@@ -10,6 +10,7 @@ + #include + #include + #include ++#include + + / { + chassis-type = "handset"; +@@ -70,6 +71,24 @@ led2: led-2 { + }; + }; + ++ hdmi-connector { ++ compatible = "hdmi-connector"; ++ type = "a"; ++ ++ port { ++ hdmi_con_in: endpoint { ++ remote-endpoint = <&hdmi_out_con>; ++ }; ++ }; ++ }; ++ ++ reg_anx1v0: anx1v0 { ++ compatible = "regulator-fixed"; ++ regulator-name = "anx1v0"; ++ regulator-min-microvolt = <1000000>; ++ regulator-max-microvolt = <1000000>; ++ }; ++ + multi-led { + compatible = "leds-group-multicolor"; + color = ; +@@ -85,6 +104,24 @@ reg_ps: ps-regulator { + regulator-boot-on; + }; + ++ reg_usb_5v: usb-5v { ++ compatible = "regulator-fixed"; ++ regulator-name = "usb-5v"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ gpio = <&pio 3 8 GPIO_ACTIVE_HIGH>; /* PD8 */ ++ enable-active-high; ++ }; ++ ++ reg_vbat_bb: vbat-bb { ++ compatible = "regulator-fixed"; ++ regulator-name = "vbat-bb"; ++ regulator-min-microvolt = <3500000>; ++ regulator-max-microvolt = <3500000>; ++ gpio = <&r_pio 0 7 GPIO_ACTIVE_HIGH>; /* PL7 */ ++ enable-active-high; ++ }; ++ + reg_vbat_wifi: vbat-wifi { + compatible = "regulator-fixed"; + regulator-min-microvolt = <3300000>; +@@ -92,6 +129,16 @@ reg_vbat_wifi: vbat-wifi { + regulator-name = "vbat-wifi"; + }; + ++ /* This is a charge pump for CC1 and CC2 pins on USB-C connector. */ ++ reg_vconn5v0: vconn5v0 { ++ compatible = "regulator-fixed"; ++ regulator-name = "vconn5v0"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ gpio = <&pio 3 9 GPIO_ACTIVE_HIGH>; /* PD9 */ ++ enable-active-high; ++ }; ++ + sgm3140: led-controller { + compatible = "sgmicro,sgm3140"; + vin-supply = <®_dcdc1>; +@@ -207,6 +254,21 @@ &ehci1 { + status = "okay"; + }; + ++&hdmi { ++ hvcc-supply = <®_dldo1>; ++ status = "okay"; ++}; ++ ++&hdmi_out { ++ hdmi_out_con: endpoint { ++ remote-endpoint = <&hdmi_con_in>; ++ }; ++}; ++ ++&sound_hdmi { ++ status = "okay"; ++}; ++ + &i2c_csi { + gc2145: front-camera@3c { + compatible = "galaxycore,gc2145"; +@@ -330,6 +392,10 @@ &i2c2 { + status = "okay"; + }; + ++&i2s2 { ++ status = "okay"; ++}; ++ + &lradc { + vref-supply = <®_aldo3>; + wakeup-source; +@@ -623,7 +689,7 @@ &uart3 { + }; + + &usb_otg { +- dr_mode = "peripheral"; ++ dr_mode = "otg"; + status = "okay"; + }; + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-detailed-OCV-to-capactiy-con.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-detailed-OCV-to-capactiy-con.patch new file mode 100644 index 000000000000..69258e24228e --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-detailed-OCV-to-capactiy-con.patch @@ -0,0 +1,187 @@ +From 382fca153620b6d38d36f13d093e524d301f8c44 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Mon, 28 Sep 2020 04:35:13 +0200 +Subject: arm64: dts: sun50i-a64-pinephone: Add detailed OCV to capactiy + conversion table + +This ensures that discharging stops at 3V, and that charging current +limits are enforced. It also allows for more precise capacity reporting +(on my device at the very least, at room temperature). + +Signed-off-by: Ondrej Jirman +--- + .../dts/allwinner/sun50i-a64-pinephone.dtsi | 151 ++++++++++++++++++ + 1 file changed, 151 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi +index e8f6c016e1b3..13213a3704a1 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi +@@ -28,6 +28,156 @@ backlight: backlight { + /* Backlight configuration differs per PinePhone revision. */ + }; + ++ bat: battery { ++ compatible = "simple-battery"; ++ voltage-min-design-microvolt = <3000000>; ++ voltage-max-design-microvolt = <4350000>; ++ energy-full-design-microwatt-hours = <12000000>; ++ charge-full-design-microamp-hours = <2750000>; ++ precharge-current-microamp = <130000>; ++ charge-term-current-microamp = <50000>; ++ constant-charge-current-max-microamp = <1300000>; ++ constant-charge-voltage-max-microvolt = <4350000>; ++ factory-internal-resistance-micro-ohms = <150000>; ++ resistance-temp-table = <20 150>; ++ ocv-capacity-celsius = <20>; ++ ocv-capacity-table-0 = ++ <4334000 100>, ++ <4319700 99>, ++ <4304300 98>, ++ <4292200 97>, ++ <4280100 96>, ++ <4269100 95>, ++ <4253700 94>, ++ <4242700 93>, ++ <4232800 92>, ++ <4222900 91>, ++ <4211900 90>, ++ <4203100 89>, ++ <4192100 88>, ++ <4177800 87>, ++ <4167900 86>, ++ <4155800 85>, ++ <4147000 84>, ++ <4136000 83>, ++ <4126100 82>, ++ <4111800 81>, ++ <4102999 80>, ++ <4093100 79>, ++ <4084300 78>, ++ <4075500 77>, ++ <4064500 76>, ++ <4054600 75>, ++ <4042500 74>, ++ <4035900 73>, ++ <4029300 72>, ++ <4018300 71>, ++ <4006200 70>, ++ <3993000 69>, ++ <3974300 68>, ++ <3964400 67>, ++ <3956700 66>, ++ <3949000 65>, ++ <3943500 64>, ++ <3938000 63>, ++ <3931400 62>, ++ <3923700 61>, ++ <3917100 60>, ++ <3903900 59>, ++ <3895100 58>, ++ <3887400 57>, ++ <3878600 56>, ++ <3870900 55>, ++ <3863200 54>, ++ <3855500 53>, ++ <3848900 52>, ++ <3837900 51>, ++ <3830200 50>, ++ <3825800 49>, ++ <3821400 48>, ++ <3815900 47>, ++ <3810400 46>, ++ <3806000 45>, ++ <3801600 44>, ++ <3797200 43>, ++ <3792800 42>, ++ <3789500 41>, ++ <3786200 40>, ++ <3781800 39>, ++ <3778500 38>, ++ <3775200 37>, ++ <3770800 36>, ++ <3764200 35>, ++ <3760900 34>, ++ <3758700 33>, ++ <3755400 32>, ++ <3754300 31>, ++ <3752100 30>, ++ <3748800 29>, ++ <3749900 28>, ++ <3745500 27>, ++ <3742200 26>, ++ <3741100 25>, ++ <3737800 24>, ++ <3734500 23>, ++ <3731200 22>, ++ <3730100 21>, ++ <3724600 20>, ++ <3722400 19>, ++ <3718000 18>, ++ <3713600 17>, ++ <3708100 16>, ++ <3701500 15>, ++ <3689400 14>, ++ <3682800 13>, ++ <3674000 12>, ++ <3666300 11>, ++ <3659700 10>, ++ <3653100 9>, ++ <3647600 8>, ++ <3641000 7>, ++ <3625600 6>, ++ <3613500 5>, ++ <3591500 4>, ++ <3554100 3>, ++ <3489200 2>, ++ <3393500 1>, ++ <3256000 0>; ++ x-powers,ocv-capacity-table = ++ <0xc0 0>, ++ <0xc1 1>, ++ <0xc2 1>, ++ <0xc3 2>, ++ <0xc4 2>, ++ <0xc5 4>, ++ <0xc6 4>, ++ <0xc7 5>, ++ <0xc8 6>, ++ <0xc9 8>, ++ <0xca 11>, ++ <0xcb 15>, ++ <0xcc 22>, ++ <0xcd 36>, ++ <0xce 44>, ++ <0xcf 48>, ++ <0xd0 51>, ++ <0xd1 53>, ++ <0xd2 56>, ++ <0xd3 60>, ++ <0xd4 64>, ++ <0xd5 69>, ++ <0xd6 71>, ++ <0xd7 75>, ++ <0xd8 78>, ++ <0xd9 82>, ++ <0xda 84>, ++ <0xdb 85>, ++ <0xdc 88>, ++ <0xdd 92>, ++ <0xde 95>, ++ <0xdf 98>; ++ }; ++ + bt_sco_codec: bt-sco-codec { + #sound-dai-cells = <1>; + compatible = "linux,bt-sco"; +@@ -499,6 +649,7 @@ axp803: pmic@3a3 { + + &battery_power_supply { + status = "okay"; ++ monitored-battery = <&bat>; + }; + + ®_aldo1 { +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-front-back-cameras.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-front-back-cameras.patch new file mode 100644 index 000000000000..b03ae63cddda --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-front-back-cameras.patch @@ -0,0 +1,130 @@ +From 46c60d4ce1900101897221b32794922bee23855f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Wed, 8 Apr 2020 14:13:08 +0200 +Subject: arm64: dts: sun50i-a64-pinephone: Add front/back cameras + +Pinephone has OV5640 back camera and GC2145 front camera. Add support +for both. + +Signed-off-by: Ondrej Jirman +--- + .../dts/allwinner/sun50i-a64-pinephone.dtsi | 89 +++++++++++++++++++ + 1 file changed, 89 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi +index 4bc6c1ef2cde..5d55c6f82352 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi +@@ -36,6 +36,15 @@ chosen { + stdout-path = "serial0:115200n8"; + }; + ++ i2c_csi: i2c-csi { ++ compatible = "i2c-gpio"; ++ sda-gpios = <&pio 4 13 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; /* PE13 */ ++ scl-gpios = <&pio 4 12 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; /* PE12 */ ++ i2c-gpio,delay-us = <3>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ + leds { + compatible = "gpio-leds"; + +@@ -134,6 +143,36 @@ &cpu3 { + cpu-supply = <®_dcdc2>; + }; + ++&csi { ++ pinctrl-0 = <&csi_pins>, <&csi_mclk_pin>; ++ status = "okay"; ++ ++ port { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ csi_ov5640_ep: endpoint@0 { ++ reg = <0>; ++ remote-endpoint = <&ov5640_ep>; ++ bus-width = <8>; ++ hsync-active = <1>; /* Active high */ ++ vsync-active = <0>; /* Active low */ ++ data-active = <1>; /* Active high */ ++ pclk-sample = <1>; /* Rising */ ++ }; ++ ++ csi_gc2145_ep: endpoint@1 { ++ reg = <1>; ++ remote-endpoint = <&gc2145_ep>; ++ bus-width = <8>; ++ hsync-active = <1>; ++ vsync-active = <1>; ++ data-active = <1>; ++ pclk-sample = <1>; ++ }; ++ }; ++}; ++ + &dai { + status = "okay"; + }; +@@ -168,6 +207,56 @@ &ehci1 { + status = "okay"; + }; + ++&i2c_csi { ++ gc2145: front-camera@3c { ++ compatible = "galaxycore,gc2145"; ++ reg = <0x3c>; ++ clocks = <&ccu CLK_CSI_MCLK>; ++ clock-names = "xclk"; ++ ++ AVDD-supply = <®_dldo3>; ++ DVDD-supply = <®_aldo1>; ++ IOVDD-supply = <®_eldo3>; ++ reset-gpios = <&pio 4 16 (GPIO_ACTIVE_LOW | GPIO_OPEN_DRAIN)>; /* PE16 */ ++ enable-gpios = <&pio 4 17 (GPIO_ACTIVE_LOW | GPIO_OPEN_DRAIN)>; /* PE17 */ ++ ++ port { ++ gc2145_ep: endpoint { ++ remote-endpoint = <&csi_gc2145_ep>; ++ bus-width = <8>; ++ hsync-active = <1>; ++ vsync-active = <1>; ++ data-active = <1>; ++ pclk-sample = <1>; ++ }; ++ }; ++ }; ++ ++ ov5640: rear-camera@4c { ++ compatible = "ovti,ov5640"; ++ reg = <0x4c>; ++ clocks = <&ccu CLK_CSI_MCLK>; ++ clock-names = "xclk"; ++ ++ AVDD-supply = <®_dldo3>; ++ DOVDD-supply = <®_aldo1>; /* shared with AFVCC */ ++ DVDD-supply = <®_eldo3>; ++ reset-gpios = <&pio 3 3 (GPIO_ACTIVE_LOW | GPIO_OPEN_DRAIN)>; /* PD3 */ ++ powerdown-gpios = <&pio 2 0 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; /* PC0 */ ++ ++ port { ++ ov5640_ep: endpoint { ++ remote-endpoint = <&csi_ov5640_ep>; ++ bus-width = <8>; ++ hsync-active = <1>; /* Active high */ ++ vsync-active = <0>; /* Active low */ ++ data-active = <1>; /* Active high */ ++ pclk-sample = <1>; /* Rising */ ++ }; ++ }; ++ }; ++}; ++ + &i2c0 { + status = "okay"; + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-interrupt-pin-for-WiFi.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-interrupt-pin-for-WiFi.patch new file mode 100644 index 000000000000..df18e76de5a8 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-interrupt-pin-for-WiFi.patch @@ -0,0 +1,26 @@ +From dd36dd39913b0dcbcd99b4d41492a5c436a056e3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sun, 17 Jan 2021 23:06:03 +0100 +Subject: arm64: dts: sun50i-a64-pinephone: Add interrupt pin for WiFi + +Signed-off-by: Ondrej Jirman +--- + arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi +index c2746a21b021..3885a1fd3916 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi +@@ -599,6 +599,8 @@ &mmc1 { + + rtl8723cs: wifi@1 { + reg = <1>; ++ interrupt-parent = <&r_pio>; ++ interrupts = <0 3 IRQ_TYPE_EDGE_FALLING>; /* PL3 */ + }; + }; + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-modem-power-manager.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-modem-power-manager.patch new file mode 100644 index 000000000000..d188548db7a5 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-modem-power-manager.patch @@ -0,0 +1,119 @@ +From 079113ffb649556d044400aa3d4ab176948c461a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Wed, 8 Jul 2020 01:00:48 +0200 +Subject: arm64: dts: sun50i-a64-pinephone: Add modem power manager + +Pinephone has a EG25-G modem that needs a special powerup/powerdown +sequence. Add a power manager to manage the modem power. + +Signed-off-by: Ondrej Jirman +--- + .../allwinner/sun50i-a64-pinephone-1.0.dts | 22 ++++++++++++++++++ + .../allwinner/sun50i-a64-pinephone-1.1.dts | 23 +++++++++++++++++++ + .../allwinner/sun50i-a64-pinephone-1.2.dts | 23 +++++++++++++++++++ + 3 files changed, 68 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.0.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.0.dts +index 498ae493d176..ff01e2be26e7 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.0.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.0.dts +@@ -86,6 +86,28 @@ ®_drivevbus { + status = "okay"; + }; + ++&uart3 { ++ modem { ++ compatible = "quectel,eg25"; ++ char-device-name = "modem-power"; ++ ++ power-supply = <®_vbat_bb>; /* PL7 */ ++ ++ enable-gpios = <&pio 7 8 GPIO_ACTIVE_LOW>; /* PH8 */ ++ reset-gpios = <&pio 2 4 GPIO_ACTIVE_HIGH>; /* PC4 */ ++ pwrkey-gpios = <&pio 1 3 GPIO_ACTIVE_HIGH>; /* PB3 */ ++ ++ sleep-gpios = <&pio 7 7 GPIO_ACTIVE_HIGH>; /* PH7 */ ++ wakeup-gpios = <&pio 1 2 GPIO_ACTIVE_HIGH>; /* PB2-RI */ ++ ++ cts-gpios = <&pio 3 5 GPIO_ACTIVE_HIGH>; /* PD5-CTS */ ++ dtr-gpios = <&r_pio 0 6 GPIO_ACTIVE_HIGH>; /* PL6-DTR */ ++ rts-gpios = <&pio 3 4 GPIO_ACTIVE_HIGH>; /* PD4-RTS */ ++ ++ quectel,qdai = "1,1,0,1,0,0,1,1"; ++ }; ++}; ++ + &usbphy { + usb-role-switch; + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.1.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.1.dts +index 73275308f484..93dc8512fe45 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.1.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.1.dts +@@ -114,6 +114,29 @@ &sgm3140 { + flash-gpios = <&pio 2 3 GPIO_ACTIVE_HIGH>; /* PC3 */ + }; + ++&uart3 { ++ modem { ++ compatible = "quectel,eg25"; ++ char-device-name = "modem-power"; ++ ++ power-supply = <®_vbat_bb>; /* PL7 */ ++ ++ enable-gpios = <&pio 7 8 GPIO_ACTIVE_LOW>; /* PH8 */ ++ reset-gpios = <&pio 2 4 GPIO_ACTIVE_HIGH>; /* PC4 */ ++ status-gpios = <&pio 1 3 GPIO_ACTIVE_HIGH>; /* PB3 */ ++ status-pwrkey-multiplexed; /* status acts as pwrkey */ ++ ++ sleep-gpios = <&pio 7 7 GPIO_ACTIVE_HIGH>; /* PH7 */ ++ wakeup-gpios = <&pio 1 2 GPIO_ACTIVE_HIGH>; /* PB2-RI */ ++ ++ dtr-gpios = <&r_pio 0 6 GPIO_ACTIVE_HIGH>; /* PL6-DTR */ ++ cts-gpios = <&pio 3 5 GPIO_ACTIVE_HIGH>; /* PD5-CTS */ ++ rts-gpios = <&pio 3 4 GPIO_ACTIVE_HIGH>; /* PD4-RTS */ ++ ++ quectel,qdai = "1,1,0,1,0,0,1,1"; ++ }; ++}; ++ + &usbphy { + usb-role-switch; + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.2.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.2.dts +index 40127186606f..5e988230e6e4 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.2.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.2.dts +@@ -103,6 +103,29 @@ &sgm3140 { + flash-gpios = <&pio 2 3 GPIO_ACTIVE_HIGH>; /* PC3 */ + }; + ++&uart3 { ++ modem { ++ compatible = "quectel,eg25"; ++ char-device-name = "modem-power"; ++ ++ power-supply = <®_vbat_bb>; /* PL7 */ ++ ++ enable-gpios = <&pio 7 8 GPIO_ACTIVE_LOW>; /* PH8 */ ++ reset-gpios = <&pio 2 4 GPIO_ACTIVE_HIGH>; /* PC4 */ ++ status-gpios = <&pio 7 9 GPIO_ACTIVE_HIGH>; /* PH9 */ ++ pwrkey-gpios = <&pio 1 3 GPIO_ACTIVE_HIGH>; /* PB3 */ ++ ++ host-ready-gpios = <&pio 7 7 GPIO_ACTIVE_HIGH>; /* PH7 */ ++ wakeup-gpios = <&r_pio 0 6 GPIO_ACTIVE_HIGH>; /* PL6-RI */ ++ ++ dtr-gpios = <&pio 1 2 GPIO_ACTIVE_HIGH>; /* PB2-DTR */ ++ cts-gpios = <&pio 3 5 GPIO_ACTIVE_HIGH>; /* PD5-CTS */ ++ rts-gpios = <&pio 3 4 GPIO_ACTIVE_HIGH>; /* PD4-RTS */ ++ ++ quectel,qdai = "1,1,0,1,0,0,1,1"; ++ }; ++}; ++ + &usbphy { + usb-role-switch; + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-power-supply-to-stk3311.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-power-supply-to-stk3311.patch new file mode 100644 index 000000000000..cdf579e244c5 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-power-supply-to-stk3311.patch @@ -0,0 +1,27 @@ +From 3aa8258da2bda4c6e5b18e223c6025fc77fd4bc5 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sat, 20 May 2023 16:58:11 +0200 +Subject: arm64: dts: sun50i-a64-pinephone: Add power supply to stk3311 + +This makes the driver disable the supply during sleep. + +Signed-off-by: Ondrej Jirman +--- + arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi +index 7de884adadb1..7d02c43cef05 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi +@@ -656,6 +656,7 @@ light-sensor@48 { + reg = <0x48>; + interrupt-parent = <&pio>; + interrupts = <1 0 IRQ_TYPE_EDGE_FALLING>; /* PB0 */ ++ vdd-supply = <®_ldo_io0>; + }; + + /* Accelerometer/gyroscope */ +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-reboot-mode-driver.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-reboot-mode-driver.patch new file mode 100644 index 000000000000..a4763224e5cc --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-reboot-mode-driver.patch @@ -0,0 +1,80 @@ +From 8cedae67577d6f1d1350e65571d6bfc6a5be9ffa Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Mon, 15 Feb 2021 17:45:13 +0100 +Subject: arm64: dts: sun50i-a64-pinephone: Add reboot mode driver + +With the latest p-boot (released 2021-02-15), you can reboot to +a particular boot option or to the menu, to FEL, to eMMC bootloader, +regardless of the user defined p-boot defaults. + +Call reboot with cmd of LINUX_REBOOT_CMD_RESTART2 and arg: + +- fel Reboot to FEL +- emmc-egon Reboot with chainload of bootloader from eMMC +- sd1-8 Reboot to SD bootfs option +- emmc1-8 Reboot to eMMC bootfs option +- menu Reboot to p-boot menu + +Signed-off-by: Ondrej Jirman +--- + .../dts/allwinner/sun50i-a64-pinephone.dtsi | 37 +++++++++++++++++++ + 1 file changed, 37 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi +index 7d02c43cef05..aeb282eba4b5 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi +@@ -423,6 +423,34 @@ sgm3140_flash: led { + }; + }; + ++ /* PinePhone specific reboot modes for p-boot. */ ++ reboot-mode { ++ compatible = "nvmem-reboot-mode"; ++ nvmem-cells = <&reboot_mode>; ++ nvmem-cell-names = "reboot-mode"; ++ ++ mode-normal = <0x0>; ++ mode-fel = <0xb0010fe1>; ++ mode-emmc-egon = <0xb001e33c>; ++ mode-sd1 = <0xb0010020>; ++ mode-sd2 = <0xb0010021>; ++ mode-sd3 = <0xb0010022>; ++ mode-sd4 = <0xb0010023>; ++ mode-sd5 = <0xb0010024>; ++ mode-sd6 = <0xb0010025>; ++ mode-sd7 = <0xb0010026>; ++ mode-sd8 = <0xb0010027>; ++ mode-emmc1 = <0xb0010000>; ++ mode-emmc2 = <0xb0010001>; ++ mode-emmc3 = <0xb0010002>; ++ mode-emmc4 = <0xb0010003>; ++ mode-emmc5 = <0xb0010004>; ++ mode-emmc6 = <0xb0010005>; ++ mode-emmc7 = <0xb0010006>; ++ mode-emmc8 = <0xb0010007>; ++ mode-menu = <0xb00100ff>; ++ }; ++ + speaker_amp: audio-amplifier { + compatible = "simple-audio-amplifier"; + enable-gpios = <&pio 2 7 GPIO_ACTIVE_HIGH>; /* PC7 */ +@@ -931,6 +959,15 @@ ®_rtc_ldo { + regulator-name = "vcc-rtc"; + }; + ++&rtc { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ reboot_mode: reboot-mode@4 { ++ reg = <0x4 0x4>; ++ }; ++}; ++ + &sound { + status = "okay"; + simple-audio-card,name = "PinePhone"; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-supply-for-i2c-bus-to-anx768.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-supply-for-i2c-bus-to-anx768.patch new file mode 100644 index 000000000000..040b4b6ec394 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-supply-for-i2c-bus-to-anx768.patch @@ -0,0 +1,54 @@ +From 1e444bc655527644b0420e0da7a3b2b41b460f6e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Mon, 18 Oct 2021 17:41:25 +0200 +Subject: arm64: dts: sun50i-a64-pinephone: Add supply for i2c bus to anx7688 + +The bus power needs to be enabled for anx7688 to be able to communicate +with the master. + +Signed-off-by: Ondrej Jirman +--- + arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.0.dts | 1 + + arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.1.dts | 1 + + arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.2.dts | 1 + + 3 files changed, 3 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.0.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.0.dts +index 8f6ea6b06aa9..cfb9518e057f 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.0.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.0.dts +@@ -43,6 +43,7 @@ hdmi-bridge@28 { + avdd18-supply = <®_ldo_io1>; + dvdd18-supply = <®_ldo_io1>; + avdd33-supply = <®_dldo1>; ++ i2c-supply = <®_ldo_io0>; + vconn-supply = <®_vconn5v0>; + hdmi_vt-supply = <®_dldo1>; + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.1.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.1.dts +index 63ceae07a0e0..35cb92e3b5f1 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.1.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.1.dts +@@ -52,6 +52,7 @@ hdmi-bridge@28 { + avdd18-supply = <®_ldo_io1>; + dvdd18-supply = <®_ldo_io1>; + avdd33-supply = <®_dldo1>; ++ i2c-supply = <®_ldo_io0>; + vconn-supply = <®_vconn5v0>; + hdmi_vt-supply = <®_dldo1>; + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.2.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.2.dts +index d28a23e98232..aeb5c69fe51d 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.2.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.2.dts +@@ -51,6 +51,7 @@ hdmi-bridge@28 { + avdd18-supply = <®_ldo_io1>; + dvdd18-supply = <®_ldo_io1>; + avdd33-supply = <®_dcdc1>; ++ i2c-supply = <®_ldo_io0>; + vconn-supply = <®_vconn5v0>; + hdmi_vt-supply = <®_dldo1>; + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-support-for-Bluetooth-audio.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-support-for-Bluetooth-audio.patch new file mode 100644 index 000000000000..609274bd8cc8 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-support-for-Bluetooth-audio.patch @@ -0,0 +1,33 @@ +From 34e327e91d7aafc189e2be3c701ceeb9b2dbaacd Mon Sep 17 00:00:00 2001 +From: Samuel Holland +Date: Thu, 6 Feb 2020 23:49:27 -0600 +Subject: arm64: dts: sun50i-a64-pinephone: Add support for Bluetooth audio + +The PinePhone has a Bluetooth chip with its PCM interface connected to +AIF3. Add the DAI link so headeset audio can be routed in hardware. + +Even though the link is mono, configuring the link for 2 slots is +required for compatibility with AIF2, which uses a single 32-bit slot, +and which shares its clock dividers with AIF3. Using equal clock +frequencies allows the modem and headset to be used at the same time. + +Signed-off-by: Samuel Holland +--- + arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi +index 91312b0ab896..4f471e80dcd8 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi +@@ -181,6 +181,7 @@ bat: battery { + bt_sco_codec: bt-sco-codec { + #sound-dai-cells = <1>; + compatible = "linux,bt-sco"; ++ sound-name-prefix = "Bluetooth"; + }; + + chosen { +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-support-for-Pinephone-1.2-be.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-support-for-Pinephone-1.2-be.patch new file mode 100644 index 000000000000..15cdb6639dcb --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-support-for-Pinephone-1.2-be.patch @@ -0,0 +1,54 @@ +From c2c2f67a5e65027ed1f142ad564fa0e170df47ca Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Thu, 10 Nov 2022 20:11:10 +0100 +Subject: arm64: dts: sun50i-a64-pinephone: Add support for Pinephone 1.2 beta + +Beta versions uses a different magnetometer chip. + +Signed-off-by: Ondrej Jirman +--- + arch/arm64/boot/dts/allwinner/Makefile | 1 + + .../allwinner/sun50i-a64-pinephone-1.2b.dts | 19 +++++++++++++++++++ + 2 files changed, 20 insertions(+) + create mode 100644 arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.2b.dts + +diff --git a/arch/arm64/boot/dts/allwinner/Makefile b/arch/arm64/boot/dts/allwinner/Makefile +index 00bed412ee31..9fd6b08ea528 100644 +--- a/arch/arm64/boot/dts/allwinner/Makefile ++++ b/arch/arm64/boot/dts/allwinner/Makefile +@@ -12,6 +12,7 @@ dtb-$(CONFIG_ARCH_SUNXI) += sun50i-a64-pinebook.dtb + dtb-$(CONFIG_ARCH_SUNXI) += sun50i-a64-pinephone-1.0.dtb + dtb-$(CONFIG_ARCH_SUNXI) += sun50i-a64-pinephone-1.1.dtb + dtb-$(CONFIG_ARCH_SUNXI) += sun50i-a64-pinephone-1.2.dtb ++dtb-$(CONFIG_ARCH_SUNXI) += sun50i-a64-pinephone-1.2b.dtb + dtb-$(CONFIG_ARCH_SUNXI) += sun50i-a64-pinetab.dtb + dtb-$(CONFIG_ARCH_SUNXI) += sun50i-a64-pinetab-early-adopter.dtb + dtb-$(CONFIG_ARCH_SUNXI) += sun50i-a64-sopine-baseboard.dtb +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.2b.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.2b.dts +new file mode 100644 +index 000000000000..f5ac2e9ca836 +--- /dev/null ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.2b.dts +@@ -0,0 +1,19 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++// Copyright (C) 2020 Ondrej Jirman ++ ++/dts-v1/; ++ ++#include "sun50i-a64-pinephone-1.2.dts" ++ ++/ { ++ model = "Pine64 PinePhone (1.2b)"; ++ compatible = "pine64,pinephone-1.2b", "pine64,pinephone", "allwinner,sun50i-a64"; ++}; ++ ++&lis3mdl { ++ status = "disabled"; ++}; ++ ++&af8133j { ++ status = "okay"; ++}; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-support-for-Pinephone-keyboa.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-support-for-Pinephone-keyboa.patch new file mode 100644 index 000000000000..de5ffb1f7085 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-support-for-Pinephone-keyboa.patch @@ -0,0 +1,170 @@ +From 671ab9b320585350524ef5f6fdf3e1d3a47b2b15 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Fri, 21 Jan 2022 23:28:48 +0100 +Subject: arm64: dts: sun50i-a64-pinephone: Add support for Pinephone keyboard + +Pinephone keyboard can be used with Pinephone. Describe it in +the device tree. + +Signed-off-by: Ondrej Jirman +--- + .../dts/allwinner/sun50i-a64-pinephone.dtsi | 136 ++++++++++++++++++ + 1 file changed, 136 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi +index 97c2f5956ef5..0124635431d5 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi +@@ -178,6 +178,122 @@ bat: battery { + <0xdf 98>; + }; + ++ ppkb_battery: keyboard-battery { ++ compatible = "simple-battery"; ++ voltage-min-design-microvolt = <3000000>; ++ voltage-max-design-microvolt = <4200000>; ++ energy-full-design-microwatt-hours = <21000000>; ++ charge-full-design-microamp-hours = <6000000>; ++ precharge-current-microamp = <100000>; ++ charge-term-current-microamp = <50000>; ++ constant-charge-current-max-microamp = <2000000>; ++ constant-charge-voltage-max-microvolt = <4200000>; ++ factory-internal-resistance-micro-ohms = <120000>; ++ ocv-capacity-celsius = <20>; ++ ocv-capacity-table-0 = ++ <4147328 100>, ++ <4132636 99>, ++ <4121720 98>, ++ <4110905 97>, ++ <4102063 96>, ++ <4092428 95>, ++ <4082371 94>, ++ <4074137 93>, ++ <4064172 92>, ++ <4056387 91>, ++ <4047198 90>, ++ <4038599 89>, ++ <4030933 88>, ++ <4021831 87>, ++ <4012613 86>, ++ <4003863 85>, ++ <3995132 84>, ++ <3986108 83>, ++ <3977434 82>, ++ <3967977 81>, ++ <3960286 80>, ++ <3951737 79>, ++ <3943518 78>, ++ <3935723 77>, ++ <3928692 76>, ++ <3920093 75>, ++ <3912676 74>, ++ <3905481 73>, ++ <3899881 72>, ++ <3892341 71>, ++ <3887140 70>, ++ <3880005 69>, ++ <3876060 68>, ++ <3869195 67>, ++ <3863234 66>, ++ <3857808 65>, ++ <3851464 64>, ++ <3846976 63>, ++ <3840724 62>, ++ <3835254 61>, ++ <3830946 60>, ++ <3826564 59>, ++ <3821275 58>, ++ <3817413 57>, ++ <3811941 56>, ++ <3808947 55>, ++ <3804959 54>, ++ <3800958 53>, ++ <3797447 52>, ++ <3794303 51>, ++ <3790341 50>, ++ <3787022 49>, ++ <3783436 48>, ++ <3779936 47>, ++ <3776176 46>, ++ <3772160 45>, ++ <3769348 44>, ++ <3766705 43>, ++ <3762481 42>, ++ <3760894 41>, ++ <3758396 40>, ++ <3755803 39>, ++ <3753543 38>, ++ <3749691 37>, ++ <3747026 36>, ++ <3743937 35>, ++ <3741872 34>, ++ <3738529 33>, ++ <3737528 32>, ++ <3734059 31>, ++ <3731586 30>, ++ <3728576 29>, ++ <3725939 28>, ++ <3721208 27>, ++ <3718239 26>, ++ <3713032 25>, ++ <3709785 24>, ++ <3704045 23>, ++ <3701203 22>, ++ <3695965 21>, ++ <3691074 20>, ++ <3684562 19>, ++ <3679140 18>, ++ <3670993 17>, ++ <3664243 16>, ++ <3655383 15>, ++ <3647666 14>, ++ <3642747 13>, ++ <3637775 12>, ++ <3632593 11>, ++ <3627120 10>, ++ <3620923 9>, ++ <3611465 8>, ++ <3597536 7>, ++ <3577366 6>, ++ <3545253 5>, ++ <3497377 4>, ++ <3430377 3>, ++ <3337780 2>, ++ <3183953 1>, ++ <3064061 0>; ++ }; ++ + bt_sco_codec: bt-sco-codec { + #sound-dai-cells = <1>; + compatible = "linux,bt-sco"; +@@ -550,6 +666,26 @@ accelerometer@68 { + /* Connected to pogo pins (external spring based pinheader for user addons) */ + &i2c2 { + status = "okay"; ++ ++ ppkb: keyboard@15 { ++ compatible = "pine64,pinephone-keyboard"; ++ reg = <0x15>; ++ interrupt-parent = <&r_pio>; ++ interrupts = <0 12 IRQ_TYPE_EDGE_FALLING>; /* PL12 */ ++ vbat-supply = <®_usb_5v>; ++ wakeup-source; ++ ++ i2c { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ charger@75 { ++ compatible = "injoinic,ip5209"; ++ reg = <0x75>; ++ monitored-battery = <&ppkb_battery>; ++ }; ++ }; ++ }; + }; + + &i2s2 { +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-support-for-modem-audio.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-support-for-modem-audio.patch new file mode 100644 index 000000000000..dd64937752d1 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Add-support-for-modem-audio.patch @@ -0,0 +1,71 @@ +From 2cf576727cd3f9434c1a9921d69ca2eb04273f3c Mon Sep 17 00:00:00 2001 +From: Samuel Holland +Date: Wed, 5 Feb 2020 23:14:27 -0600 +Subject: arm64: dts: sun50i-a64-pinephone: Add support for modem audio + +The PinePhone has a Quectel EG25-G modem (a variant of the EC25) with +its PCM interface connected to AIF2. Add the DAI link so call audio can +be routed in hardware. + +The modem supports two fixed sample rates, and one fixed word size, with +a variable BCLK frequency. Use the minimum supported BCLK frequency, +which corresponds to a slot width of 32 bits. + +Signed-off-by: Samuel Holland +--- + .../dts/allwinner/sun50i-a64-pinephone.dtsi | 25 ++++++++++++++++++- + 1 file changed, 24 insertions(+), 1 deletion(-) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi +index 4f471e80dcd8..76e55a0e415a 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi +@@ -188,6 +188,12 @@ chosen { + stdout-path = "serial0:115200n8"; + }; + ++ ec25_codec: ec25-codec { ++ #sound-dai-cells = <1>; ++ compatible = "quectel,ec25"; ++ sound-name-prefix = "Modem"; ++ }; ++ + i2c_csi: i2c-csi { + compatible = "i2c-gpio"; + sda-gpios = <&pio 4 13 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; /* PE13 */ +@@ -316,7 +322,7 @@ vibrator { + + &codec { + pinctrl-names = "default"; +- pinctrl-0 = <&aif3_pins>; ++ pinctrl-0 = <&aif2_pins>, <&aif3_pins>; + status = "okay"; + }; + +@@ -799,6 +805,23 @@ &sound { + "Headset Microphone", "HBIAS", + "MIC2", "Headset Microphone"; + ++ simple-audio-card,dai-link@1 { ++ format = "dsp_a"; ++ frame-master = <&link1_codec>; ++ bitclock-master = <&link1_codec>; ++ bitclock-inversion; ++ ++ link1_cpu: cpu { ++ sound-dai = <&ec25_codec 0>; ++ }; ++ ++ link1_codec: codec { ++ sound-dai = <&codec 1>; ++ dai-tdm-slot-num = <1>; ++ dai-tdm-slot-width = <32>; ++ }; ++ }; ++ + simple-audio-card,dai-link@2 { + format = "dsp_a"; + frame-master = <&link2_codec>; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Bump-I2C-frequency-to-400kHz.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Bump-I2C-frequency-to-400kHz.patch new file mode 100644 index 000000000000..99992b00b3de --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Bump-I2C-frequency-to-400kHz.patch @@ -0,0 +1,35 @@ +From 429717d518e8ab162336302782b040755e6f3d9f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Mon, 30 Nov 2020 06:14:07 +0100 +Subject: arm64: dts: sun50i-a64-pinephone: Bump I2C frequency to 400kHz + +High speed. :) + +Signed-off-by: Ondrej Jirman +--- + arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi +index 76e55a0e415a..c2746a21b021 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi +@@ -478,6 +478,7 @@ ov5640_ep: endpoint { + + &i2c0 { + status = "okay"; ++ clock-frequency = <400000>; + + touchscreen@5d { + compatible = "goodix,gt917s"; +@@ -495,6 +496,7 @@ touchscreen@5d { + + &i2c1 { + status = "okay"; ++ clock-frequency = <400000>; + + /* Alternative magnetometer */ + af8133j: magnetometer@1c { +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Don-t-make-lradc-keys-a-wakeup-s.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Don-t-make-lradc-keys-a-wakeup-s.patch new file mode 100644 index 000000000000..e9171c2d4cf2 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Don-t-make-lradc-keys-a-wakeup-s.patch @@ -0,0 +1,29 @@ +From c5f3ddccace279a6885d01b7919e7dd5cdf21b3c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Mon, 26 Apr 2021 01:31:27 +0200 +Subject: arm64: dts: sun50i-a64-pinephone: Don't make lradc keys a wakeup + source + +This allow the crust to reach lower suspend state. + +Signed-off-by: Ondrej Jirman +--- + arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi +index f3b1c0e5493f..97c2f5956ef5 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi +@@ -558,7 +558,7 @@ &i2s2 { + + &lradc { + vref-supply = <®_aldo3>; +- wakeup-source; ++// wakeup-source; + status = "okay"; + + button-200 { +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Enable-Pinephone-Keyboard-power-.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Enable-Pinephone-Keyboard-power-.patch new file mode 100644 index 000000000000..ab4aab7719bf --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Enable-Pinephone-Keyboard-power-.patch @@ -0,0 +1,34 @@ +From 88b3c983bfedc0ccceede32e6055320772a8c146 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sat, 16 Apr 2022 02:12:06 +0200 +Subject: arm64: dts: sun50i-a64-pinephone: Enable Pinephone Keyboard power + manager + +Signed-off-by: Ondrej Jirman +--- + arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi +index 0124635431d5..7de884adadb1 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi +@@ -434,6 +434,15 @@ vibrator { + enable-gpios = <&pio 3 2 GPIO_ACTIVE_HIGH>; /* PD2 */ + vcc-supply = <®_dcdc1>; + }; ++ ++ keyboard-power { ++ compatible = "megi,pinephone-keyboard-power-manager"; ++ phone-battery = "axp20x-battery"; ++ phone-usb = "axp20x-usb"; ++ kb-battery = "ip5xxx-battery"; ++ kb-boost = "ip5xxx-boost"; ++ kb-usb = "ip5xxx-usb"; ++ }; + }; + + &codec { +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Enable-internal-HMIC-bias.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Enable-internal-HMIC-bias.patch new file mode 100644 index 000000000000..3892e491d638 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Enable-internal-HMIC-bias.patch @@ -0,0 +1,29 @@ +From b4a237d25b94da596769bf1669e8e8017a0d2145 Mon Sep 17 00:00:00 2001 +From: Samuel Holland +Date: Wed, 23 Sep 2020 00:13:54 -0500 +Subject: arm64: dts: sun50i-a64-pinephone: Enable internal HMIC bias + +Revisions 1.0 and 1.1 of the PinePhone mainboard do not have an external +resistor connecting HBIAS to MIC2. Enable the internal resistor to +provide the appropriate mic bias. + +Signed-off-by: Samuel Holland +--- + arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.0.dts | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.0.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.0.dts +index ff01e2be26e7..8f6ea6b06aa9 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.0.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.0.dts +@@ -126,3 +126,7 @@ &sgm3140 { + enable-gpios = <&pio 2 3 GPIO_ACTIVE_HIGH>; /* PC3 */ + flash-gpios = <&pio 3 24 GPIO_ACTIVE_HIGH>; /* PD24 */ + }; ++ ++&codec_analog { ++ allwinner,internal-bias-resistor; ++}; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Fix-BH-modem-manager-behavior.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Fix-BH-modem-manager-behavior.patch new file mode 100644 index 000000000000..5be69fc53c78 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Fix-BH-modem-manager-behavior.patch @@ -0,0 +1,29 @@ +From 60ededfd0381c8c76a587daff8ca5dfe47195149 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Wed, 5 Aug 2020 11:19:01 +0200 +Subject: arm64: dts: sun50i-a64-pinephone: Fix BH modem manager behavior + +PP 1.1 has STATUS line multiplexed with POWER KEY, but reading it doesn't +work reliably. Just treat 1.1 as if it was 1.0, and ignore the STATUS. +--- + arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.1.dts | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.1.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.1.dts +index 93dc8512fe45..d67f31ef529e 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.1.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.1.dts +@@ -123,8 +123,8 @@ modem { + + enable-gpios = <&pio 7 8 GPIO_ACTIVE_LOW>; /* PH8 */ + reset-gpios = <&pio 2 4 GPIO_ACTIVE_HIGH>; /* PC4 */ +- status-gpios = <&pio 1 3 GPIO_ACTIVE_HIGH>; /* PB3 */ +- status-pwrkey-multiplexed; /* status acts as pwrkey */ ++ pwrkey-gpios = <&pio 1 3 GPIO_ACTIVE_HIGH>; /* PB3 */ ++ //status-pwrkey-multiplexed; /* status acts as pwrkey */ + + sleep-gpios = <&pio 7 7 GPIO_ACTIVE_HIGH>; /* PH7 */ + wakeup-gpios = <&pio 1 2 GPIO_ACTIVE_HIGH>; /* PB2-RI */ +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Power-off-the-touch-controller-i.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Power-off-the-touch-controller-i.patch new file mode 100644 index 000000000000..230dd5c3a569 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Power-off-the-touch-controller-i.patch @@ -0,0 +1,28 @@ +From f495e5bfb6f5967b34cf61a8e9a0dfb91c0c726d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sun, 25 Apr 2021 03:17:39 +0200 +Subject: arm64: dts: sun50i-a64-pinephone: Power off the touch controller in + sleep + +Otherwise it stays on and consumes quite a bit of power. + +Signed-off-by: Ondrej Jirman +--- + arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi +index 3885a1fd3916..f3b1c0e5493f 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi +@@ -491,6 +491,7 @@ touchscreen@5d { + VDDIO-supply = <®_ldo_io0>; + touchscreen-size-x = <720>; + touchscreen-size-y = <1440>; ++ poweroff-in-suspend; + }; + }; + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Set-minimum-backlight-duty-cycle.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Set-minimum-backlight-duty-cycle.patch new file mode 100644 index 000000000000..cfcbee717a95 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Set-minimum-backlight-duty-cycle.patch @@ -0,0 +1,73 @@ +From d7c78e25c6b1f65cbf3fdef64401a804f62392e1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Mon, 7 Jun 2021 20:23:52 +0200 +Subject: arm64: dts: sun50i-a64-pinephone: Set minimum backlight duty cycle to + 10% + +Instead of creating our own table, use existing lth_brightness +functionality in the pwm_bl driver. + +Signed-off-by: Ondrej Jirman +--- + .../dts/allwinner/sun50i-a64-pinephone-1.1.dts | 16 +--------------- + .../dts/allwinner/sun50i-a64-pinephone-1.2.dts | 17 +---------------- + 2 files changed, 2 insertions(+), 31 deletions(-) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.1.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.1.dts +index d67f31ef529e..63ceae07a0e0 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.1.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.1.dts +@@ -26,21 +26,7 @@ &axp803 { + + &backlight { + power-supply = <®_ldo_io0>; +- /* +- * PWM backlight circuit on this PinePhone revision was changed since +- * 1.0, and the lowest PWM duty cycle that doesn't lead to backlight +- * being off is around 20%. Duty cycle for the lowest brightness level +- * also varries quite a bit between individual boards, so the lowest +- * value here was chosen as a safe default. +- */ +- brightness-levels = < +- 774 793 814 842 +- 882 935 1003 1088 +- 1192 1316 1462 1633 +- 1830 2054 2309 2596 +- 2916 3271 3664 4096>; +- num-interpolated-steps = <50>; +- default-brightness-level = <400>; ++ lth-brightness = <10>; + }; + + &codec_analog { +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.2.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.2.dts +index 5e988230e6e4..d28a23e98232 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.2.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.2.dts +@@ -17,22 +17,7 @@ wifi_pwrseq: wifi-pwrseq { + + &backlight { + power-supply = <®_ldo_io0>; +- /* +- * PWM backlight circuit on this PinePhone revision was changed since 1.0, +- * and the lowest PWM duty cycle that doesn't lead to backlight being off +- * is around 10%. Duty cycle for the lowest brightness level also varries +- * quite a bit between individual boards, so the lowest value here was +- * chosen as a safe default. +- */ +- brightness-levels = < +- 5000 5248 5506 5858 6345 +- 6987 7805 8823 10062 11543 +- 13287 15317 17654 20319 23336 +- 26724 30505 34702 39335 44427 +- 50000 +- >; +- num-interpolated-steps = <50>; +- default-brightness-level = <500>; ++ lth-brightness = <10>; + }; + + &lis3mdl { +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Shorten-post-power-on-delay-on-m.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Shorten-post-power-on-delay-on-m.patch new file mode 100644 index 000000000000..59e3d658940b --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Shorten-post-power-on-delay-on-m.patch @@ -0,0 +1,43 @@ +From 02f9ee2c698d1d2debe17d60569ebb7366280942 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Thu, 6 Feb 2020 04:58:32 +0100 +Subject: arm64: dts: sun50i-a64-pinephone: Shorten post-power-on-delay on mmcs + +These are not needed. + +Signed-off-by: Ondrej Jirman +--- + arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi +index 13213a3704a1..91312b0ab896 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi +@@ -574,6 +574,7 @@ &mmc0 { + cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ + disable-wp; + bus-width = <4>; ++ post-power-on-delay-ms = <1>; /* power is already turned on by the bootloader */ + status = "okay"; + }; + +@@ -584,6 +585,7 @@ &mmc1 { + vqmmc-supply = <®_dldo4>; + bus-width = <4>; + non-removable; ++ post-power-on-delay-ms = <1>; /* wifi power is always on */ + status = "okay"; + + rtl8723cs: wifi@1 { +@@ -599,6 +601,7 @@ &mmc2 { + bus-width = <8>; + non-removable; + cap-mmc-hw-reset; ++ post-power-on-delay-ms = <1>; /* power is already turned on by the bootloader */ + status = "okay"; + }; + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Use-newer-jack-detection-impleme.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Use-newer-jack-detection-impleme.patch new file mode 100644 index 000000000000..3a9127fe9ff0 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Use-newer-jack-detection-impleme.patch @@ -0,0 +1,35 @@ +From 3eaa6aabbb8758f9309a73287cfaaf9005a6979e Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Fri, 23 Feb 2024 01:53:14 +0100 +Subject: arm64: dts: sun50i-a64-pinephone: Use newer jack detection + implementaion + +Signed-off-by: Ondrej Jirman +--- + arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi +index aeb282eba4b5..e7e90e5baff4 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi +@@ -476,6 +476,7 @@ keyboard-power { + &codec { + pinctrl-names = "default"; + pinctrl-0 = <&aif2_pins>, <&aif3_pins>; ++ jack-type = "headset"; + status = "okay"; + }; + +@@ -971,7 +972,7 @@ reboot_mode: reboot-mode@4 { + &sound { + status = "okay"; + simple-audio-card,name = "PinePhone"; +- simple-audio-card,aux-devs = <&codec_analog>, <&speaker_amp>; ++ simple-audio-card,aux-devs = <&codec_analog>, <&codec>, <&speaker_amp>; + simple-audio-card,widgets = "Microphone", "Headset Microphone", + "Microphone", "Internal Microphone", + "Headphone", "Headphone Jack", +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Workaround-broken-HDMI-HPD-signa.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Workaround-broken-HDMI-HPD-signa.patch new file mode 100644 index 000000000000..bd2467741a5b --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinephone-Workaround-broken-HDMI-HPD-signa.patch @@ -0,0 +1,84 @@ +From 5547207e11f604491659e71791488f00b010d3c9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Tue, 26 Oct 2021 01:25:46 +0200 +Subject: arm64: dts: sun50i-a64-pinephone: Workaround broken HDMI HPD signal + +HDMI HPD signal from ANX7688 to SoC's HDMI PHY is wrongly level +shifted and does not register most of the time at the SoC's input. + +We need to do the signalling in software. Link anx7688 to hdmi +driver via extcon interface. + +Signed-off-by: Ondrej Jirman +--- + arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.0.dts | 6 +++++- + arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.1.dts | 6 +++++- + arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.2.dts | 6 +++++- + 3 files changed, 15 insertions(+), 3 deletions(-) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.0.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.0.dts +index cfb9518e057f..0530881d8401 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.0.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.0.dts +@@ -25,7 +25,7 @@ &axp803 { + }; + + &i2c0 { +- hdmi-bridge@28 { ++ anx7688: hdmi-bridge@28 { + compatible = "analogix,anx7688"; + reg = <0x28>; + +@@ -131,3 +131,7 @@ &sgm3140 { + &codec_analog { + allwinner,internal-bias-resistor; + }; ++ ++&hdmi { ++ extcon = <&anx7688 0>; ++}; +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.1.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.1.dts +index 35cb92e3b5f1..053e4add9973 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.1.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.1.dts +@@ -34,7 +34,7 @@ &codec_analog { + }; + + &i2c0 { +- hdmi-bridge@28 { ++ anx7688: hdmi-bridge@28 { + compatible = "analogix,anx7688"; + reg = <0x28>; + +@@ -133,3 +133,7 @@ usb0_drd_sw: endpoint { + }; + }; + }; ++ ++&hdmi { ++ extcon = <&anx7688 0>; ++}; +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.2.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.2.dts +index aeb5c69fe51d..c4e3547e02e9 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.2.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.2.dts +@@ -34,7 +34,7 @@ &mmc1 { + }; + + &i2c0 { +- hdmi-bridge@28 { ++ anx7688: hdmi-bridge@28 { + compatible = "analogix,anx7688"; + reg = <0x28>; + +@@ -121,3 +121,7 @@ usb0_drd_sw: endpoint { + }; + }; + }; ++ ++&hdmi { ++ extcon = <&anx7688 0>; ++}; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinetab-Add-accelerometer.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinetab-Add-accelerometer.patch new file mode 100644 index 000000000000..910f70705df7 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinetab-Add-accelerometer.patch @@ -0,0 +1,38 @@ +From b76c601b4f0f85265c2d33adf1c4642c6e050e2a Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sun, 18 Feb 2024 17:39:58 +0100 +Subject: arm64: dts: sun50i-a64-pinetab: Add accelerometer + +Add accelerometer. Patch picked up from here: + +https://github.com/dreemurrs-embedded/Pine64-Arch/blob/master/PKGBUILDS/pine64/linux-megi/pinetab-accelerometer.patch + +Signed-off-by: Ondrej Jirman +--- + arch/arm64/boot/dts/allwinner/sun50i-a64-pinetab.dts | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinetab.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinetab.dts +index b7ab7d28914e..8c91ed7083d7 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinetab.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinetab.dts +@@ -237,7 +237,15 @@ &i2c0_pins { + &i2c1 { + status = "okay"; + +- /* TODO: add Bochs BMA223 accelerometer here */ ++ bma223@18 { ++ compatible = "bosch,bma223", "bosch,bma222e"; ++ reg = <0x18>; ++ interrupt-parent = <&pio>; ++ interrupts = <7 5 IRQ_TYPE_LEVEL_HIGH>; /* PH5 */ ++ mount-matrix = "0", "-1", "0", ++ "-1", "0", "0", ++ "0", "0", "-1"; ++ }; + }; + + &lradc { +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinetab-Name-sound-card-PineTab.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinetab-Name-sound-card-PineTab.patch new file mode 100644 index 000000000000..4d98add63360 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinetab-Name-sound-card-PineTab.patch @@ -0,0 +1,28 @@ +From c530886e537d311c5c8833f1c685ca457155e5fb Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sun, 18 Feb 2024 17:38:00 +0100 +Subject: arm64: dts: sun50i-a64-pinetab: Name sound card PineTab + +There's no upstream UCM for PineTab, yet. So I guess we can safely +rename it. Pine64-Arch is using this name. + +Signed-off-by: Ondrej Jirman +--- + arch/arm64/boot/dts/allwinner/sun50i-a64-pinetab.dts | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinetab.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinetab.dts +index 6056c965baf6..b7ab7d28914e 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinetab.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinetab.dts +@@ -475,6 +475,7 @@ hdmi_out_con: endpoint { + + &sound { + status = "okay"; ++ simple-audio-card,name = "PineTab"; + simple-audio-card,aux-devs = <&codec_analog>, <&speaker_amp>; + simple-audio-card,widgets = "Microphone", "Internal Microphone Left", + "Microphone", "Internal Microphone Right", +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinetab-enable-RTL8723CS-bluetooth.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinetab-enable-RTL8723CS-bluetooth.patch new file mode 100644 index 000000000000..80701925b80d --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-a64-pinetab-enable-RTL8723CS-bluetooth.patch @@ -0,0 +1,44 @@ +From ad81b4fe119c8d10e47fc7aa721f5505103176ea Mon Sep 17 00:00:00 2001 +From: Icenowy Zheng +Date: Sun, 14 Apr 2019 23:46:47 +0800 +Subject: arm64: dts: sun50i-a64-pinetab: enable RTL8723CS bluetooth + +PineTab has a RTL8723CS Wi-Fi/BT combo chip on board, the bluetooth part +of it communicates with A64 via UART, and the power of it is controlled +with some GPIO at PL bank. + +Enable the bluetooth in the device tree. + +Signed-off-by: Icenowy Zheng +--- + .../boot/dts/allwinner/sun50i-a64-pinetab.dts | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinetab.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinetab.dts +index 8c91ed7083d7..419fe19b4a6f 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinetab.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinetab.dts +@@ -511,6 +511,20 @@ &uart0 { + status = "okay"; + }; + ++&uart1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&uart1_pins>, <&uart1_rts_cts_pins>; ++ status = "okay"; ++ ++ bluetooth { ++ compatible = "realtek,rtl8723cs-bt"; ++ reset-gpios = <&r_pio 0 4 GPIO_ACTIVE_LOW>; /* PL4 */ ++ device-wake-gpios = <&r_pio 0 5 GPIO_ACTIVE_LOW>; /* PL5 */ ++ host-wake-gpios = <&r_pio 0 6 GPIO_ACTIVE_HIGH>; /* PL6 */ ++ firmware-postfix = "pinebook"; ++ }; ++}; ++ + &usb_otg { + dr_mode = "otg"; + status = "okay"; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-h5-Add-missing-GPU-trip-point.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-h5-Add-missing-GPU-trip-point.patch new file mode 100644 index 000000000000..a362ef8d9f1e --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-h5-Add-missing-GPU-trip-point.patch @@ -0,0 +1,35 @@ +From c83ffe6aa547f78dc811a570dd78baf0c482a41f Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Mon, 13 Mar 2023 06:02:46 +0100 +Subject: arm64: dts: sun50i-h5: Add missing GPU trip point + +Without this, thermal sensor driver fails to probe. + +Signed-off-by: Ondrej Jirman +--- + arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi +index e02a3848003e..a6a2e2a19e75 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi +@@ -234,6 +234,15 @@ gpu-thermal { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&ths 1>; ++ ++ trips { ++ gpu_crit: gpu-crit { ++ /* milliCelsius */ ++ temperature = <110000>; ++ hysteresis = <2000>; ++ type = "critical"; ++ }; ++ }; + }; + }; + }; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-h5-Use-my-own-more-aggressive-OPPs-on-H5.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-h5-Use-my-own-more-aggressive-OPPs-on-H5.patch new file mode 100644 index 000000000000..5a85dedab8ff --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-dts-sun50i-h5-Use-my-own-more-aggressive-OPPs-on-H5.patch @@ -0,0 +1,97 @@ +From a926b916c845808bcbfc976427be67d28c980a4c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Mon, 17 Aug 2020 23:43:53 +0200 +Subject: arm64: dts: sun50i-h5: Use my own more aggressive OPPs on H5 + +Signed-off-by: Ondrej Jirman +--- + .../boot/dts/allwinner/sun50i-h5-cpu-opp.dtsi | 44 +++++++++---------- + 1 file changed, 22 insertions(+), 22 deletions(-) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h5-cpu-opp.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h5-cpu-opp.dtsi +index 1afad8b437d7..a74463c8cc52 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-h5-cpu-opp.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-h5-cpu-opp.dtsi +@@ -6,57 +6,57 @@ cpu_opp_table: opp-table-cpu { + compatible = "operating-points-v2"; + opp-shared; + +- opp-408000000 { +- opp-hz = /bits/ 64 <408000000>; +- opp-microvolt = <1000000 1000000 1310000>; ++ opp-480000000 { ++ opp-hz = /bits/ 64 <480000000>; ++ opp-microvolt = <1040000 1040000 1300000>; + clock-latency-ns = <244144>; /* 8 32k periods */ + }; + + opp-648000000 { + opp-hz = /bits/ 64 <648000000>; +- opp-microvolt = <1040000 1040000 1310000>; ++ opp-microvolt = <1040000 1040000 1300000>; + clock-latency-ns = <244144>; /* 8 32k periods */ + }; + + opp-816000000 { + opp-hz = /bits/ 64 <816000000>; +- opp-microvolt = <1080000 1080000 1310000>; +- clock-latency-ns = <244144>; /* 8 32k periods */ +- }; +- +- opp-912000000 { +- opp-hz = /bits/ 64 <912000000>; +- opp-microvolt = <1120000 1120000 1310000>; ++ opp-microvolt = <1100000 1100000 1300000>; + clock-latency-ns = <244144>; /* 8 32k periods */ + }; + + opp-960000000 { + opp-hz = /bits/ 64 <960000000>; +- opp-microvolt = <1160000 1160000 1310000>; ++ opp-microvolt = <1200000 1200000 1300000>; + clock-latency-ns = <244144>; /* 8 32k periods */ + }; + + opp-1008000000 { + opp-hz = /bits/ 64 <1008000000>; +- opp-microvolt = <1200000 1200000 1310000>; ++ opp-microvolt = <1200000 1200000 1300000>; ++ clock-latency-ns = <244144>; /* 8 32k periods */ ++ }; ++ ++ opp-1104000000 { ++ opp-hz = /bits/ 64 <1104000000>; ++ opp-microvolt = <1320000 1320000 1320000>; + clock-latency-ns = <244144>; /* 8 32k periods */ + }; + +- opp-1056000000 { +- opp-hz = /bits/ 64 <1056000000>; +- opp-microvolt = <1240000 1240000 1310000>; ++ opp-1200000000 { ++ opp-hz = /bits/ 64 <1200000000>; ++ opp-microvolt = <1320000 1320000 1320000>; + clock-latency-ns = <244144>; /* 8 32k periods */ + }; + +- opp-1104000000 { +- opp-hz = /bits/ 64 <1104000000>; +- opp-microvolt = <1260000 1260000 1310000>; ++ opp-1296000000 { ++ opp-hz = /bits/ 64 <1296000000>; ++ opp-microvolt = <1340000 1340000 1340000>; + clock-latency-ns = <244144>; /* 8 32k periods */ + }; + +- opp-1152000000 { +- opp-hz = /bits/ 64 <1152000000>; +- opp-microvolt = <1300000 1300000 1310000>; ++ opp-1368000000 { ++ opp-hz = /bits/ 64 <1368000000>; ++ opp-microvolt = <1400000 1400000 1400000>; + clock-latency-ns = <244144>; /* 8 32k periods */ + }; + }; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-xor-Select-32regs-without-benchmark-to-speed-up-boot.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-xor-Select-32regs-without-benchmark-to-speed-up-boot.patch new file mode 100644 index 000000000000..463464f782e4 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/arm64-xor-Select-32regs-without-benchmark-to-speed-up-boot.patch @@ -0,0 +1,45 @@ +From b8f9f4c5427c44cc2c23c51687424d2dc5c25fcf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Thu, 10 Sep 2020 21:38:28 +0200 +Subject: arm64: xor: Select 32regs without benchmark to speed up boot + +32regs is fastest on Cortex-A53. + +Signed-off-by: Ondrej Jirman +--- + arch/arm64/include/asm/xor.h | 14 ++++---------- + 1 file changed, 4 insertions(+), 10 deletions(-) + +diff --git a/arch/arm64/include/asm/xor.h b/arch/arm64/include/asm/xor.h +index befcd8a7abc9..e4487ddc76e1 100644 +--- a/arch/arm64/include/asm/xor.h ++++ b/arch/arm64/include/asm/xor.h +@@ -57,21 +57,15 @@ xor_neon_5(unsigned long bytes, unsigned long * __restrict p1, + kernel_neon_end(); + } + +-static struct xor_block_template xor_block_arm64 = { ++static struct xor_block_template xor_block_arm64 __maybe_unused = { + .name = "arm64_neon", + .do_2 = xor_neon_2, + .do_3 = xor_neon_3, + .do_4 = xor_neon_4, + .do_5 = xor_neon_5 + }; +-#undef XOR_TRY_TEMPLATES +-#define XOR_TRY_TEMPLATES \ +- do { \ +- xor_speed(&xor_block_8regs); \ +- xor_speed(&xor_block_32regs); \ +- if (cpu_has_neon()) { \ +- xor_speed(&xor_block_arm64);\ +- } \ +- } while (0) ++ ++#define XOR_SELECT_TEMPLATE(x) \ ++ (&xor_block_32regs) + + #endif /* ! CONFIG_KERNEL_MODE_NEON */ +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/bluetooth-bcm-Restore-drive_rts_on_open-true-behavior-on-bcm207.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/bluetooth-bcm-Restore-drive_rts_on_open-true-behavior-on-bcm207.patch new file mode 100644 index 000000000000..b458c52f426e --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/bluetooth-bcm-Restore-drive_rts_on_open-true-behavior-on-bcm207.patch @@ -0,0 +1,35 @@ +From 23fcf552af5ba182cd53d5af326a73c86a351eae Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sun, 7 Aug 2022 14:28:16 +0200 +Subject: bluetooth: bcm: Restore drive_rts_on_open = true behavior on + bcm20702a1 + +This is what it was on v5.5. It doesn't fix the UART issues though. + +Signed-off-by: Ondrej Jirman +--- + drivers/bluetooth/hci_bcm.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c +index 9684eb16059b..5447f409a701 100644 +--- a/drivers/bluetooth/hci_bcm.c ++++ b/drivers/bluetooth/hci_bcm.c +@@ -1584,8 +1584,13 @@ static struct bcm_device_data cyw55572_device_data = { + .max_autobaud_speed = 921600, + }; + ++static struct bcm_device_data bcm20702a1_device_data = { ++ .drive_rts_on_open = true, ++ .no_early_set_baudrate = true, ++}; ++ + static const struct of_device_id bcm_bluetooth_of_match[] = { +- { .compatible = "brcm,bcm20702a1" }, ++ { .compatible = "brcm,bcm20702a1", .data = &bcm20702a1_device_data }, + { .compatible = "brcm,bcm4329-bt" }, + { .compatible = "brcm,bcm4330-bt" }, + { .compatible = "brcm,bcm4334-bt" }, +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/bluetooth-h5-Don-t-re-initialize-rtl8723cs-on-resume.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/bluetooth-h5-Don-t-re-initialize-rtl8723cs-on-resume.patch new file mode 100644 index 000000000000..b880bf8ec8ea --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/bluetooth-h5-Don-t-re-initialize-rtl8723cs-on-resume.patch @@ -0,0 +1,28 @@ +From dcd320fc4e24521978144b482cb01814b4f80c5e Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sun, 30 Apr 2023 18:12:17 +0200 +Subject: bluetooth: h5: Don't re-initialize rtl8723cs on resume + +The power is kept across during sleep, and this is not necessary. + +Signed-off-by: Ondrej Jirman +--- + drivers/bluetooth/hci_h5.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c +index c0436881a533..3f6955ce9b7c 100644 +--- a/drivers/bluetooth/hci_h5.c ++++ b/drivers/bluetooth/hci_h5.c +@@ -1108,7 +1108,7 @@ static const struct of_device_id rtl_bluetooth_of_match[] = { + { .compatible = "realtek,rtl8723bs-bt", + .data = (const void *)&h5_data_rtl8723bs }, + { .compatible = "realtek,rtl8723cs-bt", +- .data = (const void *)&h5_data_rtl8723bs }, ++ .data = (const void *)&h5_data_rtl8822cs }, + { .compatible = "realtek,rtl8723ds-bt", + .data = (const void *)&h5_data_rtl8723bs }, + #endif +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/clk-sunxi-ng-Don-t-use-CPU-PLL-gating-and-CPUX-reparenting-to-H.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/clk-sunxi-ng-Don-t-use-CPU-PLL-gating-and-CPUX-reparenting-to-H.patch new file mode 100644 index 000000000000..03174314773a --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/clk-sunxi-ng-Don-t-use-CPU-PLL-gating-and-CPUX-reparenting-to-H.patch @@ -0,0 +1,55 @@ +From c654921edfdbeca309621420e64ee5b014fc306b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Tue, 10 Mar 2020 06:04:32 +0100 +Subject: clk: sunxi-ng: Don't use CPU PLL gating and CPUX reparenting to HOSC + +It's not necessary when not using dividers on CPU PLL, and it's +causing cntvct jump backs on H3. + +Signed-off-by: Ondrej Jirman +--- + drivers/clk/sunxi-ng/ccu-sun8i-h3.c | 21 --------------------- + 1 file changed, 21 deletions(-) + +diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c +index 33254d63ecb5..9bd05a0488ff 100644 +--- a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c ++++ b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c +@@ -1029,20 +1029,6 @@ static const struct sunxi_ccu_desc sun50i_h5_ccu_desc = { + .num_resets = ARRAY_SIZE(sun50i_h5_ccu_resets), + }; + +-static struct ccu_pll_nb sun8i_h3_pll_cpu_nb = { +- .common = &pll_cpux_clk.common, +- /* copy from pll_cpux_clk */ +- .enable = BIT(31), +- .lock = BIT(28), +-}; +- +-static struct ccu_mux_nb sun8i_h3_cpu_nb = { +- .common = &cpux_clk.common, +- .cm = &cpux_clk.mux, +- .delay_us = 1, /* > 8 clock cycles at 24 MHz */ +- .bypass_index = 1, /* index of 24 MHz oscillator */ +-}; +- + static int sun8i_h3_ccu_probe(struct platform_device *pdev) + { + const struct sunxi_ccu_desc *desc; +@@ -1067,13 +1053,6 @@ static int sun8i_h3_ccu_probe(struct platform_device *pdev) + if (ret) + return ret; + +- /* Gate then ungate PLL CPU after any rate changes */ +- ccu_pll_notifier_register(&sun8i_h3_pll_cpu_nb); +- +- /* Reparent CPU during PLL CPU rate changes */ +- ccu_mux_notifier_register(pll_cpux_clk.common.hw.clk, +- &sun8i_h3_cpu_nb); +- + return 0; + } + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/clk-sunxi-ng-Export-CLK_DRAM-for-devfreq.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/clk-sunxi-ng-Export-CLK_DRAM-for-devfreq.patch new file mode 100644 index 000000000000..434a324cca48 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/clk-sunxi-ng-Export-CLK_DRAM-for-devfreq.patch @@ -0,0 +1,59 @@ +From c55cb745c5ad2b343487227c24d2961cc2aa7ec2 Mon Sep 17 00:00:00 2001 +From: Samuel Holland +Date: Sat, 3 Apr 2021 17:14:49 -0500 +Subject: clk: sunxi-ng: Export CLK_DRAM for devfreq + +MBUS needs to reference the CLK_DRAM clock, as it is responsible for +DRAM frequency scaling. + +Signed-off-by: Samuel Holland +--- + drivers/clk/sunxi-ng/ccu-sun8i-a83t.h | 4 ---- + include/dt-bindings/clock/sun8i-a83t-ccu.h | 5 ++--- + 2 files changed, 2 insertions(+), 7 deletions(-) + +diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-a83t.h b/drivers/clk/sunxi-ng/ccu-sun8i-a83t.h +index 40e575ba51d2..6c927bc75224 100644 +--- a/drivers/clk/sunxi-ng/ccu-sun8i-a83t.h ++++ b/drivers/clk/sunxi-ng/ccu-sun8i-a83t.h +@@ -42,12 +42,8 @@ + + /* module and usb clocks exported */ + +-#define CLK_DRAM 82 +- + /* dram gates and more module clocks exported */ + +-#define CLK_MBUS 95 +- + /* more module clocks exported */ + + #define CLK_NUMBER (CLK_GPU_HYD + 1) +diff --git a/include/dt-bindings/clock/sun8i-a83t-ccu.h b/include/dt-bindings/clock/sun8i-a83t-ccu.h +index 78af5085f630..affe1857eb54 100644 +--- a/include/dt-bindings/clock/sun8i-a83t-ccu.h ++++ b/include/dt-bindings/clock/sun8i-a83t-ccu.h +@@ -116,10 +116,9 @@ + #define CLK_USB_HSIC 79 + #define CLK_USB_HSIC_12M 80 + #define CLK_USB_OHCI0 81 +- ++#define CLK_DRAM 82 + #define CLK_DRAM_VE 83 + #define CLK_DRAM_CSI 84 +- + #define CLK_TCON0 85 + #define CLK_TCON1 86 + #define CLK_CSI_MISC 87 +@@ -130,7 +129,7 @@ + #define CLK_AVS 92 + #define CLK_HDMI 93 + #define CLK_HDMI_SLOW 94 +- ++#define CLK_MBUS 95 + #define CLK_MIPI_DSI0 96 + #define CLK_MIPI_DSI1 97 + #define CLK_GPU_CORE 98 +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/clk-sunxi-ng-Mark-TWD-clocks-as-critical.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/clk-sunxi-ng-Mark-TWD-clocks-as-critical.patch new file mode 100644 index 000000000000..460c13321565 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/clk-sunxi-ng-Mark-TWD-clocks-as-critical.patch @@ -0,0 +1,56 @@ +From 9ba880cc4a8d1fcd989cb000c1679e9e596dba9b Mon Sep 17 00:00:00 2001 +From: Samuel Holland +Date: Sat, 28 May 2022 18:01:42 -0500 +Subject: clk: sunxi-ng: Mark TWD clocks as critical + +Secure world firmware depends on this clock, so it cannot be disabled. + +Signed-off-by: Samuel Holland +--- + drivers/clk/sunxi-ng/ccu-sun50i-a100-r.c | 2 +- + drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c | 2 +- + drivers/clk/sunxi-ng/ccu-sun8i-r.c | 2 +- + 3 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-a100-r.c b/drivers/clk/sunxi-ng/ccu-sun50i-a100-r.c +index cb0f8d110c32..ed17dd627b8b 100644 +--- a/drivers/clk/sunxi-ng/ccu-sun50i-a100-r.c ++++ b/drivers/clk/sunxi-ng/ccu-sun50i-a100-r.c +@@ -91,7 +91,7 @@ static SUNXI_CCU_GATE_DATA(r_apb1_timer_clk, "r-apb1-timer", clk_parent_r_apb1, + 0x11c, BIT(0), 0); + + static SUNXI_CCU_GATE_DATA(r_apb1_twd_clk, "r-apb1-twd", clk_parent_r_apb1, +- 0x12c, BIT(0), 0); ++ 0x12c, BIT(0), CLK_IS_CRITICAL); + + static const char * const r_apb1_pwm_clk_parents[] = { "dcxo24M", "osc32k", + "iosc" }; +diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c b/drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c +index acb4e8b9b1ba..3d08d7f6642a 100644 +--- a/drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c ++++ b/drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c +@@ -85,7 +85,7 @@ static struct ccu_div r_apb2_clk = { + static SUNXI_CCU_GATE(r_apb1_timer_clk, "r-apb1-timer", "r-apb1", + 0x11c, BIT(0), 0); + static SUNXI_CCU_GATE(r_apb1_twd_clk, "r-apb1-twd", "r-apb1", +- 0x12c, BIT(0), 0); ++ 0x12c, BIT(0), CLK_IS_CRITICAL); + static SUNXI_CCU_GATE(r_apb1_pwm_clk, "r-apb1-pwm", "r-apb1", + 0x13c, BIT(0), 0); + static SUNXI_CCU_GATE(r_apb2_uart_clk, "r-apb2-uart", "r-apb2", +diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-r.c b/drivers/clk/sunxi-ng/ccu-sun8i-r.c +index 0e324344673b..98e7d98e6685 100644 +--- a/drivers/clk/sunxi-ng/ccu-sun8i-r.c ++++ b/drivers/clk/sunxi-ng/ccu-sun8i-r.c +@@ -73,7 +73,7 @@ static SUNXI_CCU_GATE_HWS(apb0_uart_clk, "apb0-uart", + static SUNXI_CCU_GATE_HWS(apb0_i2c_clk, "apb0-i2c", + apb0_gate_parent, 0x28, BIT(6), 0); + static SUNXI_CCU_GATE_HWS(apb0_twd_clk, "apb0-twd", +- apb0_gate_parent, 0x28, BIT(7), 0); ++ apb0_gate_parent, 0x28, BIT(7), CLK_IS_CRITICAL); + + static const char * const r_mod0_default_parents[] = { "osc32k", "osc24M" }; + static SUNXI_CCU_MP_WITH_MUX_GATE(ir_clk, "ir", +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/clk-sunxi-ng-Set-maximum-P-and-M-factors-to-1-for-H3-pll-cpux-c.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/clk-sunxi-ng-Set-maximum-P-and-M-factors-to-1-for-H3-pll-cpux-c.patch new file mode 100644 index 000000000000..babc35eb87fa --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/clk-sunxi-ng-Set-maximum-P-and-M-factors-to-1-for-H3-pll-cpux-c.patch @@ -0,0 +1,59 @@ +From 68e7e97e96211e3df8cc02638fcfe7081166ebbf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Thu, 12 Jan 2017 16:34:57 +0100 +Subject: clk: sunxi-ng: Set maximum P and M factors to 1 for H3 pll-cpux clock + +When using M factor greater than 1 system is experiencing +occasional lockups. P factor should only be used for clock +speeds below 288MHz. We don't use such speeds in the mainline +kernel. + +This change was verified to fix lockups with PLL stress +tester available at https://xff.cz/git/arisc-firmware/. + +Note that M factor must not be used outside the kernel either, +so for example u-boot needs a similar patch. + +Signed-off-by: Ondrej Jirman +--- + drivers/clk/sunxi-ng/ccu-sun8i-h3.c | 24 +++++++++++++++--------- + 1 file changed, 15 insertions(+), 9 deletions(-) + +diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c +index 740c4c97331c..33254d63ecb5 100644 +--- a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c ++++ b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c +@@ -25,15 +25,21 @@ + + #include "ccu-sun8i-h3.h" + +-static SUNXI_CCU_NKMP_WITH_GATE_LOCK(pll_cpux_clk, "pll-cpux", +- "osc24M", 0x000, +- 8, 5, /* N */ +- 4, 2, /* K */ +- 0, 2, /* M */ +- 16, 2, /* P */ +- BIT(31), /* gate */ +- BIT(28), /* lock */ +- CLK_SET_RATE_UNGATE); ++static struct ccu_nkmp pll_cpux_clk = { ++ .enable = BIT(31), ++ .lock = BIT(28), ++ .n = _SUNXI_CCU_MULT(8, 5), ++ .k = _SUNXI_CCU_MULT(4, 2), ++ .m = _SUNXI_CCU_DIV_MAX(0, 2, 1), ++ .p = _SUNXI_CCU_DIV_MAX(16, 2, 1), ++ .common = { ++ .reg = 0x000, ++ .hw.init = CLK_HW_INIT("pll-cpux", ++ "osc24M", ++ &ccu_nkmp_ops, ++ CLK_SET_RATE_UNGATE), ++ }, ++}; + + /* + * The Audio PLL is supposed to have 4 outputs: 3 fixed factors from +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/clk-sunxi-ng-a64-Increase-PLL_AUDIO-base-frequency.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/clk-sunxi-ng-a64-Increase-PLL_AUDIO-base-frequency.patch new file mode 100644 index 000000000000..917d49a293ae --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/clk-sunxi-ng-a64-Increase-PLL_AUDIO-base-frequency.patch @@ -0,0 +1,87 @@ +From 6817daf7af44eaf118c68c12b617b62b9ecc510f Mon Sep 17 00:00:00 2001 +From: Samuel Holland +Date: Sun, 19 Jul 2020 17:10:15 -0500 +Subject: clk: sunxi-ng: a64: Increase PLL_AUDIO base frequency + +Signed-off-by: Samuel Holland +--- + drivers/clk/sunxi-ng/ccu-sun50i-a64.c | 41 +++++++++++++++++++++------ + 1 file changed, 32 insertions(+), 9 deletions(-) + +diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-a64.c b/drivers/clk/sunxi-ng/ccu-sun50i-a64.c +index ba1ad267f123..4b3068bdfdf2 100644 +--- a/drivers/clk/sunxi-ng/ccu-sun50i-a64.c ++++ b/drivers/clk/sunxi-ng/ccu-sun50i-a64.c +@@ -47,15 +47,35 @@ static struct ccu_nkmp pll_cpux_clk = { + * With sigma-delta modulation for fractional-N on the audio PLL, + * we have to use specific dividers. This means the variable divider + * can no longer be used, as the audio codec requests the exact clock +- * rates we support through this mechanism. So we now hard code the +- * variable divider to 1. This means the clock rates will no longer +- * match the clock names. ++ * rates we support through this mechanism. ++ * ++ * For the audio codec to work correctly, pll-audio must be exactly ++ * 22579200 Hz or 24576000 Hz, and pll-audio-4x (1x the base) must be ++ * between 3x and 8x that frequency. For the SRC to work at 96 kHz, ++ * pll-audio-4x must be at least 6x pll-audio. ++ * ++ * For now, hard code the variable divider to 3. + */ + #define SUN50I_A64_PLL_AUDIO_REG 0x008 ++#define SUN50I_A64_PLL_AUDIO_BIAS_REG 0x224 + + static struct ccu_sdm_setting pll_audio_sdm_table[] = { +- { .rate = 22579200, .pattern = 0xc0010d84, .m = 8, .n = 7 }, +- { .rate = 24576000, .pattern = 0xc000ac02, .m = 14, .n = 14 }, ++ /* 24000000 * ( 7 + 0x10d84 / 131072 ) / 8 / 1 - 22579200 == -6.9Hz */ ++ { .rate = 22579200, .pattern = 0xc0010d84, .m = 8, .n = 7 }, ++ /* 24000000 * ( 14 + 0x0ac02 / 131072 ) / 14 / 1 - 24576000 == -2.5Hz */ ++ { .rate = 24576000, .pattern = 0xc000ac08, .m = 14, .n = 14 }, ++ /* 24000000 * ( 8 + 0x0ef35 / 131072 ) / 3 / 3 - 22579200 == 3.3Hz */ ++ { .rate = 67737600, .pattern = 0xc000ef35, .m = 3, .n = 8 }, ++ /* 24000000 * ( 15 + 0x0b852 / 131072 ) / 5 / 3 - 24576000 == 1.0Hz */ ++ { .rate = 73728000, .pattern = 0xc000b852, .m = 5, .n = 15 }, ++ /* 24000000 * ( 7 + 0x10d84 / 131072 ) / 2 / 4 - 22579200 == -6.9Hz */ ++ { .rate = 90316800, .pattern = 0xc0010d84, .m = 2, .n = 7 }, ++ /* 24000000 * ( 16 + 0x0c49c / 131072 ) / 4 / 4 - 24576000 == 4.0Hz */ ++ { .rate = 98304000, .pattern = 0xc000c49c, .m = 4, .n = 16 }, ++ /* 24000000 * ( 5 + 0x14a23 / 131072 ) / 1 / 6 - 22579200 == -6.9Hz */ ++ { .rate = 135475200, .pattern = 0xc0014a23, .m = 1, .n = 5 }, ++ /* 24000000 * ( 12 + 0x09375 / 131072 ) / 2 / 6 - 24576000 == 4.0Hz */ ++ { .rate = 147456000, .pattern = 0xc0009375, .m = 2, .n = 12 }, + }; + + static SUNXI_CCU_NM_WITH_SDM_GATE_LOCK(pll_audio_base_clk, "pll-audio-base", +@@ -610,10 +630,10 @@ static const struct clk_hw *clk_parent_pll_audio[] = { + &pll_audio_base_clk.common.hw + }; + +-/* We hardcode the divider to 1 for now */ ++/* We hardcode the divider to 3 for now */ + static CLK_FIXED_FACTOR_HWS(pll_audio_clk, "pll-audio", + clk_parent_pll_audio, +- 1, 1, CLK_SET_RATE_PARENT); ++ 3, 1, CLK_SET_RATE_PARENT); + static CLK_FIXED_FACTOR_HWS(pll_audio_2x_clk, "pll-audio-2x", + clk_parent_pll_audio, + 2, 1, CLK_SET_RATE_PARENT); +@@ -952,10 +972,13 @@ static int sun50i_a64_ccu_probe(struct platform_device *pdev) + if (IS_ERR(reg)) + return PTR_ERR(reg); + +- /* Force the PLL-Audio-1x divider to 1 */ ++ /* Force the pll-audio variable divider to 3 */ + val = readl(reg + SUN50I_A64_PLL_AUDIO_REG); + val &= ~GENMASK(19, 16); +- writel(val | (0 << 16), reg + SUN50I_A64_PLL_AUDIO_REG); ++ writel(val | (2 << 16), reg + SUN50I_A64_PLL_AUDIO_REG); ++ ++ /* Decrease the PLL AUDIO bias current to reduce noise. */ ++ writel(0x10040000, reg + SUN50I_A64_PLL_AUDIO_BIAS_REG); + + writel(0x515, reg + SUN50I_A64_PLL_MIPI_REG); + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/clk-sunxi-ng-sun50i-a64-Switch-parent-of-MIPI-DSI-to-periph0-1x.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/clk-sunxi-ng-sun50i-a64-Switch-parent-of-MIPI-DSI-to-periph0-1x.patch new file mode 100644 index 000000000000..b7876a623520 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/clk-sunxi-ng-sun50i-a64-Switch-parent-of-MIPI-DSI-to-periph0-1x.patch @@ -0,0 +1,48 @@ +From 99e1ff82c84ee35099cd1dade28df0c963fdbf20 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Mon, 14 Aug 2023 07:28:11 +0200 +Subject: clk: sunxi-ng: sun50i-a64: Switch parent of MIPI-DSI to periph0(1x) + +This makes video0(1x) clock less constrained, and improves compatibility +with external monitors on Pinephone when using both internal display +and HDMI output at once. + +Signed-off-by: Ondrej Jirman +--- + drivers/clk/sunxi-ng/ccu-sun50i-a64.c | 11 ++++++++++- + 1 file changed, 10 insertions(+), 1 deletion(-) + +diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-a64.c b/drivers/clk/sunxi-ng/ccu-sun50i-a64.c +index c8cdb342b1a0..e76a1c10f390 100644 +--- a/drivers/clk/sunxi-ng/ccu-sun50i-a64.c ++++ b/drivers/clk/sunxi-ng/ccu-sun50i-a64.c +@@ -962,6 +962,8 @@ static struct ccu_mux_nb sun50i_a64_cpu_nb = { + .bypass_index = 1, /* index of 24 MHz oscillator */ + }; + ++#define CCU_MIPI_DSI_CLK 0x168 ++ + static int sun50i_a64_ccu_probe(struct platform_device *pdev) + { + void __iomem *reg; +@@ -981,9 +983,16 @@ static int sun50i_a64_ccu_probe(struct platform_device *pdev) + writel(0x10040000, reg + SUN50I_A64_PLL_AUDIO_BIAS_REG); + + ret = of_property_read_u32_index(of_chosen, "p-boot,framebuffer-start", 0, &val); +- if (ret) ++ if (ret) { + writel(0x515, reg + SUN50I_A64_PLL_MIPI_REG); + ++ /* Set MIPI-DSI clock parent to periph0(1x), so that video0(1x) is free to change. */ ++ val = readl(reg + CCU_MIPI_DSI_CLK); ++ val &= 0x30f; ++ val |= (2 << 8) | ((4 - 1) << 0); /* M-1 */ ++ writel(val, reg + CCU_MIPI_DSI_CLK); ++ } ++ + ret = devm_sunxi_ccu_probe(&pdev->dev, reg, &sun50i_a64_ccu_desc); + if (ret) + return ret; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/cpufreq-sun50i-Show-detected-CPU-bin-for-easier-debugging.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/cpufreq-sun50i-Show-detected-CPU-bin-for-easier-debugging.patch new file mode 100644 index 000000000000..208baf6df17f --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/cpufreq-sun50i-Show-detected-CPU-bin-for-easier-debugging.patch @@ -0,0 +1,29 @@ +From 2489a1cedce4d285ac0f18f5ff39ecb07e178f4a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Thu, 31 Oct 2019 18:31:32 +0100 +Subject: cpufreq: sun50i: Show detected CPU bin, for easier debugging + +Useful for people running into crashes during boot, due to issues +with OPP voltages. + +Signed-off-by: Ondrej Jirman +--- + drivers/cpufreq/sun50i-cpufreq-nvmem.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/cpufreq/sun50i-cpufreq-nvmem.c b/drivers/cpufreq/sun50i-cpufreq-nvmem.c +index 47d6840b3489..8992d281a818 100644 +--- a/drivers/cpufreq/sun50i-cpufreq-nvmem.c ++++ b/drivers/cpufreq/sun50i-cpufreq-nvmem.c +@@ -262,6 +262,8 @@ static int sun50i_cpufreq_nvmem_probe(struct platform_device *pdev) + snprintf(name, sizeof(name), "speed%d", speed); + config.prop_name = name; + ++ pr_info("Using CPU speed bin %s\n", name); ++ + for_each_present_cpu(cpu) { + struct device *cpu_dev = get_cpu_device(cpu); + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/drm-bridge-dw-hdmi-Allow-to-accept-HPD-status-from-other-driver.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/drm-bridge-dw-hdmi-Allow-to-accept-HPD-status-from-other-driver.patch new file mode 100644 index 000000000000..9296558e2357 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/drm-bridge-dw-hdmi-Allow-to-accept-HPD-status-from-other-driver.patch @@ -0,0 +1,152 @@ +From 023860ddb7a0feb4e634ebcbdd53a1721663f512 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Fri, 1 Sep 2023 00:57:04 +0200 +Subject: drm: bridge: dw-hdmi: Allow to accept HPD status from other drivers + +This change allows other drivers to provide HPD status. + +Signed-off-by: Ondrej Jirman +--- + drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 63 +++++++++++++++++++++-- + 1 file changed, 59 insertions(+), 4 deletions(-) + +diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +index 996733ed2c00..57b2ea9225ca 100644 +--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c ++++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +@@ -9,6 +9,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -195,6 +196,8 @@ struct dw_hdmi { + hdmi_codec_plugged_cb plugged_cb; + struct device *codec_dev; + enum drm_connector_status last_connector_result; ++ struct extcon_dev *extcon; ++ struct notifier_block extcon_nb; + }; + + #define HDMI_IH_PHY_STAT0_RX_SENSE \ +@@ -1686,6 +1689,12 @@ static void dw_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data) + enum drm_connector_status dw_hdmi_phy_read_hpd(struct dw_hdmi *hdmi, + void *data) + { ++ if (hdmi->extcon) { ++ return extcon_get_state(hdmi->extcon, EXTCON_DISP_HDMI) > 0 ++ ? connector_status_connected ++ : connector_status_disconnected; ++ } ++ + return hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ? + connector_status_connected : connector_status_disconnected; + } +@@ -1696,7 +1705,7 @@ void dw_hdmi_phy_update_hpd(struct dw_hdmi *hdmi, void *data, + { + u8 old_mask = hdmi->phy_mask; + +- if (force || disabled || !rxsense) ++ if (force || disabled || !rxsense || hdmi->extcon) + hdmi->phy_mask |= HDMI_PHY_RX_SENSE; + else + hdmi->phy_mask &= ~HDMI_PHY_RX_SENSE; +@@ -3128,7 +3137,7 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id) + status = connector_status_disconnected; + } + +- if (status != connector_status_unknown) { ++ if (status != connector_status_unknown && !hdmi->extcon) { + dev_dbg(hdmi->dev, "EVENT=%s\n", + status == connector_status_connected ? + "plugin" : "plugout"); +@@ -3314,6 +3323,25 @@ bool dw_hdmi_bus_fmt_is_420(struct dw_hdmi *hdmi) + } + EXPORT_SYMBOL_GPL(dw_hdmi_bus_fmt_is_420); + ++static int dw_hdmi_extcon_notifier(struct notifier_block *nb, ++ unsigned long event, void *ptr) ++{ ++ struct dw_hdmi *hdmi = container_of(nb, struct dw_hdmi, extcon_nb); ++ ++ enum drm_connector_status status = dw_hdmi_phy_read_hpd(hdmi, NULL); ++ ++ dev_info(hdmi->dev, "EVENT=%s\n", ++ status == connector_status_connected ? "plugin" : "plugout"); ++ ++ if (hdmi->bridge.dev) { ++ //XXX: ??? ++ drm_helper_hpd_irq_event(hdmi->bridge.dev); ++ drm_bridge_hpd_notify(&hdmi->bridge, status); ++ } ++ ++ return NOTIFY_DONE; ++} ++ + struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev, + const struct dw_hdmi_plat_data *plat_data) + { +@@ -3356,15 +3384,38 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev, + if (ret < 0) + return ERR_PTR(ret); + ++ hdmi->extcon = extcon_get_edev_by_phandle(dev, 0); ++ if (IS_ERR(hdmi->extcon)) { ++ if (PTR_ERR(hdmi->extcon) == -ENODEV) { ++ hdmi->extcon = NULL; ++ } else { ++ if (PTR_ERR(hdmi->extcon) != -EPROBE_DEFER) ++ dev_err(dev, "Invalid or missing extcon\n"); ++ return ERR_CAST(hdmi->extcon); ++ } ++ } ++ ++ if (hdmi->extcon) { ++ /* don't register IRQ for native HPD */ ++ hdmi->phy_mask = (u8)~(HDMI_PHY_RX_SENSE); ++ ++ hdmi->extcon_nb.notifier_call = dw_hdmi_extcon_notifier; ++ ret = extcon_register_notifier_all(hdmi->extcon, &hdmi->extcon_nb); ++ if (ret < 0) { ++ dev_err(dev, "failed to register extcon notifier\n"); ++ return ERR_PTR(ret); ++ } ++ } ++ + ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0); + if (ddc_node) { + hdmi->ddc = of_get_i2c_adapter_by_node(ddc_node); + of_node_put(ddc_node); + if (!hdmi->ddc) { + dev_dbg(hdmi->dev, "failed to read ddc node\n"); +- return ERR_PTR(-EPROBE_DEFER); ++ ret = -EPROBE_DEFER; ++ goto err_extcon; + } +- + } else { + dev_dbg(hdmi->dev, "no ddc property found\n"); + } +@@ -3583,6 +3634,8 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev, + + err_res: + i2c_put_adapter(hdmi->ddc); ++err_extcon: ++ extcon_unregister_notifier_all(hdmi->extcon, &hdmi->extcon_nb); + + return ERR_PTR(ret); + } +@@ -3590,6 +3643,8 @@ EXPORT_SYMBOL_GPL(dw_hdmi_probe); + + void dw_hdmi_remove(struct dw_hdmi *hdmi) + { ++ extcon_unregister_notifier_all(hdmi->extcon, &hdmi->extcon_nb); ++ + drm_bridge_remove(&hdmi->bridge); + + if (hdmi->audio && !IS_ERR(hdmi->audio)) +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/drm-bridge-dw-hdmi-Report-HDMI-hotplug-events.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/drm-bridge-dw-hdmi-Report-HDMI-hotplug-events.patch new file mode 100644 index 000000000000..1726442cb2f6 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/drm-bridge-dw-hdmi-Report-HDMI-hotplug-events.patch @@ -0,0 +1,41 @@ +From d21528bf852f57845123c8d64bbd5dd5eba17e32 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Fri, 1 Sep 2023 00:59:08 +0200 +Subject: drm: bridge: dw-hdmi: Report HDMI hotplug events + +Signed-off-by: Ondrej Jirman +--- + drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 10 ++++++++-- + 1 file changed, 8 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +index 57b2ea9225ca..0f1582318119 100644 +--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c ++++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +@@ -2455,7 +2455,13 @@ static enum drm_connector_status dw_hdmi_detect(struct dw_hdmi *hdmi) + enum drm_connector_status result; + + result = hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data); +- hdmi->last_connector_result = result; ++ ++ if (result != hdmi->last_connector_result) { ++ dev_info(hdmi->dev, "read_hpd result: %d", result); ++ handle_plugged_change(hdmi, ++ result == connector_status_connected); ++ hdmi->last_connector_result = result; ++ } + + return result; + } +@@ -3138,7 +3144,7 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id) + } + + if (status != connector_status_unknown && !hdmi->extcon) { +- dev_dbg(hdmi->dev, "EVENT=%s\n", ++ dev_info(hdmi->dev, "EVENT=%s\n", + status == connector_status_connected ? + "plugin" : "plugout"); + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/drm-panel-st7703-Fix-xbd599-timings-to-make-refresh-rate-exactl.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/drm-panel-st7703-Fix-xbd599-timings-to-make-refresh-rate-exactl.patch new file mode 100644 index 000000000000..19f4533d0705 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/drm-panel-st7703-Fix-xbd599-timings-to-make-refresh-rate-exactl.patch @@ -0,0 +1,37 @@ +From 82f1ded9bc2c9cf4f9f3b541cdc8f2d6d42a6775 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Thu, 29 Oct 2020 04:55:40 +0100 +Subject: drm/panel: st7703: Fix xbd599 timings to make refresh rate exactly + 60.006Hz + +Signed-off-by: Ondrej Jirman +--- + drivers/gpu/drm/panel/panel-sitronix-st7703.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7703.c b/drivers/gpu/drm/panel/panel-sitronix-st7703.c +index 67e8e45498cb..6d3ad83be141 100644 +--- a/drivers/gpu/drm/panel/panel-sitronix-st7703.c ++++ b/drivers/gpu/drm/panel/panel-sitronix-st7703.c +@@ -312,14 +312,14 @@ static void xbd599_init_sequence(struct mipi_dsi_multi_context *dsi_ctx) + + static const struct drm_display_mode xbd599_mode = { + .hdisplay = 720, +- .hsync_start = 720 + 40, +- .hsync_end = 720 + 40 + 40, +- .htotal = 720 + 40 + 40 + 40, ++ .hsync_start = 720 + 30, ++ .hsync_end = 720 + 30 + 28, ++ .htotal = 720 + 30 + 28 + 30, + .vdisplay = 1440, + .vsync_start = 1440 + 18, + .vsync_end = 1440 + 18 + 10, + .vtotal = 1440 + 18 + 10 + 17, +- .clock = 69000, ++ .clock = 72000, + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, + .width_mm = 68, + .height_mm = 136, +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/drm-rockchip-Fix-panic-on-reboot-when-DRM-device-fails-to-bind.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/drm-rockchip-Fix-panic-on-reboot-when-DRM-device-fails-to-bind.patch new file mode 100644 index 000000000000..8d819d939aee --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/drm-rockchip-Fix-panic-on-reboot-when-DRM-device-fails-to-bind.patch @@ -0,0 +1,36 @@ +From 38f3ba49d792cecd9b89602c16a74caa99d2eded Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sat, 24 Sep 2022 21:54:23 +0200 +Subject: drm: rockchip: Fix panic on reboot when DRM device fails to bind + +When DRM device is freed, we need to clear the drvdata pointer, because +it now points to invalid memory. + +Signed-off-by: Ondrej Jirman +--- + drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +index 439edc165ff6..44ba21037f50 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +@@ -206,6 +206,7 @@ static int rockchip_drm_bind(struct device *dev) + component_unbind_all(dev, drm_dev); + err_free: + drm_dev_put(drm_dev); ++ dev_set_drvdata(dev, NULL); + return ret; + } + +@@ -222,6 +223,7 @@ static void rockchip_drm_unbind(struct device *dev) + rockchip_iommu_cleanup(drm_dev); + + drm_dev_put(drm_dev); ++ dev_set_drvdata(dev, NULL); + } + + DEFINE_DRM_GEM_FOPS(rockchip_drm_driver_fops); +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/drm-rockchip-dw-mipi-dsi-rockchip-Fix-ISP1-PHY-initialization.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/drm-rockchip-dw-mipi-dsi-rockchip-Fix-ISP1-PHY-initialization.patch new file mode 100644 index 000000000000..36690c04e34b --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/drm-rockchip-dw-mipi-dsi-rockchip-Fix-ISP1-PHY-initialization.patch @@ -0,0 +1,35 @@ +From 7b4684d211e12e8afabc94d545078e3ad070d1be Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sun, 9 Jun 2024 14:30:12 +0200 +Subject: drm: rockchip: dw-mipi-dsi-rockchip: Fix ISP1 PHY initialization + +After suspend/resume cycle, ISP1 would stop receiving data. +Re-initializing DPHY during PHY power on fixes the issue. + +Signed-off-by: Ondrej Jirman +--- + drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c +index 3398160ad75e..471851879947 100644 +--- a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c ++++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c +@@ -1245,6 +1245,14 @@ static int dw_mipi_dsi_dphy_power_on(struct phy *phy) + goto err_phy_cfg_clk; + } + ++ if (dsi->cdata->dphy_rx_init) { ++ ret = dsi->cdata->dphy_rx_init(phy); ++ if (ret < 0) { ++ DRM_DEV_ERROR(dsi->dev, "hardware-specific phy init failed: %d\n", ret); ++ goto err_pwr_on; ++ } ++ } ++ + /* do soc-variant specific init */ + if (dsi->cdata->dphy_rx_power_on) { + ret = dsi->cdata->dphy_rx_power_on(phy); +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/drm-sun4i-Implement-gamma-correction.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/drm-sun4i-Implement-gamma-correction.patch new file mode 100644 index 000000000000..7dd2c2644fee --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/drm-sun4i-Implement-gamma-correction.patch @@ -0,0 +1,131 @@ +From 8521d1baa2940c22c9eede1517ee7ff85738f751 Mon Sep 17 00:00:00 2001 +From: Vasily Khoruzhick +Date: Wed, 13 Mar 2019 19:50:17 -0700 +Subject: drm/sun4i: Implement gamma correction + +Add support for gamma corretion to sun4i TCON driver. Its LUT has 256 +entries and can be updated only when gamma correction is disabled. + +Signed-off-by: Vasily Khoruzhick +--- + drivers/gpu/drm/sun4i/sun4i_crtc.c | 14 +++++++++++++ + drivers/gpu/drm/sun4i/sun4i_tcon.c | 33 ++++++++++++++++++++++++++++++ + drivers/gpu/drm/sun4i/sun4i_tcon.h | 12 ++++++++++- + 3 files changed, 58 insertions(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.c b/drivers/gpu/drm/sun4i/sun4i_crtc.c +index 18e74047b0f5..a09944a49e89 100644 +--- a/drivers/gpu/drm/sun4i/sun4i_crtc.c ++++ b/drivers/gpu/drm/sun4i/sun4i_crtc.c +@@ -103,6 +103,20 @@ static void sun4i_crtc_atomic_flush(struct drm_crtc *crtc, + drm_crtc_send_vblank_event(crtc, event); + spin_unlock_irq(&crtc->dev->event_lock); + } ++ ++ if (crtc->state->color_mgmt_changed) { ++ if (crtc->state->gamma_lut) { ++ /* LUT can be only updated when gamma correction is ++ * disabled ++ */ ++ sun4i_tcon_enable_gamma(scrtc->tcon, false); ++ sun4i_tcon_load_gamma_lut(scrtc->tcon, ++ crtc->state->gamma_lut->data); ++ sun4i_tcon_enable_gamma(scrtc->tcon, true); ++ } else ++ sun4i_tcon_enable_gamma(scrtc->tcon, false); ++ } ++ + } + + static void sun4i_crtc_atomic_disable(struct drm_crtc *crtc, +diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c +index fca95b76e258..ac6ad2ed1cc0 100644 +--- a/drivers/gpu/drm/sun4i/sun4i_tcon.c ++++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c +@@ -240,6 +240,34 @@ void sun4i_tcon_enable_vblank(struct sun4i_tcon *tcon, bool enable) + } + EXPORT_SYMBOL(sun4i_tcon_enable_vblank); + ++void sun4i_tcon_load_gamma_lut(struct sun4i_tcon *tcon, ++ struct drm_color_lut *lut) ++{ ++ int i; ++ ++ for (i = 0; i < SUN4I_TCON_GAMMA_LUT_SIZE; i++) { ++ u32 r, g, b; ++ ++ r = drm_color_lut_extract(lut[i].red, 8); ++ g = drm_color_lut_extract(lut[i].green, 8); ++ b = drm_color_lut_extract(lut[i].blue, 8); ++ ++ regmap_write(tcon->regs, SUN4I_TCON_GAMMA_TABLE_REG + 4 * i, ++ SUN4I_TCON_GAMMA_TABLE_R(r) | ++ SUN4I_TCON_GAMMA_TABLE_G(g) | ++ SUN4I_TCON_GAMMA_TABLE_B(b)); ++ } ++} ++EXPORT_SYMBOL(sun4i_tcon_load_gamma_lut); ++ ++void sun4i_tcon_enable_gamma(struct sun4i_tcon *tcon, bool enable) ++{ ++ regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG, ++ SUN4I_TCON_GCTL_GAMMA_ENABLE, ++ enable ? SUN4I_TCON_GCTL_GAMMA_ENABLE : 0); ++} ++EXPORT_SYMBOL(sun4i_tcon_enable_gamma); ++ + /* + * This function is a helper for TCON output muxing. The TCON output + * muxing control register in earlier SoCs (without the TCON TOP block) +@@ -1288,6 +1316,11 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master, + + list_add_tail(&tcon->list, &drv->tcon_list); + ++ drm_mode_crtc_set_gamma_size(&tcon->crtc->crtc, ++ SUN4I_TCON_GAMMA_LUT_SIZE); ++ drm_crtc_enable_color_mgmt(&tcon->crtc->crtc, 0, false, ++ tcon->crtc->crtc.gamma_size); ++ + return 0; + + err_free_dclk: +diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.h b/drivers/gpu/drm/sun4i/sun4i_tcon.h +index fa23aa23fe4a..97df39db2a31 100644 +--- a/drivers/gpu/drm/sun4i/sun4i_tcon.h ++++ b/drivers/gpu/drm/sun4i/sun4i_tcon.h +@@ -19,6 +19,7 @@ + + #define SUN4I_TCON_GCTL_REG 0x0 + #define SUN4I_TCON_GCTL_TCON_ENABLE BIT(31) ++#define SUN4I_TCON_GCTL_GAMMA_ENABLE BIT(30) + #define SUN4I_TCON_GCTL_IOMAP_MASK BIT(0) + #define SUN4I_TCON_GCTL_IOMAP_TCON1 (1 << 0) + #define SUN4I_TCON_GCTL_IOMAP_TCON0 (0 << 0) +@@ -229,7 +230,13 @@ + #define SUN4I_TCON1_FILL_BEG2_REG 0x31c + #define SUN4I_TCON1_FILL_END2_REG 0x320 + #define SUN4I_TCON1_FILL_DATA2_REG 0x324 +-#define SUN4I_TCON1_GAMMA_TABLE_REG 0x400 ++ ++#define SUN4I_TCON_GAMMA_TABLE_REG 0x400 ++#define SUN4I_TCON_GAMMA_TABLE_B(x) ((x) & 0xff) ++#define SUN4I_TCON_GAMMA_TABLE_G(x) (((x) & 0xff) << 8) ++#define SUN4I_TCON_GAMMA_TABLE_R(x) (((x) & 0xff) << 16) ++ ++#define SUN4I_TCON_GAMMA_LUT_SIZE 256 + + #define SUN4I_TCON_MAX_CHANNELS 2 + +@@ -297,6 +304,9 @@ void sun4i_tcon_mode_set(struct sun4i_tcon *tcon, + const struct drm_display_mode *mode); + void sun4i_tcon_set_status(struct sun4i_tcon *crtc, + const struct drm_encoder *encoder, bool enable); ++void sun4i_tcon_load_gamma_lut(struct sun4i_tcon *tcon, ++ struct drm_color_lut *lut); ++void sun4i_tcon_enable_gamma(struct sun4i_tcon *tcon, bool enable); + + extern const struct of_device_id sun4i_tcon_of_table[]; + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/drm-sun4i-Mark-one-of-the-UI-planes-as-a-cursor-one.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/drm-sun4i-Mark-one-of-the-UI-planes-as-a-cursor-one.patch new file mode 100644 index 000000000000..e62940fcb452 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/drm-sun4i-Mark-one-of-the-UI-planes-as-a-cursor-one.patch @@ -0,0 +1,73 @@ +From 44014e4f2d9fa752d0b04d8322582aad7dcbf249 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Mon, 31 Oct 2022 03:23:11 +0100 +Subject: drm/sun4i: Mark one of the UI planes as a cursor one + +Signed-off-by: Ondrej Jirman +--- + drivers/gpu/drm/sun4i/sun8i_mixer.c | 7 ++++++- + drivers/gpu/drm/sun4i/sun8i_ui_layer.c | 7 ++----- + drivers/gpu/drm/sun4i/sun8i_ui_layer.h | 4 +++- + 3 files changed, 11 insertions(+), 7 deletions(-) + +diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c +index 8b41d33baa30..03dd180a4c31 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_mixer.c ++++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c +@@ -349,8 +349,13 @@ static struct drm_plane **sun8i_layers_init(struct drm_device *drm, + + for (i = 0; i < mixer->cfg->ui_num; i++) { + struct sun8i_layer *layer; ++ enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY; ++ if (i == 0) ++ type = DRM_PLANE_TYPE_PRIMARY; ++ else if (i == (mixer->cfg->ui_num - 1)) ++ type = DRM_PLANE_TYPE_CURSOR; + +- layer = sun8i_ui_layer_init_one(drm, mixer, i); ++ layer = sun8i_ui_layer_init_one(drm, mixer, i, type); + if (IS_ERR(layer)) { + dev_err(drm->dev, "Couldn't initialize %s plane\n", + i ? "overlay" : "primary"); +diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c +index b90e5edef4e8..0349e8bdffd6 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c ++++ b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c +@@ -277,9 +277,9 @@ static const uint64_t sun8i_layer_modifiers[] = { + + struct sun8i_layer *sun8i_ui_layer_init_one(struct drm_device *drm, + struct sun8i_mixer *mixer, +- int index) ++ int index, ++ enum drm_plane_type type) + { +- enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY; + int channel = mixer->cfg->vi_num + index; + struct sun8i_layer *layer; + unsigned int plane_cnt; +@@ -289,9 +289,6 @@ struct sun8i_layer *sun8i_ui_layer_init_one(struct drm_device *drm, + if (!layer) + return ERR_PTR(-ENOMEM); + +- if (index == 0) +- type = DRM_PLANE_TYPE_PRIMARY; +- + /* possible crtcs are set later */ + ret = drm_universal_plane_init(drm, &layer->plane, 0, + &sun8i_ui_layer_funcs, +diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_layer.h b/drivers/gpu/drm/sun4i/sun8i_ui_layer.h +index 83892f6ff211..d4530e9f64e0 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_ui_layer.h ++++ b/drivers/gpu/drm/sun4i/sun8i_ui_layer.h +@@ -51,5 +51,7 @@ struct sun8i_layer; + + struct sun8i_layer *sun8i_ui_layer_init_one(struct drm_device *drm, + struct sun8i_mixer *mixer, +- int index); ++ int index, ++ enum drm_plane_type type); ++ + #endif /* _SUN8I_UI_LAYER_H_ */ +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/drm-sun4i-Support-taking-over-display-pipeline-state-from-p-boo.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/drm-sun4i-Support-taking-over-display-pipeline-state-from-p-boo.patch new file mode 100644 index 000000000000..54b9a2f969d1 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/drm-sun4i-Support-taking-over-display-pipeline-state-from-p-boo.patch @@ -0,0 +1,547 @@ +From 0fcf6084c0e1482b81d3ee60db39b478fcd48798 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sun, 30 Apr 2023 18:19:16 +0200 +Subject: drm/sun4i: Support taking over display pipeline state from p-boot + +For perfect, flickerless and fast boot. + +Signed-off-by: Ondrej Jirman +--- + drivers/clk/sunxi-ng/ccu-sun50i-a64.c | 4 +- + drivers/gpu/drm/drm_fbdev_ttm.c | 14 +++++ + drivers/gpu/drm/panel/panel-sitronix-st7703.c | 17 ++++++ + drivers/gpu/drm/sun4i/sun4i_tcon.c | 23 ++++++++ + drivers/gpu/drm/sun4i/sun4i_tcon.h | 2 + + drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 13 +++++ + drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h | 2 + + drivers/gpu/drm/sun4i/sun8i_mixer.c | 52 +++++++++++++++++++ + drivers/gpu/drm/sun4i/sun8i_mixer.h | 3 ++ + drivers/phy/allwinner/phy-sun6i-mipi-dphy.c | 14 +++++ + drivers/video/backlight/pwm_bl.c | 25 ++++++++- + 11 files changed, 167 insertions(+), 2 deletions(-) + +diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-a64.c b/drivers/clk/sunxi-ng/ccu-sun50i-a64.c +index 4b3068bdfdf2..c8cdb342b1a0 100644 +--- a/drivers/clk/sunxi-ng/ccu-sun50i-a64.c ++++ b/drivers/clk/sunxi-ng/ccu-sun50i-a64.c +@@ -980,7 +980,9 @@ static int sun50i_a64_ccu_probe(struct platform_device *pdev) + /* Decrease the PLL AUDIO bias current to reduce noise. */ + writel(0x10040000, reg + SUN50I_A64_PLL_AUDIO_BIAS_REG); + +- writel(0x515, reg + SUN50I_A64_PLL_MIPI_REG); ++ ret = of_property_read_u32_index(of_chosen, "p-boot,framebuffer-start", 0, &val); ++ if (ret) ++ writel(0x515, reg + SUN50I_A64_PLL_MIPI_REG); + + ret = devm_sunxi_ccu_probe(&pdev->dev, reg, &sun50i_a64_ccu_desc); + if (ret) +diff --git a/drivers/gpu/drm/drm_fbdev_ttm.c b/drivers/gpu/drm/drm_fbdev_ttm.c +index 73d35d59590c..f9d71d285e2f 100644 +--- a/drivers/gpu/drm/drm_fbdev_ttm.c ++++ b/drivers/gpu/drm/drm_fbdev_ttm.c +@@ -179,6 +179,7 @@ int drm_fbdev_ttm_driver_fbdev_probe(struct drm_fb_helper *fb_helper, + struct fb_info *info; + size_t screen_size; + void *screen_buffer; ++ u32 fb_start; + u32 format; + int ret; + +@@ -228,6 +229,19 @@ int drm_fbdev_ttm_driver_fbdev_probe(struct drm_fb_helper *fb_helper, + if (ret) + goto err_drm_fb_helper_release_info; + ++ ret = of_property_read_u32_index(of_chosen, "p-boot,framebuffer-start", 0, &fb_start); ++ if (ret == 0) { ++ // copy framebuffer contents from p-boot if reasonable ++ if (screen_size != 720 * 1440 * 4) { ++ drm_err(dev, "surface width(%d), height(%d) and bpp(%d) does not match p-boot requirements\n", ++ sizes->surface_width, sizes->surface_height, ++ sizes->surface_bpp); ++ return 0; ++ } ++ ++ memcpy(screen_buffer, __va(fb_start), screen_size); ++ } ++ + return 0; + + err_drm_fb_helper_release_info: +diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7703.c b/drivers/gpu/drm/panel/panel-sitronix-st7703.c +index 6d3ad83be141..8e359c9e0a09 100644 +--- a/drivers/gpu/drm/panel/panel-sitronix-st7703.c ++++ b/drivers/gpu/drm/panel/panel-sitronix-st7703.c +@@ -62,6 +62,7 @@ struct st7703 { + struct dentry *debugfs; + const struct st7703_panel_desc *desc; + enum drm_panel_orientation orientation; ++ bool hw_preenabled; + }; + + struct st7703_panel_desc { +@@ -679,6 +680,11 @@ static int st7703_enable(struct drm_panel *panel) + struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); + struct mipi_dsi_multi_context dsi_ctx = {.dsi = dsi}; + ++ if (ctx->hw_preenabled) { ++ ctx->hw_preenabled = false; ++ return 0; ++ } ++ + ctx->desc->init_sequence(&dsi_ctx); + + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); +@@ -726,8 +732,10 @@ static int st7703_prepare(struct drm_panel *panel) + struct st7703 *ctx = panel_to_st7703(panel); + int ret; + ++ if (!ctx->hw_preenabled) { + dev_dbg(ctx->dev, "Resetting the panel\n"); + gpiod_set_value_cansleep(ctx->reset_gpio, 1); ++ } + + ret = regulator_enable(ctx->iovcc); + if (ret < 0) { +@@ -743,10 +751,12 @@ static int st7703_prepare(struct drm_panel *panel) + } + + /* Give power supplies time to stabilize before deasserting reset. */ ++ if (!ctx->hw_preenabled) { + usleep_range(10000, 20000); + + gpiod_set_value_cansleep(ctx->reset_gpio, 0); + usleep_range(15000, 20000); ++ } + + return 0; + } +@@ -844,12 +854,19 @@ static int st7703_probe(struct mipi_dsi_device *dsi) + { + struct device *dev = &dsi->dev; + struct st7703 *ctx; ++ u32 fb_start; + int ret; + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + ++ ret = of_property_read_u32_index(of_chosen, "p-boot,framebuffer-start", 0, &fb_start); ++ if (ret == 0) { ++ /* the display pipeline is already initialized by p-boot */ ++ ctx->hw_preenabled = true; ++ } ++ + ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ctx->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), "Failed to get reset gpio\n"); +diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c +index ac6ad2ed1cc0..b36c1ea0948c 100644 +--- a/drivers/gpu/drm/sun4i/sun4i_tcon.c ++++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c +@@ -40,6 +40,8 @@ + #include "sun8i_tcon_top.h" + #include "sunxi_engine.h" + ++static bool hw_preconfigured; ++ + static struct drm_connector *sun4i_tcon_get_connector(const struct drm_encoder *encoder) + { + struct drm_connector *connector; +@@ -743,6 +745,13 @@ void sun4i_tcon_mode_set(struct sun4i_tcon *tcon, + const struct drm_encoder *encoder, + const struct drm_display_mode *mode) + { ++ if (tcon->hw_preconfigured) { ++ // avoid the first modeset ++ tcon->hw_preconfigured = false; ++ hw_preconfigured = false; ++ return; ++ } ++ + switch (encoder->encoder_type) { + case DRM_MODE_ENCODER_DSI: + /* DSI is tied to special case of CPU interface */ +@@ -883,6 +892,7 @@ static int sun4i_tcon_init_regmap(struct device *dev, + return PTR_ERR(tcon->regs); + } + ++ if (!tcon->hw_preconfigured) { + /* Make sure the TCON is disabled and all IRQs are off */ + regmap_write(tcon->regs, SUN4I_TCON_GCTL_REG, 0); + regmap_write(tcon->regs, SUN4I_TCON_GINT0_REG, 0); +@@ -891,6 +901,7 @@ static int sun4i_tcon_init_regmap(struct device *dev, + /* Disable IO lines and set them to tristate */ + regmap_write(tcon->regs, SUN4I_TCON0_IO_TRI_REG, ~0); + regmap_write(tcon->regs, SUN4I_TCON1_IO_TRI_REG, ~0); ++ } + + return 0; + } +@@ -1162,6 +1173,9 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master, + tcon->dev = dev; + tcon->id = engine->id; + tcon->quirks = of_device_get_match_data(dev); ++ ++ if (tcon->id == 0) ++ tcon->hw_preconfigured = hw_preconfigured; + + tcon->lcd_rst = devm_reset_control_get(dev, "lcd"); + if (IS_ERR(tcon->lcd_rst)) { +@@ -1183,12 +1197,14 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master, + } + } + ++ if (!tcon->hw_preconfigured) { + /* Make sure our TCON is reset */ + ret = reset_control_reset(tcon->lcd_rst); + if (ret) { + dev_err(dev, "Couldn't deassert our reset line\n"); + return ret; + } ++ } + + if (tcon->quirks->supports_lvds) { + /* +@@ -1352,8 +1368,15 @@ static int sun4i_tcon_probe(struct platform_device *pdev) + const struct sun4i_tcon_quirks *quirks; + struct drm_bridge *bridge; + struct drm_panel *panel; ++ u32 fb_start; + int ret; + ++ ret = of_property_read_u32_index(of_chosen, "p-boot,framebuffer-start", 0, &fb_start); ++ if (ret == 0) { ++ /* the display pipeline is already initialized by p-boot */ ++ hw_preconfigured = true; ++ } ++ + quirks = of_device_get_match_data(&pdev->dev); + + /* panels and bridges are present only on TCONs with channel 0 */ +diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.h b/drivers/gpu/drm/sun4i/sun4i_tcon.h +index 97df39db2a31..864d70b9d242 100644 +--- a/drivers/gpu/drm/sun4i/sun4i_tcon.h ++++ b/drivers/gpu/drm/sun4i/sun4i_tcon.h +@@ -293,6 +293,8 @@ struct sun4i_tcon { + + /* TCON list management */ + struct list_head list; ++ ++ bool hw_preconfigured; + }; + + struct drm_bridge *sun4i_tcon_find_bridge(struct device_node *node); +diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c +index c35b70d83e53..3644db360a30 100644 +--- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c ++++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c +@@ -732,6 +732,7 @@ static void sun6i_dsi_encoder_enable(struct drm_encoder *encoder) + reset_control_deassert(dsi->reset); + clk_prepare_enable(dsi->mod_clk); + ++ if (!dsi->hw_preconfigured) { + /* + * Enable the DSI block. + */ +@@ -758,6 +759,7 @@ static void sun6i_dsi_encoder_enable(struct drm_encoder *encoder) + sun6i_dsi_setup_inst_loop(dsi, mode); + sun6i_dsi_setup_format(dsi, mode); + sun6i_dsi_setup_timings(dsi, mode); ++ } + + phy_init(dsi->dphy); + +@@ -787,11 +789,15 @@ static void sun6i_dsi_encoder_enable(struct drm_encoder *encoder) + if (dsi->panel) + drm_panel_enable(dsi->panel); + ++ if (!dsi->hw_preconfigured) { + sun6i_dsi_start(dsi, DSI_START_HSC); + + udelay(1000); + + sun6i_dsi_start(dsi, DSI_START_HSD); ++ } ++ ++ dsi->hw_preconfigured = false; + } + + static void sun6i_dsi_encoder_disable(struct drm_encoder *encoder) +@@ -1105,6 +1111,7 @@ static int sun6i_dsi_probe(struct platform_device *pdev) + struct device *dev = &pdev->dev; + struct sun6i_dsi *dsi; + void __iomem *base; ++ u32 fb_start; + int ret; + + variant = device_get_match_data(dev); +@@ -1120,6 +1127,12 @@ static int sun6i_dsi_probe(struct platform_device *pdev) + dsi->host.dev = dev; + dsi->variant = variant; + ++ ret = of_property_read_u32_index(of_chosen, "p-boot,framebuffer-start", 0, &fb_start); ++ if (ret == 0) { ++ /* the display pipeline is already initialized by p-boot */ ++ dsi->hw_preconfigured = true; ++ } ++ + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) { + dev_err(dev, "Couldn't map the DSI encoder registers\n"); +diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h +index f1ddefe0f554..958c2997ab43 100644 +--- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h ++++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h +@@ -38,6 +38,8 @@ struct sun6i_dsi { + struct drm_panel *panel; + + const struct sun6i_dsi_variant *variant; ++ ++ bool hw_preconfigured; + }; + + static inline struct sun6i_dsi *host_to_sun6i_dsi(struct mipi_dsi_host *host) +diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c +index 03dd180a4c31..913490acee53 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_mixer.c ++++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c +@@ -24,6 +24,7 @@ + #include + + #include "sun4i_drv.h" ++#include "sun4i_tcon.h" + #include "sun8i_mixer.h" + #include "sun8i_ui_layer.h" + #include "sun8i_vi_layer.h" +@@ -34,6 +35,8 @@ struct de2_fmt_info { + u32 de2_fmt; + }; + ++static bool hw_preconfigured; ++ + static const struct de2_fmt_info de2_formats[] = { + { + .drm_fmt = DRM_FORMAT_ARGB8888, +@@ -278,6 +281,33 @@ static void sun8i_mixer_commit(struct sunxi_engine *engine, + struct drm_plane *plane; + u32 route = 0, pipe_en = 0; + ++ if (mixer->hw_preconfigured && engine->id == 0) { ++ struct sun4i_tcon* tcon; ++ u32 val, saved, ret; ++ ++ /* ++ * This is the first commit, wait for vblank on tcon0 before continuing. ++ */ ++ list_for_each_entry(tcon, &mixer->drv->tcon_list, list) { ++ if (tcon->id == 0) { ++ regmap_read(tcon->regs, SUN4I_TCON_GINT0_REG, &saved); ++ saved &= 0xffff0000; ++ ++ regmap_write(tcon->regs, SUN4I_TCON_GINT0_REG, 0); ++ ++ ret = regmap_read_poll_timeout(tcon->regs, SUN4I_TCON_GINT0_REG, val, ++ val & (SUN4I_TCON_GINT0_VBLANK_INT(0) | ++ SUN4I_TCON_GINT0_VBLANK_INT(1) | ++ SUN4I_TCON_GINT0_TCON0_TRI_FINISH_INT), ++ 100, 40000); ++ ++ regmap_write(tcon->regs, SUN4I_TCON_GINT0_REG, saved); ++ } ++ } ++ ++ mixer->hw_preconfigured = false; ++ } ++ + DRM_DEBUG_DRIVER("Committing changes\n"); + + drm_for_each_plane(plane, state->dev) { +@@ -461,6 +491,7 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master, + dev_set_drvdata(dev, mixer); + mixer->engine.ops = &sun8i_engine_ops; + mixer->engine.node = dev->of_node; ++ mixer->drv = drv; + + if (of_property_present(dev->of_node, "iommus")) { + /* +@@ -485,6 +516,11 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master, + */ + mixer->engine.id = sun8i_mixer_of_get_id(dev->of_node); + ++ if (mixer->engine.id == 0) { ++ mixer->hw_preconfigured = hw_preconfigured; ++ hw_preconfigured = false; ++ } ++ + mixer->cfg = of_device_get_match_data(dev); + if (!mixer->cfg) + return -EINVAL; +@@ -532,8 +568,11 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master, + * reason for the mixer to be functional. Make sure it's the + * case. + */ ++ ++ if (!mixer->hw_preconfigured) { + if (mixer->cfg->mod_rate) + clk_set_rate(mixer->mod_clk, mixer->cfg->mod_rate); ++ } + + clk_prepare_enable(mixer->mod_clk); + +@@ -541,6 +580,7 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master, + + base = sun8i_blender_base(mixer); + ++ if (!mixer->hw_preconfigured) { + /* Reset registers and disable unused sub-engines */ + if (mixer->cfg->is_de3) { + for (i = 0; i < DE3_MIXER_UNIT_SIZE; i += 4) +@@ -572,6 +612,7 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master, + /* Enable the mixer */ + regmap_write(mixer->engine.regs, SUN8I_MIXER_GLOBAL_CTL, + SUN8I_MIXER_GLOBAL_CTL_RT_EN); ++ } /* hw_preconfigured */ + + /* Set background color to black */ + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_BKCOLOR(base), +@@ -592,8 +633,10 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master, + SUN8I_MIXER_BLEND_MODE(base, i), + SUN8I_MIXER_BLEND_MODE_DEF); + ++ if (!mixer->hw_preconfigured) { + regmap_update_bits(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL(base), + SUN8I_MIXER_BLEND_PIPE_CTL_EN_MSK, 0); ++ } + + return 0; + +@@ -623,6 +666,15 @@ static const struct component_ops sun8i_mixer_ops = { + + static int sun8i_mixer_probe(struct platform_device *pdev) + { ++ int ret; ++ u32 fb_start; ++ ++ ret = of_property_read_u32_index(of_chosen, "p-boot,framebuffer-start", 0, &fb_start); ++ if (ret == 0) { ++ /* the display pipeline is already initialized by p-boot */ ++ hw_preconfigured = true; ++ } ++ + return component_add(&pdev->dev, &sun8i_mixer_ops); + } + +diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h b/drivers/gpu/drm/sun4i/sun8i_mixer.h +index d7898c9c9cc0..68e2741b0962 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_mixer.h ++++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h +@@ -184,6 +184,9 @@ struct sun8i_mixer { + + struct clk *bus_clk; + struct clk *mod_clk; ++ ++ struct sun4i_drv *drv; ++ bool hw_preconfigured; + }; + + enum { +diff --git a/drivers/phy/allwinner/phy-sun6i-mipi-dphy.c b/drivers/phy/allwinner/phy-sun6i-mipi-dphy.c +index 36eab95271b2..a19a27cea860 100644 +--- a/drivers/phy/allwinner/phy-sun6i-mipi-dphy.c ++++ b/drivers/phy/allwinner/phy-sun6i-mipi-dphy.c +@@ -195,6 +195,8 @@ struct sun6i_dphy { + + const struct sun6i_dphy_variant *variant; + enum sun6i_dphy_direction direction; ++ ++ bool hw_preconfigured; + }; + + static int sun6i_dphy_init(struct phy *phy) +@@ -226,6 +228,11 @@ static void sun6i_a31_mipi_dphy_tx_power_on(struct sun6i_dphy *dphy) + { + u8 lanes_mask = GENMASK(dphy->config.lanes - 1, 0); + ++ if (dphy->hw_preconfigured) { ++ dphy->hw_preconfigured = false; ++ return; ++ } ++ + regmap_write(dphy->regs, SUN6I_DPHY_ANA0_REG, + SUN6I_DPHY_ANA0_REG_PWS | + SUN6I_DPHY_ANA0_REG_DMPC | +@@ -551,6 +558,7 @@ static int sun6i_dphy_probe(struct platform_device *pdev) + struct sun6i_dphy *dphy; + const char *direction; + void __iomem *regs; ++ u32 fb_start; + int ret; + + dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL); +@@ -561,6 +569,12 @@ static int sun6i_dphy_probe(struct platform_device *pdev) + if (!dphy->variant) + return -EINVAL; + ++ ret = of_property_read_u32_index(of_chosen, "p-boot,framebuffer-start", 0, &fb_start); ++ if (ret == 0) { ++ /* the display pipeline is already initialized by p-boot */ ++ dphy->hw_preconfigured = true; ++ } ++ + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) { + dev_err(&pdev->dev, "Couldn't map the DPHY encoder registers\n"); +diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c +index 237d3d3f3bb1..f7d9d8813d92 100644 +--- a/drivers/video/backlight/pwm_bl.c ++++ b/drivers/video/backlight/pwm_bl.c +@@ -444,7 +444,7 @@ static int pwm_backlight_probe(struct platform_device *pdev) + struct backlight_properties props; + struct backlight_device *bl; + struct pwm_bl_data *pb; +- struct pwm_state state; ++ struct pwm_state state, state_real; + unsigned int i; + int ret; + +@@ -509,6 +509,11 @@ static int pwm_backlight_probe(struct platform_device *pdev) + /* Sync up PWM state. */ + pwm_init_state(pb->pwm, &state); + ++ /* Read real state, but only if the PWM is enabled. */ ++ pwm_get_state(pb->pwm, &state_real); ++ if (state_real.enabled) ++ state = state_real; ++ + /* + * The DT case will set the pwm_period_ns field to 0 and store the + * period, parsed from the DT, in the PWM device. For the non-DT case, +@@ -601,6 +606,24 @@ static int pwm_backlight_probe(struct platform_device *pdev) + + bl->props.brightness = data->dft_brightness; + bl->props.power = pwm_backlight_initial_power_state(pb); ++ if (bl->props.power == FB_BLANK_UNBLANK && pb->levels) { ++ u64 level; ++ ++ /* If the backlight is already on, determine the default ++ * brightness from PWM duty cycle instead of forcing ++ * the brightness determined by the driver ++ */ ++ pwm_get_state(pb->pwm, &state); ++ level = (u64)state.duty_cycle * pb->scale; ++ do_div(level, (u64)state.period); ++ ++ for (i = 0; i <= data->max_brightness; i++) { ++ if (data->levels[i] > level) { ++ bl->props.brightness = i; ++ break; ++ } ++ } ++ } + backlight_update_status(bl); + + platform_set_drvdata(pdev, bl); +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/drm-sun4i-tcon-Support-keeping-dclk-rate-upon-ancestor-clock-ch.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/drm-sun4i-tcon-Support-keeping-dclk-rate-upon-ancestor-clock-ch.patch new file mode 100644 index 000000000000..cbfa0952d5fa --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/drm-sun4i-tcon-Support-keeping-dclk-rate-upon-ancestor-clock-ch.patch @@ -0,0 +1,179 @@ +From 47d49fbc4a674b45279c3dbc20ab911e3ea470fe Mon Sep 17 00:00:00 2001 +From: Frank Oltmanns +Date: Sun, 10 Mar 2024 14:32:29 +0100 +Subject: drm/sun4i: tcon: Support keeping dclk rate upon ancestor clock + changes + +Allow the dclk to reset its rate when a rate change is initiated from an +ancestor clock. This makes it possible to no longer to get an exclusive +lock. As a consequence, it is now possible to set new rates if +necessary, e.g. when an external display is connected. + +The first user of this functionality is the A64 because PLL-VIDEO0 is an +ancestor for both HDMI and TCON0. This allows to select an optimal rate +for TCON0 as long as there is no external HDMI connection. Once a change +in PLL-VIDEO0 is performed when an HDMI connection is established, TCON0 +can react gracefully and select an optimal rate based on this the new +constraint. + +Signed-off-by: Frank Oltmanns +--- + drivers/gpu/drm/sun4i/sun4i_tcon.c | 70 +++++++++++++++++++++++++++--- + drivers/gpu/drm/sun4i/sun4i_tcon.h | 12 +++++ + 2 files changed, 76 insertions(+), 6 deletions(-) + +diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c +index b36c1ea0948c..221df37406d8 100644 +--- a/drivers/gpu/drm/sun4i/sun4i_tcon.c ++++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c +@@ -110,9 +110,11 @@ static void sun4i_tcon_channel_set_status(struct sun4i_tcon *tcon, int channel, + + if (enabled) { + clk_prepare_enable(clk); +- clk_rate_exclusive_get(clk); ++ if (!tcon->quirks->restores_rate) ++ clk_rate_exclusive_get(clk); + } else { +- clk_rate_exclusive_put(clk); ++ if (!tcon->quirks->restores_rate) ++ clk_rate_exclusive_put(clk); + clk_disable_unprepare(clk); + } + } +@@ -373,6 +375,53 @@ static void sun4i_tcon0_mode_set_dithering(struct sun4i_tcon *tcon, + regmap_write(tcon->regs, SUN4I_TCON_FRM_CTL_REG, val); + } + ++static void sun4i_rate_reset_notifier_delayed_update(struct work_struct *work) ++{ ++ struct sun4i_rate_reset_nb *rate_reset = container_of(work, struct sun4i_rate_reset_nb, ++ reset_rate_work.work); ++ ++ clk_set_rate(rate_reset->target_clk, rate_reset->saved_rate); ++} ++ ++static int sun4i_rate_reset_notifier_cb(struct notifier_block *nb, ++ unsigned long event, void *data) ++{ ++ struct sun4i_rate_reset_nb *rate_reset = to_sun4i_rate_reset_nb(nb); ++ ++ if (event == POST_RATE_CHANGE) ++ mod_delayed_work(system_wq, &rate_reset->reset_rate_work, msecs_to_jiffies(100)); ++ ++ return NOTIFY_DONE; ++} ++ ++static void sun4i_rate_reset_notifier_register(struct sun4i_rate_reset_nb *rate_reset_nb) ++{ ++ if (rate_reset_nb->is_registered) ++ return; ++ ++ rate_reset_nb->clk_nb.notifier_call = sun4i_rate_reset_notifier_cb; ++ ++ INIT_DELAYED_WORK(&rate_reset_nb->reset_rate_work, ++ sun4i_rate_reset_notifier_delayed_update); ++ ++ if (!clk_notifier_register(rate_reset_nb->target_clk, ++ &rate_reset_nb->clk_nb)) ++ rate_reset_nb->is_registered = true; ++} ++ ++static struct sun4i_rate_reset_nb tcon_rate_reset_tcon0_nb; ++ ++static void sun4i_tcon0_set_dclk_rate(struct sun4i_tcon *tcon, unsigned long rate) ++{ ++ clk_set_rate(tcon->dclk, rate); ++ ++ if (tcon->quirks->restores_rate) { ++ tcon_rate_reset_tcon0_nb.target_clk = tcon->dclk; ++ tcon_rate_reset_tcon0_nb.saved_rate = rate; ++ sun4i_rate_reset_notifier_register(&tcon_rate_reset_tcon0_nb); ++ } ++} ++ + static void sun4i_tcon0_mode_set_cpu(struct sun4i_tcon *tcon, + const struct drm_encoder *encoder, + const struct drm_display_mode *mode) +@@ -390,8 +439,8 @@ static void sun4i_tcon0_mode_set_cpu(struct sun4i_tcon *tcon, + */ + tcon->dclk_min_div = SUN6I_DSI_TCON_DIV; + tcon->dclk_max_div = SUN6I_DSI_TCON_DIV; +- clk_set_rate(tcon->dclk, mode->crtc_clock * 1000 * (bpp / lanes) +- / SUN6I_DSI_TCON_DIV); ++ sun4i_tcon0_set_dclk_rate(tcon, mode->crtc_clock * 1000 * (bpp / lanes) ++ / SUN6I_DSI_TCON_DIV); + + /* Set the resolution */ + regmap_write(tcon->regs, SUN4I_TCON0_BASIC0_REG, +@@ -464,7 +513,7 @@ static void sun4i_tcon0_mode_set_lvds(struct sun4i_tcon *tcon, + + tcon->dclk_min_div = 7; + tcon->dclk_max_div = 7; +- clk_set_rate(tcon->dclk, mode->crtc_clock * 1000); ++ sun4i_tcon0_set_dclk_rate(tcon, mode->crtc_clock * 1000); + + /* Set the resolution */ + regmap_write(tcon->regs, SUN4I_TCON0_BASIC0_REG, +@@ -548,7 +597,7 @@ static void sun4i_tcon0_mode_set_rgb(struct sun4i_tcon *tcon, + + tcon->dclk_min_div = tcon->quirks->dclk_min_div; + tcon->dclk_max_div = 127; +- clk_set_rate(tcon->dclk, mode->crtc_clock * 1000); ++ sun4i_tcon0_set_dclk_rate(tcon, mode->crtc_clock * 1000); + + /* Set the resolution */ + regmap_write(tcon->regs, SUN4I_TCON0_BASIC0_REG, +@@ -1561,6 +1610,14 @@ static const struct sun4i_tcon_quirks sun8i_a33_quirks = { + .supports_lvds = true, + }; + ++static const struct sun4i_tcon_quirks sun50i_a64_lcd_quirks = { ++ .supports_lvds = true, ++ .has_channel_0 = true, ++ .restores_rate = true, ++ .dclk_min_div = 1, ++ .setup_lvds_phy = sun6i_tcon_setup_lvds_phy, ++}; ++ + static const struct sun4i_tcon_quirks sun8i_a83t_lcd_quirks = { + .supports_lvds = true, + .has_channel_0 = true, +@@ -1619,6 +1676,7 @@ const struct of_device_id sun4i_tcon_of_table[] = { + { .compatible = "allwinner,sun9i-a80-tcon-tv", .data = &sun9i_a80_tcon_tv_quirks }, + { .compatible = "allwinner,sun20i-d1-tcon-lcd", .data = &sun20i_d1_lcd_quirks }, + { .compatible = "allwinner,sun20i-d1-tcon-tv", .data = &sun8i_r40_tv_quirks }, ++ { .compatible = "allwinner,sun50i-a64-tcon-lcd", .data = &sun50i_a64_lcd_quirks }, + { } + }; + MODULE_DEVICE_TABLE(of, sun4i_tcon_of_table); +diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.h b/drivers/gpu/drm/sun4i/sun4i_tcon.h +index 864d70b9d242..729d64b78846 100644 +--- a/drivers/gpu/drm/sun4i/sun4i_tcon.h ++++ b/drivers/gpu/drm/sun4i/sun4i_tcon.h +@@ -250,6 +250,7 @@ struct sun4i_tcon_quirks { + bool needs_edp_reset; /* a80 edp reset needed for tcon0 access */ + bool supports_lvds; /* Does the TCON support an LVDS output? */ + bool polarity_in_ch0; /* some tcon1 channels have polarity bits in tcon0 pol register */ ++ bool restores_rate; /* restores the initial rate when rate changes */ + u8 dclk_min_div; /* minimum divider for TCON0 DCLK */ + + /* callback to handle tcon muxing options */ +@@ -312,4 +313,15 @@ void sun4i_tcon_enable_gamma(struct sun4i_tcon *tcon, bool enable); + + extern const struct of_device_id sun4i_tcon_of_table[]; + ++struct sun4i_rate_reset_nb { ++ struct notifier_block clk_nb; ++ struct delayed_work reset_rate_work; ++ ++ struct clk *target_clk; ++ unsigned long saved_rate; ++ bool is_registered; ++}; ++ ++#define to_sun4i_rate_reset_nb(_nb) container_of(_nb, struct sun4i_rate_reset_nb, clk_nb) ++ + #endif /* __SUN4I_TCON_H__ */ +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/dt-bindings-axp20x-adc-allow-to-use-TS-pin-as-GPADC.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/dt-bindings-axp20x-adc-allow-to-use-TS-pin-as-GPADC.patch new file mode 100644 index 000000000000..d46437891848 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/dt-bindings-axp20x-adc-allow-to-use-TS-pin-as-GPADC.patch @@ -0,0 +1,34 @@ +From 9d267992ddf49ed7c35fec49416bea500dd7efdf Mon Sep 17 00:00:00 2001 +From: Icenowy Zheng +Date: Tue, 10 Sep 2019 12:02:23 +0800 +Subject: dt-bindings: axp20x-adc: allow to use TS pin as GPADC + +The AXP series PMICs allow to use the TS pin as GPADC rather than the +battery temperature sensor. + +Add the property to enable this mode. + +Signed-off-by: Icenowy Zheng +--- + .../devicetree/bindings/iio/adc/x-powers,axp209-adc.yaml | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/Documentation/devicetree/bindings/iio/adc/x-powers,axp209-adc.yaml b/Documentation/devicetree/bindings/iio/adc/x-powers,axp209-adc.yaml +index 1caa896fce82..46ed0cdd925e 100644 +--- a/Documentation/devicetree/bindings/iio/adc/x-powers,axp209-adc.yaml ++++ b/Documentation/devicetree/bindings/iio/adc/x-powers,axp209-adc.yaml +@@ -73,6 +73,11 @@ properties: + "#io-channel-cells": + const: 1 + ++ x-powers,ts-as-gpadc: ++ description: | ++ Boolean. Defines whether the TS pin should be used ++ as GPADC rather than battery temperature sensor pin. ++ + additionalProperties: false + + examples: +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/dt-bindings-input-gpio-vibrator-Don-t-require-enable-gpios.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/dt-bindings-input-gpio-vibrator-Don-t-require-enable-gpios.patch new file mode 100644 index 000000000000..f66383cc4291 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/dt-bindings-input-gpio-vibrator-Don-t-require-enable-gpios.patch @@ -0,0 +1,35 @@ +From 02cd44cdcb83d7b3b4adb5908fa184cfb60a67e1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sun, 23 Feb 2020 00:00:00 +0100 +Subject: dt-bindings: input: gpio-vibrator: Don't require enable-gpios + +It is possible to turn the motor on/off just by enabling/disabling +the vcc-supply. Change the binding to require either enable-gpios +or vcc-supply or both. + +Signed-off-by: Ondrej Jirman +--- + Documentation/devicetree/bindings/input/gpio-vibrator.yaml | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/Documentation/devicetree/bindings/input/gpio-vibrator.yaml b/Documentation/devicetree/bindings/input/gpio-vibrator.yaml +index 2384465eaa19..082ac06471db 100644 +--- a/Documentation/devicetree/bindings/input/gpio-vibrator.yaml ++++ b/Documentation/devicetree/bindings/input/gpio-vibrator.yaml +@@ -24,7 +24,12 @@ properties: + + required: + - compatible +- - enable-gpios ++ ++anyOf: ++ - required: ++ - enable-gpios ++ - required: ++ - vcc-supply + + additionalProperties: false + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/dt-bindings-leds-Add-a-binding-for-AXP813-charger-led.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/dt-bindings-leds-Add-a-binding-for-AXP813-charger-led.patch new file mode 100644 index 000000000000..6e92a412bedc --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/dt-bindings-leds-Add-a-binding-for-AXP813-charger-led.patch @@ -0,0 +1,46 @@ +From 881c70e2d4624f2af93a7464c0b7471896b2cb9a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sun, 23 Feb 2020 13:40:15 +0100 +Subject: dt-bindings: leds: Add a binding for AXP813 charger led + +The AXP813 PMIC can control one LED. Add binding to represent the LED. + +Signed-off-by: Ondrej Jirman +--- + .../devicetree/bindings/leds/leds-axp20x.yaml | 24 +++++++++++++++++++ + 1 file changed, 24 insertions(+) + create mode 100644 Documentation/devicetree/bindings/leds/leds-axp20x.yaml + +diff --git a/Documentation/devicetree/bindings/leds/leds-axp20x.yaml b/Documentation/devicetree/bindings/leds/leds-axp20x.yaml +new file mode 100644 +index 000000000000..110edae82204 +--- /dev/null ++++ b/Documentation/devicetree/bindings/leds/leds-axp20x.yaml +@@ -0,0 +1,24 @@ ++# SPDX-License-Identifier: GPL-2.0 ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/leds/leds-axp20x.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: LED driver for AXP813 PMIC from X-Powers. ++ ++maintainers: ++ - Ondrej Jirman ++ ++description: | ++ This module is part of the AXP20x MFD device. For more details ++ see Documentation/devicetree/bindings/mfd/axp20x.txt. ++ ++ The LED controller is represented as a sub-node of the PMIC node on ++ the device tree. ++ ++properties: ++ compatible: ++ const: x-powers,axp813-charger-led ++ ++required: ++ - compatible +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/dt-bindings-media-Add-bindings-for-Himax-HM5065-camera-sensor.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/dt-bindings-media-Add-bindings-for-Himax-HM5065-camera-sensor.patch new file mode 100644 index 000000000000..3062996ef08c --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/dt-bindings-media-Add-bindings-for-Himax-HM5065-camera-sensor.patch @@ -0,0 +1,71 @@ +From 3680411d51a3657965cf6226c91dca9e7b0140fc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sat, 30 Sep 2017 02:30:39 +0200 +Subject: dt-bindings: media: Add bindings for Himax HM5065 camera sensor + +HM5065 is 5MP CMOS sensor... + +Signed-off-by: Ondrej Jirman +--- + .../devicetree/bindings/media/i2c/hm5065.txt | 49 +++++++++++++++++++ + 1 file changed, 49 insertions(+) + create mode 100644 Documentation/devicetree/bindings/media/i2c/hm5065.txt + +diff --git a/Documentation/devicetree/bindings/media/i2c/hm5065.txt b/Documentation/devicetree/bindings/media/i2c/hm5065.txt +new file mode 100644 +index 000000000000..92ba6cac0d2f +--- /dev/null ++++ b/Documentation/devicetree/bindings/media/i2c/hm5065.txt +@@ -0,0 +1,49 @@ ++* Himax HM5065 CSI camera sensor ++ ++Required Properties: ++- compatible: should be "himax,hm5065" ++- reg: I2C device address (0x1f) ++- clocks: reference to the external input clock for the sensor. ++- clock-names: should be "xclk". ++- IOVDD-supply: Digital I/O voltage supply, 2.8 volts ++- AVDD-supply: Analog voltage supply, 2.8 volts ++- DVDD-supply: Digital core voltage supply, 1.8 volts ++- AFVDD-supply: Auto focus voltage supply, 2.8 volts ++ ++Optional Properties (one or both must be configured): ++- reset-gpios: reference to the GPIO connected to the reset pin, if any. ++ This is an active low signal to the HM5065. ++- enable-gpios: reference to the GPIO connected to the CE pin, ++ if any. This is an active high signal to the HM5065. ++ ++The device node must contain one 'port' child node for its digital output ++video port, in accordance with the video interface bindings defined in ++Documentation/devicetree/bindings/media/video-interfaces.txt. ++ ++Example: ++ ++&i2c1 { ++ hm5065: camera@1f { ++ compatible = "himax,hm5065"; ++ reg = <0x1f>; ++ clocks = <&ccu CLK_CSI_MCLK>; ++ clock-names = "xclk"; ++ IOVDD-supply = <®_dldo3>; ++ AVDD-supply = <®_dldo4>; ++ DVDD-supply = <®_eldo3>; ++ AFVDD-supply = <®_dldo3>; ++ reset-gpios = <&pio 4 18 GPIO_ACTIVE_LOW>; /* PE18 */ ++ enable-gpios = <&pio 4 19 GPIO_ACTIVE_HIGH>; /* PE19 */ ++ ++ port { ++ hm5065_ep: endpoint { ++ remote-endpoint = <&csi0_hm5065_ep>; ++ bus-width = <8>; ++ hsync-active = <1>; ++ vsync-active = <1>; ++ data-active = <1>; ++ pclk-sample = <1>; ++ }; ++ }; ++ }; ++}; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/dt-bindings-mfd-Add-codec-related-properties-to-AC100-PMIC.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/dt-bindings-mfd-Add-codec-related-properties-to-AC100-PMIC.patch new file mode 100644 index 000000000000..9c7b9fbfc62c --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/dt-bindings-mfd-Add-codec-related-properties-to-AC100-PMIC.patch @@ -0,0 +1,84 @@ +From b1949039cf1e2d3d460e0ae3da77e6f51e3df3f0 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sat, 17 Feb 2024 02:01:49 +0100 +Subject: dt: bindings: mfd: Add codec related properties to AC100 PMIC + +This PMIC includes a co-packaged audio codec with analog and digital +parts. Add additional properties that describe power supplies to +the codec, and the node for analog part of the codec. + +Signed-off-by: Ondrej Jirman +--- + .../bindings/mfd/x-powers,ac100.yaml | 38 +++++++++++++++++++ + 1 file changed, 38 insertions(+) + +diff --git a/Documentation/devicetree/bindings/mfd/x-powers,ac100.yaml b/Documentation/devicetree/bindings/mfd/x-powers,ac100.yaml +index f3d8394b27e7..59ad898fbf85 100644 +--- a/Documentation/devicetree/bindings/mfd/x-powers,ac100.yaml ++++ b/Documentation/devicetree/bindings/mfd/x-powers,ac100.yaml +@@ -34,11 +34,39 @@ properties: + description: > + Name of the 4M_adda clock exposed by the codec + ++ "#sound-dai-cells": ++ const: 1 ++ ++ LDOIN-supply: true ++ AVCC-supply: true ++ VDDIO1-supply: true ++ VDDIO2-supply: true ++ + required: + - "#clock-cells" + - compatible + - interrupts + - clock-output-names ++ - "#sound-dai-cells" ++ - LDOIN-supply ++ - AVCC-supply ++ - VDDIO1-supply ++ - VDDIO2-supply ++ ++ additionalProperties: false ++ ++ codec-analog: ++ type: object ++ ++ properties: ++ compatible: ++ const: x-powers,ac100-codec-analog ++ ++ CPVDD-supply: true ++ ++ required: ++ - compatible ++ - CPVDD-supply + + additionalProperties: false + +@@ -94,12 +122,22 @@ examples: + compatible = "x-powers,ac100"; + reg = <0xe89>; + ++ ac100_codec_analog: codec-analog { ++ compatible = "x-powers,ac100-codec-analog"; ++ CPVDD-supply = <®_aldo2>; ++ }; ++ + ac100_codec: codec { ++ #sound-dai-cells = <1>; + compatible = "x-powers,ac100-codec"; + interrupt-parent = <&r_pio>; + interrupts = <0 9 IRQ_TYPE_LEVEL_LOW>; /* PL9 */ + #clock-cells = <0>; + clock-output-names = "4M_adda"; ++ LDOIN-supply = <®_aldo2>; ++ AVCC-supply = <®_aldo3>; ++ VDDIO1-supply = <®_dcdc1>; ++ VDDIO2-supply = <®_dldo1>; + }; + + ac100_rtc: rtc { +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/dt-bindings-sound-Add-jack-type-property-to-sun8i-a33-codec.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/dt-bindings-sound-Add-jack-type-property-to-sun8i-a33-codec.patch new file mode 100644 index 000000000000..62246f6e3fdd --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/dt-bindings-sound-Add-jack-type-property-to-sun8i-a33-codec.patch @@ -0,0 +1,36 @@ +From f20f5106e8f16311a5a99f9de7ef1049d9e8bbd7 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Fri, 23 Feb 2024 00:47:12 +0100 +Subject: dt-bindings: sound: Add jack-type property to sun8i-a33-codec + +The codec driver needs to know what jack connector is connected to +on the board. Add proprty to describe the type of connector. + +Signed-off-by: Ondrej Jirman +--- + .../bindings/sound/allwinner,sun8i-a33-codec.yaml | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/Documentation/devicetree/bindings/sound/allwinner,sun8i-a33-codec.yaml b/Documentation/devicetree/bindings/sound/allwinner,sun8i-a33-codec.yaml +index 63eadc4200ac..399fc00ad3f4 100644 +--- a/Documentation/devicetree/bindings/sound/allwinner,sun8i-a33-codec.yaml ++++ b/Documentation/devicetree/bindings/sound/allwinner,sun8i-a33-codec.yaml +@@ -44,6 +44,15 @@ properties: + - const: bus + - const: mod + ++ jack-type: ++ enum: ++ - headset ++ - headphone ++ description: ++ Type of jack connector connected to the codec. This can be either ++ a 3-pin headphone connector or 4-pin headset connector. If not ++ specified, jack detection is disabled. ++ + required: + - "#sound-dai-cells" + - compatible +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/firmware-arm_scpi-Support-unidirectional-mailbox-channels.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/firmware-arm_scpi-Support-unidirectional-mailbox-channels.patch new file mode 100644 index 000000000000..e6ccb925b762 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/firmware-arm_scpi-Support-unidirectional-mailbox-channels.patch @@ -0,0 +1,127 @@ +From 7ccfcf014f1793ab9388a98cdabe65e0a336259a Mon Sep 17 00:00:00 2001 +From: Samuel Holland +Date: Tue, 5 Mar 2019 22:02:41 -0600 +Subject: firmware: arm_scpi: Support unidirectional mailbox channels + +Some mailbox controllers have only unidirectional channels, so we need a +pair of them for each SCPI channel. If a mbox-names property is present, +look for "rx" and "tx" mbox channels; otherwise, the existing behavior +is preserved, and a single mbox channel is used for each SCPI channel. + +Note that since the mailbox framework only supports a single phandle +with each name (mbox_request_channel_byname always returns the first +one), this new mode only supports a single SCPI channel. + +Signed-off-by: Samuel Holland +--- + drivers/firmware/arm_scpi.c | 58 +++++++++++++++++++++++++++++-------- + 1 file changed, 46 insertions(+), 12 deletions(-) + +diff --git a/drivers/firmware/arm_scpi.c b/drivers/firmware/arm_scpi.c +index 0967c9212653..46c541bd9447 100644 +--- a/drivers/firmware/arm_scpi.c ++++ b/drivers/firmware/arm_scpi.c +@@ -237,7 +237,8 @@ struct scpi_xfer { + + struct scpi_chan { + struct mbox_client cl; +- struct mbox_chan *chan; ++ struct mbox_chan *rx_chan; ++ struct mbox_chan *tx_chan; + void __iomem *tx_payload; + void __iomem *rx_payload; + struct list_head rx_pending; +@@ -511,7 +512,7 @@ static int scpi_send_message(u8 idx, void *tx_buf, unsigned int tx_len, + msg->rx_len = rx_len; + reinit_completion(&msg->done); + +- ret = mbox_send_message(scpi_chan->chan, msg); ++ ret = mbox_send_message(scpi_chan->tx_chan, msg); + if (ret < 0 || !rx_buf) + goto out; + +@@ -872,8 +873,13 @@ static void scpi_free_channels(void *data) + struct scpi_drvinfo *info = data; + int i; + +- for (i = 0; i < info->num_chans; i++) +- mbox_free_channel(info->channels[i].chan); ++ for (i = 0; i < info->num_chans; i++) { ++ struct scpi_chan *pchan = &info->channels[i]; ++ ++ if (pchan->tx_chan != pchan->rx_chan) ++ mbox_free_channel(pchan->tx_chan); ++ mbox_free_channel(pchan->rx_chan); ++ } + } + + static void scpi_remove(struct platform_device *pdev) +@@ -923,6 +929,7 @@ static int scpi_probe(struct platform_device *pdev) + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct scpi_drvinfo *scpi_drvinfo; ++ bool use_mbox_names = false; + + scpi_drvinfo = devm_kzalloc(dev, sizeof(*scpi_drvinfo), GFP_KERNEL); + if (!scpi_drvinfo) +@@ -935,6 +942,14 @@ static int scpi_probe(struct platform_device *pdev) + dev_err(dev, "no mboxes property in '%pOF'\n", np); + return -ENODEV; + } ++ if (of_get_property(dev->of_node, "mbox-names", NULL)) { ++ use_mbox_names = true; ++ if (count != 2) { ++ dev_err(dev, "need exactly 2 mboxes with mbox-names\n"); ++ return -ENODEV; ++ } ++ count /= 2; ++ } + + scpi_drvinfo->channels = + devm_kcalloc(dev, count, sizeof(struct scpi_chan), GFP_KERNEL); +@@ -983,15 +998,34 @@ static int scpi_probe(struct platform_device *pdev) + mutex_init(&pchan->xfers_lock); + + ret = scpi_alloc_xfer_list(dev, pchan); +- if (!ret) { +- pchan->chan = mbox_request_channel(cl, idx); +- if (!IS_ERR(pchan->chan)) +- continue; +- ret = PTR_ERR(pchan->chan); +- if (ret != -EPROBE_DEFER) +- dev_err(dev, "failed to get channel%d err %d\n", +- idx, ret); ++ if (ret) ++ return ret; ++ ++ if (use_mbox_names) { ++ pchan->rx_chan = mbox_request_channel_byname(cl, "rx"); ++ if (IS_ERR(pchan->rx_chan)) { ++ ret = PTR_ERR(pchan->rx_chan); ++ goto fail; ++ } ++ pchan->tx_chan = mbox_request_channel_byname(cl, "tx"); ++ if (IS_ERR(pchan->rx_chan)) { ++ ret = PTR_ERR(pchan->tx_chan); ++ goto fail; ++ } ++ } else { ++ pchan->rx_chan = mbox_request_channel(cl, idx); ++ if (IS_ERR(pchan->rx_chan)) { ++ ret = PTR_ERR(pchan->rx_chan); ++ goto fail; ++ } ++ pchan->tx_chan = pchan->rx_chan; + } ++ continue; ++ ++fail: ++ if (ret != -EPROBE_DEFER) ++ dev_err(dev, "failed to get channel%d err %d\n", ++ idx, ret); + return ret; + } + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/firmware-scpi-Add-support-for-sending-a-SCPI_CMD_SET_SYS_PWR_ST.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/firmware-scpi-Add-support-for-sending-a-SCPI_CMD_SET_SYS_PWR_ST.patch new file mode 100644 index 000000000000..6b0932fe185a --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/firmware-scpi-Add-support-for-sending-a-SCPI_CMD_SET_SYS_PWR_ST.patch @@ -0,0 +1,87 @@ +From 0cfb0b5ca768b2516a5aa7b0f88be0daeea31692 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sat, 2 Nov 2019 15:14:10 +0100 +Subject: firmware: scpi: Add support for sending a SCPI_CMD_SET_SYS_PWR_STATE + msg + +This is NOT the right message to signal SCP we want to suspend the +system, but we use it anyway, because we simply want to do a fixed +function suspend sequence, instead of more complicated and granular +cluster/cpu/system power management. + +Normally we'd signal system suspend by sending a CSS power state +message. I guess that can be done later, if wanted. + +For now, Linux will kill all secondary CPU cores via MCPM, and SCP +will kill the last CPU, and suspend the system. + +Signed-off-by: Ondrej Jirman +--- + drivers/firmware/arm_scpi.c | 10 ++++++++++ + include/linux/scpi_protocol.h | 1 + + 2 files changed, 11 insertions(+) + +diff --git a/drivers/firmware/arm_scpi.c b/drivers/firmware/arm_scpi.c +index 87c323de17b9..0967c9212653 100644 +--- a/drivers/firmware/arm_scpi.c ++++ b/drivers/firmware/arm_scpi.c +@@ -187,6 +187,7 @@ enum scpi_drv_cmds { + CMD_SENSOR_VALUE, + CMD_SET_DEVICE_PWR_STATE, + CMD_GET_DEVICE_PWR_STATE, ++ CMD_SET_SYS_PWR_STATE, + CMD_MAX_COUNT, + }; + +@@ -203,6 +204,7 @@ static int scpi_std_commands[CMD_MAX_COUNT] = { + SCPI_CMD_SENSOR_VALUE, + SCPI_CMD_SET_DEVICE_PWR_STATE, + SCPI_CMD_GET_DEVICE_PWR_STATE, ++ SCPI_CMD_SET_SYS_PWR_STATE, + }; + + static int scpi_legacy_commands[CMD_MAX_COUNT] = { +@@ -218,6 +220,7 @@ static int scpi_legacy_commands[CMD_MAX_COUNT] = { + LEGACY_SCPI_CMD_SENSOR_VALUE, + -1, /* SET_DEVICE_PWR_STATE */ + -1, /* GET_DEVICE_PWR_STATE */ ++ LEGACY_SCPI_CMD_SYS_PWR_STATE, + }; + + struct scpi_xfer { +@@ -785,6 +788,12 @@ static int scpi_device_set_power_state(u16 dev_id, u8 pstate) + sizeof(dev_set), &stat, sizeof(stat)); + } + ++static int scpi_sys_set_power_state(u8 pstate) ++{ ++ return scpi_send_message(CMD_SET_SYS_PWR_STATE, &pstate, ++ sizeof(pstate), NULL, 0); ++} ++ + static struct scpi_ops scpi_ops = { + .get_version = scpi_get_version, + .clk_get_range = scpi_clk_get_range, +@@ -801,6 +810,7 @@ static struct scpi_ops scpi_ops = { + .sensor_get_value = scpi_sensor_get_value, + .device_get_power_state = scpi_device_get_power_state, + .device_set_power_state = scpi_device_set_power_state, ++ .sys_set_power_state = scpi_sys_set_power_state, + }; + + struct scpi_ops *get_scpi_ops(void) +diff --git a/include/linux/scpi_protocol.h b/include/linux/scpi_protocol.h +index d2176a56828a..6169348c3b72 100644 +--- a/include/linux/scpi_protocol.h ++++ b/include/linux/scpi_protocol.h +@@ -76,6 +76,7 @@ struct scpi_ops { + int (*sensor_get_value)(u16, u64 *); + int (*device_get_power_state)(u16); + int (*device_set_power_state)(u16, u8); ++ int (*sys_set_power_state)(u8); + }; + + #if IS_REACHABLE(CONFIG_ARM_SCPI_PROTOCOL) +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/gnss-ubx-Send-soft-powerdown-message-on-suspend.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/gnss-ubx-Send-soft-powerdown-message-on-suspend.patch new file mode 100644 index 000000000000..ed98593c0a73 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/gnss-ubx-Send-soft-powerdown-message-on-suspend.patch @@ -0,0 +1,64 @@ +From 21307662463898619177f8290f24007d38d3810e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Wed, 23 Oct 2019 05:08:35 +0200 +Subject: gnss: ubx: Send soft powerdown message on suspend + +Signed-off-by: Ondrej Jirman +--- + drivers/gnss/ubx.c | 35 +++++++++++++++++++++++++++++++++++ + 1 file changed, 35 insertions(+) + +diff --git a/drivers/gnss/ubx.c b/drivers/gnss/ubx.c +index 92402f6082c4..fd0ccb054477 100644 +--- a/drivers/gnss/ubx.c ++++ b/drivers/gnss/ubx.c +@@ -33,11 +33,46 @@ static int ubx_set_active(struct gnss_serial *gserial) + return 0; + } + ++// RXM-PMREQ ++static const u8 ubx_suspend_packet[] = { ++ 0xb5, 0x62, // 'ub' ++ 0x02, 0x41, // message class and id ++ 0x08, 0x00, // message length ++ 0x00, 0x00, 0x00, 0x00, // duration (0 == infinite) ++ 0x02, 0x00, 0x00, 0x00, // flags (backup) ++ 0x4d, 0x3b, // checksum ++}; ++ + static int ubx_set_standby(struct gnss_serial *gserial) + { + struct ubx_data *data = gnss_serial_get_drvdata(gserial); + int ret; + ++#if 0 ++ struct serdev_device *serdev = gserial->serdev; ++ ++ // we can't know what state the device is in, so first make sure ++ // it's woken up by writing a zero byte to it and then suspend it ++ // for sure ++ ++ ret = serdev_device_write(serdev, "", 1, MAX_SCHEDULE_TIMEOUT); ++ if (ret < 0) ++ return ret; ++ ++ serdev_device_wait_until_sent(serdev, 0); ++ ++ // wait for wakeup ++ mdelay(100); ++ ++ ret = serdev_device_write(serdev, ubx_suspend_packet, ++ sizeof(ubx_suspend_packet), ++ MAX_SCHEDULE_TIMEOUT); ++ if (ret < 0 || ret < sizeof(ubx_suspend_packet)) ++ return ret; ++ ++ serdev_device_wait_until_sent(serdev, 0); ++#endif ++ + ret = regulator_disable(data->vcc); + if (ret) + return ret; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/hm5065-yaml-bindings-wip.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/hm5065-yaml-bindings-wip.patch new file mode 100644 index 000000000000..95462e00ccfb --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/hm5065-yaml-bindings-wip.patch @@ -0,0 +1,109 @@ +From 95c2f6cf0f9b2c0f358946ebd92947e817bb2ba1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Mon, 20 May 2019 17:58:05 +0200 +Subject: hm5065: yaml bindings (wip) + +--- + .../devicetree/bindings/media/i2c/hm5065.yaml | 90 +++++++++++++++++++ + 1 file changed, 90 insertions(+) + create mode 100644 Documentation/devicetree/bindings/media/i2c/hm5065.yaml + +diff --git a/Documentation/devicetree/bindings/media/i2c/hm5065.yaml b/Documentation/devicetree/bindings/media/i2c/hm5065.yaml +new file mode 100644 +index 000000000000..df7a4f514b3f +--- /dev/null ++++ b/Documentation/devicetree/bindings/media/i2c/hm5065.yaml +@@ -0,0 +1,90 @@ ++# SPDX-License-Identifier: GPL-2.0 ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/media/i2c/hm5065.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Himax HM5065 CSI camera sensor ++ ++description: |+ ++ At least one of reset-gpios and enable-gpios must be configured. ++ ++ The device node must contain one 'port' child node for its digital output ++ video port, in accordance with the video interface bindings defined in ++ ++ Documentation/devicetree/bindings/media/video-interfaces.txt. ++ ++maintainers: ++ - Ondrej Jirman ++ ++properties: ++ compatible: ++ const: himax,hm5065 ++ ++ reg: ++ maxItems: 1 ++ ++ reset-gpios: ++ maxItems: 1 ++ description: reference to the GPIO connected to the reset pin, if any. This is an active low signal to the HM5065. ++ ++ enable-gpios: ++ maxItems: 1 ++ description: reference to the GPIO connected to the CE pin, if any. This is an active high signal to the HM5065. ++ ++ IOVDD-supply: ++ description: Digital I/O voltage supply, 2.8 volts ++ ++ AVDD-supply: ++ description: Analog voltage supply, 2.8 volts ++ ++ DVDD-supply: ++ description: Digital core voltage supply, 1.8 volts ++ ++ AFVDD-supply: ++ description: Auto focus voltage supply, 2.8 volts ++ ++ clocks: ++ minItems: 1 ++ maxItems: 1 ++ description: reference to the external input clock for the sensor. ++ ++- clock-names: should be "xclk". ++ ++required: ++ - compatible ++ - reg ++ - clocks ++ - clock-names ++ - IOVDD-supply ++ - AVDD-supply ++ - DVDD-supply ++ - AFVDD-supply ++ ++Example: ++ ++&i2c1 { ++ hm5065: camera@1f { ++ compatible = "himax,hm5065"; ++ reg = <0x1f>; ++ clocks = <&ccu CLK_CSI_MCLK>; ++ clock-names = "xclk"; ++ IOVDD-supply = <®_dldo3>; ++ AVDD-supply = <®_dldo4>; ++ DVDD-supply = <®_eldo3>; ++ AFVDD-supply = <®_dldo3>; ++ reset-gpios = <&pio 4 18 GPIO_ACTIVE_LOW>; /* PE18 */ ++ enable-gpios = <&pio 4 19 GPIO_ACTIVE_HIGH>; /* PE19 */ ++ ++ port { ++ hm5065_ep: endpoint { ++ remote-endpoint = <&csi0_hm5065_ep>; ++ bus-width = <8>; ++ hsync-active = <1>; ++ vsync-active = <1>; ++ data-active = <1>; ++ pclk-sample = <1>; ++ }; ++ }; ++ }; ++}; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/i2c-mv64xxx-Don-t-make-a-fuss-when-pinctrl-recovery-state-is-no.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/i2c-mv64xxx-Don-t-make-a-fuss-when-pinctrl-recovery-state-is-no.patch new file mode 100644 index 000000000000..b8f1eabba728 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/i2c-mv64xxx-Don-t-make-a-fuss-when-pinctrl-recovery-state-is-no.patch @@ -0,0 +1,35 @@ +From 329092d43e60dd4c8f1308330231a77c7cb00bd9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sun, 17 Jan 2021 23:19:52 +0100 +Subject: i2c: mv64xxx: Don't make a fuss when pinctrl recovery state is not + present + +Otherwise dmesg log presents a useless error. + +Signed-off-by: Ondrej Jirman +--- + drivers/i2c/busses/i2c-mv64xxx.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c +index 874309580c33..d7cac1434126 100644 +--- a/drivers/i2c/busses/i2c-mv64xxx.c ++++ b/drivers/i2c/busses/i2c-mv64xxx.c +@@ -950,6 +950,14 @@ static int mv64xxx_i2c_init_recovery_info(struct mv64xxx_i2c_data *drv_data, + return -ENODEV; + } + ++ if (IS_ERR(pinctrl_lookup_state(rinfo->pinctrl, "gpio")) && ++ IS_ERR(pinctrl_lookup_state(rinfo->pinctrl, "recovery"))) { ++ /* No recovery state is vailable in pinctrl. */ ++ devm_pinctrl_put(rinfo->pinctrl); ++ rinfo->pinctrl = NULL; ++ return 0; ++ } ++ + drv_data->adapter.bus_recovery_info = rinfo; + return 0; + } +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/iio-adc-axp20x_adc-allow-to-set-TS-pin-to-GPADC-mode.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/iio-adc-axp20x_adc-allow-to-set-TS-pin-to-GPADC-mode.patch new file mode 100644 index 000000000000..ba33f252ca4a --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/iio-adc-axp20x_adc-allow-to-set-TS-pin-to-GPADC-mode.patch @@ -0,0 +1,42 @@ +From 5617049aa26b140a13a7eb7c3e38ab940c6cbca0 Mon Sep 17 00:00:00 2001 +From: Icenowy Zheng +Date: Tue, 10 Sep 2019 12:03:32 +0800 +Subject: iio: adc: axp20x_adc: allow to set TS pin to GPADC mode + +The TS pin of AXP PMICs can be set to GPADC mode. In this situation, it +doesn't affect battery charging. + +Add the code to do the mode setting. + +Signed-off-by: Icenowy Zheng +--- + drivers/iio/adc/axp20x_adc.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/drivers/iio/adc/axp20x_adc.c b/drivers/iio/adc/axp20x_adc.c +index 9fd7027623d0..50eef565df2c 100644 +--- a/drivers/iio/adc/axp20x_adc.c ++++ b/drivers/iio/adc/axp20x_adc.c +@@ -58,6 +58,7 @@ + #define AXP813_TS_GPIO0_ADC_RATE_HZ(x) AXP20X_ADC_RATE_HZ(x) + #define AXP813_V_I_ADC_RATE_HZ(x) ((ilog2((x) / 100) << 4) & AXP813_V_I_ADC_RATE_MASK) + #define AXP813_ADC_RATE_HZ(x) (AXP20X_ADC_RATE_HZ(x) | AXP813_V_I_ADC_RATE_HZ(x)) ++#define AXP20X_TS_FUNCTION_GPADC BIT(2) + + #define AXP20X_ADC_CHANNEL(_channel, _name, _type, _reg) \ + { \ +@@ -1131,6 +1132,11 @@ static int axp20x_probe(struct platform_device *pdev) + regmap_set_bits(info->regmap, info->data->adc_en2, + info->data->adc_en2_mask); + ++ if (of_property_read_bool(pdev->dev.of_node, "x-powers,ts-as-gpadc")) ++ regmap_update_bits(info->regmap, AXP20X_ADC_RATE, ++ AXP20X_TS_FUNCTION_GPADC, ++ AXP20X_TS_FUNCTION_GPADC); ++ + /* Configure ADCs rate */ + if (info->data->adc_rate) + info->data->adc_rate(info, 100); +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/iio-adc-sun4i-gpadc-iio-Allow-to-use-sun5i-a13-gpadc-iio-from-D.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/iio-adc-sun4i-gpadc-iio-Allow-to-use-sun5i-a13-gpadc-iio-from-D.patch new file mode 100644 index 000000000000..0bfb8408a0fe --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/iio-adc-sun4i-gpadc-iio-Allow-to-use-sun5i-a13-gpadc-iio-from-D.patch @@ -0,0 +1,61 @@ +From 0ff022dc3b2674f575bcf30a2a988002845eb698 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Mon, 7 Oct 2019 02:12:34 +0200 +Subject: iio: adc: sun4i-gpadc-iio: Allow to use sun5i-a13-gpadc-iio from DT + +We can now use allwinner,sun5i-a13-gpadc-iio to create a sub-node of +allwinner,sun5i-a13-ts, that can be referenced by other drivers +via IIO framework. + +Signed-off-by: Ondrej Jirman +--- + arch/arm/boot/dts/allwinner/sun5i.dtsi | 5 +++++ + drivers/iio/adc/sun4i-gpadc-iio.c | 3 ++- + drivers/mfd/sun4i-gpadc.c | 1 + + 3 files changed, 8 insertions(+), 1 deletion(-) + +diff --git a/arch/arm/boot/dts/allwinner/sun5i.dtsi b/arch/arm/boot/dts/allwinner/sun5i.dtsi +index d7c7b454a11a..e6836fd4b8dc 100644 +--- a/arch/arm/boot/dts/allwinner/sun5i.dtsi ++++ b/arch/arm/boot/dts/allwinner/sun5i.dtsi +@@ -657,6 +657,11 @@ rtp: rtp@1c25000 { + reg = <0x01c25000 0x100>; + interrupts = <29>; + #thermal-sensor-cells = <0>; ++ ++ rtp_adc: adc { ++ compatible = "allwinner,sun5i-a13-gpadc-iio"; ++ #io-channel-cells = <1>; ++ }; + }; + + uart0: serial@1c28000 { +diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c +index 8b27458dcd66..3c673c35629f 100644 +--- a/drivers/iio/adc/sun4i-gpadc-iio.c ++++ b/drivers/iio/adc/sun4i-gpadc-iio.c +@@ -618,7 +618,8 @@ static int sun4i_gpadc_probe(struct platform_device *pdev) + indio_dev->info = &sun4i_gpadc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + +- if (pdev->dev.of_node) ++ if (pdev->dev.of_node && of_match_node(sun4i_gpadc_of_id, ++ pdev->dev.of_node)) + ret = sun4i_gpadc_probe_dt(pdev, indio_dev); + else + ret = sun4i_gpadc_probe_mfd(pdev, indio_dev); +diff --git a/drivers/mfd/sun4i-gpadc.c b/drivers/mfd/sun4i-gpadc.c +index 3029d48e982c..51da961cffd5 100644 +--- a/drivers/mfd/sun4i-gpadc.c ++++ b/drivers/mfd/sun4i-gpadc.c +@@ -53,6 +53,7 @@ static struct mfd_cell sun4i_gpadc_cells[] = { + static struct mfd_cell sun5i_gpadc_cells[] = { + { + .name = "sun5i-a13-gpadc-iio", ++ .of_compatible = "allwinner,sun5i-a13-gpadc-iio", + .resources = adc_resources, + .num_resources = ARRAY_SIZE(adc_resources), + }, +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/iio-st_sensors-Don-t-report-error-when-the-device-is-not-presen.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/iio-st_sensors-Don-t-report-error-when-the-device-is-not-presen.patch new file mode 100644 index 000000000000..e24bffabe61e --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/iio-st_sensors-Don-t-report-error-when-the-device-is-not-presen.patch @@ -0,0 +1,39 @@ +From db9ba37a021ab7935acc6d7b7ff4e40756314229 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sat, 29 Oct 2022 23:12:49 +0200 +Subject: iio: st_sensors: Don't report error when the device is not present on + I2C bus + +On Pinephone, this is not an error, because there are two alternate +magentometer configurations. + +Signed-off-by: Ondrej Jirman +--- + drivers/iio/common/st_sensors/st_sensors_core.c | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c +index e4f5a7ff7e74..5ee2ab1fc619 100644 +--- a/drivers/iio/common/st_sensors/st_sensors_core.c ++++ b/drivers/iio/common/st_sensors/st_sensors_core.c +@@ -600,8 +600,16 @@ int st_sensors_verify_id(struct iio_dev *indio_dev) + err = regmap_read(sdata->regmap, + sdata->sensor_settings->wai_addr, &wai); + if (err < 0) { ++ if (err == -ENXIO) { ++ dev_info(&indio_dev->dev, ++ "%s is not present on the bus\n", ++ indio_dev->name); ++ return err; ++ } ++ + dev_err(&indio_dev->dev, +- "failed to read Who-Am-I register.\n"); ++ "%s: failed to read Who-Am-I register (%d).\n", ++ indio_dev->name, err); + return err; + } + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Clear-the-ids-buffer-in-a-saner-way.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Clear-the-ids-buffer-in-a-saner-way.patch new file mode 100644 index 000000000000..e6a8c25757a1 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Clear-the-ids-buffer-in-a-saner-way.patch @@ -0,0 +1,32 @@ +From eeddb82ccb6cc3d77349fc5eb465b3636383017f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sat, 28 Sep 2019 18:00:33 +0200 +Subject: input: cyttsp4: Clear the ids buffer in a saner way + +Fixes memory corruption / NPE failures during touch data +acquisition. + +Signed-off-by: Ondrej Jirman +--- + drivers/input/touchscreen/cyttsp4_core.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/drivers/input/touchscreen/cyttsp4_core.c b/drivers/input/touchscreen/cyttsp4_core.c +index 31fd75477322..1205fc6658ad 100644 +--- a/drivers/input/touchscreen/cyttsp4_core.c ++++ b/drivers/input/touchscreen/cyttsp4_core.c +@@ -854,10 +854,9 @@ static void cyttsp4_get_mt_touches(struct cyttsp4 *cd, int num_cur_tch) + struct cyttsp4_sysinfo *si = cd->si; + struct cyttsp4_touch tch; + int i, j, t = 0; +- int ids[max(CY_TMA1036_MAX_TCH, CY_TMA4XX_MAX_TCH)]; ++ int ids[max(CY_TMA1036_MAX_TCH, CY_TMA4XX_MAX_TCH)] = {0}; + struct cyttsp4_signal_def* sig; + +- memset(ids, 0, si->si_ofs.tch_abs[CY_TCH_T].max * sizeof(int)); + for (i = 0; i < num_cur_tch; i++) { + cyttsp4_get_touch(cd, &tch, si->xy_data + + (i * si->si_ofs.tch_rec_size)); +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-De-obfuscate-MT-signals-setup-platform-data.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-De-obfuscate-MT-signals-setup-platform-data.patch new file mode 100644 index 000000000000..e202ca0b1e91 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-De-obfuscate-MT-signals-setup-platform-data.patch @@ -0,0 +1,263 @@ +From 3b73c055dddefa34abf2bc744b607c23411e3099 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sat, 28 Sep 2019 16:01:06 +0200 +Subject: input: cyttsp4: De-obfuscate MT signals setup/platform data + +Original code does a lot of array math, and is needlessly complicated. +Make it easier to use and clean it up. + +Signed-off-by: Ondrej Jirman +--- + drivers/input/touchscreen/cyttsp4_core.c | 102 ++++++++++------------- + drivers/input/touchscreen/cyttsp4_core.h | 26 ------ + include/linux/platform_data/cyttsp4.h | 27 ++++-- + 3 files changed, 66 insertions(+), 89 deletions(-) + +diff --git a/drivers/input/touchscreen/cyttsp4_core.c b/drivers/input/touchscreen/cyttsp4_core.c +index 867c18abc89b..31fd75477322 100644 +--- a/drivers/input/touchscreen/cyttsp4_core.c ++++ b/drivers/input/touchscreen/cyttsp4_core.c +@@ -853,31 +853,26 @@ static void cyttsp4_get_mt_touches(struct cyttsp4 *cd, int num_cur_tch) + struct device *dev = &cd->input->dev; + struct cyttsp4_sysinfo *si = cd->si; + struct cyttsp4_touch tch; +- int sig; + int i, j, t = 0; + int ids[max(CY_TMA1036_MAX_TCH, CY_TMA4XX_MAX_TCH)]; ++ struct cyttsp4_signal_def* sig; + + memset(ids, 0, si->si_ofs.tch_abs[CY_TCH_T].max * sizeof(int)); + for (i = 0; i < num_cur_tch; i++) { + cyttsp4_get_touch(cd, &tch, si->xy_data + +- (i * si->si_ofs.tch_rec_size)); +- if ((tch.abs[CY_TCH_T] < cd->pdata->frmwrk->abs +- [(CY_ABS_ID_OST * CY_NUM_ABS_SET) + CY_MIN_OST]) || +- (tch.abs[CY_TCH_T] > cd->pdata->frmwrk->abs +- [(CY_ABS_ID_OST * CY_NUM_ABS_SET) + CY_MAX_OST])) { ++ (i * si->si_ofs.tch_rec_size)); ++ ++ sig = &cd->pdata->signals[CY_ABS_ID_OST]; ++ if (tch.abs[CY_TCH_T] < sig->min || ++ tch.abs[CY_TCH_T] > sig->max) { + dev_err(dev, "%s: tch=%d -> bad trk_id=%d max_id=%d\n", +- __func__, i, tch.abs[CY_TCH_T], +- cd->pdata->frmwrk->abs[(CY_ABS_ID_OST * +- CY_NUM_ABS_SET) + CY_MAX_OST]); ++ __func__, i, tch.abs[CY_TCH_T], sig->max); + continue; + } + + /* use 0 based track id's */ +- sig = cd->pdata->frmwrk->abs +- [(CY_ABS_ID_OST * CY_NUM_ABS_SET) + 0]; +- if (sig != CY_IGNORE_VALUE) { +- t = tch.abs[CY_TCH_T] - cd->pdata->frmwrk->abs +- [(CY_ABS_ID_OST * CY_NUM_ABS_SET) + CY_MIN_OST]; ++ if (sig->signal >= 0) { ++ t = tch.abs[CY_TCH_T] - sig->min; + if (tch.abs[CY_TCH_E] == CY_EV_LIFTOFF) { + dev_dbg(dev, "%s: t=%d e=%d lift-off\n", + __func__, t, tch.abs[CY_TCH_E]); +@@ -885,18 +880,18 @@ static void cyttsp4_get_mt_touches(struct cyttsp4 *cd, int num_cur_tch) + } + input_mt_slot(cd->input, t); + input_mt_report_slot_state(cd->input, MT_TOOL_FINGER, +- true); ++ true); + ids[t] = true; + } + + /* all devices: position and pressure fields */ + for (j = 0; j <= CY_ABS_W_OST; j++) { +- sig = cd->pdata->frmwrk->abs[((CY_ABS_X_OST + j) * +- CY_NUM_ABS_SET) + 0]; +- if (sig != CY_IGNORE_VALUE) +- input_report_abs(cd->input, sig, +- tch.abs[CY_TCH_X + j]); ++ sig = &cd->pdata->signals[j]; ++ if (sig->signal >= 0) ++ input_report_abs(cd->input, sig->signal, ++ tch.abs[CY_TCH_X + j]); + } ++ + if (si->si_ofs.tch_rec_size > CY_TMA1036_TCH_REC_SIZE) { + /* + * TMA400 size and orientation fields: +@@ -909,12 +904,10 @@ static void cyttsp4_get_mt_touches(struct cyttsp4 *cd, int num_cur_tch) + + /* Get the extended touch fields */ + for (j = 0; j < CY_NUM_EXT_TCH_FIELDS; j++) { +- sig = cd->pdata->frmwrk->abs +- [((CY_ABS_MAJ_OST + j) * +- CY_NUM_ABS_SET) + 0]; +- if (sig != CY_IGNORE_VALUE) +- input_report_abs(cd->input, sig, +- tch.abs[CY_TCH_MAJ + j]); ++ sig = &cd->pdata->signals[CY_ABS_MAJ_OST + j]; ++ if (sig->signal >= 0) ++ input_report_abs(cd->input, sig->signal, ++ tch.abs[CY_TCH_MAJ + j]); + } + } + +@@ -1878,7 +1871,7 @@ static void cyttsp4_mt_close(struct input_dev *input) + static int cyttsp4_setup_input_device(struct cyttsp4 *cd) + { + struct device *dev = cd->dev; +- int signal = CY_IGNORE_VALUE; ++ struct cyttsp4_signal_def *signal; + int max_x, max_y, max_p, min, max; + int max_x_tmp, max_y_tmp; + int i; +@@ -1903,37 +1896,30 @@ static int cyttsp4_setup_input_device(struct cyttsp4 *cd) + max_p = cd->si->si_ofs.max_p; + + /* set event signal capabilities */ +- for (i = 0; i < (cd->pdata->frmwrk->size / CY_NUM_ABS_SET); i++) { +- signal = cd->pdata->frmwrk->abs +- [(i * CY_NUM_ABS_SET) + CY_SIGNAL_OST]; +- if (signal != CY_IGNORE_VALUE) { +- __set_bit(signal, cd->input->absbit); +- min = cd->pdata->frmwrk->abs +- [(i * CY_NUM_ABS_SET) + CY_MIN_OST]; +- max = cd->pdata->frmwrk->abs +- [(i * CY_NUM_ABS_SET) + CY_MAX_OST]; +- if (i == CY_ABS_ID_OST) { +- /* shift track ids down to start at 0 */ +- max = max - min; +- min = min - min; +- } else if (i == CY_ABS_X_OST) +- max = max_x; +- else if (i == CY_ABS_Y_OST) +- max = max_y; +- else if (i == CY_ABS_P_OST) +- max = max_p; +- input_set_abs_params(cd->input, signal, min, max, +- cd->pdata->frmwrk->abs +- [(i * CY_NUM_ABS_SET) + CY_FUZZ_OST], +- cd->pdata->frmwrk->abs +- [(i * CY_NUM_ABS_SET) + CY_FLAT_OST]); +- dev_dbg(dev, "%s: register signal=%02X min=%d max=%d\n", +- __func__, signal, min, max); +- if ((i == CY_ABS_ID_OST) && +- (cd->si->si_ofs.tch_rec_size < +- CY_TMA4XX_TCH_REC_SIZE)) +- break; +- } ++ for (i = 0; i < cd->pdata->n_signals; i++) { ++ signal = &cd->pdata->signals[i]; ++ ++ __set_bit(signal->signal, cd->input->absbit); ++ ++ max = signal->max; ++ min = signal->min; ++ ++ if (signal->signal == ABS_MT_POSITION_X) ++ max = max_x; ++ else if (signal->signal == ABS_MT_POSITION_Y) ++ max = max_y; ++ else if (signal->signal == ABS_MT_PRESSURE) ++ max = max_p; ++ ++ input_set_abs_params(cd->input, signal->signal, min, max, ++ signal->fuzz, signal->flat); ++ dev_dbg(dev, "%s: register signal=%02X min=%d max=%d\n", ++ __func__, signal->signal, min, max); ++ ++ if ((i == CY_ABS_ID_OST) && ++ (cd->si->si_ofs.tch_rec_size < ++ CY_TMA4XX_TCH_REC_SIZE)) ++ break; + } + + input_mt_init_slots(cd->input, cd->si->si_ofs.tch_abs[CY_TCH_T].max, +diff --git a/drivers/input/touchscreen/cyttsp4_core.h b/drivers/input/touchscreen/cyttsp4_core.h +index 05fb30058d87..995bfd0a54d0 100644 +--- a/drivers/input/touchscreen/cyttsp4_core.h ++++ b/drivers/input/touchscreen/cyttsp4_core.h +@@ -357,32 +357,6 @@ enum cyttsp4_hst_mode_bits { + CY_HST_RESET = (1 << 0), + }; + +-/* abs settings */ +-#define CY_IGNORE_VALUE 0xFFFF +- +-/* abs signal capabilities offsets in the frameworks array */ +-enum cyttsp4_sig_caps { +- CY_SIGNAL_OST, +- CY_MIN_OST, +- CY_MAX_OST, +- CY_FUZZ_OST, +- CY_FLAT_OST, +- CY_NUM_ABS_SET /* number of signal capability fields */ +-}; +- +-/* abs axis signal offsets in the framworks array */ +-enum cyttsp4_sig_ost { +- CY_ABS_X_OST, +- CY_ABS_Y_OST, +- CY_ABS_P_OST, +- CY_ABS_W_OST, +- CY_ABS_ID_OST, +- CY_ABS_MAJ_OST, +- CY_ABS_MIN_OST, +- CY_ABS_OR_OST, +- CY_NUM_ABS_OST /* number of abs signals */ +-}; +- + enum cyttsp4_flags { + CY_FLAG_NONE = 0x00, + CY_FLAG_HOVER = 0x04, +diff --git a/include/linux/platform_data/cyttsp4.h b/include/linux/platform_data/cyttsp4.h +index 362fa181ac04..1b5b30796e43 100644 +--- a/include/linux/platform_data/cyttsp4.h ++++ b/include/linux/platform_data/cyttsp4.h +@@ -21,15 +21,31 @@ + + #define CY_TOUCH_SETTINGS_MAX 32 + +-struct touch_framework { +- const uint16_t *abs; +- uint8_t size; +-} __packed; ++/* abs axis signal offsets in the signals array */ ++enum cyttsp4_sig_ost { ++ CY_ABS_X_OST, ++ CY_ABS_Y_OST, ++ CY_ABS_P_OST, ++ CY_ABS_W_OST, ++ CY_ABS_ID_OST, ++ CY_ABS_MAJ_OST, ++ CY_ABS_MIN_OST, ++ CY_ABS_OR_OST, ++ CY_NUM_ABS_OST /* number of abs signals */ ++}; + + struct cyttsp4_virtual_key { + int code; + }; + ++struct cyttsp4_signal_def { ++ int signal; ++ int min; ++ int max; ++ int fuzz; ++ int flat; ++}; ++ + struct cyttsp4_platform_data { + char const *inp_dev_name; + unsigned short flags; +@@ -46,7 +62,8 @@ struct cyttsp4_platform_data { + int (*irq_stat)(struct cyttsp4_platform_data *pdata, + struct device *dev); + +- struct touch_framework *frmwrk; ++ int n_signals; ++ struct cyttsp4_signal_def *signals; + + int n_keys; + struct cyttsp4_virtual_key* keys; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-De-obfuscate-platform-data-for-keys.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-De-obfuscate-platform-data-for-keys.patch new file mode 100644 index 000000000000..3f201fb28072 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-De-obfuscate-platform-data-for-keys.patch @@ -0,0 +1,85 @@ +From 942e10d26c88fc22ffb1a520d4441b31912cc5fd Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sat, 28 Sep 2019 14:46:11 +0200 +Subject: input: cyttsp4: De-obfuscate platform data for keys + +Signed-off-by: Ondrej Jirman +--- + drivers/input/touchscreen/cyttsp4_core.c | 22 +++------------------- + include/linux/platform_data/cyttsp4.h | 12 ++++++------ + 2 files changed, 9 insertions(+), 25 deletions(-) + +diff --git a/drivers/input/touchscreen/cyttsp4_core.c b/drivers/input/touchscreen/cyttsp4_core.c +index 7cb26929dc73..f2dd87cdeb8f 100644 +--- a/drivers/input/touchscreen/cyttsp4_core.c ++++ b/drivers/input/touchscreen/cyttsp4_core.c +@@ -541,8 +541,6 @@ static int cyttsp4_si_get_btn_data(struct cyttsp4 *cd) + { + struct cyttsp4_sysinfo *si = &cd->sysinfo; + int btn; +- int num_defined_keys; +- u16 *key_table; + void *p; + int rc = 0; + +@@ -559,24 +557,10 @@ static int cyttsp4_si_get_btn_data(struct cyttsp4 *cd) + } + si->btn = p; + +- if (cd->cpdata->sett[CY_IC_GRPNUM_BTN_KEYS] == NULL) +- num_defined_keys = 0; +- else if (cd->cpdata->sett[CY_IC_GRPNUM_BTN_KEYS]->data == NULL) +- num_defined_keys = 0; +- else +- num_defined_keys = cd->cpdata->sett +- [CY_IC_GRPNUM_BTN_KEYS]->size; +- +- for (btn = 0; btn < si->si_ofs.num_btns && +- btn < num_defined_keys; btn++) { +- key_table = (u16 *)cd->cpdata->sett +- [CY_IC_GRPNUM_BTN_KEYS]->data; +- si->btn[btn].key_code = key_table[btn]; +- si->btn[btn].state = CY_BTN_RELEASED; +- si->btn[btn].enabled = true; +- } +- for (; btn < si->si_ofs.num_btns; btn++) { ++ for (btn = 0; btn < si->si_ofs.num_btns; btn++) { + si->btn[btn].key_code = KEY_RESERVED; ++ if (btn < cd->cpdata->n_keys) ++ si->btn[btn].key_code = cd->cpdata->keys[btn].code; + si->btn[btn].state = CY_BTN_RELEASED; + si->btn[btn].enabled = true; + } +diff --git a/include/linux/platform_data/cyttsp4.h b/include/linux/platform_data/cyttsp4.h +index 5dc9d2be384b..c54160c17b0b 100644 +--- a/include/linux/platform_data/cyttsp4.h ++++ b/include/linux/platform_data/cyttsp4.h +@@ -33,11 +33,9 @@ struct cyttsp4_mt_platform_data { + char const *inp_dev_name; + }; + +-struct touch_settings { +- const uint8_t *data; +- uint32_t size; +- uint8_t tag; +-} __packed; ++struct cyttsp4_virtual_key { ++ int code; ++}; + + struct cyttsp4_core_platform_data { + int irq_gpio; +@@ -51,7 +49,9 @@ struct cyttsp4_core_platform_data { + int on, struct device *dev, atomic_t *ignore_irq); + int (*irq_stat)(struct cyttsp4_core_platform_data *pdata, + struct device *dev); +- struct touch_settings *sett[CY_TOUCH_SETTINGS_MAX]; ++ ++ int n_keys; ++ struct cyttsp4_virtual_key* keys; + }; + + struct cyttsp4_platform_data { +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-ENOSYS-error-is-ok-when-powering-up.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-ENOSYS-error-is-ok-when-powering-up.patch new file mode 100644 index 000000000000..23785caa6db5 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-ENOSYS-error-is-ok-when-powering-up.patch @@ -0,0 +1,29 @@ +From fc58479f81ed2a3d2eaf37030addf1534fcf10d0 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sat, 28 Sep 2019 18:01:49 +0200 +Subject: input: cyttsp4: ENOSYS error is ok when powering up + +This error is handled gracefully, don't report it. + +Signed-off-by: Ondrej Jirman +--- + drivers/input/touchscreen/cyttsp4_core.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/input/touchscreen/cyttsp4_core.c b/drivers/input/touchscreen/cyttsp4_core.c +index 1205fc6658ad..1e277ceca20f 100644 +--- a/drivers/input/touchscreen/cyttsp4_core.c ++++ b/drivers/input/touchscreen/cyttsp4_core.c +@@ -1764,7 +1764,8 @@ static int cyttsp4_core_wake_(struct cyttsp4 *cd) + rc = -ENOSYS; + } + if (rc < 0) { +- dev_err(dev, "%s: HW Power up fails r=%d\n", ++ if (rc != -ENOSYS) ++ dev_err(dev, "%s: HW Power up fails r=%d\n", + __func__, rc); + + /* Initiate a read transaction to wake up */ +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Faster-recovery-from-failed-wakeup-HACK.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Faster-recovery-from-failed-wakeup-HACK.patch new file mode 100644 index 000000000000..7ff27992b2c4 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Faster-recovery-from-failed-wakeup-HACK.patch @@ -0,0 +1,31 @@ +From d360ec7955abbb6da5b01a392a0192cb561a54c3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sat, 28 Sep 2019 18:03:41 +0200 +Subject: input: cyttsp4: Faster recovery from failed wakeup (HACK) + +For some reason on Pocket Book Touch Lux 3, sending and I2C +data, doesn't wake up the device, and it always times out. + +Lower the timeout, so that reset is done ASAP after wakeup. + +Signed-off-by: Ondrej Jirman +--- + drivers/input/touchscreen/cyttsp4_core.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/input/touchscreen/cyttsp4_core.c b/drivers/input/touchscreen/cyttsp4_core.c +index 1e277ceca20f..c30e2088c732 100644 +--- a/drivers/input/touchscreen/cyttsp4_core.c ++++ b/drivers/input/touchscreen/cyttsp4_core.c +@@ -26,7 +26,7 @@ + #define CY_CORE_SLEEP_REQUEST_EXCLUSIVE_TIMEOUT 5000 + #define CY_CORE_MODE_CHANGE_TIMEOUT 1000 + #define CY_CORE_RESET_AND_WAIT_TIMEOUT 500 +-#define CY_CORE_WAKEUP_TIMEOUT 500 ++#define CY_CORE_WAKEUP_TIMEOUT 50 + + #define CY_CORE_STARTUP_RETRY_COUNT 3 + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Fix-compile-issue.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Fix-compile-issue.patch new file mode 100644 index 000000000000..dde944276549 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Fix-compile-issue.patch @@ -0,0 +1,28 @@ +From 224d61a728afdbd5131719f99e33b6be432ea25c Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Fri, 22 Nov 2024 23:42:03 +0100 +Subject: input: cyttsp4: Fix compile issue + +We can't use {0} initializer with v6.12+ + +Signed-off-by: Ondrej Jirman +--- + drivers/input/touchscreen/cyttsp4_core.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/input/touchscreen/cyttsp4_core.c b/drivers/input/touchscreen/cyttsp4_core.c +index 67cb72ad6620..97264bfbfca4 100644 +--- a/drivers/input/touchscreen/cyttsp4_core.c ++++ b/drivers/input/touchscreen/cyttsp4_core.c +@@ -851,7 +851,7 @@ static void cyttsp4_get_mt_touches(struct cyttsp4 *cd, int num_cur_tch) + struct cyttsp4_sysinfo *si = cd->si; + struct cyttsp4_touch tch; + int i, j, t = 0; +- int ids[max(CY_TMA1036_MAX_TCH, CY_TMA4XX_MAX_TCH)] = {0}; ++ int ids[max(CY_TMA1036_MAX_TCH, CY_TMA4XX_MAX_TCH)] = {}; + struct cyttsp4_signal_def* sig; + + for (i = 0; i < num_cur_tch; i++) { +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Fix-probe-oops.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Fix-probe-oops.patch new file mode 100644 index 000000000000..aef98e65ef70 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Fix-probe-oops.patch @@ -0,0 +1,29 @@ +From 4909c2986e0c930ce9ee49e30f53eae3a1fd5abd Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Fri, 11 Mar 2022 22:01:10 +0100 +Subject: input: cyttsp4: Fix probe oops + +__set_bit is called with bit offset -1 which causes oops. + +Signed-off-by: Ondrej Jirman +--- + drivers/input/touchscreen/cyttsp4_core.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/input/touchscreen/cyttsp4_core.c b/drivers/input/touchscreen/cyttsp4_core.c +index c353ac73c106..67cb72ad6620 100644 +--- a/drivers/input/touchscreen/cyttsp4_core.c ++++ b/drivers/input/touchscreen/cyttsp4_core.c +@@ -1903,6 +1903,9 @@ static int cyttsp4_setup_input_device(struct cyttsp4 *cd) + for (i = 0; i < cd->n_signals; i++) { + signal = &cd->signals[i]; + ++ if (signal->signal < 0) ++ continue; ++ + __set_bit(signal->signal, cd->input->absbit); + + max = signal->max; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Fix-warnings.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Fix-warnings.patch new file mode 100644 index 000000000000..d32a0d29921e --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Fix-warnings.patch @@ -0,0 +1,47 @@ +From 46db61d367d9b20e72bb4660be54a418672262ec Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Mon, 7 Oct 2019 06:22:56 +0200 +Subject: input: cyttsp4: Fix warnings + +Signed-off-by: Ondrej Jirman +--- + drivers/input/touchscreen/cyttsp4_core.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/drivers/input/touchscreen/cyttsp4_core.c b/drivers/input/touchscreen/cyttsp4_core.c +index 1ddb0b466a46..d9243c9db69a 100644 +--- a/drivers/input/touchscreen/cyttsp4_core.c ++++ b/drivers/input/touchscreen/cyttsp4_core.c +@@ -1461,6 +1461,7 @@ static void cyttsp4_watchdog_work(struct work_struct *work) + return; + } + ++#if 0 + static int cyttsp4_set_power(struct cyttsp4* cd, int on) + { + dev_dbg(cd->dev, "Power %s\n", on ? "up" : "down"); +@@ -1476,6 +1477,7 @@ static int cyttsp4_set_power(struct cyttsp4* cd, int on) + + return -ENOSYS; + } ++#endif + + static int cyttsp4_core_sleep_(struct cyttsp4 *cd) + { +@@ -1743,12 +1745,12 @@ static int cyttsp4_core_sleep(struct cyttsp4 *cd) + + static int cyttsp4_core_wake_(struct cyttsp4 *cd) + { ++#if 0 + struct device *dev = cd->dev; + int rc; + u8 mode; + int t; + +-#if 0 + /* Already woken? */ + mutex_lock(&cd->system_lock); + if (cd->sleep_state == SS_SLEEP_OFF) { +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Make-the-driver-not-hog-the-system-s-workqueue.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Make-the-driver-not-hog-the-system-s-workqueue.patch new file mode 100644 index 000000000000..919a6049cd55 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Make-the-driver-not-hog-the-system-s-workqueue.patch @@ -0,0 +1,105 @@ +From 1445518f66b5f3d00bee5a10cf66fda0d0adae18 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Mon, 14 Oct 2019 18:00:32 +0200 +Subject: input: cyttsp4: Make the driver not hog the system's workqueue + +The driver's work items can take 100's of ms, use a separate +thread for this. + +Signed-off-by: Ondrej Jirman +--- + drivers/input/touchscreen/cyttsp4_core.c | 20 ++++++++++++++------ + drivers/input/touchscreen/cyttsp4_core.h | 3 ++- + 2 files changed, 16 insertions(+), 7 deletions(-) + +diff --git a/drivers/input/touchscreen/cyttsp4_core.c b/drivers/input/touchscreen/cyttsp4_core.c +index d9243c9db69a..c353ac73c106 100644 +--- a/drivers/input/touchscreen/cyttsp4_core.c ++++ b/drivers/input/touchscreen/cyttsp4_core.c +@@ -721,7 +721,7 @@ static void cyttsp4_queue_startup_(struct cyttsp4 *cd) + { + if (cd->startup_state == STARTUP_NONE) { + cd->startup_state = STARTUP_QUEUED; +- schedule_work(&cd->startup_work); ++ queue_work(cd->wq, &cd->startup_work); + dev_dbg(cd->dev, "%s: cyttsp4_startup queued\n", __func__); + } else { + dev_dbg(cd->dev, "%s: startup_state = %d\n", __func__, +@@ -1237,9 +1237,7 @@ static void cyttsp4_watchdog_timer(struct timer_list *t) + + dev_vdbg(cd->dev, "%s: Watchdog timer triggered\n", __func__); + +- schedule_work(&cd->watchdog_work); +- +- return; ++ queue_work(cd->wq, &cd->watchdog_work); + } + + static int cyttsp4_request_exclusive(struct cyttsp4 *cd, void *ownptr, +@@ -2048,6 +2046,13 @@ struct cyttsp4 *cyttsp4_probe(const struct cyttsp4_bus_ops *ops, + goto error_disable_vdd; + } + ++ cd->wq = alloc_workqueue("cyttsp4", WQ_SYSFS, 0); ++ if (!cd->wq) { ++ rc = -ENOMEM; ++ dev_err(dev, "failed to allocate workqueue\n"); ++ goto error_disable_vdd; ++ } ++ + /* Initialize device info */ + cd->dev = dev; + cd->bus_ops = ops; +@@ -2071,7 +2076,7 @@ struct cyttsp4 *cyttsp4_probe(const struct cyttsp4_bus_ops *ops, + if (rc) { + dev_err(cd->dev, "failed to request IRQ %d, err: %d\n", + cd->irq, rc); +- goto error_disable_vdd; ++ goto error_free_wq; + } + + /* Setup watchdog timer */ +@@ -2105,6 +2110,8 @@ struct cyttsp4 *cyttsp4_probe(const struct cyttsp4_bus_ops *ops, + cyttsp4_stop_wd_timer(cd); + pm_runtime_disable(dev); + cyttsp4_free_si_ptrs(cd); ++error_free_wq: ++ destroy_workqueue(cd->wq); + error_disable_vdd: + gpiod_set_value_cansleep(cd->reset_gpio, 1); + gpiod_set_value_cansleep(cd->power_gpio, 0); +@@ -2132,8 +2139,9 @@ int cyttsp4_remove(struct cyttsp4 *cd) + pm_runtime_suspend(dev); + pm_runtime_disable(dev); + +- cancel_work_sync(&cd->startup_work); + cyttsp4_stop_wd_timer(cd); ++ cancel_work_sync(&cd->startup_work); ++ destroy_workqueue(cd->wq); + cyttsp4_free_si_ptrs(cd); + return 0; + } +diff --git a/drivers/input/touchscreen/cyttsp4_core.h b/drivers/input/touchscreen/cyttsp4_core.h +index cff547979b28..ac2d7b303f39 100644 +--- a/drivers/input/touchscreen/cyttsp4_core.h ++++ b/drivers/input/touchscreen/cyttsp4_core.h +@@ -62,7 +62,7 @@ enum cyttsp_cmd_bits { + }; + + /* Timeout in ms. */ +-#define CY_WATCHDOG_TIMEOUT 1000 ++#define CY_WATCHDOG_TIMEOUT 10000 + + #define CY_MAX_PRINT_SIZE 512 + #ifdef VERBOSE_DEBUG +@@ -316,6 +316,7 @@ struct cyttsp4 { + enum cyttsp4_startup_state startup_state; + int int_status; + wait_queue_head_t wait_q; ++ struct workqueue_struct *wq; + int irq; + struct work_struct startup_work; + struct work_struct watchdog_work; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Port-the-driver-to-use-device-properties.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Port-the-driver-to-use-device-properties.patch new file mode 100644 index 000000000000..da130be0533f --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Port-the-driver-to-use-device-properties.patch @@ -0,0 +1,590 @@ +From 7fd7b9301fa4d426d48cc017cd1920cec28be8a0 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Tue, 1 Oct 2019 00:31:06 +0200 +Subject: input: cyttsp4: Port the driver to use device properties + +The driver has several unimplemented features, that are nevertheless +expressed in its platform data. Drop this. + +If needed, interested parties can implement them properly in the +future. + +Signed-off-by: Ondrej Jirman +--- + drivers/input/touchscreen/cyttsp4_core.c | 234 +++++++++++------------ + drivers/input/touchscreen/cyttsp4_core.h | 36 +++- + include/linux/platform_data/cyttsp4.h | 70 ------- + 3 files changed, 151 insertions(+), 189 deletions(-) + +diff --git a/drivers/input/touchscreen/cyttsp4_core.c b/drivers/input/touchscreen/cyttsp4_core.c +index c30e2088c732..58eb9586a46f 100644 +--- a/drivers/input/touchscreen/cyttsp4_core.c ++++ b/drivers/input/touchscreen/cyttsp4_core.c +@@ -17,7 +17,9 @@ + #include + #include + #include ++#include + #include ++#include + #include + #include + +@@ -131,23 +133,20 @@ static int cyttsp4_hw_soft_reset(struct cyttsp4 *cd) + return 0; + } + +-static int cyttsp4_hw_hard_reset(struct cyttsp4 *cd) +-{ +- if (cd->pdata->xres) { +- cd->pdata->xres(cd->pdata, cd->dev); +- dev_dbg(cd->dev, "%s: execute HARD reset\n", __func__); +- return 0; +- } +- dev_err(cd->dev, "%s: FAILED to execute HARD reset\n", __func__); +- return -ENOSYS; +-} +- + static int cyttsp4_hw_reset(struct cyttsp4 *cd) + { +- int rc = cyttsp4_hw_hard_reset(cd); +- if (rc == -ENOSYS) +- rc = cyttsp4_hw_soft_reset(cd); +- return rc; ++ if (!cd->reset_gpio) ++ return cyttsp4_hw_soft_reset(cd); ++ ++ gpiod_set_value_cansleep(cd->reset_gpio, 1); ++ msleep(20); ++ ++ gpiod_set_value_cansleep(cd->reset_gpio, 0); ++ msleep(40); ++ ++ gpiod_set_value_cansleep(cd->reset_gpio, 1); ++ msleep(20); ++ return 0; + } + + /* +@@ -559,8 +558,8 @@ static int cyttsp4_si_get_btn_data(struct cyttsp4 *cd) + + for (btn = 0; btn < si->si_ofs.num_btns; btn++) { + si->btn[btn].key_code = KEY_RESERVED; +- if (btn < cd->pdata->n_keys) +- si->btn[btn].key_code = cd->pdata->keys[btn].code; ++ if (btn < cd->n_keys) ++ si->btn[btn].key_code = cd->keys[btn].code; + si->btn[btn].state = CY_BTN_RELEASED; + si->btn[btn].enabled = true; + } +@@ -803,13 +802,13 @@ static void cyttsp4_get_touch(struct cyttsp4 *cd, + touch->abs[abs], touch->abs[abs]); + } + +- if (cd->pdata->flags & CY_FLAG_FLIP) { ++ if (cd->flags & CY_FLAG_FLIP) { + swap(touch->abs[CY_TCH_X], touch->abs[CY_TCH_Y]); + flipped = true; + } else + flipped = false; + +- if (cd->pdata->flags & CY_FLAG_INV_X) { ++ if (cd->flags & CY_FLAG_INV_X) { + if (flipped) + touch->abs[CY_TCH_X] = cd->si->si_ofs.max_y - + touch->abs[CY_TCH_X]; +@@ -817,7 +816,7 @@ static void cyttsp4_get_touch(struct cyttsp4 *cd, + touch->abs[CY_TCH_X] = cd->si->si_ofs.max_x - + touch->abs[CY_TCH_X]; + } +- if (cd->pdata->flags & CY_FLAG_INV_Y) { ++ if (cd->flags & CY_FLAG_INV_Y) { + if (flipped) + touch->abs[CY_TCH_Y] = cd->si->si_ofs.max_x - + touch->abs[CY_TCH_Y]; +@@ -828,8 +827,8 @@ static void cyttsp4_get_touch(struct cyttsp4 *cd, + + dev_vdbg(dev, "%s: flip=%s inv-x=%s inv-y=%s x=%04X(%d) y=%04X(%d)\n", + __func__, flipped ? "true" : "false", +- cd->pdata->flags & CY_FLAG_INV_X ? "true" : "false", +- cd->pdata->flags & CY_FLAG_INV_Y ? "true" : "false", ++ cd->flags & CY_FLAG_INV_X ? "true" : "false", ++ cd->flags & CY_FLAG_INV_Y ? "true" : "false", + touch->abs[CY_TCH_X], touch->abs[CY_TCH_X], + touch->abs[CY_TCH_Y], touch->abs[CY_TCH_Y]); + } +@@ -861,7 +860,7 @@ static void cyttsp4_get_mt_touches(struct cyttsp4 *cd, int num_cur_tch) + cyttsp4_get_touch(cd, &tch, si->xy_data + + (i * si->si_ofs.tch_rec_size)); + +- sig = &cd->pdata->signals[CY_ABS_ID_OST]; ++ sig = &cd->signals[CY_ABS_ID_OST]; + if (tch.abs[CY_TCH_T] < sig->min || + tch.abs[CY_TCH_T] > sig->max) { + dev_err(dev, "%s: tch=%d -> bad trk_id=%d max_id=%d\n", +@@ -885,7 +884,7 @@ static void cyttsp4_get_mt_touches(struct cyttsp4 *cd, int num_cur_tch) + + /* all devices: position and pressure fields */ + for (j = 0; j <= CY_ABS_W_OST; j++) { +- sig = &cd->pdata->signals[j]; ++ sig = &cd->signals[j]; + if (sig->signal >= 0) + input_report_abs(cd->input, sig->signal, + tch.abs[CY_TCH_X + j]); +@@ -903,7 +902,7 @@ static void cyttsp4_get_mt_touches(struct cyttsp4 *cd, int num_cur_tch) + + /* Get the extended touch fields */ + for (j = 0; j < CY_NUM_EXT_TCH_FIELDS; j++) { +- sig = &cd->pdata->signals[CY_ABS_MAJ_OST + j]; ++ sig = &cd->signals[CY_ABS_MAJ_OST + j]; + if (sig->signal >= 0) + input_report_abs(cd->input, sig->signal, + tch.abs[CY_TCH_MAJ + j]); +@@ -1207,13 +1206,6 @@ static irqreturn_t cyttsp4_irq(int irq, void *handle) + dev_err(dev, "%s: Fail handshake mode=0x%02X r=%d\n", + __func__, mode[0], rc); + +- /* +- * a non-zero udelay period is required for using +- * IRQF_TRIGGER_LOW in order to delay until the +- * device completes isr deassert +- */ +- udelay(cd->pdata->level_irq_udelay); +- + cyttsp4_irq_exit: + mutex_unlock(&cd->system_lock); + return IRQ_HANDLED; +@@ -1471,6 +1463,22 @@ static void cyttsp4_watchdog_work(struct work_struct *work) + return; + } + ++static int cyttsp4_set_power(struct cyttsp4* cd, int on) ++{ ++ dev_dbg(cd->dev, "Power %s\n", on ? "up" : "down"); ++ ++ if (!cd->power_gpio) ++ return -ENOSYS; ++ ++ //atomic_set(&cd->ignore_irq, 1); ++ ++ //gpiod_set_value_cansleep(cd->power_gpio, on); ++ ++ //atomic_set(&cd->ignore_irq, 0); ++ ++ return -ENOSYS; ++} ++ + static int cyttsp4_core_sleep_(struct cyttsp4 *cd) + { + enum cyttsp4_sleep_state ss = SS_SLEEP_ON; +@@ -1517,13 +1525,7 @@ static int cyttsp4_core_sleep_(struct cyttsp4 *cd) + } + dev_vdbg(cd->dev, "%s: write DEEP SLEEP succeeded\n", __func__); + +- if (cd->pdata->power) { +- dev_dbg(cd->dev, "%s: Power down HW\n", __func__); +- rc = cd->pdata->power(cd->pdata, 0, cd->dev, &cd->ignore_irq); +- } else { +- dev_dbg(cd->dev, "%s: No power function\n", __func__); +- rc = 0; +- } ++ rc = cyttsp4_set_power(cd, 0); + if (rc < 0) { + dev_err(cd->dev, "%s: HW Power down fails r=%d\n", + __func__, rc); +@@ -1756,13 +1758,7 @@ static int cyttsp4_core_wake_(struct cyttsp4 *cd) + cd->int_status |= CY_INT_AWAKE; + cd->sleep_state = SS_WAKING; + +- if (cd->pdata->power) { +- dev_dbg(dev, "%s: Power up HW\n", __func__); +- rc = cd->pdata->power(cd->pdata, 1, dev, &cd->ignore_irq); +- } else { +- dev_dbg(dev, "%s: No power function\n", __func__); +- rc = -ENOSYS; +- } ++ rc = cyttsp4_set_power(cd, 1); + if (rc < 0) { + if (rc != -ENOSYS) + dev_err(dev, "%s: HW Power up fails r=%d\n", +@@ -1886,7 +1882,7 @@ static int cyttsp4_setup_input_device(struct cyttsp4 *cd) + max_y_tmp = cd->si->si_ofs.max_y; + + /* get maximum values from the sysinfo data */ +- if (cd->pdata->flags & CY_FLAG_FLIP) { ++ if (cd->flags & CY_FLAG_FLIP) { + max_x = max_y_tmp - 1; + max_y = max_x_tmp - 1; + } else { +@@ -1896,8 +1892,8 @@ static int cyttsp4_setup_input_device(struct cyttsp4 *cd) + max_p = cd->si->si_ofs.max_p; + + /* set event signal capabilities */ +- for (i = 0; i < cd->pdata->n_signals; i++) { +- signal = &cd->pdata->signals[i]; ++ for (i = 0; i < cd->n_signals; i++) { ++ signal = &cd->signals[i]; + + __set_bit(signal->signal, cd->input->absbit); + +@@ -1948,7 +1944,7 @@ static int cyttsp4_mt_probe(struct cyttsp4 *cd) + goto error_alloc_failed; + } + +- cd->input->name = cd->pdata->inp_dev_name; ++ cd->input->name = "mt_ctp"; + scnprintf(cd->phys, sizeof(cd->phys)-1, "%s", dev_name(dev)); + cd->input->phys = cd->phys; + cd->input->id.bustype = cd->bus_ops->bustype; +@@ -1973,38 +1969,77 @@ static int cyttsp4_mt_probe(struct cyttsp4 *cd) + return rc; + } + ++static struct cyttsp4_signal_def cyttsp4_default_signals[CY_NUM_ABS_OST] = { ++ [CY_ABS_X_OST] = { ABS_MT_POSITION_X, 0, 758, }, ++ [CY_ABS_Y_OST] = { ABS_MT_POSITION_Y, 0, 1024, }, ++ [CY_ABS_P_OST] = { ABS_MT_PRESSURE, 0, 255, }, ++ [CY_ABS_W_OST] = { -1 }, ++ [CY_ABS_ID_OST] = { ABS_MT_TRACKING_ID, 0, 15, }, ++ [CY_ABS_MAJ_OST] = { ABS_MT_TOUCH_MAJOR, 0, 255, }, ++ [CY_ABS_MIN_OST] = { ABS_MT_TOUCH_MINOR, 0, 255, }, ++ [CY_ABS_OR_OST] = { ABS_MT_ORIENTATION, -127, 127, }, ++}; ++ + struct cyttsp4 *cyttsp4_probe(const struct cyttsp4_bus_ops *ops, + struct device *dev, u16 irq, size_t xfer_buf_size) + { + struct cyttsp4 *cd; +- struct cyttsp4_platform_data *pdata = dev_get_platdata(dev); +- unsigned long irq_flags; + int rc = 0; + +- if (!pdata) { +- dev_err(dev, "%s: Missing platform data\n", __func__); +- rc = -ENODEV; +- goto error_no_pdata; +- } +- +- cd = kzalloc(sizeof(*cd), GFP_KERNEL); ++ cd = devm_kzalloc(dev, sizeof(*cd), GFP_KERNEL); + if (!cd) { + dev_err(dev, "%s: Error, kzalloc\n", __func__); +- rc = -ENOMEM; +- goto error_alloc_data; ++ return ERR_PTR(-ENOMEM); + } + +- cd->xfer_buf = kzalloc(xfer_buf_size, GFP_KERNEL); ++ cd->signals = cyttsp4_default_signals; ++ cd->n_signals = ARRAY_SIZE(cyttsp4_default_signals); ++ ++ if (device_property_read_bool(dev, "flip")) ++ cd->flags |= CY_FLAG_FLIP; ++ if (device_property_read_bool(dev, "invert-x")) ++ cd->flags |= CY_FLAG_INV_X; ++ if (device_property_read_bool(dev, "invert-y")) ++ cd->flags |= CY_FLAG_INV_Y; ++ ++ cd->vdd_supply = devm_regulator_get(dev, "vdd"); ++ if (IS_ERR(cd->vdd_supply)) { ++ rc = PTR_ERR(cd->vdd_supply); ++ dev_err(dev, "can't get vdd power (%d)\n", rc); ++ return ERR_PTR(rc); ++ } ++ ++ rc = regulator_enable(cd->vdd_supply); ++ if (rc) { ++ dev_err(dev, "can't enable vdd power (%d)\n", rc); ++ return ERR_PTR(rc); ++ } ++ ++ cd->power_gpio = devm_gpiod_get(dev, "power", GPIOD_OUT_HIGH); ++ if (IS_ERR(cd->reset_gpio)) { ++ rc = PTR_ERR(cd->reset_gpio); ++ dev_err(dev, "can't get reset gpio (%d)\n", rc); ++ return ERR_PTR(rc); ++ } ++ ++ cd->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); ++ if (IS_ERR(cd->reset_gpio)) { ++ rc = PTR_ERR(cd->reset_gpio); ++ dev_err(dev, "can't get reset gpio (%d)\n", rc); ++ return ERR_PTR(rc); ++ } ++ ++ cd->xfer_buf = devm_kzalloc(dev, xfer_buf_size, GFP_KERNEL); + if (!cd->xfer_buf) { + dev_err(dev, "%s: Error, kzalloc\n", __func__); + rc = -ENOMEM; +- goto error_free_cd; ++ goto error_disable_vdd; + } + + /* Initialize device info */ + cd->dev = dev; +- cd->pdata = pdata; + cd->bus_ops = ops; ++ dev_set_drvdata(dev, cd); + + /* Initialize mutexes and spinlocks */ + mutex_init(&cd->system_lock); +@@ -2018,43 +2053,20 @@ struct cyttsp4 *cyttsp4_probe(const struct cyttsp4_bus_ops *ops, + INIT_WORK(&cd->watchdog_work, cyttsp4_watchdog_work); + + /* Initialize IRQ */ +- cd->irq = gpio_to_irq(cd->pdata->irq_gpio); +- if (cd->irq < 0) { +- rc = -EINVAL; +- goto error_free_xfer; +- } +- +- dev_set_drvdata(dev, cd); +- +- /* Call platform init function */ +- if (cd->pdata->init) { +- dev_dbg(cd->dev, "%s: Init HW\n", __func__); +- rc = cd->pdata->init(cd->pdata, 1, cd->dev); +- } else { +- dev_dbg(cd->dev, "%s: No HW INIT function\n", __func__); +- rc = 0; +- } +- if (rc < 0) +- dev_err(cd->dev, "%s: HW Init fail r=%d\n", __func__, rc); +- +- dev_dbg(dev, "%s: initialize threaded irq=%d\n", __func__, cd->irq); +- if (cd->pdata->level_irq_udelay > 0) +- /* use level triggered interrupts */ +- irq_flags = IRQF_TRIGGER_LOW | IRQF_ONESHOT; +- else +- /* use edge triggered interrupts */ +- irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; +- +- rc = request_threaded_irq(cd->irq, NULL, cyttsp4_irq, irq_flags, +- dev_name(dev), cd); +- if (rc < 0) { +- dev_err(dev, "%s: Error, could not request irq\n", __func__); +- goto error_request_irq; ++ rc = devm_request_threaded_irq(dev, cd->irq, NULL, cyttsp4_irq, ++ IRQF_ONESHOT | IRQF_TRIGGER_FALLING, ++ dev_name(dev), cd); ++ if (rc) { ++ dev_err(cd->dev, "failed to request IRQ %d, err: %d\n", ++ cd->irq, rc); ++ goto error_disable_vdd; + } + + /* Setup watchdog timer */ + timer_setup(&cd->watchdog_timer, cyttsp4_watchdog_timer, 0); + ++ //XXX: msleep(300); ++ + /* + * call startup directly to ensure that the device + * is tested before leaving the probe +@@ -2062,17 +2074,15 @@ struct cyttsp4 *cyttsp4_probe(const struct cyttsp4_bus_ops *ops, + rc = cyttsp4_startup(cd); + + /* Do not fail probe if startup fails but the device is detected */ +- if (rc < 0 && cd->mode == CY_MODE_UNKNOWN) { +- dev_err(cd->dev, "%s: Fail initial startup r=%d\n", ++ if (rc && cd->mode == CY_MODE_UNKNOWN) { ++ dev_err(cd->dev, "%s: Fail initial startup rc=%d\n", + __func__, rc); + goto error_startup; + } + + rc = cyttsp4_mt_probe(cd); +- if (rc < 0) { +- dev_err(dev, "%s: Error, fail mt probe\n", __func__); ++ if (rc) + goto error_startup; +- } + + pm_runtime_enable(dev); + +@@ -2083,17 +2093,10 @@ struct cyttsp4 *cyttsp4_probe(const struct cyttsp4_bus_ops *ops, + cyttsp4_stop_wd_timer(cd); + pm_runtime_disable(dev); + cyttsp4_free_si_ptrs(cd); +- free_irq(cd->irq, cd); +-error_request_irq: +- if (cd->pdata->init) +- cd->pdata->init(cd->pdata, 0, dev); +-error_free_xfer: +- kfree(cd->xfer_buf); +-error_free_cd: +- kfree(cd); +-error_alloc_data: +-error_no_pdata: +- dev_err(dev, "%s failed.\n", __func__); ++error_disable_vdd: ++ gpiod_set_value_cansleep(cd->reset_gpio, 1); ++ gpiod_set_value_cansleep(cd->power_gpio, 0); ++ regulator_disable(cd->vdd_supply); + return ERR_PTR(rc); + } + EXPORT_SYMBOL_GPL(cyttsp4_probe); +@@ -2118,14 +2121,8 @@ int cyttsp4_remove(struct cyttsp4 *cd) + pm_runtime_disable(dev); + + cancel_work_sync(&cd->startup_work); +- + cyttsp4_stop_wd_timer(cd); +- +- free_irq(cd->irq, cd); +- if (cd->pdata->init) +- cd->pdata->init(cd->pdata, 0, dev); + cyttsp4_free_si_ptrs(cd); +- kfree(cd); + return 0; + } + EXPORT_SYMBOL_GPL(cyttsp4_remove); +@@ -2133,3 +2130,4 @@ EXPORT_SYMBOL_GPL(cyttsp4_remove); + MODULE_LICENSE("GPL"); + MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard touchscreen core driver"); + MODULE_AUTHOR("Cypress"); ++MODULE_AUTHOR("Ondrej Jirman "); +diff --git a/drivers/input/touchscreen/cyttsp4_core.h b/drivers/input/touchscreen/cyttsp4_core.h +index 995bfd0a54d0..cff547979b28 100644 +--- a/drivers/input/touchscreen/cyttsp4_core.h ++++ b/drivers/input/touchscreen/cyttsp4_core.h +@@ -24,6 +24,8 @@ + #include + #include + #include ++#include ++#include + + #define CY_REG_BASE 0x00 + +@@ -329,12 +331,19 @@ struct cyttsp4 { + bool is_suspended; + char phys[NAME_MAX]; + int num_prv_tch; +- struct cyttsp4_platform_data *pdata; + const struct cyttsp4_bus_ops *bus_ops; + u8 *xfer_buf; + #ifdef VERBOSE_DEBUG + u8 pr_buf[CY_MAX_PRBUF_SIZE]; + #endif ++ int flags; ++ int n_signals; ++ struct cyttsp4_signal_def *signals; ++ int n_keys; ++ struct cyttsp4_virtual_key* keys; ++ struct gpio_desc *reset_gpio; ++ struct gpio_desc *power_gpio; ++ struct regulator *vdd_supply; + }; + + struct cyttsp4_bus_ops { +@@ -392,6 +401,31 @@ enum cyttsp4_event_id { + /* y-axis, 0:origin is on top side of panel, 1: bottom */ + #define CY_PCFG_ORIGIN_Y_MASK 0x80 + ++/* abs axis signal offsets in the signals array */ ++enum cyttsp4_sig_ost { ++ CY_ABS_X_OST, ++ CY_ABS_Y_OST, ++ CY_ABS_P_OST, ++ CY_ABS_W_OST, ++ CY_ABS_ID_OST, ++ CY_ABS_MAJ_OST, ++ CY_ABS_MIN_OST, ++ CY_ABS_OR_OST, ++ CY_NUM_ABS_OST /* number of abs signals */ ++}; ++ ++struct cyttsp4_virtual_key { ++ int code; ++}; ++ ++struct cyttsp4_signal_def { ++ int signal; ++ int min; ++ int max; ++ int fuzz; ++ int flat; ++}; ++ + static inline int cyttsp4_adap_read(struct cyttsp4 *ts, u16 addr, int size, + void *buf) + { +diff --git a/include/linux/platform_data/cyttsp4.h b/include/linux/platform_data/cyttsp4.h +index e718d2204ce7..e69de29bb2d1 100644 +--- a/include/linux/platform_data/cyttsp4.h ++++ b/include/linux/platform_data/cyttsp4.h +@@ -1,70 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0-only */ +-/* +- * Header file for: +- * Cypress TrueTouch(TM) Standard Product (TTSP) touchscreen drivers. +- * For use with Cypress Txx3xx parts. +- * Supported parts include: +- * CY8CTST341 +- * CY8CTMA340 +- * +- * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. +- * Copyright (C) 2012 Javier Martinez Canillas +- * +- * Contact Cypress Semiconductor at www.cypress.com (kev@cypress.com) +- */ +-#ifndef _CYTTSP4_H_ +-#define _CYTTSP4_H_ +- +-#define CYTTSP4_MT_NAME "cyttsp4_mt" +- +-#define CY_TOUCH_SETTINGS_MAX 32 +- +-/* abs axis signal offsets in the signals array */ +-enum cyttsp4_sig_ost { +- CY_ABS_X_OST, +- CY_ABS_Y_OST, +- CY_ABS_P_OST, +- CY_ABS_W_OST, +- CY_ABS_ID_OST, +- CY_ABS_MAJ_OST, +- CY_ABS_MIN_OST, +- CY_ABS_OR_OST, +- CY_NUM_ABS_OST /* number of abs signals */ +-}; +- +-struct cyttsp4_virtual_key { +- int code; +-}; +- +-struct cyttsp4_signal_def { +- int signal; +- int min; +- int max; +- int fuzz; +- int flat; +-}; +- +-struct cyttsp4_platform_data { +- char const *inp_dev_name; +- unsigned short flags; +- +- int irq_gpio; +- int rst_gpio; +- int level_irq_udelay; +- int (*xres)(struct cyttsp4_platform_data *pdata, +- struct device *dev); +- int (*init)(struct cyttsp4_platform_data *pdata, +- int on, struct device *dev); +- int (*power)(struct cyttsp4_platform_data *pdata, +- int on, struct device *dev, atomic_t *ignore_irq); +- int (*irq_stat)(struct cyttsp4_platform_data *pdata, +- struct device *dev); +- +- int n_signals; +- struct cyttsp4_signal_def *signals; +- +- int n_keys; +- struct cyttsp4_virtual_key* keys; +-}; +- +-#endif /* _CYTTSP4_H_ */ +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Remove-unused-enable_vkeys.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Remove-unused-enable_vkeys.patch new file mode 100644 index 000000000000..bd63188556a9 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Remove-unused-enable_vkeys.patch @@ -0,0 +1,27 @@ +From 20f3eaeeca3ca518b9f5d566ea206afc70569569 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sat, 28 Sep 2019 15:06:16 +0200 +Subject: input: cyttsp4: Remove unused enable_vkeys + +It doesn't seem to be used anywhere. + +Signed-off-by: Ondrej Jirman +--- + include/linux/platform_data/cyttsp4.h | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/include/linux/platform_data/cyttsp4.h b/include/linux/platform_data/cyttsp4.h +index 1f945aa2466e..362fa181ac04 100644 +--- a/include/linux/platform_data/cyttsp4.h ++++ b/include/linux/platform_data/cyttsp4.h +@@ -24,7 +24,6 @@ + struct touch_framework { + const uint16_t *abs; + uint8_t size; +- uint8_t enable_vkeys; + } __packed; + + struct cyttsp4_virtual_key { +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Remove-useless-indirection-with-driver-platform-d.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Remove-useless-indirection-with-driver-platform-d.patch new file mode 100644 index 000000000000..30fc8a645f29 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Remove-useless-indirection-with-driver-platform-d.patch @@ -0,0 +1,718 @@ +From c384e31b371b8cd94b47ed60fc35c0f72247b71a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sat, 28 Sep 2019 15:05:39 +0200 +Subject: input: cyttsp4: Remove useless indirection with driver/platform data + +It is not necessary to have struct cyttsp4_mt_data fields separate +from struct cyttsp4, as cyttsp4_mt_data was embedded as 'md' field +anyway. + +The same goes for struct cyttsp4_mt_platform_data. + +Remove all this indirection. + +Signed-off-by: Ondrej Jirman +--- + drivers/input/touchscreen/cyttsp4_core.c | 233 +++++++++++------------ + drivers/input/touchscreen/cyttsp4_core.h | 18 +- + include/linux/platform_data/cyttsp4.h | 26 +-- + 3 files changed, 128 insertions(+), 149 deletions(-) + +diff --git a/drivers/input/touchscreen/cyttsp4_core.c b/drivers/input/touchscreen/cyttsp4_core.c +index f2dd87cdeb8f..867c18abc89b 100644 +--- a/drivers/input/touchscreen/cyttsp4_core.c ++++ b/drivers/input/touchscreen/cyttsp4_core.c +@@ -133,8 +133,8 @@ static int cyttsp4_hw_soft_reset(struct cyttsp4 *cd) + + static int cyttsp4_hw_hard_reset(struct cyttsp4 *cd) + { +- if (cd->cpdata->xres) { +- cd->cpdata->xres(cd->cpdata, cd->dev); ++ if (cd->pdata->xres) { ++ cd->pdata->xres(cd->pdata, cd->dev); + dev_dbg(cd->dev, "%s: execute HARD reset\n", __func__); + return 0; + } +@@ -559,8 +559,8 @@ static int cyttsp4_si_get_btn_data(struct cyttsp4 *cd) + + for (btn = 0; btn < si->si_ofs.num_btns; btn++) { + si->btn[btn].key_code = KEY_RESERVED; +- if (btn < cd->cpdata->n_keys) +- si->btn[btn].key_code = cd->cpdata->keys[btn].code; ++ if (btn < cd->pdata->n_keys) ++ si->btn[btn].key_code = cd->pdata->keys[btn].code; + si->btn[btn].state = CY_BTN_RELEASED; + si->btn[btn].enabled = true; + } +@@ -732,41 +732,41 @@ static void cyttsp4_queue_startup_(struct cyttsp4 *cd) + } + } + +-static void cyttsp4_report_slot_liftoff(struct cyttsp4_mt_data *md, ++static void cyttsp4_report_slot_liftoff(struct cyttsp4 *cd, + int max_slots) + { + int t; + +- if (md->num_prv_tch == 0) ++ if (cd->num_prv_tch == 0) + return; + + for (t = 0; t < max_slots; t++) { +- input_mt_slot(md->input, t); +- input_mt_report_slot_inactive(md->input); ++ input_mt_slot(cd->input, t); ++ input_mt_report_slot_inactive(cd->input); + } + } + +-static void cyttsp4_lift_all(struct cyttsp4_mt_data *md) ++static void cyttsp4_lift_all(struct cyttsp4 *cd) + { +- if (!md->si) ++ if (!cd->si) + return; + +- if (md->num_prv_tch != 0) { +- cyttsp4_report_slot_liftoff(md, +- md->si->si_ofs.tch_abs[CY_TCH_T].max); +- input_sync(md->input); +- md->num_prv_tch = 0; ++ if (cd->num_prv_tch != 0) { ++ cyttsp4_report_slot_liftoff(cd, ++ cd->si->si_ofs.tch_abs[CY_TCH_T].max); ++ input_sync(cd->input); ++ cd->num_prv_tch = 0; + } + } + +-static void cyttsp4_get_touch_axis(struct cyttsp4_mt_data *md, ++static void cyttsp4_get_touch_axis(struct cyttsp4 *cd, + int *axis, int size, int max, u8 *xy_data, int bofs) + { + int nbyte; + int next; + + for (nbyte = 0, *axis = 0, next = 0; nbyte < size; nbyte++) { +- dev_vdbg(&md->input->dev, ++ dev_vdbg(&cd->input->dev, + "%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p" + " xy_data[%d]=%02X(%d) bofs=%d\n", + __func__, *axis, *axis, size, max, xy_data, next, +@@ -777,23 +777,23 @@ static void cyttsp4_get_touch_axis(struct cyttsp4_mt_data *md, + + *axis &= max - 1; + +- dev_vdbg(&md->input->dev, ++ dev_vdbg(&cd->input->dev, + "%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p" + " xy_data[%d]=%02X(%d)\n", + __func__, *axis, *axis, size, max, xy_data, next, + xy_data[next], xy_data[next]); + } + +-static void cyttsp4_get_touch(struct cyttsp4_mt_data *md, ++static void cyttsp4_get_touch(struct cyttsp4 *cd, + struct cyttsp4_touch *touch, u8 *xy_data) + { +- struct device *dev = &md->input->dev; +- struct cyttsp4_sysinfo *si = md->si; ++ struct device *dev = &cd->input->dev; ++ struct cyttsp4_sysinfo *si = cd->si; + enum cyttsp4_tch_abs abs; + bool flipped; + + for (abs = CY_TCH_X; abs < CY_TCH_NUM_ABS; abs++) { +- cyttsp4_get_touch_axis(md, &touch->abs[abs], ++ cyttsp4_get_touch_axis(cd, &touch->abs[abs], + si->si_ofs.tch_abs[abs].size, + si->si_ofs.tch_abs[abs].max, + xy_data + si->si_ofs.tch_abs[abs].ofs, +@@ -803,33 +803,33 @@ static void cyttsp4_get_touch(struct cyttsp4_mt_data *md, + touch->abs[abs], touch->abs[abs]); + } + +- if (md->pdata->flags & CY_FLAG_FLIP) { ++ if (cd->pdata->flags & CY_FLAG_FLIP) { + swap(touch->abs[CY_TCH_X], touch->abs[CY_TCH_Y]); + flipped = true; + } else + flipped = false; + +- if (md->pdata->flags & CY_FLAG_INV_X) { ++ if (cd->pdata->flags & CY_FLAG_INV_X) { + if (flipped) +- touch->abs[CY_TCH_X] = md->si->si_ofs.max_y - ++ touch->abs[CY_TCH_X] = cd->si->si_ofs.max_y - + touch->abs[CY_TCH_X]; + else +- touch->abs[CY_TCH_X] = md->si->si_ofs.max_x - ++ touch->abs[CY_TCH_X] = cd->si->si_ofs.max_x - + touch->abs[CY_TCH_X]; + } +- if (md->pdata->flags & CY_FLAG_INV_Y) { ++ if (cd->pdata->flags & CY_FLAG_INV_Y) { + if (flipped) +- touch->abs[CY_TCH_Y] = md->si->si_ofs.max_x - ++ touch->abs[CY_TCH_Y] = cd->si->si_ofs.max_x - + touch->abs[CY_TCH_Y]; + else +- touch->abs[CY_TCH_Y] = md->si->si_ofs.max_y - ++ touch->abs[CY_TCH_Y] = cd->si->si_ofs.max_y - + touch->abs[CY_TCH_Y]; + } + + dev_vdbg(dev, "%s: flip=%s inv-x=%s inv-y=%s x=%04X(%d) y=%04X(%d)\n", + __func__, flipped ? "true" : "false", +- md->pdata->flags & CY_FLAG_INV_X ? "true" : "false", +- md->pdata->flags & CY_FLAG_INV_Y ? "true" : "false", ++ cd->pdata->flags & CY_FLAG_INV_X ? "true" : "false", ++ cd->pdata->flags & CY_FLAG_INV_Y ? "true" : "false", + touch->abs[CY_TCH_X], touch->abs[CY_TCH_X], + touch->abs[CY_TCH_Y], touch->abs[CY_TCH_Y]); + } +@@ -848,10 +848,10 @@ static void cyttsp4_final_sync(struct input_dev *input, int max_slots, int *ids) + input_sync(input); + } + +-static void cyttsp4_get_mt_touches(struct cyttsp4_mt_data *md, int num_cur_tch) ++static void cyttsp4_get_mt_touches(struct cyttsp4 *cd, int num_cur_tch) + { +- struct device *dev = &md->input->dev; +- struct cyttsp4_sysinfo *si = md->si; ++ struct device *dev = &cd->input->dev; ++ struct cyttsp4_sysinfo *si = cd->si; + struct cyttsp4_touch tch; + int sig; + int i, j, t = 0; +@@ -859,42 +859,42 @@ static void cyttsp4_get_mt_touches(struct cyttsp4_mt_data *md, int num_cur_tch) + + memset(ids, 0, si->si_ofs.tch_abs[CY_TCH_T].max * sizeof(int)); + for (i = 0; i < num_cur_tch; i++) { +- cyttsp4_get_touch(md, &tch, si->xy_data + ++ cyttsp4_get_touch(cd, &tch, si->xy_data + + (i * si->si_ofs.tch_rec_size)); +- if ((tch.abs[CY_TCH_T] < md->pdata->frmwrk->abs ++ if ((tch.abs[CY_TCH_T] < cd->pdata->frmwrk->abs + [(CY_ABS_ID_OST * CY_NUM_ABS_SET) + CY_MIN_OST]) || +- (tch.abs[CY_TCH_T] > md->pdata->frmwrk->abs ++ (tch.abs[CY_TCH_T] > cd->pdata->frmwrk->abs + [(CY_ABS_ID_OST * CY_NUM_ABS_SET) + CY_MAX_OST])) { + dev_err(dev, "%s: tch=%d -> bad trk_id=%d max_id=%d\n", + __func__, i, tch.abs[CY_TCH_T], +- md->pdata->frmwrk->abs[(CY_ABS_ID_OST * ++ cd->pdata->frmwrk->abs[(CY_ABS_ID_OST * + CY_NUM_ABS_SET) + CY_MAX_OST]); + continue; + } + + /* use 0 based track id's */ +- sig = md->pdata->frmwrk->abs ++ sig = cd->pdata->frmwrk->abs + [(CY_ABS_ID_OST * CY_NUM_ABS_SET) + 0]; + if (sig != CY_IGNORE_VALUE) { +- t = tch.abs[CY_TCH_T] - md->pdata->frmwrk->abs ++ t = tch.abs[CY_TCH_T] - cd->pdata->frmwrk->abs + [(CY_ABS_ID_OST * CY_NUM_ABS_SET) + CY_MIN_OST]; + if (tch.abs[CY_TCH_E] == CY_EV_LIFTOFF) { + dev_dbg(dev, "%s: t=%d e=%d lift-off\n", + __func__, t, tch.abs[CY_TCH_E]); + goto cyttsp4_get_mt_touches_pr_tch; + } +- input_mt_slot(md->input, t); +- input_mt_report_slot_state(md->input, MT_TOOL_FINGER, ++ input_mt_slot(cd->input, t); ++ input_mt_report_slot_state(cd->input, MT_TOOL_FINGER, + true); + ids[t] = true; + } + + /* all devices: position and pressure fields */ + for (j = 0; j <= CY_ABS_W_OST; j++) { +- sig = md->pdata->frmwrk->abs[((CY_ABS_X_OST + j) * ++ sig = cd->pdata->frmwrk->abs[((CY_ABS_X_OST + j) * + CY_NUM_ABS_SET) + 0]; + if (sig != CY_IGNORE_VALUE) +- input_report_abs(md->input, sig, ++ input_report_abs(cd->input, sig, + tch.abs[CY_TCH_X + j]); + } + if (si->si_ofs.tch_rec_size > CY_TMA1036_TCH_REC_SIZE) { +@@ -909,11 +909,11 @@ static void cyttsp4_get_mt_touches(struct cyttsp4_mt_data *md, int num_cur_tch) + + /* Get the extended touch fields */ + for (j = 0; j < CY_NUM_EXT_TCH_FIELDS; j++) { +- sig = md->pdata->frmwrk->abs ++ sig = cd->pdata->frmwrk->abs + [((CY_ABS_MAJ_OST + j) * + CY_NUM_ABS_SET) + 0]; + if (sig != CY_IGNORE_VALUE) +- input_report_abs(md->input, sig, ++ input_report_abs(cd->input, sig, + tch.abs[CY_TCH_MAJ + j]); + } + } +@@ -940,9 +940,9 @@ static void cyttsp4_get_mt_touches(struct cyttsp4_mt_data *md, int num_cur_tch) + tch.abs[CY_TCH_E]); + } + +- cyttsp4_final_sync(md->input, si->si_ofs.tch_abs[CY_TCH_T].max, ids); ++ cyttsp4_final_sync(cd->input, si->si_ofs.tch_abs[CY_TCH_T].max, ids); + +- md->num_prv_tch = num_cur_tch; ++ cd->num_prv_tch = num_cur_tch; + + return; + } +@@ -950,9 +950,8 @@ static void cyttsp4_get_mt_touches(struct cyttsp4_mt_data *md, int num_cur_tch) + /* read xy_data for all current touches */ + static int cyttsp4_xy_worker(struct cyttsp4 *cd) + { +- struct cyttsp4_mt_data *md = &cd->md; +- struct device *dev = &md->input->dev; +- struct cyttsp4_sysinfo *si = md->si; ++ struct device *dev = &cd->input->dev; ++ struct cyttsp4_sysinfo *si = cd->si; + u8 num_cur_tch; + u8 hst_mode; + u8 rep_len; +@@ -1024,9 +1023,9 @@ static int cyttsp4_xy_worker(struct cyttsp4 *cd) + dev_vdbg(dev, "%s: extract data num_cur_tch=%d\n", __func__, + num_cur_tch); + if (num_cur_tch) +- cyttsp4_get_mt_touches(md, num_cur_tch); ++ cyttsp4_get_mt_touches(cd, num_cur_tch); + else +- cyttsp4_lift_all(md); ++ cyttsp4_lift_all(cd); + + rc = 0; + +@@ -1037,21 +1036,20 @@ static int cyttsp4_xy_worker(struct cyttsp4 *cd) + static int cyttsp4_mt_attention(struct cyttsp4 *cd) + { + struct device *dev = cd->dev; +- struct cyttsp4_mt_data *md = &cd->md; + int rc = 0; + +- if (!md->si) ++ if (!cd->si) + return 0; + +- mutex_lock(&md->report_lock); +- if (!md->is_suspended) { ++ mutex_lock(&cd->report_lock); ++ if (!cd->is_suspended) { + /* core handles handshake */ + rc = cyttsp4_xy_worker(cd); + } else { + dev_vdbg(dev, "%s: Ignoring report while suspended\n", + __func__); + } +- mutex_unlock(&md->report_lock); ++ mutex_unlock(&cd->report_lock); + if (rc < 0) + dev_err(dev, "%s: xy_worker error r=%d\n", __func__, rc); + +@@ -1222,7 +1220,7 @@ static irqreturn_t cyttsp4_irq(int irq, void *handle) + * IRQF_TRIGGER_LOW in order to delay until the + * device completes isr deassert + */ +- udelay(cd->cpdata->level_irq_udelay); ++ udelay(cd->pdata->level_irq_udelay); + + cyttsp4_irq_exit: + mutex_unlock(&cd->system_lock); +@@ -1527,9 +1525,9 @@ static int cyttsp4_core_sleep_(struct cyttsp4 *cd) + } + dev_vdbg(cd->dev, "%s: write DEEP SLEEP succeeded\n", __func__); + +- if (cd->cpdata->power) { ++ if (cd->pdata->power) { + dev_dbg(cd->dev, "%s: Power down HW\n", __func__); +- rc = cd->cpdata->power(cd->cpdata, 0, cd->dev, &cd->ignore_irq); ++ rc = cd->pdata->power(cd->pdata, 0, cd->dev, &cd->ignore_irq); + } else { + dev_dbg(cd->dev, "%s: No power function\n", __func__); + rc = 0; +@@ -1644,7 +1642,7 @@ static int cyttsp4_startup_(struct cyttsp4 *cd) + goto exit; + } + +- cyttsp4_lift_all(&cd->md); ++ cyttsp4_lift_all(cd); + + /* restore to sleep if was suspended */ + mutex_lock(&cd->system_lock); +@@ -1766,9 +1764,9 @@ static int cyttsp4_core_wake_(struct cyttsp4 *cd) + cd->int_status |= CY_INT_AWAKE; + cd->sleep_state = SS_WAKING; + +- if (cd->cpdata->power) { ++ if (cd->pdata->power) { + dev_dbg(dev, "%s: Power up HW\n", __func__); +- rc = cd->cpdata->power(cd->cpdata, 1, dev, &cd->ignore_irq); ++ rc = cd->pdata->power(cd->pdata, 1, dev, &cd->ignore_irq); + } else { + dev_dbg(dev, "%s: No power function\n", __func__); + rc = -ENOSYS; +@@ -1830,10 +1828,9 @@ static int cyttsp4_core_wake(struct cyttsp4 *cd) + static int cyttsp4_core_suspend(struct device *dev) + { + struct cyttsp4 *cd = dev_get_drvdata(dev); +- struct cyttsp4_mt_data *md = &cd->md; + int rc; + +- md->is_suspended = true; ++ cd->is_suspended = true; + + rc = cyttsp4_core_sleep(cd); + if (rc < 0) { +@@ -1846,10 +1843,9 @@ static int cyttsp4_core_suspend(struct device *dev) + static int cyttsp4_core_resume(struct device *dev) + { + struct cyttsp4 *cd = dev_get_drvdata(dev); +- struct cyttsp4_mt_data *md = &cd->md; + int rc; + +- md->is_suspended = false; ++ cd->is_suspended = false; + + rc = cyttsp4_core_wake(cd); + if (rc < 0) { +@@ -1871,18 +1867,17 @@ static int cyttsp4_mt_open(struct input_dev *input) + + static void cyttsp4_mt_close(struct input_dev *input) + { +- struct cyttsp4_mt_data *md = input_get_drvdata(input); +- mutex_lock(&md->report_lock); +- if (!md->is_suspended) ++ struct cyttsp4 *cd = input_get_drvdata(input); ++ mutex_lock(&cd->report_lock); ++ if (!cd->is_suspended) + pm_runtime_put(input->dev.parent); +- mutex_unlock(&md->report_lock); ++ mutex_unlock(&cd->report_lock); + } + + + static int cyttsp4_setup_input_device(struct cyttsp4 *cd) + { + struct device *dev = cd->dev; +- struct cyttsp4_mt_data *md = &cd->md; + int signal = CY_IGNORE_VALUE; + int max_x, max_y, max_p, min, max; + int max_x_tmp, max_y_tmp; +@@ -1890,32 +1885,32 @@ static int cyttsp4_setup_input_device(struct cyttsp4 *cd) + int rc; + + dev_vdbg(dev, "%s: Initialize event signals\n", __func__); +- __set_bit(EV_ABS, md->input->evbit); +- __set_bit(EV_REL, md->input->evbit); +- __set_bit(EV_KEY, md->input->evbit); ++ __set_bit(EV_ABS, cd->input->evbit); ++ __set_bit(EV_REL, cd->input->evbit); ++ __set_bit(EV_KEY, cd->input->evbit); + +- max_x_tmp = md->si->si_ofs.max_x; +- max_y_tmp = md->si->si_ofs.max_y; ++ max_x_tmp = cd->si->si_ofs.max_x; ++ max_y_tmp = cd->si->si_ofs.max_y; + + /* get maximum values from the sysinfo data */ +- if (md->pdata->flags & CY_FLAG_FLIP) { ++ if (cd->pdata->flags & CY_FLAG_FLIP) { + max_x = max_y_tmp - 1; + max_y = max_x_tmp - 1; + } else { + max_x = max_x_tmp - 1; + max_y = max_y_tmp - 1; + } +- max_p = md->si->si_ofs.max_p; ++ max_p = cd->si->si_ofs.max_p; + + /* set event signal capabilities */ +- for (i = 0; i < (md->pdata->frmwrk->size / CY_NUM_ABS_SET); i++) { +- signal = md->pdata->frmwrk->abs ++ for (i = 0; i < (cd->pdata->frmwrk->size / CY_NUM_ABS_SET); i++) { ++ signal = cd->pdata->frmwrk->abs + [(i * CY_NUM_ABS_SET) + CY_SIGNAL_OST]; + if (signal != CY_IGNORE_VALUE) { +- __set_bit(signal, md->input->absbit); +- min = md->pdata->frmwrk->abs ++ __set_bit(signal, cd->input->absbit); ++ min = cd->pdata->frmwrk->abs + [(i * CY_NUM_ABS_SET) + CY_MIN_OST]; +- max = md->pdata->frmwrk->abs ++ max = cd->pdata->frmwrk->abs + [(i * CY_NUM_ABS_SET) + CY_MAX_OST]; + if (i == CY_ABS_ID_OST) { + /* shift track ids down to start at 0 */ +@@ -1927,23 +1922,23 @@ static int cyttsp4_setup_input_device(struct cyttsp4 *cd) + max = max_y; + else if (i == CY_ABS_P_OST) + max = max_p; +- input_set_abs_params(md->input, signal, min, max, +- md->pdata->frmwrk->abs ++ input_set_abs_params(cd->input, signal, min, max, ++ cd->pdata->frmwrk->abs + [(i * CY_NUM_ABS_SET) + CY_FUZZ_OST], +- md->pdata->frmwrk->abs ++ cd->pdata->frmwrk->abs + [(i * CY_NUM_ABS_SET) + CY_FLAT_OST]); + dev_dbg(dev, "%s: register signal=%02X min=%d max=%d\n", + __func__, signal, min, max); + if ((i == CY_ABS_ID_OST) && +- (md->si->si_ofs.tch_rec_size < ++ (cd->si->si_ofs.tch_rec_size < + CY_TMA4XX_TCH_REC_SIZE)) + break; + } + } + +- input_mt_init_slots(md->input, md->si->si_ofs.tch_abs[CY_TCH_T].max, ++ input_mt_init_slots(cd->input, cd->si->si_ofs.tch_abs[CY_TCH_T].max, + INPUT_MT_DIRECT); +- rc = input_register_device(md->input); ++ rc = input_register_device(cd->input); + if (rc < 0) + dev_err(dev, "%s: Error, failed register input device r=%d\n", + __func__, rc); +@@ -1953,34 +1948,31 @@ static int cyttsp4_setup_input_device(struct cyttsp4 *cd) + static int cyttsp4_mt_probe(struct cyttsp4 *cd) + { + struct device *dev = cd->dev; +- struct cyttsp4_mt_data *md = &cd->md; +- struct cyttsp4_mt_platform_data *pdata = cd->pdata->mt_pdata; + int rc = 0; + +- mutex_init(&md->report_lock); +- md->pdata = pdata; ++ mutex_init(&cd->report_lock); + /* Create the input device and register it. */ + dev_vdbg(dev, "%s: Create the input device and register it\n", + __func__); +- md->input = input_allocate_device(); +- if (md->input == NULL) { ++ cd->input = input_allocate_device(); ++ if (cd->input == NULL) { + dev_err(dev, "%s: Error, failed to allocate input device\n", + __func__); + rc = -ENOSYS; + goto error_alloc_failed; + } + +- md->input->name = pdata->inp_dev_name; +- scnprintf(md->phys, sizeof(md->phys)-1, "%s", dev_name(dev)); +- md->input->phys = md->phys; +- md->input->id.bustype = cd->bus_ops->bustype; +- md->input->dev.parent = dev; +- md->input->open = cyttsp4_mt_open; +- md->input->close = cyttsp4_mt_close; +- input_set_drvdata(md->input, md); ++ cd->input->name = cd->pdata->inp_dev_name; ++ scnprintf(cd->phys, sizeof(cd->phys)-1, "%s", dev_name(dev)); ++ cd->input->phys = cd->phys; ++ cd->input->id.bustype = cd->bus_ops->bustype; ++ cd->input->dev.parent = dev; ++ cd->input->open = cyttsp4_mt_open; ++ cd->input->close = cyttsp4_mt_close; ++ input_set_drvdata(cd->input, cd); + + /* get sysinfo */ +- md->si = &cd->sysinfo; ++ cd->si = &cd->sysinfo; + + rc = cyttsp4_setup_input_device(cd); + if (rc) +@@ -1989,7 +1981,7 @@ static int cyttsp4_mt_probe(struct cyttsp4 *cd) + return 0; + + error_init_input: +- input_free_device(md->input); ++ input_free_device(cd->input); + error_alloc_failed: + dev_err(dev, "%s failed.\n", __func__); + return rc; +@@ -2003,7 +1995,7 @@ struct cyttsp4 *cyttsp4_probe(const struct cyttsp4_bus_ops *ops, + unsigned long irq_flags; + int rc = 0; + +- if (!pdata || !pdata->core_pdata || !pdata->mt_pdata) { ++ if (!pdata) { + dev_err(dev, "%s: Missing platform data\n", __func__); + rc = -ENODEV; + goto error_no_pdata; +@@ -2026,7 +2018,6 @@ struct cyttsp4 *cyttsp4_probe(const struct cyttsp4_bus_ops *ops, + /* Initialize device info */ + cd->dev = dev; + cd->pdata = pdata; +- cd->cpdata = pdata->core_pdata; + cd->bus_ops = ops; + + /* Initialize mutexes and spinlocks */ +@@ -2041,7 +2032,7 @@ struct cyttsp4 *cyttsp4_probe(const struct cyttsp4_bus_ops *ops, + INIT_WORK(&cd->watchdog_work, cyttsp4_watchdog_work); + + /* Initialize IRQ */ +- cd->irq = gpio_to_irq(cd->cpdata->irq_gpio); ++ cd->irq = gpio_to_irq(cd->pdata->irq_gpio); + if (cd->irq < 0) { + rc = -EINVAL; + goto error_free_xfer; +@@ -2050,9 +2041,9 @@ struct cyttsp4 *cyttsp4_probe(const struct cyttsp4_bus_ops *ops, + dev_set_drvdata(dev, cd); + + /* Call platform init function */ +- if (cd->cpdata->init) { ++ if (cd->pdata->init) { + dev_dbg(cd->dev, "%s: Init HW\n", __func__); +- rc = cd->cpdata->init(cd->cpdata, 1, cd->dev); ++ rc = cd->pdata->init(cd->pdata, 1, cd->dev); + } else { + dev_dbg(cd->dev, "%s: No HW INIT function\n", __func__); + rc = 0; +@@ -2061,7 +2052,7 @@ struct cyttsp4 *cyttsp4_probe(const struct cyttsp4_bus_ops *ops, + dev_err(cd->dev, "%s: HW Init fail r=%d\n", __func__, rc); + + dev_dbg(dev, "%s: initialize threaded irq=%d\n", __func__, cd->irq); +- if (cd->cpdata->level_irq_udelay > 0) ++ if (cd->pdata->level_irq_udelay > 0) + /* use level triggered interrupts */ + irq_flags = IRQF_TRIGGER_LOW | IRQF_ONESHOT; + else +@@ -2108,8 +2099,8 @@ struct cyttsp4 *cyttsp4_probe(const struct cyttsp4_bus_ops *ops, + cyttsp4_free_si_ptrs(cd); + free_irq(cd->irq, cd); + error_request_irq: +- if (cd->cpdata->init) +- cd->cpdata->init(cd->cpdata, 0, dev); ++ if (cd->pdata->init) ++ cd->pdata->init(cd->pdata, 0, dev); + error_free_xfer: + kfree(cd->xfer_buf); + error_free_cd: +@@ -2121,17 +2112,17 @@ struct cyttsp4 *cyttsp4_probe(const struct cyttsp4_bus_ops *ops, + } + EXPORT_SYMBOL_GPL(cyttsp4_probe); + +-static void cyttsp4_mt_release(struct cyttsp4_mt_data *md) ++static void cyttsp4_mt_release(struct cyttsp4 *cd) + { +- input_unregister_device(md->input); +- input_set_drvdata(md->input, NULL); ++ input_unregister_device(cd->input); ++ input_set_drvdata(cd->input, NULL); + } + + int cyttsp4_remove(struct cyttsp4 *cd) + { + struct device *dev = cd->dev; + +- cyttsp4_mt_release(&cd->md); ++ cyttsp4_mt_release(cd); + + /* + * Suspend the device before freeing the startup_work and stopping +@@ -2145,8 +2136,8 @@ int cyttsp4_remove(struct cyttsp4 *cd) + cyttsp4_stop_wd_timer(cd); + + free_irq(cd->irq, cd); +- if (cd->cpdata->init) +- cd->cpdata->init(cd->cpdata, 0, dev); ++ if (cd->pdata->init) ++ cd->pdata->init(cd->pdata, 0, dev); + cyttsp4_free_si_ptrs(cd); + kfree(cd); + return 0; +diff --git a/drivers/input/touchscreen/cyttsp4_core.h b/drivers/input/touchscreen/cyttsp4_core.h +index 6262f6e45075..05fb30058d87 100644 +--- a/drivers/input/touchscreen/cyttsp4_core.h ++++ b/drivers/input/touchscreen/cyttsp4_core.h +@@ -305,16 +305,6 @@ struct cyttsp4_sysinfo { + u8 *xy_data; /* operational touch regs */ + }; + +-struct cyttsp4_mt_data { +- struct cyttsp4_mt_platform_data *pdata; +- struct cyttsp4_sysinfo *si; +- struct input_dev *input; +- struct mutex report_lock; +- bool is_suspended; +- char phys[NAME_MAX]; +- int num_prv_tch; +-}; +- + struct cyttsp4 { + struct device *dev; + struct mutex system_lock; +@@ -333,9 +323,13 @@ struct cyttsp4 { + int exclusive_waits; + atomic_t ignore_irq; + bool invalid_touch_app; +- struct cyttsp4_mt_data md; ++ struct cyttsp4_sysinfo *si; ++ struct input_dev *input; ++ struct mutex report_lock; ++ bool is_suspended; ++ char phys[NAME_MAX]; ++ int num_prv_tch; + struct cyttsp4_platform_data *pdata; +- struct cyttsp4_core_platform_data *cpdata; + const struct cyttsp4_bus_ops *bus_ops; + u8 *xfer_buf; + #ifdef VERBOSE_DEBUG +diff --git a/include/linux/platform_data/cyttsp4.h b/include/linux/platform_data/cyttsp4.h +index c54160c17b0b..1f945aa2466e 100644 +--- a/include/linux/platform_data/cyttsp4.h ++++ b/include/linux/platform_data/cyttsp4.h +@@ -27,36 +27,30 @@ struct touch_framework { + uint8_t enable_vkeys; + } __packed; + +-struct cyttsp4_mt_platform_data { +- struct touch_framework *frmwrk; +- unsigned short flags; +- char const *inp_dev_name; +-}; +- + struct cyttsp4_virtual_key { + int code; + }; + +-struct cyttsp4_core_platform_data { ++struct cyttsp4_platform_data { ++ char const *inp_dev_name; ++ unsigned short flags; ++ + int irq_gpio; + int rst_gpio; + int level_irq_udelay; +- int (*xres)(struct cyttsp4_core_platform_data *pdata, ++ int (*xres)(struct cyttsp4_platform_data *pdata, + struct device *dev); +- int (*init)(struct cyttsp4_core_platform_data *pdata, ++ int (*init)(struct cyttsp4_platform_data *pdata, + int on, struct device *dev); +- int (*power)(struct cyttsp4_core_platform_data *pdata, ++ int (*power)(struct cyttsp4_platform_data *pdata, + int on, struct device *dev, atomic_t *ignore_irq); +- int (*irq_stat)(struct cyttsp4_core_platform_data *pdata, ++ int (*irq_stat)(struct cyttsp4_platform_data *pdata, + struct device *dev); + ++ struct touch_framework *frmwrk; ++ + int n_keys; + struct cyttsp4_virtual_key* keys; + }; + +-struct cyttsp4_platform_data { +- struct cyttsp4_core_platform_data *core_pdata; +- struct cyttsp4_mt_platform_data *mt_pdata; +-}; +- + #endif /* _CYTTSP4_H_ */ +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Restart-on-wakeup-wakeup-by-I2C-read-doesn-t-work.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Restart-on-wakeup-wakeup-by-I2C-read-doesn-t-work.patch new file mode 100644 index 000000000000..5c8550d3edbd --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Restart-on-wakeup-wakeup-by-I2C-read-doesn-t-work.patch @@ -0,0 +1,125 @@ +From 722dc128c2cfef58b8ed8737f6c76224be25cd28 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Tue, 1 Oct 2019 02:21:43 +0200 +Subject: input: cyttsp4: Restart on wakeup (wakeup by I2C read doesn't work) + +Signed-off-by: Ondrej Jirman +--- + drivers/input/touchscreen/cyttsp4_core.c | 30 ++++++++++++++++-------- + 1 file changed, 20 insertions(+), 10 deletions(-) + +diff --git a/drivers/input/touchscreen/cyttsp4_core.c b/drivers/input/touchscreen/cyttsp4_core.c +index 58eb9586a46f..1ddb0b466a46 100644 +--- a/drivers/input/touchscreen/cyttsp4_core.c ++++ b/drivers/input/touchscreen/cyttsp4_core.c +@@ -28,7 +28,7 @@ + #define CY_CORE_SLEEP_REQUEST_EXCLUSIVE_TIMEOUT 5000 + #define CY_CORE_MODE_CHANGE_TIMEOUT 1000 + #define CY_CORE_RESET_AND_WAIT_TIMEOUT 500 +-#define CY_CORE_WAKEUP_TIMEOUT 50 ++#define CY_CORE_WAKEUP_TIMEOUT 250 + + #define CY_CORE_STARTUP_RETRY_COUNT 3 + +@@ -139,13 +139,11 @@ static int cyttsp4_hw_reset(struct cyttsp4 *cd) + return cyttsp4_hw_soft_reset(cd); + + gpiod_set_value_cansleep(cd->reset_gpio, 1); +- msleep(20); +- +- gpiod_set_value_cansleep(cd->reset_gpio, 0); + msleep(40); + +- gpiod_set_value_cansleep(cd->reset_gpio, 1); ++ gpiod_set_value_cansleep(cd->reset_gpio, 0); + msleep(20); ++ + return 0; + } + +@@ -1525,12 +1523,14 @@ static int cyttsp4_core_sleep_(struct cyttsp4 *cd) + } + dev_vdbg(cd->dev, "%s: write DEEP SLEEP succeeded\n", __func__); + ++ /* + rc = cyttsp4_set_power(cd, 0); + if (rc < 0) { + dev_err(cd->dev, "%s: HW Power down fails r=%d\n", + __func__, rc); + goto error; + } ++ */ + + /* Give time to FW to sleep */ + msleep(50); +@@ -1748,6 +1748,7 @@ static int cyttsp4_core_wake_(struct cyttsp4 *cd) + u8 mode; + int t; + ++#if 0 + /* Already woken? */ + mutex_lock(&cd->system_lock); + if (cd->sleep_state == SS_SLEEP_OFF) { +@@ -1765,7 +1766,8 @@ static int cyttsp4_core_wake_(struct cyttsp4 *cd) + __func__, rc); + + /* Initiate a read transaction to wake up */ +- cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(mode), &mode); ++ rc = cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(mode), &mode); ++ dev_err(dev, "Waking up by read rc=%d\n", rc); + } else + dev_vdbg(cd->dev, "%s: HW power up succeeds\n", + __func__); +@@ -1782,7 +1784,13 @@ static int cyttsp4_core_wake_(struct cyttsp4 *cd) + cyttsp4_queue_startup_(cd); + mutex_unlock(&cd->system_lock); + } +- ++#else ++ mutex_lock(&cd->system_lock); ++ cd->int_status &= ~CY_INT_AWAKE; ++ /* Try starting up */ ++ cyttsp4_queue_startup_(cd); ++ mutex_unlock(&cd->system_lock); ++#endif + mutex_lock(&cd->system_lock); + cd->sleep_state = SS_SLEEP_OFF; + mutex_unlock(&cd->system_lock); +@@ -1992,6 +2000,7 @@ struct cyttsp4 *cyttsp4_probe(const struct cyttsp4_bus_ops *ops, + return ERR_PTR(-ENOMEM); + } + ++ cd->irq = irq; + cd->signals = cyttsp4_default_signals; + cd->n_signals = ARRAY_SIZE(cyttsp4_default_signals); + +@@ -2005,7 +2014,8 @@ struct cyttsp4 *cyttsp4_probe(const struct cyttsp4_bus_ops *ops, + cd->vdd_supply = devm_regulator_get(dev, "vdd"); + if (IS_ERR(cd->vdd_supply)) { + rc = PTR_ERR(cd->vdd_supply); +- dev_err(dev, "can't get vdd power (%d)\n", rc); ++ if (rc != -EPROBE_DEFER) ++ dev_err(dev, "can't get vdd power (%d)\n", rc); + return ERR_PTR(rc); + } + +@@ -2019,14 +2029,14 @@ struct cyttsp4 *cyttsp4_probe(const struct cyttsp4_bus_ops *ops, + if (IS_ERR(cd->reset_gpio)) { + rc = PTR_ERR(cd->reset_gpio); + dev_err(dev, "can't get reset gpio (%d)\n", rc); +- return ERR_PTR(rc); ++ goto error_disable_vdd; + } + + cd->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(cd->reset_gpio)) { + rc = PTR_ERR(cd->reset_gpio); + dev_err(dev, "can't get reset gpio (%d)\n", rc); +- return ERR_PTR(rc); ++ goto error_disable_vdd; + } + + cd->xfer_buf = devm_kzalloc(dev, xfer_buf_size, GFP_KERNEL); +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Use-i2c-spi-names-directly-in-the-driver.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Use-i2c-spi-names-directly-in-the-driver.patch new file mode 100644 index 000000000000..b6f968ebb1aa --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/input-cyttsp4-Use-i2c-spi-names-directly-in-the-driver.patch @@ -0,0 +1,64 @@ +From 08cc518ba716985004d51794be20d684e18bdd45 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Mon, 6 Mar 2023 01:17:09 +0100 +Subject: input: cyttsp4: Use i2c/spi names directly in the driver + +Platform data is going to be removed. + +Signed-off-by: Ondrej Jirman +--- + drivers/input/touchscreen/cyttsp4_i2c.c | 4 ++-- + drivers/input/touchscreen/cyttsp4_spi.c | 2 +- + include/linux/platform_data/cyttsp4.h | 2 -- + 3 files changed, 3 insertions(+), 5 deletions(-) + +diff --git a/drivers/input/touchscreen/cyttsp4_i2c.c b/drivers/input/touchscreen/cyttsp4_i2c.c +index da32c151def5..5b38a4f34209 100644 +--- a/drivers/input/touchscreen/cyttsp4_i2c.c ++++ b/drivers/input/touchscreen/cyttsp4_i2c.c +@@ -50,14 +50,14 @@ static void cyttsp4_i2c_remove(struct i2c_client *client) + } + + static const struct i2c_device_id cyttsp4_i2c_id[] = { +- { CYTTSP4_I2C_NAME }, ++ { "cyttsp4_i2c_adapter" }, + { } + }; + MODULE_DEVICE_TABLE(i2c, cyttsp4_i2c_id); + + static struct i2c_driver cyttsp4_i2c_driver = { + .driver = { +- .name = CYTTSP4_I2C_NAME, ++ .name = "cyttsp4_i2c_adapter", + .pm = pm_ptr(&cyttsp4_pm_ops), + }, + .probe = cyttsp4_i2c_probe, +diff --git a/drivers/input/touchscreen/cyttsp4_spi.c b/drivers/input/touchscreen/cyttsp4_spi.c +index 944fbbe9113e..f3cca8c9c119 100644 +--- a/drivers/input/touchscreen/cyttsp4_spi.c ++++ b/drivers/input/touchscreen/cyttsp4_spi.c +@@ -172,7 +172,7 @@ static void cyttsp4_spi_remove(struct spi_device *spi) + + static struct spi_driver cyttsp4_spi_driver = { + .driver = { +- .name = CYTTSP4_SPI_NAME, ++ .name = "cyttsp4_spi_adapter", + .pm = pm_ptr(&cyttsp4_pm_ops), + }, + .probe = cyttsp4_spi_probe, +diff --git a/include/linux/platform_data/cyttsp4.h b/include/linux/platform_data/cyttsp4.h +index 1b5b30796e43..e718d2204ce7 100644 +--- a/include/linux/platform_data/cyttsp4.h ++++ b/include/linux/platform_data/cyttsp4.h +@@ -16,8 +16,6 @@ + #define _CYTTSP4_H_ + + #define CYTTSP4_MT_NAME "cyttsp4_mt" +-#define CYTTSP4_I2C_NAME "cyttsp4_i2c_adapter" +-#define CYTTSP4_SPI_NAME "cyttsp4_spi_adapter" + + #define CY_TOUCH_SETTINGS_MAX 32 + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/input-gpio-vibra-Allow-to-use-vcc-supply-alone-to-control-the-v.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/input-gpio-vibra-Allow-to-use-vcc-supply-alone-to-control-the-v.patch new file mode 100644 index 000000000000..8abb01a6990e --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/input-gpio-vibra-Allow-to-use-vcc-supply-alone-to-control-the-v.patch @@ -0,0 +1,76 @@ +From 2ff865418f446773e58aacf75a2af9a9327abedb Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Fri, 8 Sep 2023 01:07:30 +0200 +Subject: input: gpio-vibra: Allow to use vcc-supply alone to control the + vibrator + +Make enable-gpio optional to allow using this driver with boards that +have vibrator connected to a power supply without intermediate gpio +based enable circuitry. + +Also avoid a case where neither regulator nor enable gpio is specified, +and bail out in probe in such a case. + +Signed-off-by: Ondrej Jirman +--- + drivers/input/misc/gpio-vibra.c | 25 ++++++++++++++++++------- + 1 file changed, 18 insertions(+), 7 deletions(-) + +diff --git a/drivers/input/misc/gpio-vibra.c b/drivers/input/misc/gpio-vibra.c +index ad44b4d18a2a..de24e28458b0 100644 +--- a/drivers/input/misc/gpio-vibra.c ++++ b/drivers/input/misc/gpio-vibra.c +@@ -39,7 +39,7 @@ static int gpio_vibrator_start(struct gpio_vibrator *vibrator) + struct device *pdev = vibrator->input->dev.parent; + int err; + +- if (!vibrator->vcc_on) { ++ if (vibrator->vcc && !vibrator->vcc_on) { + err = regulator_enable(vibrator->vcc); + if (err) { + dev_err(pdev, "failed to enable regulator: %d\n", err); +@@ -57,7 +57,7 @@ static void gpio_vibrator_stop(struct gpio_vibrator *vibrator) + { + gpiod_set_value_cansleep(vibrator->gpio, 0); + +- if (vibrator->vcc_on) { ++ if (vibrator->vcc && vibrator->vcc_on) { + regulator_disable(vibrator->vcc); + vibrator->vcc_on = false; + } +@@ -112,16 +112,27 @@ static int gpio_vibrator_probe(struct platform_device *pdev) + if (!vibrator->input) + return -ENOMEM; + +- vibrator->vcc = devm_regulator_get(&pdev->dev, "vcc"); +- if (IS_ERR(vibrator->vcc)) +- return dev_err_probe(&pdev->dev, PTR_ERR(vibrator->vcc), ++ vibrator->vcc = devm_regulator_get_optional(&pdev->dev, "vcc"); ++ err = PTR_ERR_OR_ZERO(vibrator->vcc); ++ if (err == -ENODEV) { ++ vibrator->vcc = NULL; ++ } else if (err) { ++ return dev_err_probe(&pdev->dev, err, + "Failed to request regulator\n"); ++ } + +- vibrator->gpio = devm_gpiod_get(&pdev->dev, "enable", GPIOD_OUT_LOW); +- if (IS_ERR(vibrator->gpio)) ++ vibrator->gpio = devm_gpiod_get_optional(&pdev->dev, "enable", ++ GPIOD_OUT_LOW); ++ err = PTR_ERR_OR_ZERO(vibrator->gpio); ++ if (err) + return dev_err_probe(&pdev->dev, PTR_ERR(vibrator->gpio), + "Failed to request main gpio\n"); + ++ if (!vibrator->vcc && !vibrator->gpio) { ++ dev_err(&pdev->dev, "Neither gpio nor regulator provided\n"); ++ return -EINVAL; ++ } ++ + INIT_WORK(&vibrator->play_work, gpio_vibrator_play_work); + + vibrator->input->name = "gpio-vibrator"; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/leds-axp20x-Support-charger-LED-on-AXP20x-like-PMICs.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/leds-axp20x-Support-charger-LED-on-AXP20x-like-PMICs.patch new file mode 100644 index 000000000000..0311559d59ac --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/leds-axp20x-Support-charger-LED-on-AXP20x-like-PMICs.patch @@ -0,0 +1,345 @@ +From aa883f0bc0aa293a2e1dc1e1f1514a9f75aa2eb2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Thu, 4 Feb 2021 00:55:20 +0100 +Subject: leds: axp20x: Support charger LED on AXP20x like PMICs + +There is single LED that can be turned on and off by the user, or set to +be controlled by the charger in 2 different modes. + +The driver initializes the LED to be controlled by the charger, but +allows to switch it to user control, and change the mode of charging +indication via a sysfs. + +The driver was developed on AXP813, but should work on other PMICs like +that without changes. + +Signed-off-by: Ondrej Jirman +--- + drivers/leds/Kconfig | 15 +++ + drivers/leds/Makefile | 1 + + drivers/leds/leds-axp20x.c | 253 +++++++++++++++++++++++++++++++++++++ + drivers/mfd/axp20x.c | 3 + + 4 files changed, 272 insertions(+) + create mode 100644 drivers/leds/leds-axp20x.c + +diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig +index 2b27d043921c..6ee7b96aa3d2 100644 +--- a/drivers/leds/Kconfig ++++ b/drivers/leds/Kconfig +@@ -996,6 +996,14 @@ config LEDS_IP30 + To compile this driver as a module, choose M here: the module + will be called leds-ip30. + ++config LEDS_SGM3140 ++ tristate "LED support for the SGM3140" ++ depends on LEDS_CLASS_FLASH ++ depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS ++ help ++ This option enables support for the SGM3140 500mA Buck/Boost Charge ++ Pump LED Driver. ++ + config LEDS_ACER_A500 + tristate "Power button LED support for Acer Iconia Tab A500" + depends on LEDS_CLASS && MFD_ACER_A500_EC +@@ -1003,6 +1011,13 @@ config LEDS_ACER_A500 + This option enables support for the Power Button LED of + Acer Iconia Tab A500. + ++config LEDS_AXP20X ++ tristate "Charger LED support for AXP20X-like PMICs (AXP813, ...)" ++ depends on LEDS_CLASS && MFD_AXP20X ++ help ++ This option enables support for on-chip LED driver on ++ AXP20X-like PMICs. ++ + source "drivers/leds/blink/Kconfig" + + comment "Flash and Torch LED drivers" +diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile +index 6ad52e219ec6..b8626ca58a6f 100644 +--- a/drivers/leds/Makefile ++++ b/drivers/leds/Makefile +@@ -96,6 +96,7 @@ obj-$(CONFIG_LEDS_UPBOARD) += leds-upboard.o + obj-$(CONFIG_LEDS_WM831X_STATUS) += leds-wm831x-status.o + obj-$(CONFIG_LEDS_WM8350) += leds-wm8350.o + obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o ++obj-$(CONFIG_LEDS_AXP20X) += leds-axp20x.o + + # Kinetic ExpressWire Protocol + obj-$(CONFIG_LEDS_EXPRESSWIRE) += leds-expresswire.o +diff --git a/drivers/leds/leds-axp20x.c b/drivers/leds/leds-axp20x.c +new file mode 100644 +index 000000000000..e2877af1032a +--- /dev/null ++++ b/drivers/leds/leds-axp20x.c +@@ -0,0 +1,253 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * LED Driver for X-Powers AXP813 PMIC and similar. ++ * ++ * Copyright(c) 2020 Ondrej Jirman ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define AXP20X_CHGLED_CTRL_MASK BIT(3) ++#define AXP20X_CHGLED_CTRL_CHARGER BIT(3) ++#define AXP20X_CHGLED_CTRL_USER 0 ++ ++#define AXP20X_CHRG_CTRL2_MODE BIT(4) ++ ++#define AXP20X_CHGLED_USER_STATE_MASK GENMASK(5, 4) ++#define AXP20X_CHGLED_USER_STATE_OFF (0 << 4) ++#define AXP20X_CHGLED_USER_STATE_BLINK_SLOW (1 << 4) ++#define AXP20X_CHGLED_USER_STATE_BLINK_FAST (2 << 4) ++#define AXP20X_CHGLED_USER_STATE_ON (3 << 4) ++ ++static struct led_hw_trigger_type axp20x_charger_led_trigger_type; ++ ++struct axp20x_led { ++ struct led_classdev cdev; ++ struct regmap *regmap; ++}; ++ ++static int axp20x_led_set(struct led_classdev *led_cdev, ++ enum led_brightness value) ++{ ++ struct axp20x_led *led = ++ container_of(led_cdev, struct axp20x_led, cdev); ++ unsigned int val; ++ ++ val = value == LED_OFF ? AXP20X_CHGLED_USER_STATE_OFF : ++ AXP20X_CHGLED_USER_STATE_ON; ++ ++ return regmap_update_bits(led->regmap, AXP20X_OFF_CTRL, ++ AXP20X_CHGLED_USER_STATE_MASK, val); ++ ++} ++ ++static int axp20x_set_charger_control(struct led_classdev *led_cdev, bool on) ++{ ++ struct axp20x_led *led = container_of(led_cdev, struct axp20x_led, cdev); ++ ++ return regmap_update_bits(led->regmap, AXP20X_OFF_CTRL, ++ AXP20X_CHGLED_CTRL_MASK, ++ on ? AXP20X_CHGLED_CTRL_CHARGER : ++ AXP20X_CHGLED_CTRL_USER); ++} ++ ++static int axp20x_trig_charger_activate(struct led_classdev *led_cdev) ++{ ++ return axp20x_set_charger_control(led_cdev, true); ++} ++ ++static void axp20x_trig_charger_deactivate(struct led_classdev *led_cdev) ++{ ++ axp20x_set_charger_control(led_cdev, false); ++} ++ ++static ssize_t charger_mode_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct led_classdev *led_cdev = led_trigger_get_led(dev); ++ struct axp20x_led *led = container_of(led_cdev, struct axp20x_led, cdev); ++ unsigned int val; ++ int ret; ++ ++ ret = regmap_read(led->regmap, AXP20X_CHRG_CTRL2, &val); ++ if (ret) ++ return ret; ++ ++ return scnprintf(buf, PAGE_SIZE, "%u\n", ++ val & AXP20X_CHRG_CTRL2_MODE ? 1 : 0); ++} ++ ++static ssize_t charger_mode_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t len) ++{ ++ struct led_classdev *led_cdev = led_trigger_get_led(dev); ++ struct axp20x_led *led = container_of(led_cdev, struct axp20x_led, cdev); ++ unsigned int mode; ++ int ret; ++ ++ ret = kstrtouint(buf, 0, &mode); ++ if (ret) ++ return ret; ++ ++ if (mode > 1) ++ return -ERANGE; ++ ++ ret = regmap_update_bits(led->regmap, AXP20X_CHRG_CTRL2, ++ AXP20X_CHRG_CTRL2_MODE, ++ mode ? AXP20X_CHRG_CTRL2_MODE : 0); ++ if (ret) ++ return ret; ++ ++ return len; ++} ++static DEVICE_ATTR_RW(charger_mode); ++ ++static struct attribute *axp20x_led_attrs[] = { ++ &dev_attr_charger_mode.attr, ++ NULL, ++}; ++ ++ATTRIBUTE_GROUPS(axp20x_led); ++ ++static struct led_trigger axp20x_charger_led_trigger = { ++ .name = "charger", ++ .trigger_type = &axp20x_charger_led_trigger_type, ++ .activate = axp20x_trig_charger_activate, ++ .deactivate = axp20x_trig_charger_deactivate, ++ .groups = axp20x_led_groups, ++}; ++ ++static int axp20x_led_probe(struct platform_device *pdev) ++{ ++ struct axp20x_dev *axp20x; ++ struct axp20x_led *led; ++ unsigned int val; ++ int ret; ++ ++ if (!of_device_is_available(pdev->dev.of_node)) ++ return -ENODEV; ++ ++ axp20x = dev_get_drvdata(pdev->dev.parent); ++ if (!axp20x) ++ return -EINVAL; ++ ++ led = devm_kzalloc(&pdev->dev, ++ sizeof(struct axp20x_led), ++ GFP_KERNEL); ++ if (!led) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, led); ++ ++ led->regmap = axp20x->regmap; ++ ++ led->cdev.name = "axp20x-chgarger-led"; ++ led->cdev.brightness_set_blocking = axp20x_led_set; ++ led->cdev.brightness = LED_OFF; ++ led->cdev.max_brightness = 1; ++ led->cdev.trigger_type = &axp20x_charger_led_trigger_type; ++ ++ ret = regmap_read(led->regmap, AXP20X_OFF_CTRL, &val); ++ if (ret) { ++ dev_err(&pdev->dev, "Failed to read charger control status\n"); ++ return ret; ++ } ++ ++ if ((val & AXP20X_CHGLED_CTRL_MASK) == AXP20X_CHGLED_CTRL_CHARGER) ++ led->cdev.default_trigger = axp20x_charger_led_trigger.name; ++ ++ ret = devm_led_classdev_register(pdev->dev.parent, &led->cdev); ++ if (ret) { ++ dev_err(&pdev->dev, "Failed to register led %s\n", ++ led->cdev.name); ++ return ret; ++ } ++ ++ ret = regmap_update_bits(led->regmap, AXP20X_OFF_CTRL, ++ AXP20X_CHGLED_CTRL_MASK, ++ AXP20X_CHGLED_CTRL_CHARGER); ++ if (ret) { ++ dev_err(&pdev->dev, "Failed to enable charger control\n"); ++ return ret; ++ } ++ ++ ret = axp20x_led_set(&led->cdev, led->cdev.brightness); ++ if (ret) { ++ dev_err(&pdev->dev, "Failed to init led brightness\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void axp20x_led_shutdown(struct platform_device *pdev) ++{ ++ struct axp20x_led *led = platform_get_drvdata(pdev); ++ ++ /* On shutdown, we want to give LED control back to the PMIC, ++ * so that it doesn't stay on, while the system is off. ++ */ ++ ++ axp20x_led_set(&led->cdev, LED_OFF); ++ axp20x_set_charger_control(&led->cdev, true); ++} ++ ++static void axp20x_led_remove(struct platform_device *pdev) ++{ ++ axp20x_led_shutdown(pdev); ++} ++ ++static const struct of_device_id axp20x_leds_of_match[] = { ++ { .compatible = "x-powers,axp813-charger-led", }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, axp20x_leds_of_match); ++ ++static struct platform_driver axp20x_led_driver = { ++ .driver = { ++ .name = "leds-axp20x", ++ .of_match_table = axp20x_leds_of_match, ++ }, ++ .probe = axp20x_led_probe, ++ .remove = axp20x_led_remove, ++ .shutdown = axp20x_led_shutdown, ++}; ++ ++static int __init axp20x_led_driver_init(void) ++{ ++ int ret; ++ ++ ret = led_trigger_register(&axp20x_charger_led_trigger); ++ if (ret) ++ return ret; ++ ++ ret = platform_driver_register(&axp20x_led_driver); ++ if (ret) { ++ led_trigger_unregister(&axp20x_charger_led_trigger); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void __exit axp20x_led_driver_exit(void) ++{ ++ platform_driver_unregister(&axp20x_led_driver); ++ led_trigger_unregister(&axp20x_charger_led_trigger); ++} ++ ++module_init(axp20x_led_driver_init); ++module_exit(axp20x_led_driver_exit); ++ ++MODULE_AUTHOR("Ondrej Jirman "); ++MODULE_DESCRIPTION("LED driver for AXP813 PMIC"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:leds-axp20x"); +diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c +index cff56deba24f..4e52c79f8f50 100644 +--- a/drivers/mfd/axp20x.c ++++ b/drivers/mfd/axp20x.c +@@ -1214,6 +1214,9 @@ static const struct mfd_cell axp813_cells[] = { + .num_resources = ARRAY_SIZE(axp803_usb_power_supply_resources), + .resources = axp803_usb_power_supply_resources, + .of_compatible = "x-powers,axp813-usb-power-supply", ++ }, { ++ .name = "axp20x-charger-led", ++ .of_compatible = "x-powers,axp813-charger-led", + }, + }; + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/mailbox-Allow-to-run-mailbox-while-timekeeping-is-suspended.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/mailbox-Allow-to-run-mailbox-while-timekeeping-is-suspended.patch new file mode 100644 index 000000000000..7aab58a648b1 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/mailbox-Allow-to-run-mailbox-while-timekeeping-is-suspended.patch @@ -0,0 +1,64 @@ +From 18fbe636256b55efe55c17b1a9d039ee1fc8c538 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sat, 2 Nov 2019 15:09:01 +0100 +Subject: mailbox: Allow to run mailbox while timekeeping is suspended + +This makes it possible to send messages from CPU suspend finisher. + +We simply implement cl->tx_block using a busywait loop when +timekeeping is suspended, instead of using hrtimer. + +Signed-off-by: Ondrej Jirman +--- + drivers/mailbox/mailbox.c | 28 ++++++++++++++++++++++++---- + 1 file changed, 24 insertions(+), 4 deletions(-) + +diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c +index d3d26a2c9895..01f918ae71b6 100644 +--- a/drivers/mailbox/mailbox.c ++++ b/drivers/mailbox/mailbox.c +@@ -84,10 +84,12 @@ static void msg_submit(struct mbox_chan *chan) + spin_unlock_irqrestore(&chan->lock, flags); + + if (!err && (chan->txdone_method & TXDONE_BY_POLL)) { +- /* kick start the timer immediately to avoid delays */ +- spin_lock_irqsave(&chan->mbox->poll_hrt_lock, flags); +- hrtimer_start(&chan->mbox->poll_hrt, 0, HRTIMER_MODE_REL); +- spin_unlock_irqrestore(&chan->mbox->poll_hrt_lock, flags); ++ if (!timekeeping_suspended) { ++ /* kick start the timer immediately to avoid delays */ ++ spin_lock_irqsave(&chan->mbox->poll_hrt_lock, flags); ++ hrtimer_start(&chan->mbox->poll_hrt, 0, HRTIMER_MODE_REL); ++ spin_unlock_irqrestore(&chan->mbox->poll_hrt_lock, flags); ++ } + } + } + +@@ -269,6 +271,24 @@ int mbox_send_message(struct mbox_chan *chan, void *mssg) + + msg_submit(chan); + ++ if (chan->cl->tx_block && timekeeping_suspended) { ++ int i = chan->cl->tx_tout * 10; ++ bool txdone; ++ ++ while (i--) { ++ txdone = chan->mbox->ops->last_tx_done(chan); ++ if (txdone) { ++ tx_tick(chan, 0); ++ return 0; ++ } ++ ++ udelay(100); ++ } ++ ++ tx_tick(chan, -ETIME); ++ return -ETIME; ++ } ++ + if (chan->cl->tx_block) { + unsigned long wait; + int ret; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/media-cedrus-Fix-failure-to-clean-up-hardware-on-probe-failure.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/media-cedrus-Fix-failure-to-clean-up-hardware-on-probe-failure.patch new file mode 100644 index 000000000000..039bb686cff0 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/media-cedrus-Fix-failure-to-clean-up-hardware-on-probe-failure.patch @@ -0,0 +1,35 @@ +From d7e104605f90e71fd43fa8a8579b6082c6ffdd76 Mon Sep 17 00:00:00 2001 +From: Samuel Holland +Date: Sun, 26 Apr 2020 14:38:15 -0500 +Subject: media: cedrus: Fix failure to clean up hardware on probe failure + +Signed-off-by: Samuel Holland +--- + drivers/staging/media/sunxi/cedrus/cedrus.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c +index 52a9588462ce..b91174246e58 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus.c ++++ b/drivers/staging/media/sunxi/cedrus/cedrus.c +@@ -478,7 +478,7 @@ static int cedrus_probe(struct platform_device *pdev) + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); + if (ret) { + dev_err(&pdev->dev, "Failed to register V4L2 device\n"); +- return ret; ++ goto err_hw; + } + + vfd = &dev->vfd; +@@ -539,6 +539,8 @@ static int cedrus_probe(struct platform_device *pdev) + v4l2_m2m_release(dev->m2m_dev); + err_v4l2: + v4l2_device_unregister(&dev->v4l2_dev); ++err_hw: ++ cedrus_hw_remove(dev); + + return ret; + } +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/media-gc2145-Add-PIXEL_RATE-HBLANK-and-VBLANK-controls.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/media-gc2145-Add-PIXEL_RATE-HBLANK-and-VBLANK-controls.patch new file mode 100644 index 000000000000..d91063a29788 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/media-gc2145-Add-PIXEL_RATE-HBLANK-and-VBLANK-controls.patch @@ -0,0 +1,549 @@ +From b909ea0af9550a47ce455f0bac451bf8ed37e916 Mon Sep 17 00:00:00 2001 +From: Benjamin Schaaf +Date: Mon, 22 Nov 2021 23:38:26 +1100 +Subject: media: gc2145: Add PIXEL_RATE, HBLANK and VBLANK controls + +--- + drivers/media/i2c/gc2145.c | 469 ++++++++++++++++++++----------------- + 1 file changed, 258 insertions(+), 211 deletions(-) + +diff --git a/drivers/media/i2c/gc2145.c b/drivers/media/i2c/gc2145.c +index df15b72651e2..749dce832aa0 100644 +--- a/drivers/media/i2c/gc2145.c ++++ b/drivers/media/i2c/gc2145.c +@@ -216,6 +216,9 @@ static const char * const gc2145_supply_name[] = { + + struct gc2145_ctrls { + struct v4l2_ctrl_handler handler; ++ struct v4l2_ctrl *pixel_rate; ++ struct v4l2_ctrl *hblank; ++ struct v4l2_ctrl *vblank; + struct { + struct v4l2_ctrl *auto_exposure; + struct v4l2_ctrl *exposure; +@@ -866,6 +869,224 @@ static int gc2145_set_exposure(struct gc2145_dev *sensor) + return gc2145_tx_commit(sensor);; + } + ++struct gc2145_sensor_params { ++ unsigned int enable_scaler; ++ unsigned int col_scaler_only; ++ unsigned int row_skip; ++ unsigned int col_skip; ++ unsigned long sh_delay; ++ unsigned long hb; ++ unsigned long vb; ++ unsigned long st; ++ unsigned long et; ++ unsigned long win_width; ++ unsigned long win_height; ++ unsigned long width; ++ unsigned long height; ++}; ++ ++static void gc2145_sensor_params_init(struct gc2145_sensor_params* p, int width, int height) ++{ ++ p->win_height = height + 32; ++ p->win_width = (width + 16); ++ p->width = width; ++ p->height = height; ++ p->st = 2; ++ p->et = 2; ++ p->vb = 8; ++ p->hb = 0x1f0; ++ p->sh_delay = 30; ++} ++ ++// unit is PCLK periods ++static unsigned long ++gc2145_sensor_params_get_row_period(struct gc2145_sensor_params* p) ++{ ++ return 2 * (p->win_width / 2 / (p->col_skip + 1) + p->sh_delay + p->hb + 4); ++} ++ ++static unsigned long ++gc2145_sensor_params_get_frame_period(struct gc2145_sensor_params* p) ++{ ++ unsigned long rt = gc2145_sensor_params_get_row_period(p); ++ ++ return rt * (p->vb + p->win_height) / (p->row_skip + 1); ++} ++ ++static void ++gc2145_sensor_params_fit_hb_to_power_line_period(struct gc2145_sensor_params* p, ++ unsigned long power_line_freq, ++ unsigned long pclk) ++{ ++ unsigned long rt, power_line_ratio; ++ ++ for (p->hb = 0x1f0; p->hb < 2047; p->hb++) { ++ rt = gc2145_sensor_params_get_row_period(p); ++ ++ // power_line_ratio is row_freq / power_line_freq * 1000 ++ power_line_ratio = pclk / power_line_freq * 1000 / rt; ++ ++ // if we're close enough, stop the search ++ if (power_line_ratio % 1000 < 50) ++ break; ++ } ++ ++ // finding the optimal Hb is not critical ++ if (p->hb == 2047) ++ p->hb = 0x1f0; ++} ++ ++static void ++gc2145_sensor_params_fit_vb_to_frame_period(struct gc2145_sensor_params* p, ++ unsigned long frame_period) ++{ ++ unsigned long rt, fp; ++ ++ p->vb = 8; ++ rt = gc2145_sensor_params_get_row_period(p); ++ fp = gc2145_sensor_params_get_frame_period(p); ++ ++ if (frame_period > fp) ++ p->vb = frame_period * (p->row_skip + 1) / rt - p->win_height; ++ ++ if (p->vb > 4095) ++ p->vb = 4095; ++} ++ ++static struct gc2145_sensor_params gc2145_get_sensor_params( ++ unsigned long framerate, ++ unsigned long width, ++ unsigned long height, ++ unsigned long *pclk2) ++{ ++ struct gc2145_sensor_params params = {0}; ++ bool scaling_desired; ++ unsigned long frame_period; ++ unsigned long power_line_freq = 50; ++ ++ /* ++ * Equations for calculating framerate are: ++ * ++ * ww = width + 16 ++ * wh = height + 32 ++ * Rt = (ww / 2 / (col_skip + 1) + sh_delay + Hb + 4) ++ * Ft = Rt * (Vb + wh) / (row_skip + 1) ++ * framerate = 2pclk / 4 / Ft ++ * ++ * Based on these equations: ++ * ++ * 1) First we need to determine what 2PCLK frequency to use. The 2PCLK ++ * frequency is not arbitrarily precise, so we need to calculate the ++ * actual frequency used, after setting our target frequency. ++ * ++ * We use a simple heuristic: ++ * ++ * If pixel_count * 2 * framerate * 1.15 is > 40MHz, we use 60MHz, ++ * otherwise we use 40MHz. ++ * ++ * 2) We want to determine lowest Hb that we can use to extend row ++ * period so that row time takes an integer fraction of the power ++ * line frequency period. Minimum Hb is 0x1f0. ++ * ++ * 3) If the requested resolution is less than half the sensor's size, ++ * we'll use scaling, or row skipping + column scaling, or row and ++ * column skiping, depending on what allows us to achieve the ++ * requested framerate. ++ * ++ * 4) We use the selected Hb to calculate Vb value that will give ++ * us the desired framerate, given the scaling/skipping option ++ * selected in 3). ++ */ ++ ++ scaling_desired = width <= GC2145_SENSOR_WIDTH_MAX / 2 ++ && height <= GC2145_SENSOR_HEIGHT_MAX / 2; ++ ++ *pclk2 = 60000000; ++ ++ gc2145_sensor_params_init(¶ms, width, height); ++ ++ // if the resolution is < half the sensor size, enable the scaler ++ // to cover more area of the chip ++ if (scaling_desired) { ++ params.enable_scaler = 1; ++ *pclk2 *= 2; ++ gc2145_sensor_params_init(¶ms, width * 2, height * 2); ++ } ++ ++ // we need to call this each time pclk or power_line_freq is changed ++ gc2145_sensor_params_fit_hb_to_power_line_period(¶ms, ++ power_line_freq, ++ *pclk2 / 2); ++ ++ frame_period = gc2145_sensor_params_get_frame_period(¶ms); ++ if (framerate <= *pclk2 / 2 / frame_period) ++ goto apply; ++ ++ if (scaling_desired) { ++ // try using just the column scaler + row skip ++ params.col_scaler_only = 1; ++ params.row_skip = 1; ++ gc2145_sensor_params_fit_hb_to_power_line_period(¶ms, ++ power_line_freq, ++ *pclk2 / 2); ++ ++ frame_period = gc2145_sensor_params_get_frame_period(¶ms); ++ if (framerate <= *pclk2 / 2 / frame_period) ++ goto apply; ++ ++ ++ /* ++ // try disabling the scaler and just use skipping ++ params.enable_scaler = 0; ++ *pclk2 /= 2; ++ params.col_scaler_only = 0; ++ params.col_skip = 1; ++ gc2145_sensor_params_fit_hb_to_power_line_period(¶ms, power_line_freq, *pclk2 / 2); ++ ++ frame_period = gc2145_sensor_params_get_frame_period(¶ms); ++ ++ if (framerate <= *pclk2 / 2 / frame_period) ++ goto apply; ++ */ ++ } ++ ++apply: ++ // adjust vb to fit the target framerate ++ gc2145_sensor_params_fit_vb_to_frame_period(¶ms, ++ *pclk2 / 2 / framerate); ++ ++ return params; ++} ++ ++static int gc2145_sensor_params_apply(struct gc2145_dev *sensor, ++ struct gc2145_sensor_params* p) ++{ ++ u32 off_x = (GC2145_SENSOR_WIDTH_MAX - p->width) / 2; ++ u32 off_y = (GC2145_SENSOR_HEIGHT_MAX - p->height) / 2; ++ ++ gc2145_tx_start(sensor); ++ ++ gc2145_tx_write8(sensor, 0xfd, (p->enable_scaler ? BIT(0) : 0) ++ | (p->col_scaler_only ? BIT(1) : 0)); ++ ++ gc2145_tx_write8(sensor, 0x18, 0x0a ++ | (p->col_skip ? BIT(7) : 0) ++ | (p->row_skip ? BIT(6) : 0)); ++ ++ gc2145_tx_write16(sensor, 0x09, off_y); ++ gc2145_tx_write16(sensor, 0x0b, off_x); ++ gc2145_tx_write16(sensor, 0x0d, p->win_height); ++ gc2145_tx_write16(sensor, 0x0f, p->win_width); ++ gc2145_tx_write16(sensor, 0x05, p->hb); ++ gc2145_tx_write16(sensor, 0x07, p->vb); ++ gc2145_tx_write16(sensor, 0x11, p->sh_delay); ++ ++ gc2145_tx_write8(sensor, 0x13, p->st); ++ gc2145_tx_write8(sensor, 0x14, p->et); ++ ++ return gc2145_tx_commit(sensor); ++} ++ + /* Test patterns */ + + enum { +@@ -1054,12 +1275,31 @@ static int gc2145_init_controls(struct gc2145_dev *sensor) + //u64 wb_mask = 0; + //unsigned int i; + int ret; ++ struct gc2145_sensor_params params; ++ unsigned long pixel_rate; + + v4l2_ctrl_handler_init(hdl, 32); + + /* we can use our own mutex for the ctrl lock */ + hdl->lock = &sensor->lock; + ++ /* Clock related controls */ ++ params = gc2145_get_sensor_params(sensor->frame_interval.denominator, ++ sensor->fmt.width, ++ sensor->fmt.height, ++ &pixel_rate); ++ ctrls->pixel_rate = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_PIXEL_RATE, ++ 0, INT_MAX, 1, ++ pixel_rate); ++ ++ ctrls->hblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK, ++ 0, INT_MAX, 1, ++ params.win_height); ++ ++ ctrls->vblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK, ++ 0, INT_MAX, 1, ++ params.win_width); ++ + /* Exposure controls */ + ctrls->auto_exposure = v4l2_ctrl_new_std_menu(hdl, ops, + V4L2_CID_EXPOSURE_AUTO, +@@ -1153,6 +1393,10 @@ static int gc2145_init_controls(struct gc2145_dev *sensor) + v4l2_ctrl_auto_cluster(3, &ctrls->wb, V4L2_WHITE_BALANCE_MANUAL, false); + #endif + ++ ctrls->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY; ++ ctrls->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; ++ ctrls->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; ++ + v4l2_ctrl_auto_cluster(4, &ctrls->auto_exposure, V4L2_EXPOSURE_MANUAL, + true); + +@@ -1386,125 +1630,11 @@ static int gc2145_setup_aec(struct gc2145_dev *sensor, + return gc2145_tx_commit(sensor); + } + +-struct gc2145_sensor_params { +- unsigned int enable_scaler; +- unsigned int col_scaler_only; +- unsigned int row_skip; +- unsigned int col_skip; +- unsigned long sh_delay; +- unsigned long hb; +- unsigned long vb; +- unsigned long st; +- unsigned long et; +- unsigned long win_width; +- unsigned long win_height; +- unsigned long width; +- unsigned long height; +-}; +- +-static void gc2145_sensor_params_init(struct gc2145_sensor_params* p, int width, int height) +-{ +- p->win_height = height + 32; +- p->win_width = (width + 16); +- p->width = width; +- p->height = height; +- p->st = 2; +- p->et = 2; +- p->vb = 8; +- p->hb = 0x1f0; +- p->sh_delay = 30; +-} +- +-// unit is PCLK periods +-static unsigned long +-gc2145_sensor_params_get_row_period(struct gc2145_sensor_params* p) +-{ +- return 2 * (p->win_width / 2 / (p->col_skip + 1) + p->sh_delay + p->hb + 4); +-} +- +-static unsigned long +-gc2145_sensor_params_get_frame_period(struct gc2145_sensor_params* p) +-{ +- unsigned long rt = gc2145_sensor_params_get_row_period(p); +- +- return rt * (p->vb + p->win_height) / (p->row_skip + 1); +-} +- +-static void +-gc2145_sensor_params_fit_hb_to_power_line_period(struct gc2145_sensor_params* p, +- unsigned long power_line_freq, +- unsigned long pclk) +-{ +- unsigned long rt, power_line_ratio; +- +- for (p->hb = 0x1f0; p->hb < 2047; p->hb++) { +- rt = gc2145_sensor_params_get_row_period(p); +- +- // power_line_ratio is row_freq / power_line_freq * 1000 +- power_line_ratio = pclk / power_line_freq * 1000 / rt; +- +- // if we're close enough, stop the search +- if (power_line_ratio % 1000 < 50) +- break; +- } +- +- // finding the optimal Hb is not critical +- if (p->hb == 2047) +- p->hb = 0x1f0; +-} +- +-static void +-gc2145_sensor_params_fit_vb_to_frame_period(struct gc2145_sensor_params* p, +- unsigned long frame_period) +-{ +- unsigned long rt, fp; +- +- p->vb = 8; +- rt = gc2145_sensor_params_get_row_period(p); +- fp = gc2145_sensor_params_get_frame_period(p); +- +- if (frame_period > fp) +- p->vb = frame_period * (p->row_skip + 1) / rt - p->win_height; +- +- if (p->vb > 4095) +- p->vb = 4095; +-} +- +-static int gc2145_sensor_params_apply(struct gc2145_dev *sensor, +- struct gc2145_sensor_params* p) +-{ +- u32 off_x = (GC2145_SENSOR_WIDTH_MAX - p->width) / 2; +- u32 off_y = (GC2145_SENSOR_HEIGHT_MAX - p->height) / 2; +- +- gc2145_tx_start(sensor); +- +- gc2145_tx_write8(sensor, 0xfd, (p->enable_scaler ? BIT(0) : 0) +- | (p->col_scaler_only ? BIT(1) : 0)); +- +- gc2145_tx_write8(sensor, 0x18, 0x0a +- | (p->col_skip ? BIT(7) : 0) +- | (p->row_skip ? BIT(6) : 0)); +- +- gc2145_tx_write16(sensor, 0x09, off_y); +- gc2145_tx_write16(sensor, 0x0b, off_x); +- gc2145_tx_write16(sensor, 0x0d, p->win_height); +- gc2145_tx_write16(sensor, 0x0f, p->win_width); +- gc2145_tx_write16(sensor, 0x05, p->hb); +- gc2145_tx_write16(sensor, 0x07, p->vb); +- gc2145_tx_write16(sensor, 0x11, p->sh_delay); +- +- gc2145_tx_write8(sensor, 0x13, p->st); +- gc2145_tx_write8(sensor, 0x14, p->et); +- +- return gc2145_tx_commit(sensor); +-} +- + static int gc2145_setup_mode(struct gc2145_dev *sensor) + { +- int scaling_desired, ret, pad, i; +- struct gc2145_sensor_params params = {0}; +- unsigned long pclk2, frame_period; +- unsigned long power_line_freq = 50; ++ int ret, pad, i; ++ struct gc2145_sensor_params params; ++ unsigned long pclk2; + unsigned long width = sensor->fmt.width; + unsigned long height = sensor->fmt.height; + unsigned long framerate = sensor->frame_interval.denominator; +@@ -1518,100 +1648,7 @@ static int gc2145_setup_mode(struct gc2145_dev *sensor) + return -EINVAL; + } + +- /* +- * Equations for calculating framerate are: +- * +- * ww = width + 16 +- * wh = height + 32 +- * Rt = (ww / 2 / (col_skip + 1) + sh_delay + Hb + 4) +- * Ft = Rt * (Vb + wh) / (row_skip + 1) +- * framerate = 2pclk / 4 / Ft +- * +- * Based on these equations: +- * +- * 1) First we need to determine what 2PCLK frequency to use. The 2PCLK +- * frequency is not arbitrarily precise, so we need to calculate the +- * actual frequency used, after setting our target frequency. +- * +- * We use a simple heuristic: +- * +- * If pixel_count * 2 * framerate * 1.15 is > 40MHz, we use 60MHz, +- * otherwise we use 40MHz. +- * +- * 2) We want to determine lowest Hb that we can use to extend row +- * period so that row time takes an integer fraction of the power +- * line frequency period. Minimum Hb is 0x1f0. +- * +- * 3) If the requested resolution is less than half the sensor's size, +- * we'll use scaling, or row skipping + column scaling, or row and +- * column skiping, depending on what allows us to achieve the +- * requested framerate. +- * +- * 4) We use the selected Hb to calculate Vb value that will give +- * us the desired framerate, given the scaling/skipping option +- * selected in 3). +- */ +- +- scaling_desired = width <= GC2145_SENSOR_WIDTH_MAX / 2 +- && height <= GC2145_SENSOR_HEIGHT_MAX / 2; +- +- pclk2 = 60000000; +- +- ret = gc2145_set_2pclk(sensor, &pclk2, false); +- if (ret < 0) +- return ret; +- +- gc2145_sensor_params_init(¶ms, width, height); +- +- // if the resolution is < half the sensor size, enable the scaler +- // to cover more area of the chip +- if (scaling_desired) { +- params.enable_scaler = 1; +- pclk2 *= 2; +- gc2145_sensor_params_init(¶ms, width * 2, height * 2); +- } +- +- // we need to call this each time pclk or power_line_freq is changed +- gc2145_sensor_params_fit_hb_to_power_line_period(¶ms, +- power_line_freq, +- pclk2 / 2); +- +- frame_period = gc2145_sensor_params_get_frame_period(¶ms); +- if (framerate <= pclk2 / 2 / frame_period) +- goto apply; +- +- if (scaling_desired) { +- // try using just the column scaler + row skip +- params.col_scaler_only = 1; +- params.row_skip = 1; +- gc2145_sensor_params_fit_hb_to_power_line_period(¶ms, +- power_line_freq, +- pclk2 / 2); +- +- frame_period = gc2145_sensor_params_get_frame_period(¶ms); +- if (framerate <= pclk2 / 2 / frame_period) +- goto apply; +- +- +- /* +- // try disabling the scaler and just use skipping +- params.enable_scaler = 0; +- pclk2 /= 2; +- params.col_scaler_only = 0; +- params.col_skip = 1; +- gc2145_sensor_params_fit_hb_to_power_line_period(¶ms, power_line_freq, pclk2 / 2); +- +- frame_period = gc2145_sensor_params_get_frame_period(¶ms); +- +- if (framerate <= pclk2 / 2 / frame_period) +- goto apply; +- */ +- } +- +-apply: +- // adjust vb to fit the target framerate +- gc2145_sensor_params_fit_vb_to_frame_period(¶ms, +- pclk2 / 2 / framerate); ++ params = gc2145_get_sensor_params(framerate, width, height, &pclk2); + + gc2145_sensor_params_apply(sensor, ¶ms); + +@@ -1845,6 +1882,8 @@ static int gc2145_set_fmt(struct v4l2_subdev *sd, + struct gc2145_dev *sensor = to_gc2145_dev(sd); + struct v4l2_mbus_framefmt *mf = &format->format; + const struct gc2145_pixfmt *pixfmt; ++ struct gc2145_sensor_params params; ++ unsigned long pixel_rate; + int ret = 0; + + if (format->pad != 0) +@@ -1884,6 +1923,14 @@ static int gc2145_set_fmt(struct v4l2_subdev *sd, + + sensor->fmt = *mf; + sensor->pending_mode_change = true; ++ ++ params = gc2145_get_sensor_params(sensor->frame_interval.denominator, mf->width, mf->height, &pixel_rate); ++ __v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate, ++ pixel_rate); ++ __v4l2_ctrl_s_ctrl(sensor->ctrls.hblank, ++ params.win_height); ++ __v4l2_ctrl_s_ctrl(sensor->ctrls.vblank, ++ params.win_width); + out: + mutex_unlock(&sensor->lock); + return ret; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/media-gc2145-Added-BGGR-bayer-mode.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/media-gc2145-Added-BGGR-bayer-mode.patch new file mode 100644 index 000000000000..54eb5e287c03 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/media-gc2145-Added-BGGR-bayer-mode.patch @@ -0,0 +1,30 @@ +From c39a8e59c4abd741e172f47ec80b08ad12b85f39 Mon Sep 17 00:00:00 2001 +From: Martijn Braam +Date: Fri, 4 Sep 2020 17:35:39 +0200 +Subject: media: gc2145: Added BGGR bayer mode + +Not all raw bayer modes from the sensor match up with the ones defined +in v4l, mostly because they're mirrored. +--- + drivers/media/i2c/gc2145.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/drivers/media/i2c/gc2145.c b/drivers/media/i2c/gc2145.c +index cc85ac772d5a..7539b122eeef 100644 +--- a/drivers/media/i2c/gc2145.c ++++ b/drivers/media/i2c/gc2145.c +@@ -187,6 +187,11 @@ static const struct gc2145_pixfmt gc2145_formats[] = { + .colorspace = V4L2_COLORSPACE_SRGB, + .fmt_setup = 0x06, + }, ++ { ++ .code = MEDIA_BUS_FMT_SBGGR8_1X8, ++ .colorspace = V4L2_COLORSPACE_RAW, ++ .fmt_setup = 0x17, ++ }, + }; + + static const struct gc2145_pixfmt *gc2145_find_format(u32 code) +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/media-gc2145-Disable-debug-output.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/media-gc2145-Disable-debug-output.patch new file mode 100644 index 000000000000..18223184f5f3 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/media-gc2145-Disable-debug-output.patch @@ -0,0 +1,37 @@ +From 420354a4c5c188185fcc37270e4c41bdbb608e60 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Wed, 27 Oct 2021 17:22:51 +0200 +Subject: media: gc2145: Disable debug output + +People don't like it. Hmm... + +Signed-off-by: Ondrej Jirman +--- + drivers/media/i2c/gc2145.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/media/i2c/gc2145.c b/drivers/media/i2c/gc2145.c +index 7539b122eeef..df15b72651e2 100644 +--- a/drivers/media/i2c/gc2145.c ++++ b/drivers/media/i2c/gc2145.c +@@ -8,7 +8,7 @@ + * (at your option) any later version. + */ + +-#define DEBUG ++//#define DEBUG + + #include + #include +@@ -374,7 +374,7 @@ static int gc2145_switch_bank(struct gc2145_dev *sensor, u16 reg) + return ret; + + sensor->current_bank = bank; +- dev_info(&sensor->i2c_client->dev, "bank switch: 0x%02x\n", ++ dev_dbg(&sensor->i2c_client->dev, "bank switch: 0x%02x\n", + (unsigned int)sensor->current_bank); + } + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/media-gc2145-Galaxycore-camera-module-driver.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/media-gc2145-Galaxycore-camera-module-driver.patch new file mode 100644 index 000000000000..8c14e25c5c13 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/media-gc2145-Galaxycore-camera-module-driver.patch @@ -0,0 +1,2306 @@ +From 3226585412fac36bbcb0dc88f54020aeb25feadb Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Tue, 23 Jun 2020 19:51:18 +0200 +Subject: media: gc2145: Galaxycore camera module driver + +WIP! + +Signed-off-by: Ondrej Jirman +--- + drivers/media/i2c/Kconfig | 10 + + drivers/media/i2c/Makefile | 1 + + drivers/media/i2c/gc2145.c | 2252 ++++++++++++++++++++++++++++++++++++ + 3 files changed, 2263 insertions(+) + create mode 100644 drivers/media/i2c/gc2145.c + +diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig +index 43be07cc5b37..2b3b4635c2f0 100644 +--- a/drivers/media/i2c/Kconfig ++++ b/drivers/media/i2c/Kconfig +@@ -711,6 +711,16 @@ config VIDEO_HM5065 + This is a V4L2 sensor-level driver for Himax HM5065 + 5 Mpixel camera. + ++config VIDEO_GC2145 ++ tristate "GalaxyCore GC2145 sensor support" ++ depends on I2C ++ select V4L2_FWNODE ++ select VIDEO_V4L2_SUBDEV_API ++ select MEDIA_CONTROLLER ++ help ++ This is a V4L2 sensor-level driver for GalaxyCore GC2145 ++ 2 Mpixel camera. ++ + endif + + menu "Camera ISPs" +diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile +index 08bd346db54e..8d79cbb0f29c 100644 +--- a/drivers/media/i2c/Makefile ++++ b/drivers/media/i2c/Makefile +@@ -158,3 +158,4 @@ obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o + obj-$(CONFIG_VIDEO_WM8739) += wm8739.o + obj-$(CONFIG_VIDEO_WM8775) += wm8775.o + obj-$(CONFIG_VIDEO_HM5065) += hm5065.o ++obj-$(CONFIG_VIDEO_GC2145) += gc2145.o +diff --git a/drivers/media/i2c/gc2145.c b/drivers/media/i2c/gc2145.c +new file mode 100644 +index 000000000000..cc85ac772d5a +--- /dev/null ++++ b/drivers/media/i2c/gc2145.c +@@ -0,0 +1,2252 @@ ++/* ++ * Galaxycore GC2145 driver. ++ * Copyright (C) 2018 OndÅ™ej Jirman . ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++ ++#define DEBUG ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * GC2145 ++ * - 2M pixel ++ * - 1600 x 1200, max frame rate: 720P, 30fps@96MHz ++ * - Bayer RGB, RGB565, YCbCr 4:2:2 ++ * - AE, AWB ++ * - PLL ++ * - AVDD 2.7-3V, DVDD 1.7-1.9V, IOVDD 1.7-3V ++ * - Power 180mW / 200uA standby ++ * - Interpolation, denoise, gamma, edge enhance ++ * I2C: ++ * - write reg8 ++ * - read reg8 ++ * - write reg8 multi ++ * ++ * Power on: ++ * MCLK on ++ * PWDN, RESET low ++ * IOVDD, AVDD, DVDD on in sequence ++ * RESET high ++ * ++ * Power off: ++ * PWDN, RESET low ++ * RESET high ++ * delay ++ * PWDN high ++ * RESET low ++ * IOVDD, AVDD, DVDD off ++ * PWDN low? ++ * MCLK off ++ * ++ * Init: ++ * - check chip id ++ * - setup pll ++ * - setup CSI interface / PAD drive strength ++ * - setup resolution/fps ++ * - enable postprocessing ++ * (ISP related chapter) ++ * ++ * Stream on: ++ * - ??? ++ */ ++ ++#define GC2145_FIRMWARE_PARAMETERS "gc2145-init.bin" ++ ++#define GC2145_SENSOR_WIDTH_MIN 88u ++#define GC2145_SENSOR_HEIGHT_MIN 72u ++ ++//XXX: 1616x1232 8H/16V dummy pixels on each side ++#define GC2145_SENSOR_WIDTH_MAX 1600u ++#define GC2145_SENSOR_HEIGHT_MAX 1200u ++ ++/* {{{ Register definitions */ ++ ++/* system registers */ ++#define GC2145_REG_CHIP_ID 0xf0 ++#define GC2145_REG_CHIP_ID_VALUE 0x2145 ++ ++#define GC2145_REG_PAD_IO 0xf2 ++#define GC2145_REG_PLL_MODE1 0xf7 ++#define GC2145_REG_PLL_MODE2 0xf8 ++#define GC2145_REG_CM_MODE 0xf9 ++#define GC2145_REG_CLK_DIV_MODE 0xfa ++#define GC2145_REG_ANALOG_PWC 0xfc ++#define GC2145_REG_SCALER_MODE 0xfd ++#define GC2145_REG_RESET 0xfe ++ ++#define GC2145_P0_EXPOSURE_HI 0x03 ++#define GC2145_P0_EXPOSURE_LO 0x04 ++#define GC2145_P0_HBLANK_DELAY_HI 0x05 ++#define GC2145_P0_HBLANK_DELAY_LO 0x06 ++#define GC2145_P0_VBLANK_DELAY_HI 0x07 ++#define GC2145_P0_VBLANK_DELAY_LO 0x08 ++#define GC2145_P0_ROW_START_HI 0x09 ++#define GC2145_P0_ROW_START_LO 0x0a ++#define GC2145_P0_COL_START_HI 0x0b ++#define GC2145_P0_COL_START_LO 0x0c ++#define GC2145_P0_WIN_HEIGHT_HI 0x0d ++#define GC2145_P0_WIN_HEIGHT_LO 0x0e ++#define GC2145_P0_WIN_WIDTH_HI 0x0f ++#define GC2145_P0_WIN_WIDTH_LO 0x10 ++#define GC2145_P0_SH_DELAY_HI 0x11 ++#define GC2145_P0_SH_DELAY_LO 0x12 ++#define GC2145_P0_START_TIME 0x13 ++#define GC2145_P0_END_TIME 0x14 ++ ++#define GC2145_P0_ISP_BLK_ENABLE1 0x80 ++#define GC2145_P0_ISP_BLK_ENABLE2 0x81 ++#define GC2145_P0_ISP_BLK_ENABLE3 0x82 ++#define GC2145_P0_ISP_SPECIAL_EFFECT 0x83 ++#define GC2145_P0_ISP_OUT_FORMAT 0x84 ++#define GC2145_P0_FRAME_START 0x85 ++#define GC2145_P0_SYNC_MODE 0x86 ++#define GC2145_P0_ISP_BLK_ENABLE4 0x87 ++#define GC2145_P0_ISP_MODULE_GATING 0x88 ++#define GC2145_P0_ISP_BYPASS_MODE 0x89 ++#define GC2145_P0_DEBUG_MODE2 0x8c ++#define GC2145_P0_DEBUG_MODE3 0x8d ++ ++#define GC2145_P0_CROP_ENABLE 0x90 ++#define GC2145_P0_CROP_Y1_HI 0x91 ++#define GC2145_P0_CROP_Y1_LO 0x92 ++#define GC2145_P0_CROP_X1_HI 0x93 ++#define GC2145_P0_CROP_X1_LO 0x94 ++#define GC2145_P0_CROP_WIN_HEIGHT_HI 0x95 ++#define GC2145_P0_CROP_WIN_HEIGHT_LO 0x96 ++#define GC2145_P0_CROP_WIN_WIDTH_HI 0x97 ++#define GC2145_P0_CROP_WIN_WIDTH_LO 0x98 ++ ++#define GC2145_P0_SUBSAMPLE_RATIO 0x99 ++#define GC2145_P0_SUBSAMPLE_MODE 0x9a ++#define GC2145_P0_SUB_ROW_N1 0x9b ++#define GC2145_P0_SUB_ROW_N2 0x9c ++#define GC2145_P0_SUB_ROW_N3 0x9d ++#define GC2145_P0_SUB_ROW_N4 0x9e ++#define GC2145_P0_SUB_COL_N1 0x9f ++#define GC2145_P0_SUB_COL_N2 0xa0 ++#define GC2145_P0_SUB_COL_N3 0xa1 ++#define GC2145_P0_SUB_COL_N4 0xa2 ++#define GC2145_P0_OUT_BUF_ENABLE 0xc2 ++ ++/* }}} */ ++ ++struct gc2145_pixfmt { ++ u32 code; ++ u32 colorspace; ++ u8 fmt_setup; ++}; ++ ++static const struct gc2145_pixfmt gc2145_formats[] = { ++ { ++ .code = MEDIA_BUS_FMT_UYVY8_2X8, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ .fmt_setup = 0x00, ++ }, ++ { ++ .code = MEDIA_BUS_FMT_VYUY8_2X8, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ .fmt_setup = 0x01, ++ }, ++ { ++ .code = MEDIA_BUS_FMT_YUYV8_2X8, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ .fmt_setup = 0x02, ++ }, ++ { ++ .code = MEDIA_BUS_FMT_YVYU8_2X8, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ .fmt_setup = 0x03, ++ }, ++ { ++ .code = MEDIA_BUS_FMT_RGB565_2X8_LE, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ .fmt_setup = 0x06, ++ }, ++}; ++ ++static const struct gc2145_pixfmt *gc2145_find_format(u32 code) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(gc2145_formats); i++) ++ if (gc2145_formats[i].code == code) ++ return &gc2145_formats[i]; ++ ++ return NULL; ++} ++ ++/* regulator supplies */ ++static const char * const gc2145_supply_name[] = { ++ "IOVDD", /* Digital I/O (1.7-3V) suppply */ ++ "AVDD", /* Analog (2.7-3V) supply */ ++ "DVDD", /* Digital Core (1.7-1.9V) supply */ ++}; ++ ++#define GC2145_NUM_SUPPLIES ARRAY_SIZE(gc2145_supply_name) ++ ++struct gc2145_ctrls { ++ struct v4l2_ctrl_handler handler; ++ struct { ++ struct v4l2_ctrl *auto_exposure; ++ struct v4l2_ctrl *exposure; ++ struct v4l2_ctrl *d_gain; ++ struct v4l2_ctrl *a_gain; ++ }; ++ struct v4l2_ctrl *metering; ++ struct v4l2_ctrl *exposure_bias; ++ struct { ++ struct v4l2_ctrl *wb; ++ struct v4l2_ctrl *blue_balance; ++ struct v4l2_ctrl *red_balance; ++ }; ++ struct v4l2_ctrl *aaa_lock; ++ struct v4l2_ctrl *hflip; ++ struct v4l2_ctrl *vflip; ++ struct v4l2_ctrl *pl_freq; ++ struct v4l2_ctrl *colorfx; ++ struct v4l2_ctrl *brightness; ++ struct v4l2_ctrl *saturation; ++ struct v4l2_ctrl *contrast; ++ struct v4l2_ctrl *gamma; ++ struct v4l2_ctrl *test_pattern; ++ struct v4l2_ctrl *test_data[4]; ++}; ++ ++enum { ++ TX_WRITE = 1, ++ TX_WRITE16, ++ TX_UPDATE_BITS, ++}; ++ ++#define GC2145_MAX_OPS 64 ++ ++struct gc2145_tx_op { ++ int op; ++ u16 reg; ++ u16 val; ++ u16 mask; ++}; ++ ++struct gc2145_dev { ++ struct i2c_client *i2c_client; ++ struct v4l2_subdev sd; ++ struct media_pad pad; ++ struct v4l2_fwnode_endpoint ep; /* the parsed DT endpoint info */ ++ struct clk *xclk; /* external clock for GC2145 */ ++ ++ struct regulator_bulk_data supplies[GC2145_NUM_SUPPLIES]; ++ struct gpio_desc *reset_gpio; // nrst pin ++ struct gpio_desc *enable_gpio; // ce pin ++ ++ /* lock to protect all members below */ ++ struct mutex lock; ++ ++ struct v4l2_mbus_framefmt fmt; ++ struct v4l2_fract frame_interval; ++ struct gc2145_ctrls ctrls; ++ ++ bool pending_mode_change; ++ bool powered; ++ bool streaming; ++ ++ u8 current_bank; ++ ++ struct gc2145_tx_op ops[GC2145_MAX_OPS]; ++ int n_ops; ++ int tx_started; ++}; ++ ++static inline struct gc2145_dev *to_gc2145_dev(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct gc2145_dev, sd); ++} ++ ++/* {{{ Register access helpers */ ++ ++static int gc2145_write_regs(struct gc2145_dev *sensor, u8 addr, ++ u8 *data, int data_size) ++{ ++ struct i2c_client *client = sensor->i2c_client; ++ struct i2c_msg msg; ++ u8 buf[128 + 1]; ++ int ret; ++ ++ if (data_size > sizeof(buf) - 1) { ++ v4l2_err(&sensor->sd, "%s: oversized transfer (size=%d)\n", ++ __func__, data_size); ++ return -EINVAL; ++ } ++ ++ buf[0] = addr; ++ memcpy(buf + 1, data, data_size); ++ ++ msg.addr = client->addr; ++ msg.flags = client->flags; ++ msg.buf = buf; ++ msg.len = data_size + 1; ++ ++ dev_dbg(&sensor->i2c_client->dev, "[wr %02x] <= %*ph\n", ++ (u32)addr, data_size, data); ++ ++ ret = i2c_transfer(client->adapter, &msg, 1); ++ if (ret < 0) { ++ v4l2_err(&sensor->sd, ++ "%s: error %d: addr=%x, data=%*ph\n", ++ __func__, ret, (u32)addr, data_size, data); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int gc2145_read_regs(struct gc2145_dev *sensor, u8 addr, ++ u8 *data, int data_size) ++{ ++ struct i2c_client *client = sensor->i2c_client; ++ struct i2c_msg msg[2]; ++ int ret; ++ ++ msg[0].addr = client->addr; ++ msg[0].flags = client->flags; ++ msg[0].buf = &addr; ++ msg[0].len = 1; ++ ++ msg[1].addr = client->addr; ++ msg[1].flags = client->flags | I2C_M_RD; ++ msg[1].buf = data; ++ msg[1].len = data_size; ++ ++ ret = i2c_transfer(client->adapter, msg, 2); ++ if (ret < 0) { ++ v4l2_err(&sensor->sd, ++ "%s: error %d: start_index=%x, data_size=%d\n", ++ __func__, ret, (u32)addr, data_size); ++ return ret; ++ } ++ ++ dev_dbg(&sensor->i2c_client->dev, "[rd %02x] => %*ph\n", ++ (u32)addr, data_size, data); ++ ++ return 0; ++} ++ ++static int gc2145_switch_bank(struct gc2145_dev *sensor, u16 reg) ++{ ++ int ret; ++ u8 bank = reg >> 8; ++ ++ if (bank & ~3u) ++ return -ERANGE; ++ ++ if (sensor->current_bank != bank) { ++ ret = gc2145_write_regs(sensor, GC2145_REG_RESET, &bank, 1); ++ if (ret) ++ return ret; ++ ++ sensor->current_bank = bank; ++ dev_info(&sensor->i2c_client->dev, "bank switch: 0x%02x\n", ++ (unsigned int)sensor->current_bank); ++ } ++ ++ return 0; ++} ++ ++static int gc2145_read(struct gc2145_dev *sensor, u16 reg, u8 *val) ++{ ++ int ret; ++ ++ ret = gc2145_switch_bank(sensor, reg); ++ if (ret) ++ return ret; ++ ++ return gc2145_read_regs(sensor, reg, val, 1); ++} ++ ++static int gc2145_write(struct gc2145_dev *sensor, u16 reg, u8 val) ++{ ++ int ret; ++ ++ ret = gc2145_switch_bank(sensor, reg); ++ if (ret) ++ return ret; ++ ++ if ((reg & 0xffu) == GC2145_REG_RESET) ++ sensor->current_bank = val & 3; ++ ++ return gc2145_write_regs(sensor, reg, &val, 1); ++} ++ ++static int gc2145_update_bits(struct gc2145_dev *sensor, u16 reg, u8 mask, u8 val) ++{ ++ int ret; ++ u8 tmp; ++ ++ ret = gc2145_read(sensor, reg, &tmp); ++ if (ret) ++ return ret; ++ ++ tmp &= ~mask; ++ tmp |= val & mask; ++ ++ return gc2145_write(sensor, reg, tmp); ++} ++ ++static int gc2145_read16(struct gc2145_dev *sensor, u16 reg, u16 *val) ++{ ++ int ret; ++ ++ ret = gc2145_switch_bank(sensor, reg); ++ if (ret) ++ return ret; ++ ++ ret = gc2145_read_regs(sensor, reg, (u8 *)val, sizeof(*val)); ++ if (ret) ++ return ret; ++ ++ *val = be16_to_cpu(*val); ++ return 0; ++} ++ ++static int gc2145_write16(struct gc2145_dev *sensor, u16 reg, u16 val) ++{ ++ u16 tmp = cpu_to_be16(val); ++ int ret; ++ ++ ret = gc2145_switch_bank(sensor, reg); ++ if (ret) ++ return ret; ++ ++ return gc2145_write_regs(sensor, reg, (u8 *)&tmp, sizeof(tmp)); ++} ++ ++static void gc2145_tx_start(struct gc2145_dev *sensor) ++{ ++ if (sensor->tx_started++) ++ dev_err(&sensor->i2c_client->dev, ++ "tx_start called multiple times\n"); ++ ++ sensor->n_ops = 0; ++} ++ ++static void gc2145_tx_add(struct gc2145_dev *sensor, int kind, ++ u16 reg, u16 val, u16 mask) ++{ ++ struct gc2145_tx_op *op; ++ ++ if (!sensor->tx_started) { ++ dev_err(&sensor->i2c_client->dev, ++ "op added without calling tx_start\n"); ++ return; ++ } ++ ++ if (sensor->n_ops >= ARRAY_SIZE(sensor->ops)) { ++ dev_err(&sensor->i2c_client->dev, ++ "ops overflow, increase GC2145_MAX_OPS\n"); ++ return; ++ } ++ ++ op = &sensor->ops[sensor->n_ops++]; ++ op->op = kind; ++ op->reg = reg; ++ op->val = val; ++ op->mask = mask; ++} ++ ++static void gc2145_tx_write8(struct gc2145_dev *sensor, u16 reg, u8 val) ++{ ++ return gc2145_tx_add(sensor, TX_WRITE, reg, val, 0); ++} ++ ++static void gc2145_tx_write16(struct gc2145_dev *sensor, u16 reg, u16 val) ++{ ++ return gc2145_tx_add(sensor, TX_WRITE16, reg, val, 0); ++} ++ ++static void gc2145_tx_update_bits(struct gc2145_dev *sensor, u16 reg, ++ u8 mask, u8 val) ++{ ++ return gc2145_tx_add(sensor, TX_UPDATE_BITS, reg, val, mask); ++} ++ ++static int gc2145_tx_commit(struct gc2145_dev *sensor) ++{ ++ struct gc2145_tx_op* op; ++ int i, ret, n_ops; ++ ++ if (!sensor->tx_started) { ++ dev_err(&sensor->i2c_client->dev, ++ "tx_commit called without tx_start\n"); ++ return 0; ++ } ++ ++ n_ops = sensor->n_ops; ++ sensor->tx_started = 0; ++ sensor->n_ops = 0; ++ ++ for (i = 0; i < n_ops; i++) { ++ op = &sensor->ops[i]; ++ ++ switch (op->op) { ++ case TX_WRITE: ++ ret = gc2145_write(sensor, op->reg, op->val); ++ break; ++ case TX_WRITE16: ++ ret = gc2145_write16(sensor, op->reg, op->val); ++ break; ++ case TX_UPDATE_BITS: ++ ret = gc2145_update_bits(sensor, op->reg, op->mask, op->val); ++ break; ++ default: ++ dev_err(&sensor->i2c_client->dev, "invalid op at %d\n", i); ++ ret = -EINVAL; ++ } ++ ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/* ++ * Efficiently write to a set of registers, using auto-increment ++ * when possible. User must not use address 0xff. To switch banks, ++ * use sequence: 0xfe, bank_no. ++ */ ++static int gc2145_set_registers(struct gc2145_dev *sensor, ++ const uint8_t* data, size_t data_len) ++{ ++ int ret = 0, i = 0; ++ u16 start, len; ++ u8 buf[128]; ++ ++ if (data_len % 2 != 0) { ++ v4l2_err(&sensor->sd, "Register map has invalid size\n"); ++ return -EINVAL; ++ } ++ ++ /* we speed up communication by using auto-increment functionality */ ++ while (i < data_len) { ++ start = data[i]; ++ len = 0; ++ ++ while (i < data_len && data[i] == (start + len) && ++ len < sizeof(buf)) { ++ buf[len++] = data[i + 1]; ++ i += 2; ++ } ++ ++ ret = gc2145_write_regs(sensor, start, buf, len); ++ if (ret) ++ return ret; ++ } ++ ++ sensor->current_bank = 0xff; ++ return 0; ++} ++ ++/* ++ * The firmware format: ++ * , ..., ++ * "record" is a 1-byte register address followed by 1-byte data ++ */ ++static int gc2145_load_firmware(struct gc2145_dev *sensor, const char *name) ++{ ++ const struct firmware *fw; ++ int ret; ++ ++ ret = request_firmware(&fw, name, sensor->sd.v4l2_dev->dev); ++ if (ret) { ++ v4l2_warn(&sensor->sd, ++ "Failed to read firmware %s, continuing anyway...\n", ++ name); ++ return 1; ++ } ++ ++ if (fw->size == 0) ++ return 1; ++ ++ ret = gc2145_set_registers(sensor, fw->data, fw->size); ++ ++ release_firmware(fw); ++ return ret; ++} ++ ++/* }}} */ ++/* {{{ Controls */ ++ ++static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) ++{ ++ return &container_of(ctrl->handler, struct gc2145_dev, ++ ctrls.handler)->sd; ++} ++ ++#if 0 ++static const u8 gc2145_wb_opts[][2] = { ++ { V4L2_WHITE_BALANCE_MANUAL, GC2145_REG_WB_MODE_OFF }, ++ { V4L2_WHITE_BALANCE_INCANDESCENT, GC2145_REG_WB_MODE_TUNGSTEN_PRESET }, ++ { V4L2_WHITE_BALANCE_FLUORESCENT, ++ GC2145_REG_WB_MODE_FLUORESCENT_PRESET }, ++ { V4L2_WHITE_BALANCE_HORIZON, GC2145_REG_WB_MODE_HORIZON_PRESET }, ++ { V4L2_WHITE_BALANCE_CLOUDY, GC2145_REG_WB_MODE_CLOUDY_PRESET }, ++ { V4L2_WHITE_BALANCE_DAYLIGHT, GC2145_REG_WB_MODE_SUNNY_PRESET }, ++ { V4L2_WHITE_BALANCE_AUTO, GC2145_REG_WB_MODE_AUTOMATIC }, ++}; ++ ++static int gc2145_set_power_line_frequency(struct gc2145_dev *sensor, s32 val) ++{ ++ u16 freq; ++ int ret; ++ ++ switch (val) { ++ case V4L2_CID_POWER_LINE_FREQUENCY_DISABLED: ++ ret = gc2145_write(sensor, GC2145_REG_ANTI_FLICKER_MODE, 0); ++ if (ret) ++ return ret; ++ ++ return gc2145_write(sensor, GC2145_REG_FD_ENABLE_DETECT, 0); ++ case V4L2_CID_POWER_LINE_FREQUENCY_50HZ: ++ case V4L2_CID_POWER_LINE_FREQUENCY_60HZ: ++ ret = gc2145_write(sensor, GC2145_REG_ANTI_FLICKER_MODE, 1); ++ if (ret) ++ return ret; ++ ++ ret = gc2145_write(sensor, GC2145_REG_FD_ENABLE_DETECT, 0); ++ if (ret) ++ return ret; ++ ++ freq = (val == V4L2_CID_POWER_LINE_FREQUENCY_50HZ) ? ++ 0x4b20 : 0x4bc0; ++ ++ return gc2145_write16(sensor, GC2145_REG_FD_FLICKER_FREQUENCY, ++ freq); ++ case V4L2_CID_POWER_LINE_FREQUENCY_AUTO: ++ ret = gc2145_write(sensor, GC2145_REG_FD_ENABLE_DETECT, 1); ++ if (ret) ++ return ret; ++ ++ ret = gc2145_write(sensor, GC2145_REG_ANTI_FLICKER_MODE, 1); ++ if (ret) ++ return ret; ++ ++ ret = gc2145_write16(sensor, GC2145_REG_FD_MAX_NUMBER_ATTEMP, ++ 100); ++ if (ret) ++ return ret; ++ ++ ret = gc2145_write16(sensor, GC2145_REG_FD_FLICKER_FREQUENCY, ++ 0); ++ if (ret) ++ return ret; ++ ++ return gc2145_write(sensor, GC2145_REG_FD_DETECTION_START, 1); ++ default: ++ return -EINVAL; ++ } ++} ++ ++static int gc2145_set_colorfx(struct gc2145_dev *sensor, s32 val) ++{ ++ int ret; ++ ++ ret = gc2145_write(sensor, GC2145_REG_EFFECTS_COLOR, ++ GC2145_REG_EFFECTS_COLOR_NORMAL); ++ if (ret) ++ return ret; ++ ++ ret = gc2145_write(sensor, GC2145_REG_EFFECTS_NEGATIVE, 0); ++ if (ret) ++ return ret; ++ ++ ret = gc2145_write(sensor, GC2145_REG_EFFECTS_SOLARISING, 0); ++ if (ret) ++ return ret; ++ ++ ret = gc2145_write(sensor, GC2145_REG_EFFECTS_SKECTH, 0); ++ if (ret) ++ return ret; ++ ++ switch (val) { ++ case V4L2_COLORFX_NONE: ++ return 0; ++ case V4L2_COLORFX_NEGATIVE: ++ return gc2145_write(sensor, GC2145_REG_EFFECTS_NEGATIVE, 1); ++ case V4L2_COLORFX_SOLARIZATION: ++ return gc2145_write(sensor, GC2145_REG_EFFECTS_SOLARISING, 1); ++ case V4L2_COLORFX_SKETCH: ++ return gc2145_write(sensor, GC2145_REG_EFFECTS_SKECTH, 1); ++ case V4L2_COLORFX_ANTIQUE: ++ return gc2145_write(sensor, GC2145_REG_EFFECTS_COLOR, ++ GC2145_REG_EFFECTS_COLOR_ANTIQUE); ++ case V4L2_COLORFX_SEPIA: ++ return gc2145_write(sensor, GC2145_REG_EFFECTS_COLOR, ++ GC2145_REG_EFFECTS_COLOR_SEPIA); ++ case V4L2_COLORFX_AQUA: ++ return gc2145_write(sensor, GC2145_REG_EFFECTS_COLOR, ++ GC2145_REG_EFFECTS_COLOR_AQUA); ++ case V4L2_COLORFX_BW: ++ return gc2145_write(sensor, GC2145_REG_EFFECTS_COLOR, ++ GC2145_REG_EFFECTS_COLOR_BLACK_WHITE); ++ default: ++ return -EINVAL; ++ } ++} ++ ++static int gc2145_3a_lock(struct gc2145_dev *sensor, struct v4l2_ctrl *ctrl) ++{ ++ bool awb_lock = ctrl->val & V4L2_LOCK_WHITE_BALANCE; ++ bool ae_lock = ctrl->val & V4L2_LOCK_EXPOSURE; ++ int ret = 0; ++ ++ if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_EXPOSURE ++ && sensor->ctrls.auto_exposure->val == V4L2_EXPOSURE_AUTO) { ++ ret = gc2145_write(sensor, GC2145_REG_FREEZE_AUTO_EXPOSURE, ++ ae_lock); ++ if (ret) ++ return ret; ++ } ++ ++ if (((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_WHITE_BALANCE) ++ && sensor->ctrls.wb->val == V4L2_WHITE_BALANCE_AUTO) { ++ ret = gc2145_write(sensor, GC2145_REG_WB_MISC_SETTINGS, ++ awb_lock ? ++ GC2145_REG_WB_MISC_SETTINGS_FREEZE_ALGO : 0); ++ if (ret) ++ return ret; ++ } ++ ++ return ret; ++} ++ ++static int gc2145_set_white_balance(struct gc2145_dev *sensor) ++{ ++ struct gc2145_ctrls *ctrls = &sensor->ctrls; ++ bool manual_wb = ctrls->wb->val == V4L2_WHITE_BALANCE_MANUAL; ++ int ret = 0, i; ++ s32 val; ++ ++ if (ctrls->wb->is_new) { ++ for (i = 0; i < ARRAY_SIZE(gc2145_wb_opts); i++) { ++ if (gc2145_wb_opts[i][0] != ctrls->wb->val) ++ continue; ++ ++ ret = gc2145_write(sensor, GC2145_REG_WB_MODE, ++ gc2145_wb_opts[i][1]); ++ if (ret) ++ return ret; ++ goto next; ++ } ++ ++ return -EINVAL; ++ } ++ ++next: ++ if (ctrls->wb->is_new || ctrls->blue_balance->is_new) { ++ val = manual_wb ? ctrls->blue_balance->val : 1000; ++ ret = gc2145_write16(sensor, GC2145_REG_WB_HUE_B_BIAS, ++ gc2145_mili_to_fp16(val)); ++ if (ret) ++ return ret; ++ } ++ ++ if (ctrls->wb->is_new || ctrls->red_balance->is_new) { ++ val = manual_wb ? ctrls->red_balance->val : 1000; ++ ret = gc2145_write16(sensor, GC2145_REG_WB_HUE_R_BIAS, ++ gc2145_mili_to_fp16(val)); ++ } ++ ++ return ret; ++} ++ ++#endif ++ ++/* Exposure */ ++ ++static int gc2145_get_exposure(struct gc2145_dev *sensor) ++{ ++ struct gc2145_ctrls *ctrls = &sensor->ctrls; ++ u8 again, dgain; ++ u16 exp; ++ int ret; ++ ++ ret = gc2145_read(sensor, 0xb1, &again); ++ if (ret) ++ return ret; ++ ++ ret = gc2145_read(sensor, 0xb2, &dgain); ++ if (ret) ++ return ret; ++ ++ ret = gc2145_read16(sensor, 0x03, &exp); ++ if (ret) ++ return ret; ++ ++ ctrls->exposure->val = exp; ++ ctrls->d_gain->val = dgain; ++ ctrls->a_gain->val = again; ++ ++ return 0; ++} ++ ++#define AE_BIAS_MENU_DEFAULT_VALUE_INDEX 4 ++static const s64 ae_bias_menu_values[] = { ++ -4000, -3000, -2000, -1000, 0, 1000, 2000, 3000, 4000 ++}; ++ ++static const s8 ae_bias_menu_reg_values[] = { ++ 0x55, 0x60, 0x65, 0x70, 0x7b, 0x85, 0x90, 0x95, 0xa0 ++}; ++ ++static int gc2145_set_exposure(struct gc2145_dev *sensor) ++{ ++ struct gc2145_ctrls *ctrls = &sensor->ctrls; ++ bool is_auto = (ctrls->auto_exposure->val != V4L2_EXPOSURE_MANUAL); ++ ++ gc2145_tx_start(sensor); ++ ++ if (ctrls->auto_exposure->is_new) { ++ gc2145_tx_write8(sensor, 0xb6, is_auto ? 1 : 0); ++ ++ //XXX: remove? ++ //if (ctrls->auto_exposure->cur.val != ctrls->auto_exposure->val && ++ //!is_auto) { ++ /* ++ * Hack: At this point, there are current volatile ++ * values in val, but control framework will not ++ * update the cur values for our autocluster, as it ++ * should. I couldn't find the reason. This fixes ++ * it for our driver. Remove this after the kernel ++ * is fixed. ++ */ ++ //ctrls->exposure->cur.val = ctrls->exposure->val; ++ //ctrls->d_gain->cur.val = ctrls->d_gain->val; ++ //ctrls->a_gain->cur.val = ctrls->a_gain->val; ++ //} ++ } ++ ++ if (!is_auto && ctrls->exposure->is_new) ++ gc2145_tx_write16(sensor, 0x03, ctrls->exposure->val); ++ ++ if (!is_auto && ctrls->d_gain->is_new) ++ gc2145_tx_write8(sensor, 0xb2, ctrls->d_gain->val); ++ ++ if (!is_auto && ctrls->a_gain->is_new) ++ gc2145_tx_write8(sensor, 0xb1, ctrls->a_gain->val); ++ ++ return gc2145_tx_commit(sensor);; ++} ++ ++/* Test patterns */ ++ ++enum { ++ GC2145_TEST_PATTERN_DISABLED, ++ GC2145_TEST_PATTERN_VGA_COLOR_BARS, ++ GC2145_TEST_PATTERN_UXGA_COLOR_BARS, ++ GC2145_TEST_PATTERN_SKIN_MAP, ++ GC2145_TEST_PATTERN_SOLID_COLOR, ++}; ++ ++static const char * const test_pattern_menu[] = { ++ "Disabled", ++ "VGA color bars", ++ "UXGA color bars", ++ "Skin map", ++ "Solid black color", ++ "Solid light gray color", ++ "Solid gray color", ++ "Solid dark gray color", ++ "Solid white color", ++ "Solid red color", ++ "Solid green color", ++ "Solid blue color", ++ "Solid yellow color", ++ "Solid cyan color", ++ "Solid magenta color", ++}; ++ ++static int gc2145_g_volatile_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd = ctrl_to_sd(ctrl); ++ struct gc2145_dev *sensor = to_gc2145_dev(sd); ++ int ret; ++ ++ /* v4l2_ctrl_lock() locks our own mutex */ ++ ++ if (!sensor->powered) ++ return -EIO; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_EXPOSURE_AUTO: ++ ret = gc2145_get_exposure(sensor); ++ if (ret) ++ return ret; ++ break; ++ default: ++ dev_err(&sensor->i2c_client->dev, "getting unknown control %d\n", ctrl->id); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int gc2145_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd = ctrl_to_sd(ctrl); ++ struct gc2145_dev *sensor = to_gc2145_dev(sd); ++ struct gc2145_ctrls *ctrls = &sensor->ctrls; ++ s32 val = ctrl->val; ++ unsigned int i; ++ int ret; ++ u8 test1, test2; ++ ++ /* v4l2_ctrl_lock() locks our own mutex */ ++ ++ /* ++ * If the device is not powered up by the host driver do ++ * not apply any controls to H/W at this time. Instead ++ * the controls will be restored right after power-up. ++ */ ++ if (!sensor->powered) ++ return 0; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_EXPOSURE_AUTO: ++ return gc2145_set_exposure(sensor); ++ ++ case V4L2_CID_AUTO_EXPOSURE_BIAS: ++ if (val < 0 || val >= ARRAY_SIZE(ae_bias_menu_reg_values)) { ++ dev_err(&sensor->i2c_client->dev, "ae bias out of range\n"); ++ return -EINVAL; ++ } ++ ++ return gc2145_write(sensor, 0x113, ++ (u8)ae_bias_menu_reg_values[val]); ++ ++ case V4L2_CID_VFLIP: ++ return gc2145_update_bits(sensor, 0x17, BIT(1), val ? BIT(1) : 0); ++ ++ case V4L2_CID_HFLIP: ++ return gc2145_update_bits(sensor, 0x17, BIT(0), val ? BIT(0) : 0); ++ ++ case V4L2_CID_TEST_PATTERN: ++ for (i = 0; i < ARRAY_SIZE(ctrls->test_data); i++) ++ v4l2_ctrl_activate(ctrls->test_data[i], ++ val == 6); /* solid color */ ++ ++ test1 = 0; ++ test2 = 0x01; ++ ++ if (val == GC2145_TEST_PATTERN_VGA_COLOR_BARS) ++ test1 = 0x04; ++ else if (val == GC2145_TEST_PATTERN_UXGA_COLOR_BARS) ++ test1 = 0x44; ++ else if (val == GC2145_TEST_PATTERN_SKIN_MAP) ++ test1 = 0x10; ++ else if (val >= GC2145_TEST_PATTERN_SOLID_COLOR) { ++ test1 = 0x04; ++ test2 = ((val - GC2145_TEST_PATTERN_SOLID_COLOR) << 4) | 0x8; ++ } else if (val != GC2145_TEST_PATTERN_DISABLED) { ++ dev_err(&sensor->i2c_client->dev, "test pattern out of range\n"); ++ return -EINVAL; ++ } ++ ++ ret = gc2145_write(sensor, 0x8c, test1); ++ if (ret) ++ return ret; ++ ++ return gc2145_write(sensor, 0x8d, test2); ++ ++#if 0 ++ case V4L2_CID_EXPOSURE_METERING: ++ if (val == V4L2_EXPOSURE_METERING_AVERAGE) ++ reg = GC2145_REG_EXPOSURE_METERING_FLAT; ++ else if (val == V4L2_EXPOSURE_METERING_CENTER_WEIGHTED) ++ reg = GC2145_REG_EXPOSURE_METERING_CENTERED; ++ else ++ return -EINVAL; ++ ++ return gc2145_write(sensor, GC2145_REG_EXPOSURE_METERING, reg); ++ ++ case V4L2_CID_CONTRAST: ++ return gc2145_write(sensor, GC2145_REG_CONTRAST, val); ++ ++ case V4L2_CID_SATURATION: ++ return gc2145_write(sensor, GC2145_REG_COLOR_SATURATION, val); ++ ++ case V4L2_CID_BRIGHTNESS: ++ return gc2145_write(sensor, GC2145_REG_BRIGHTNESS, val); ++ ++ case V4L2_CID_POWER_LINE_FREQUENCY: ++ return gc2145_set_power_line_frequency(sensor, val); ++ ++ case V4L2_CID_GAMMA: ++ return gc2145_write(sensor, GC2145_REG_P0_GAMMA_GAIN, val); ++ ++ case V4L2_CID_COLORFX: ++ return gc2145_set_colorfx(sensor, val); ++ ++ case V4L2_CID_3A_LOCK: ++ return gc2145_3a_lock(sensor, ctrl); ++ ++ case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: ++ return gc2145_set_white_balance(sensor); ++ ++ case V4L2_CID_TEST_PATTERN_RED: ++ return gc2145_write16(sensor, GC2145_REG_TESTDATA_RED, val); ++ ++ case V4L2_CID_TEST_PATTERN_GREENR: ++ return gc2145_write16(sensor, GC2145_REG_TESTDATA_GREEN_R, val); ++ ++ case V4L2_CID_TEST_PATTERN_BLUE: ++ return gc2145_write16(sensor, GC2145_REG_TESTDATA_BLUE, val); ++ ++ case V4L2_CID_TEST_PATTERN_GREENB: ++ return gc2145_write16(sensor, GC2145_REG_TESTDATA_GREEN_B, val); ++ ++#endif ++ default: ++ dev_err(&sensor->i2c_client->dev, "setting unknown control %d\n", ctrl->id); ++ return -EINVAL; ++ } ++} ++ ++static const struct v4l2_ctrl_ops gc2145_ctrl_ops = { ++ .g_volatile_ctrl = gc2145_g_volatile_ctrl, ++ .s_ctrl = gc2145_s_ctrl, ++}; ++ ++static int gc2145_init_controls(struct gc2145_dev *sensor) ++{ ++ const struct v4l2_ctrl_ops *ops = &gc2145_ctrl_ops; ++ struct gc2145_ctrls *ctrls = &sensor->ctrls; ++ struct v4l2_ctrl_handler *hdl = &ctrls->handler; ++ //u8 wb_max = 0; ++ //u64 wb_mask = 0; ++ //unsigned int i; ++ int ret; ++ ++ v4l2_ctrl_handler_init(hdl, 32); ++ ++ /* we can use our own mutex for the ctrl lock */ ++ hdl->lock = &sensor->lock; ++ ++ /* Exposure controls */ ++ ctrls->auto_exposure = v4l2_ctrl_new_std_menu(hdl, ops, ++ V4L2_CID_EXPOSURE_AUTO, ++ V4L2_EXPOSURE_MANUAL, 0, ++ V4L2_EXPOSURE_AUTO); ++ ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, ++ 1, 0x1fff, 1, 0x80); ++ ctrls->a_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN, ++ 0, 255, 1, 0x20); ++ ctrls->d_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_DIGITAL_GAIN, ++ 0, 255, 1, 0x40); ++ ctrls->exposure_bias = ++ v4l2_ctrl_new_int_menu(hdl, ops, V4L2_CID_AUTO_EXPOSURE_BIAS, ++ ARRAY_SIZE(ae_bias_menu_values) - 1, ++ AE_BIAS_MENU_DEFAULT_VALUE_INDEX, ++ ae_bias_menu_values); ++ ++ /* V/H flips */ ++ ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, ++ V4L2_CID_HFLIP, 0, 1, 1, 0); ++ ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, ++ V4L2_CID_VFLIP, 0, 1, 1, 0); ++ ++ ++ /* Test patterns */ ++ ctrls->test_pattern = ++ v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN, ++ ARRAY_SIZE(test_pattern_menu) - 1, ++ 0, 0, test_pattern_menu); ++#if 0 ++ ++ ctrls->metering = ++ v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_EXPOSURE_METERING, ++ V4L2_EXPOSURE_METERING_CENTER_WEIGHTED, ++ 0, V4L2_EXPOSURE_METERING_AVERAGE); ++ ++ for (i = 0; i < ARRAY_SIZE(gc2145_wb_opts); i++) { ++ if (wb_max < gc2145_wb_opts[i][0]) ++ wb_max = gc2145_wb_opts[i][0]; ++ wb_mask |= BIT(gc2145_wb_opts[i][0]); ++ } ++ ++ ctrls->wb = v4l2_ctrl_new_std_menu(hdl, ops, ++ V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, ++ wb_max, ~wb_mask, V4L2_WHITE_BALANCE_AUTO); ++ ++ ctrls->blue_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BLUE_BALANCE, ++ 0, 4000, 1, 1000); ++ ctrls->red_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_RED_BALANCE, ++ 0, 4000, 1, 1000); ++ ++ ctrls->gamma = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAMMA, ++ 0, 31, 1, 20); ++ ++ ctrls->colorfx = ++ v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_COLORFX, 15, ++ ~(BIT(V4L2_COLORFX_NONE) | ++ BIT(V4L2_COLORFX_NEGATIVE) | ++ BIT(V4L2_COLORFX_SOLARIZATION) | ++ BIT(V4L2_COLORFX_SKETCH) | ++ BIT(V4L2_COLORFX_SEPIA) | ++ BIT(V4L2_COLORFX_ANTIQUE) | ++ BIT(V4L2_COLORFX_AQUA) | ++ BIT(V4L2_COLORFX_BW)), ++ V4L2_COLORFX_NONE); ++ ++ ctrls->pl_freq = ++ v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_POWER_LINE_FREQUENCY, ++ V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0, ++ V4L2_CID_POWER_LINE_FREQUENCY_50HZ); ++ ++ ctrls->brightness = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, ++ 0, 200, 1, 90); ++ ctrls->saturation = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION, ++ 0, 200, 1, 110); ++ ctrls->contrast = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, ++ 0, 200, 1, 108); ++ ++ ctrls->aaa_lock = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_3A_LOCK, ++ 0, 0x7, 0, 0); ++ ++ for (i = 0; i < ARRAY_SIZE(ctrls->test_data); i++) ++ ctrls->test_data[i] = ++ v4l2_ctrl_new_std(hdl, ops, ++ V4L2_CID_TEST_PATTERN_RED + i, ++ 0, 1023, 1, 0); ++ ++ ctrls->af_status->flags |= V4L2_CTRL_FLAG_VOLATILE | ++ V4L2_CTRL_FLAG_READ_ONLY; ++ ++ v4l2_ctrl_auto_cluster(3, &ctrls->wb, V4L2_WHITE_BALANCE_MANUAL, false); ++#endif ++ ++ v4l2_ctrl_auto_cluster(4, &ctrls->auto_exposure, V4L2_EXPOSURE_MANUAL, ++ true); ++ ++ if (hdl->error) { ++ ret = hdl->error; ++ goto free_ctrls; ++ } ++ ++ sensor->sd.ctrl_handler = hdl; ++ return 0; ++ ++free_ctrls: ++ v4l2_ctrl_handler_free(hdl); ++ return ret; ++} ++ ++/* }}} */ ++/* {{{ Video ops */ ++ ++/* ++ * Clock tree ++ * ---------- ++ * ++ * MCLK pin ++ * | ++ * DIV2 (optional) - Divide input MCLK by 2 when 0xf7[1] == 1 ++ * | ++ * /- PLL mux -\ - PLL selected by 0xf8[7], otherwise fixed 32x mult ++ * | | ++ * PLL | - PLL multiplies by 0xf8[5:0]+1 * 4 ++ * | Fixed 32/48x - Multiplies 32x when 0xf7[2] == 1 otherwise 48x ++ * \_________/ ++ * | ++ * DOUBLE (div by 4 or 8) based on 0xf7[3] ++ * | ++ * /-------\ ++ * | | ++ * pclk_div sclk_div ++ * | | ++ * 2pclk sclk ++ */ ++__maybe_unused ++static int gc2145_get_2pclk(struct gc2145_dev *sensor, unsigned long* pclk) ++{ ++ u8 pll_mode1, pll_mode2, clk_div_mode; ++ bool mclk_div2_en; // 0xf7[1] ++ bool pll_en; // 0xf8[7] ++ bool double_clk; // 0xf7[3] ++ bool fixed_32x; // 0xf7[2] ++ unsigned long pll_mult; // 0xf8[5:0] + 1 ++ unsigned long sclk_div; // 1 << (0xf7[5:4] + 1) ++ unsigned long pclk_div; // 0xfa[7:4] + 1 ++ unsigned long int_clk; ++ unsigned long mclk; ++ int ret; ++ ++ ret = gc2145_read(sensor, 0xf7, &pll_mode1); ++ if (ret) ++ return ret; ++ ++ ret = gc2145_read(sensor, 0xf8, &pll_mode2); ++ if (ret) ++ return ret; ++ ++ ret = gc2145_read(sensor, 0xfa, &clk_div_mode); ++ if (ret) ++ return ret; ++ ++ mclk = clk_get_rate(sensor->xclk); ++ if (mclk == 0) ++ return -EINVAL; ++ ++ mclk_div2_en = pll_mode1 & BIT(1); ++ pll_en = pll_mode2 & BIT(7); ++ double_clk = pll_mode1 & BIT(3); ++ fixed_32x = pll_mode1 & BIT(2); ++ pll_mult = (pll_mode2 & 0x3f) + 1; ++ pclk_div = (clk_div_mode >> 4) + 1; ++ sclk_div = 1 << (((pll_mode1 >> 4) & 0x3) + 1); ++ ++ int_clk = mclk / (mclk_div2_en ? 2 : 1); ++ ++ if (pll_en) ++ int_clk *= pll_mult * 4; ++ else ++ int_clk *= fixed_32x ? 32 : 48; ++ ++ int_clk /= double_clk ? 4 : 8; ++ ++ if (pclk) ++ *pclk = int_clk / pclk_div; ++ ++ return 0; ++} ++ ++static int gc2145_set_2pclk(struct gc2145_dev *sensor, ++ unsigned long *freq, bool apply) ++{ ++ unsigned long pll_mult, pll_mult_max, /*sclk_div,*/ pclk_div, pclk2,/* sclk,*/ ++ mclk; ++ unsigned long pll_mult_best = 0, pclk_div_best = 0, diff_best = ULONG_MAX, diff, ++ pclk2_best = 0; ++ int mclk_div2_en; //, double_clk; ++ int mclk_div2_en_best = 0; //, double_clk_best; ++ ++ mclk = clk_get_rate(sensor->xclk); ++ if (mclk == 0) ++ return -EINVAL; ++ ++ for (mclk_div2_en = 0; mclk_div2_en <= 1; mclk_div2_en++) { ++ pll_mult_max = 768000000 / 4 / (mclk / (mclk_div2_en ? 2 : 1)); ++ if (pll_mult_max > 32) ++ pll_mult_max = 32; ++ ++ for (pll_mult = 2; pll_mult <= pll_mult_max; pll_mult++) { ++ for (pclk_div = 1; pclk_div <= 8; pclk_div++) { ++ pclk2 = mclk / (mclk_div2_en ? 2 : 1) * pll_mult / pclk_div; ++ ++ if (pclk2 > *freq) ++ continue; ++ ++ diff = *freq - pclk2; ++ ++ if (diff < diff_best) { ++ diff_best = diff; ++ pclk2_best = pclk2; ++ ++ pll_mult_best = pll_mult; ++ pclk_div_best = pclk_div; ++ mclk_div2_en_best = mclk_div2_en; ++ } ++ ++ if (diff == 0) ++ goto found; ++ } ++ } ++ } ++ ++ if (diff_best == ULONG_MAX) ++ return -1; ++ ++found: ++ *freq = pclk2_best; ++ if (!apply) ++ return 0; ++ ++ gc2145_tx_start(sensor); ++ ++ gc2145_tx_write8(sensor, 0xf7, ++ ((pclk_div_best - 1)) << 4 | ++ (mclk_div2_en_best << 1) | BIT(0) /* pll_en */); ++ gc2145_tx_write8(sensor, 0xf8, BIT(7) | (pll_mult_best - 1)); ++ gc2145_tx_write8(sensor, 0xfa, ++ (pclk_div_best - 1) << 4 | ++ (((pclk_div_best - 1) / 2) & 0xf)); ++ ++ return gc2145_tx_commit(sensor); ++} ++ ++static int gc2145_setup_awb(struct gc2145_dev *sensor, ++ u16 x1, u16 y1, u16 x2, u16 y2) ++{ ++ int ratio = 8; //XXX: manual for gc2035 FAE says 4 ++ ++ gc2145_tx_start(sensor); ++ ++ // disable awb ++ gc2145_tx_update_bits(sensor, 0x82, BIT(1), 0); ++ ++ // reset white balance RGB gains ++ gc2145_tx_write8(sensor, 0xb3, 0x40); ++ gc2145_tx_write8(sensor, 0xb4, 0x40); ++ gc2145_tx_write8(sensor, 0xb5, 0x40); ++ ++ // awb window ++ gc2145_tx_write8(sensor, 0x1ec, x1 / ratio); ++ gc2145_tx_write8(sensor, 0x1ed, y1 / ratio); ++ gc2145_tx_write8(sensor, 0x1ee, x2 / ratio); ++ gc2145_tx_write8(sensor, 0x1ef, y2 / ratio); ++ ++ // eanble awb ++ gc2145_tx_update_bits(sensor, 0x82, BIT(1), BIT(1)); ++ ++ //1051 { 0xfe, 0x01 }, ++ //1052 { 0x74, 0x01 }, ++ ++ return gc2145_tx_commit(sensor); ++} ++ ++static int gc2145_setup_aec(struct gc2145_dev *sensor, ++ u16 x1, u16 y1, u16 x2, u16 y2, ++ u16 cx1, u16 cy1, u16 cx2, u16 cy2) ++{ ++ u16 x_ratio = 8; ++ ++ //XXX: gc2035 has x ratio 16 ++ //XXX: gc2035 doesn't have low light mode ++ gc2145_tx_start(sensor); ++ ++ // disable AEC ++ gc2145_tx_write8(sensor, 0xb6, 0); ++ ++ // set reasonable initial exposure and gains ++ gc2145_tx_write16(sensor, 0x03, 1200); ++ gc2145_tx_write8(sensor, 0xb1, 0x20); ++ gc2145_tx_write8(sensor, 0xb2, 0xe0); ++ ++ // setup measure window ++ gc2145_tx_write8(sensor, 0x101, x1 / x_ratio); ++ gc2145_tx_write8(sensor, 0x102, x2 / x_ratio); ++ gc2145_tx_write8(sensor, 0x103, y1 / 8); ++ gc2145_tx_write8(sensor, 0x104, y2 / 8); ++ ++ // setup center ++ gc2145_tx_write8(sensor, 0x105, cx1 / x_ratio); ++ gc2145_tx_write8(sensor, 0x106, cx2 / x_ratio); ++ gc2145_tx_write8(sensor, 0x107, cy1 / 8); ++ gc2145_tx_write8(sensor, 0x108, cy2 / 8); ++ ++ // increase maximum exposure level to 4 ++ //gc2145_tx_write8(sensor, 0x13c, 0x60); ++ // setup AEC mode: measure point, adjust_max_gain, skip_mode = 2 ++ //gc2145_tx_write8(sensor, 0x10a, 0xc2); ++ ++ // AEC_ASDE_select_luma_value AEC_low_light_exp_THD_max: ++ //gc2145_tx_write8(sensor, 0x121, 0x15); ++ ++ // enable AEC again ++ gc2145_tx_write8(sensor, 0xb6, 1); ++ ++ return gc2145_tx_commit(sensor); ++} ++ ++struct gc2145_sensor_params { ++ unsigned int enable_scaler; ++ unsigned int col_scaler_only; ++ unsigned int row_skip; ++ unsigned int col_skip; ++ unsigned long sh_delay; ++ unsigned long hb; ++ unsigned long vb; ++ unsigned long st; ++ unsigned long et; ++ unsigned long win_width; ++ unsigned long win_height; ++ unsigned long width; ++ unsigned long height; ++}; ++ ++static void gc2145_sensor_params_init(struct gc2145_sensor_params* p, int width, int height) ++{ ++ p->win_height = height + 32; ++ p->win_width = (width + 16); ++ p->width = width; ++ p->height = height; ++ p->st = 2; ++ p->et = 2; ++ p->vb = 8; ++ p->hb = 0x1f0; ++ p->sh_delay = 30; ++} ++ ++// unit is PCLK periods ++static unsigned long ++gc2145_sensor_params_get_row_period(struct gc2145_sensor_params* p) ++{ ++ return 2 * (p->win_width / 2 / (p->col_skip + 1) + p->sh_delay + p->hb + 4); ++} ++ ++static unsigned long ++gc2145_sensor_params_get_frame_period(struct gc2145_sensor_params* p) ++{ ++ unsigned long rt = gc2145_sensor_params_get_row_period(p); ++ ++ return rt * (p->vb + p->win_height) / (p->row_skip + 1); ++} ++ ++static void ++gc2145_sensor_params_fit_hb_to_power_line_period(struct gc2145_sensor_params* p, ++ unsigned long power_line_freq, ++ unsigned long pclk) ++{ ++ unsigned long rt, power_line_ratio; ++ ++ for (p->hb = 0x1f0; p->hb < 2047; p->hb++) { ++ rt = gc2145_sensor_params_get_row_period(p); ++ ++ // power_line_ratio is row_freq / power_line_freq * 1000 ++ power_line_ratio = pclk / power_line_freq * 1000 / rt; ++ ++ // if we're close enough, stop the search ++ if (power_line_ratio % 1000 < 50) ++ break; ++ } ++ ++ // finding the optimal Hb is not critical ++ if (p->hb == 2047) ++ p->hb = 0x1f0; ++} ++ ++static void ++gc2145_sensor_params_fit_vb_to_frame_period(struct gc2145_sensor_params* p, ++ unsigned long frame_period) ++{ ++ unsigned long rt, fp; ++ ++ p->vb = 8; ++ rt = gc2145_sensor_params_get_row_period(p); ++ fp = gc2145_sensor_params_get_frame_period(p); ++ ++ if (frame_period > fp) ++ p->vb = frame_period * (p->row_skip + 1) / rt - p->win_height; ++ ++ if (p->vb > 4095) ++ p->vb = 4095; ++} ++ ++static int gc2145_sensor_params_apply(struct gc2145_dev *sensor, ++ struct gc2145_sensor_params* p) ++{ ++ u32 off_x = (GC2145_SENSOR_WIDTH_MAX - p->width) / 2; ++ u32 off_y = (GC2145_SENSOR_HEIGHT_MAX - p->height) / 2; ++ ++ gc2145_tx_start(sensor); ++ ++ gc2145_tx_write8(sensor, 0xfd, (p->enable_scaler ? BIT(0) : 0) ++ | (p->col_scaler_only ? BIT(1) : 0)); ++ ++ gc2145_tx_write8(sensor, 0x18, 0x0a ++ | (p->col_skip ? BIT(7) : 0) ++ | (p->row_skip ? BIT(6) : 0)); ++ ++ gc2145_tx_write16(sensor, 0x09, off_y); ++ gc2145_tx_write16(sensor, 0x0b, off_x); ++ gc2145_tx_write16(sensor, 0x0d, p->win_height); ++ gc2145_tx_write16(sensor, 0x0f, p->win_width); ++ gc2145_tx_write16(sensor, 0x05, p->hb); ++ gc2145_tx_write16(sensor, 0x07, p->vb); ++ gc2145_tx_write16(sensor, 0x11, p->sh_delay); ++ ++ gc2145_tx_write8(sensor, 0x13, p->st); ++ gc2145_tx_write8(sensor, 0x14, p->et); ++ ++ return gc2145_tx_commit(sensor); ++} ++ ++static int gc2145_setup_mode(struct gc2145_dev *sensor) ++{ ++ int scaling_desired, ret, pad, i; ++ struct gc2145_sensor_params params = {0}; ++ unsigned long pclk2, frame_period; ++ unsigned long power_line_freq = 50; ++ unsigned long width = sensor->fmt.width; ++ unsigned long height = sensor->fmt.height; ++ unsigned long framerate = sensor->frame_interval.denominator; ++ const struct gc2145_pixfmt *pix_fmt; ++ unsigned long rt, ft, ft_rt; ++ ++ pix_fmt = gc2145_find_format(sensor->fmt.code); ++ if (!pix_fmt) { ++ dev_err(&sensor->i2c_client->dev, ++ "pixel format not supported %u\n", sensor->fmt.code); ++ return -EINVAL; ++ } ++ ++ /* ++ * Equations for calculating framerate are: ++ * ++ * ww = width + 16 ++ * wh = height + 32 ++ * Rt = (ww / 2 / (col_skip + 1) + sh_delay + Hb + 4) ++ * Ft = Rt * (Vb + wh) / (row_skip + 1) ++ * framerate = 2pclk / 4 / Ft ++ * ++ * Based on these equations: ++ * ++ * 1) First we need to determine what 2PCLK frequency to use. The 2PCLK ++ * frequency is not arbitrarily precise, so we need to calculate the ++ * actual frequency used, after setting our target frequency. ++ * ++ * We use a simple heuristic: ++ * ++ * If pixel_count * 2 * framerate * 1.15 is > 40MHz, we use 60MHz, ++ * otherwise we use 40MHz. ++ * ++ * 2) We want to determine lowest Hb that we can use to extend row ++ * period so that row time takes an integer fraction of the power ++ * line frequency period. Minimum Hb is 0x1f0. ++ * ++ * 3) If the requested resolution is less than half the sensor's size, ++ * we'll use scaling, or row skipping + column scaling, or row and ++ * column skiping, depending on what allows us to achieve the ++ * requested framerate. ++ * ++ * 4) We use the selected Hb to calculate Vb value that will give ++ * us the desired framerate, given the scaling/skipping option ++ * selected in 3). ++ */ ++ ++ scaling_desired = width <= GC2145_SENSOR_WIDTH_MAX / 2 ++ && height <= GC2145_SENSOR_HEIGHT_MAX / 2; ++ ++ pclk2 = 60000000; ++ ++ ret = gc2145_set_2pclk(sensor, &pclk2, false); ++ if (ret < 0) ++ return ret; ++ ++ gc2145_sensor_params_init(¶ms, width, height); ++ ++ // if the resolution is < half the sensor size, enable the scaler ++ // to cover more area of the chip ++ if (scaling_desired) { ++ params.enable_scaler = 1; ++ pclk2 *= 2; ++ gc2145_sensor_params_init(¶ms, width * 2, height * 2); ++ } ++ ++ // we need to call this each time pclk or power_line_freq is changed ++ gc2145_sensor_params_fit_hb_to_power_line_period(¶ms, ++ power_line_freq, ++ pclk2 / 2); ++ ++ frame_period = gc2145_sensor_params_get_frame_period(¶ms); ++ if (framerate <= pclk2 / 2 / frame_period) ++ goto apply; ++ ++ if (scaling_desired) { ++ // try using just the column scaler + row skip ++ params.col_scaler_only = 1; ++ params.row_skip = 1; ++ gc2145_sensor_params_fit_hb_to_power_line_period(¶ms, ++ power_line_freq, ++ pclk2 / 2); ++ ++ frame_period = gc2145_sensor_params_get_frame_period(¶ms); ++ if (framerate <= pclk2 / 2 / frame_period) ++ goto apply; ++ ++ ++ /* ++ // try disabling the scaler and just use skipping ++ params.enable_scaler = 0; ++ pclk2 /= 2; ++ params.col_scaler_only = 0; ++ params.col_skip = 1; ++ gc2145_sensor_params_fit_hb_to_power_line_period(¶ms, power_line_freq, pclk2 / 2); ++ ++ frame_period = gc2145_sensor_params_get_frame_period(¶ms); ++ ++ if (framerate <= pclk2 / 2 / frame_period) ++ goto apply; ++ */ ++ } ++ ++apply: ++ // adjust vb to fit the target framerate ++ gc2145_sensor_params_fit_vb_to_frame_period(¶ms, ++ pclk2 / 2 / framerate); ++ ++ gc2145_sensor_params_apply(sensor, ¶ms); ++ ++ ret = gc2145_set_2pclk(sensor, &pclk2, true); ++ if (ret < 0) ++ return ret; ++ ++ pad = (width > 256 && height > 256) ? 32 : 16; ++ ++ ret = gc2145_setup_awb(sensor, pad, pad, width - pad * 2, height - pad * 2); ++ if (ret) ++ return ret; ++ ++ ret = gc2145_setup_aec(sensor, ++ pad, pad, width - pad * 2, height - pad * 2, ++ 2 * pad, 2 * pad, width - pad * 4, height - pad * 4); ++ if (ret) ++ return ret; ++ ++ gc2145_tx_start(sensor); ++ ++ // anti-flicker step ++ //gc2145_tx_write16(sensor, 0x125, 360); //XXX: get this from the calculator (hb related) ++ ++ //XXX: calculate auto exposure settings, there are 4 slots that the HW ++ //uses and exposure settings are set in row_time units ++ ++ rt = gc2145_sensor_params_get_row_period(¶ms); ++ ft = gc2145_sensor_params_get_frame_period(¶ms); ++ ft_rt = ft / rt / 4; ++ ++ for (i = 0; i < 7; i++) { ++ // exposure settings for exposure levels ++ gc2145_tx_write16(sensor, 0x127 + 2 * i, ft_rt * (i + 1)); ++ // max dg gains ++ gc2145_tx_write8(sensor, 0x135 + i, 0x50); ++ } ++ ++ // max analog gain ++ gc2145_tx_write8(sensor, 0x11f, 0x50); ++ // max digital gain ++ gc2145_tx_write8(sensor, 0x120, 0xe0); ++ ++ gc2145_tx_write8(sensor, GC2145_P0_ISP_OUT_FORMAT, pix_fmt->fmt_setup); ++ ++ // set gamma curve ++ gc2145_tx_update_bits(sensor, 0x80, BIT(6), BIT(6)); ++ ++ // disable denoising ++ gc2145_tx_update_bits(sensor, 0x80, BIT(2), 0); ++ ++ // drive strength ++ gc2145_tx_write8(sensor, 0x24, ++ (pclk2 / (params.enable_scaler + 1)) > 40000000 ? ++ 0xff : 0x55); ++ ++ return gc2145_tx_commit(sensor); ++} ++ ++static int gc2145_set_stream(struct gc2145_dev *sensor, int enable) ++{ ++ gc2145_tx_start(sensor); ++ ++ gc2145_tx_write8(sensor, GC2145_REG_PAD_IO, enable ? 0x0f : 0); ++ ++ //XXX: maybe disable cam module function blocks that are not used ++ //and downclock the PLL/disable it when not streaming? ++ ++ return gc2145_tx_commit(sensor); ++} ++ ++static int gc2145_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ struct gc2145_dev *sensor = to_gc2145_dev(sd); ++ int ret = 0; ++ ++ mutex_lock(&sensor->lock); ++ ++ if (sensor->streaming == !enable) { ++ if (enable && sensor->pending_mode_change) { ++ ret = gc2145_setup_mode(sensor); ++ if (ret) ++ goto out; ++ } ++ ++ ret = gc2145_set_stream(sensor, enable); ++ if (ret) ++ goto out; ++ ++ sensor->streaming = !!enable; ++ } ++ ++out: ++ mutex_unlock(&sensor->lock); ++ return ret; ++} ++ ++/* }}} */ ++/* {{{ Pad ops */ ++ ++static int gc2145_get_frame_interval(struct v4l2_subdev *sd, ++ struct v4l2_subdev_state *state, ++ struct v4l2_subdev_frame_interval *fi) ++{ ++ struct gc2145_dev *sensor = to_gc2145_dev(sd); ++ ++ if (fi->pad != 0) ++ return -EINVAL; ++ ++ mutex_lock(&sensor->lock); ++ fi->interval = sensor->frame_interval; ++ mutex_unlock(&sensor->lock); ++ ++ return 0; ++} ++ ++static int gc2145_set_frame_interval(struct v4l2_subdev *sd, ++ struct v4l2_subdev_state *state, ++ struct v4l2_subdev_frame_interval *fi) ++{ ++ struct gc2145_dev *sensor = to_gc2145_dev(sd); ++ int ret = 0, fps; ++ struct gc2145_sensor_params params; ++ unsigned long pixel_rate; ++ ++ if (fi->pad != 0) ++ return -EINVAL; ++ ++ mutex_lock(&sensor->lock); ++ ++ /* user requested infinite frame rate */ ++ if (fi->interval.numerator == 0) ++ fps = 60; ++ else ++ fps = DIV_ROUND_CLOSEST(fi->interval.denominator, ++ fi->interval.numerator); ++ ++ fps = clamp(fps, 1, 60); ++ ++ sensor->frame_interval.numerator = 1; ++ sensor->frame_interval.denominator = fps; ++ fi->interval = sensor->frame_interval; ++ ++ params = gc2145_get_sensor_params(fps, sensor->fmt.width, sensor->fmt.height, &pixel_rate); ++ __v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate, ++ pixel_rate); ++ ++#if 0 ++ if (sensor->streaming) { ++ ret = gc2145_write16(sensor, GC2145_REG_DESIRED_FRAME_RATE_NUM, ++ fps); ++ if (ret) ++ goto err_unlock; ++ } ++err_unlock: ++#endif ++ ++ mutex_unlock(&sensor->lock); ++ return ret; ++} ++ ++static int gc2145_enum_mbus_code(struct v4l2_subdev *sd, ++ struct v4l2_subdev_state *sd_state, ++ struct v4l2_subdev_mbus_code_enum *code) ++{ ++ if (code->pad != 0 || code->index >= ARRAY_SIZE(gc2145_formats)) ++ return -EINVAL; ++ ++ code->code = gc2145_formats[code->index].code; ++ ++ return 0; ++} ++ ++static int gc2145_enum_frame_size(struct v4l2_subdev *sd, ++ struct v4l2_subdev_state *sd_state, ++ struct v4l2_subdev_frame_size_enum *fse) ++{ ++ if (fse->pad != 0 || fse->index > 0) ++ return -EINVAL; ++ ++ fse->min_width = GC2145_SENSOR_WIDTH_MIN; ++ fse->max_width = GC2145_SENSOR_WIDTH_MAX; ++ ++ fse->min_height = GC2145_SENSOR_HEIGHT_MIN; ++ fse->max_height = GC2145_SENSOR_HEIGHT_MAX; ++ ++ return 0; ++} ++ ++static int gc2145_enum_frame_interval( ++ struct v4l2_subdev *sd, ++ struct v4l2_subdev_state *sd_state, ++ struct v4l2_subdev_frame_interval_enum *fie) ++{ ++ if (fie->pad != 0 || fie->index > 0) ++ return -EINVAL; ++ ++ fie->interval.numerator = 1; ++ fie->interval.denominator = 30; ++ ++ return 0; ++} ++ ++static int gc2145_get_fmt(struct v4l2_subdev *sd, ++ struct v4l2_subdev_state *sd_state, ++ struct v4l2_subdev_format *format) ++{ ++ struct gc2145_dev *sensor = to_gc2145_dev(sd); ++ struct v4l2_mbus_framefmt *mf; ++ ++ if (format->pad != 0) ++ return -EINVAL; ++ ++ if (format->which == V4L2_SUBDEV_FORMAT_TRY) { ++ mf = v4l2_subdev_state_get_format(sd_state, format->pad); ++ format->format = *mf; ++ return 0; ++ } ++ ++ mutex_lock(&sensor->lock); ++ format->format = sensor->fmt; ++ mutex_unlock(&sensor->lock); ++ ++ return 0; ++} ++ ++static int gc2145_set_fmt(struct v4l2_subdev *sd, ++ struct v4l2_subdev_state *sd_state, ++ struct v4l2_subdev_format *format) ++{ ++ struct gc2145_dev *sensor = to_gc2145_dev(sd); ++ struct v4l2_mbus_framefmt *mf = &format->format; ++ const struct gc2145_pixfmt *pixfmt; ++ int ret = 0; ++ ++ if (format->pad != 0) ++ return -EINVAL; ++ ++ /* check if we support requested mbus fmt */ ++ pixfmt = gc2145_find_format(mf->code); ++ if (!pixfmt) ++ pixfmt = &gc2145_formats[0]; ++ ++ mf->code = pixfmt->code; ++ mf->colorspace = pixfmt->colorspace; ++ mf->xfer_func = V4L2_XFER_FUNC_DEFAULT; ++ mf->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; ++ mf->quantization = V4L2_QUANTIZATION_DEFAULT; ++ mf->field = V4L2_FIELD_NONE; ++ ++ mutex_lock(&sensor->lock); ++ ++ mf->width = clamp(mf->width, GC2145_SENSOR_WIDTH_MIN, ++ GC2145_SENSOR_WIDTH_MAX); ++ mf->height = clamp(mf->height, GC2145_SENSOR_HEIGHT_MIN, ++ GC2145_SENSOR_HEIGHT_MAX); ++ ++ if (format->which == V4L2_SUBDEV_FORMAT_TRY) { ++ struct v4l2_mbus_framefmt *try_mf; ++ ++ try_mf = v4l2_subdev_state_get_format(sd_state, format->pad); ++ *try_mf = *mf; ++ goto out; ++ } ++ ++ if (sensor->streaming) { ++ ret = -EBUSY; ++ goto out; ++ } ++ ++ sensor->fmt = *mf; ++ sensor->pending_mode_change = true; ++out: ++ mutex_unlock(&sensor->lock); ++ return ret; ++} ++ ++/* }}} */ ++/* {{{ Core Ops */ ++ ++static int gc2145_configure(struct gc2145_dev *sensor) ++{ ++ struct v4l2_mbus_config_parallel *bus = &sensor->ep.bus.parallel; ++ u8 sync_mode = 0; ++ u16 chip_id; ++ int ret; ++ ++ ret = gc2145_read16(sensor, GC2145_REG_CHIP_ID, &chip_id); ++ if (ret) ++ return ret; ++ ++ dev_info(&sensor->i2c_client->dev, "device id: 0x%04x\n", ++ (unsigned int)chip_id); ++ ++ if (chip_id != GC2145_REG_CHIP_ID_VALUE) { ++ dev_err(&sensor->i2c_client->dev, ++ "unsupported device id: 0x%04x\n", ++ (unsigned int)chip_id); ++ return -EINVAL; ++ } ++ ++ // setup parallel bus ++ ++ if (bus->flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) ++ sync_mode |= 0x01; ++ ++ if (bus->flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) ++ sync_mode |= 0x02; ++ ++ if (bus->flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) ++ sync_mode |= 0x04; ++ ++ gc2145_tx_start(sensor); ++ ++ // soft reset ++ gc2145_tx_write8(sensor, GC2145_REG_RESET, 0xf0); ++ ++ // enable analog/digital parts ++ gc2145_tx_write8(sensor, GC2145_REG_ANALOG_PWC, 0x06); ++ ++ // safe initial PLL setting ++ gc2145_tx_write8(sensor, GC2145_REG_PLL_MODE1, 0x1d); ++ gc2145_tx_write8(sensor, GC2145_REG_PLL_MODE2, 0x84); ++ gc2145_tx_write8(sensor, GC2145_REG_CLK_DIV_MODE, 0x00); ++ ++ gc2145_tx_write8(sensor, GC2145_REG_CM_MODE, 0xfe); ++ ++ // disable pads ++ gc2145_tx_write8(sensor, GC2145_REG_PAD_IO, 0); ++ ++ gc2145_tx_write8(sensor, 0x19, 0x0c); // set AD pipe number ++ gc2145_tx_write8(sensor, 0x20, 0x01); // AD clk mode ++ ++ // enable defect correction, etc. ++ gc2145_tx_write8(sensor, 0x80, 0x0b); ++ ++ gc2145_tx_write8(sensor, GC2145_P0_SYNC_MODE, sync_mode); ++ ++ ret = gc2145_tx_commit(sensor); ++ if (ret) ++ return ret; ++ ++ // load default register values from the firmware file ++ ret = gc2145_load_firmware(sensor, GC2145_FIRMWARE_PARAMETERS); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int gc2145_set_power(struct gc2145_dev *sensor, bool on) ++{ ++ int ret = 0; ++ ++ if (on) { ++ ret = regulator_bulk_enable(GC2145_NUM_SUPPLIES, ++ sensor->supplies); ++ if (ret) ++ return ret; ++ ++ ret = clk_set_rate(sensor->xclk, 24000000); ++ if (ret) ++ goto xclk_off; ++ ++ ret = clk_prepare_enable(sensor->xclk); ++ if (ret) ++ goto power_off; ++ ++ usleep_range(10000, 12000); ++ gpiod_direction_output(sensor->reset_gpio, 1); ++ usleep_range(10000, 12000); ++ gpiod_direction_output(sensor->enable_gpio, 1); ++ usleep_range(10000, 12000); ++ gpiod_direction_output(sensor->reset_gpio, 0); ++ usleep_range(40000, 50000); ++ ++ ret = gc2145_configure(sensor); ++ if (ret) ++ goto xclk_off; ++ ++ ret = gc2145_setup_mode(sensor); ++ if (ret) ++ goto xclk_off; ++ ++ return 0; ++ } ++ ++xclk_off: ++ clk_disable_unprepare(sensor->xclk); ++power_off: ++ gpiod_direction_input(sensor->reset_gpio); ++ gpiod_direction_input(sensor->enable_gpio); ++ regulator_bulk_disable(GC2145_NUM_SUPPLIES, sensor->supplies); ++ msleep(100); ++ return ret; ++} ++ ++static int gc2145_s_power(struct v4l2_subdev *sd, int on) ++{ ++ struct gc2145_dev *sensor = to_gc2145_dev(sd); ++ bool power_up, power_down; ++ int ret = 0; ++ ++ mutex_lock(&sensor->lock); ++ ++ power_up = on && !sensor->powered; ++ power_down = !on && sensor->powered; ++ ++ if (power_up || power_down) { ++ ret = gc2145_set_power(sensor, power_up); ++ if (!ret) ++ sensor->powered = on; ++ } ++ ++ mutex_unlock(&sensor->lock); ++ ++ if (!ret && power_up) { ++ /* restore controls */ ++ ret = v4l2_ctrl_handler_setup(&sensor->ctrls.handler); ++ if (ret) ++ gc2145_s_power(sd, 0); ++ } ++ ++ return ret; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int gc2145_g_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct gc2145_dev *sensor = to_gc2145_dev(sd); ++ int ret; ++ u8 val = 0; ++ ++ if (reg->reg > 0xffff) ++ return -EINVAL; ++ ++ reg->size = 1; ++ ++ mutex_lock(&sensor->lock); ++ ret = gc2145_read(sensor, reg->reg, &val); ++ mutex_unlock(&sensor->lock); ++ if (ret) ++ return -EIO; ++ ++ reg->val = val; ++ return 0; ++} ++ ++static int gc2145_s_register(struct v4l2_subdev *sd, ++ const struct v4l2_dbg_register *reg) ++{ ++ struct gc2145_dev *sensor = to_gc2145_dev(sd); ++ int ret; ++ ++ if (reg->reg > 0xffff || reg->val > 0xff) ++ return -EINVAL; ++ ++ mutex_lock(&sensor->lock); ++ ret = gc2145_write(sensor, reg->reg, reg->val); ++ mutex_unlock(&sensor->lock); ++ ++ return ret; ++} ++#endif ++ ++/* }}} */ ++ ++static const struct v4l2_subdev_core_ops gc2145_core_ops = { ++ .s_power = gc2145_s_power, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = gc2145_g_register, ++ .s_register = gc2145_s_register, ++#endif ++}; ++ ++static const struct v4l2_subdev_pad_ops gc2145_pad_ops = { ++ .enum_mbus_code = gc2145_enum_mbus_code, ++ .enum_frame_size = gc2145_enum_frame_size, ++ .enum_frame_interval = gc2145_enum_frame_interval, ++ .get_fmt = gc2145_get_fmt, ++ .set_fmt = gc2145_set_fmt, ++ .get_frame_interval = gc2145_get_frame_interval, ++ .set_frame_interval = gc2145_set_frame_interval, ++}; ++ ++static const struct v4l2_subdev_video_ops gc2145_video_ops = { ++ .s_stream = gc2145_s_stream, ++}; ++ ++static const struct v4l2_subdev_ops gc2145_subdev_ops = { ++ .core = &gc2145_core_ops, ++ .pad = &gc2145_pad_ops, ++ .video = &gc2145_video_ops, ++}; ++ ++static int gc2145_get_regulators(struct gc2145_dev *sensor) ++{ ++ int i; ++ ++ for (i = 0; i < GC2145_NUM_SUPPLIES; i++) ++ sensor->supplies[i].supply = gc2145_supply_name[i]; ++ ++ return devm_regulator_bulk_get(&sensor->i2c_client->dev, ++ GC2145_NUM_SUPPLIES, ++ sensor->supplies); ++} ++ ++static int gc2145_probe(struct i2c_client *client) ++{ ++ struct device *dev = &client->dev; ++ struct fwnode_handle *endpoint; ++ struct gc2145_dev *sensor; ++ int ret; ++ ++ sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); ++ if (!sensor) ++ return -ENOMEM; ++ ++ sensor->i2c_client = client; ++ ++ sensor->fmt.code = gc2145_formats[0].code; ++ sensor->fmt.width = 1600; ++ sensor->fmt.height = 1200; ++ sensor->fmt.field = V4L2_FIELD_NONE; ++ sensor->frame_interval.numerator = 1; ++ sensor->frame_interval.denominator = 10; ++ sensor->pending_mode_change = true; ++ sensor->current_bank = 0xff; ++ ++ endpoint = fwnode_graph_get_next_endpoint( ++ of_fwnode_handle(client->dev.of_node), NULL); ++ if (!endpoint) { ++ dev_err(dev, "endpoint node not found\n"); ++ return -EINVAL; ++ } ++ ++ ret = v4l2_fwnode_endpoint_parse(endpoint, &sensor->ep); ++ fwnode_handle_put(endpoint); ++ if (ret) { ++ dev_err(dev, "could not parse endpoint\n"); ++ return ret; ++ } ++ ++ if (sensor->ep.bus_type != V4L2_MBUS_PARALLEL) { ++ dev_err(dev, "unsupported bus type %d\n", sensor->ep.bus_type); ++ return -EINVAL; ++ } ++ ++ sensor->xclk = devm_clk_get(dev, "xclk"); ++ if (IS_ERR(sensor->xclk)) { ++ dev_err(dev, "failed to get xclk\n"); ++ return PTR_ERR(sensor->xclk); ++ } ++ ++ sensor->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_IN); ++ if (IS_ERR(sensor->enable_gpio)) { ++ dev_err(dev, "failed to get enable gpio\n"); ++ return PTR_ERR(sensor->enable_gpio); ++ } ++ ++ sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_IN); ++ if (IS_ERR(sensor->reset_gpio)) { ++ dev_err(dev, "failed to get reset gpio\n"); ++ return PTR_ERR(sensor->reset_gpio); ++ } ++ ++ if (!sensor->enable_gpio || !sensor->reset_gpio) { ++ dev_err(dev, "enable and reset pins must be configured\n"); ++ return ret; ++ } ++ ++ v4l2_i2c_subdev_init(&sensor->sd, client, &gc2145_subdev_ops); ++ ++ sensor->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; ++ sensor->pad.flags = MEDIA_PAD_FL_SOURCE; ++ sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; ++ ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad); ++ if (ret) ++ return ret; ++ ++ mutex_init(&sensor->lock); ++ ++ ret = gc2145_get_regulators(sensor); ++ if (ret) ++ goto entity_cleanup; ++ ++ ret = gc2145_init_controls(sensor); ++ if (ret) ++ goto entity_cleanup; ++ ++ ret = v4l2_async_register_subdev(&sensor->sd); ++ if (ret) ++ goto free_ctrls; ++ ++ return 0; ++ ++free_ctrls: ++ v4l2_ctrl_handler_free(&sensor->ctrls.handler); ++entity_cleanup: ++ mutex_destroy(&sensor->lock); ++ media_entity_cleanup(&sensor->sd.entity); ++ return ret; ++} ++ ++static void gc2145_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct gc2145_dev *sensor = to_gc2145_dev(sd); ++ ++ v4l2_async_unregister_subdev(&sensor->sd); ++ mutex_destroy(&sensor->lock); ++ media_entity_cleanup(&sensor->sd.entity); ++ v4l2_ctrl_handler_free(&sensor->ctrls.handler); ++} ++ ++static const struct i2c_device_id gc2145_id[] = { ++ {"gc2145", 0}, ++ {}, ++}; ++MODULE_DEVICE_TABLE(i2c, gc2145_id); ++ ++static const struct of_device_id gc2145_dt_ids[] = { ++ { .compatible = "galaxycore,gc2145" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, gc2145_dt_ids); ++ ++static struct i2c_driver gc2145_i2c_driver = { ++ .driver = { ++ .name = "gc2145", ++ .of_match_table = gc2145_dt_ids, ++ }, ++ .id_table = gc2145_id, ++ .probe = gc2145_probe, ++ .remove = gc2145_remove, ++}; ++ ++module_i2c_driver(gc2145_i2c_driver); ++ ++MODULE_AUTHOR("Ondrej Jirman "); ++MODULE_DESCRIPTION("GC2145 Camera Subdev Driver"); ++MODULE_LICENSE("GPL"); +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/media-gc2145-fix-white-balance-colors.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/media-gc2145-fix-white-balance-colors.patch new file mode 100644 index 000000000000..4249b7883c95 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/media-gc2145-fix-white-balance-colors.patch @@ -0,0 +1,183 @@ +From 7499b88dc111f8367eb0f8a807e4603627ee2e4e Mon Sep 17 00:00:00 2001 +From: Andrey Skvortsov +Date: Sat, 26 Aug 2023 12:10:49 +0300 +Subject: media: gc2145: fix white-balance colors + +awb initialization settings were taken from gc2145.c driver developed +by STMicroelectronics with a help of Galaxycore. Most of these +registers are not documented in a publicly available gc2145 +documentation. + +Don't reset white balance gains, exposure and gains on every stream +start. That keeps old values set by AWB/AEC previously and makes +AWB/AEC algorithms achieve correct settings on stream restart faster. + +For Megapixels DCP calibration file for front camera has to be removed +to get good quality pictures. +``` +rm /usr/share/megapixels/config/pine64,pinephone,front.dcp +``` + +Signed-off-by: Andrey Skvortsov +--- + drivers/media/i2c/gc2145.c | 118 ++++++++++++++++++++++++++++++------- + 1 file changed, 97 insertions(+), 21 deletions(-) + +diff --git a/drivers/media/i2c/gc2145.c b/drivers/media/i2c/gc2145.c +index 429d86bf5f01..b505b3f07387 100644 +--- a/drivers/media/i2c/gc2145.c ++++ b/drivers/media/i2c/gc2145.c +@@ -1,6 +1,7 @@ + /* + * Galaxycore GC2145 driver. + * Copyright (C) 2018 OndÅ™ej Jirman . ++ * Copyright (C) 2022, STMicroelectronics SA + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -1556,34 +1557,114 @@ static int gc2145_set_2pclk(struct gc2145_dev *sensor, + return gc2145_tx_commit(sensor); + } + ++struct gc2145_reg { ++ unsigned char address; ++ unsigned char val; ++}; ++ ++static const struct gc2145_reg gc2145_awb_regs[] = { ++ {0xfe, 0x01}, ++ {0x4f, 0x00}, {0x4f, 0x00}, {0x4b, 0x01}, {0x4f, 0x00}, ++ {0x4c, 0x01}, {0x4d, 0x71}, {0x4e, 0x01}, ++ {0x4c, 0x01}, {0x4d, 0x91}, {0x4e, 0x01}, ++ {0x4c, 0x01}, {0x4d, 0x70}, {0x4e, 0x01}, ++ {0x4c, 0x01}, {0x4d, 0x90}, {0x4e, 0x02}, ++ {0x4c, 0x01}, {0x4d, 0xb0}, {0x4e, 0x02}, ++ {0x4c, 0x01}, {0x4d, 0x8f}, {0x4e, 0x02}, ++ {0x4c, 0x01}, {0x4d, 0x6f}, {0x4e, 0x02}, ++ {0x4c, 0x01}, {0x4d, 0xaf}, {0x4e, 0x02}, ++ {0x4c, 0x01}, {0x4d, 0xd0}, {0x4e, 0x02}, ++ {0x4c, 0x01}, {0x4d, 0xf0}, {0x4e, 0x02}, ++ {0x4c, 0x01}, {0x4d, 0xcf}, {0x4e, 0x02}, ++ {0x4c, 0x01}, {0x4d, 0xef}, {0x4e, 0x02}, ++ {0x4c, 0x01}, {0x4d, 0x6e}, {0x4e, 0x03}, ++ {0x4c, 0x01}, {0x4d, 0x8e}, {0x4e, 0x03}, ++ {0x4c, 0x01}, {0x4d, 0xae}, {0x4e, 0x03}, ++ {0x4c, 0x01}, {0x4d, 0xce}, {0x4e, 0x03}, ++ {0x4c, 0x01}, {0x4d, 0x4d}, {0x4e, 0x03}, ++ {0x4c, 0x01}, {0x4d, 0x6d}, {0x4e, 0x03}, ++ {0x4c, 0x01}, {0x4d, 0x8d}, {0x4e, 0x03}, ++ {0x4c, 0x01}, {0x4d, 0xad}, {0x4e, 0x03}, ++ {0x4c, 0x01}, {0x4d, 0xcd}, {0x4e, 0x03}, ++ {0x4c, 0x01}, {0x4d, 0x4c}, {0x4e, 0x03}, ++ {0x4c, 0x01}, {0x4d, 0x6c}, {0x4e, 0x03}, ++ {0x4c, 0x01}, {0x4d, 0x8c}, {0x4e, 0x03}, ++ {0x4c, 0x01}, {0x4d, 0xac}, {0x4e, 0x03}, ++ {0x4c, 0x01}, {0x4d, 0xcc}, {0x4e, 0x03}, ++ {0x4c, 0x01}, {0x4d, 0xcb}, {0x4e, 0x03}, ++ {0x4c, 0x01}, {0x4d, 0x4b}, {0x4e, 0x03}, ++ {0x4c, 0x01}, {0x4d, 0x6b}, {0x4e, 0x03}, ++ {0x4c, 0x01}, {0x4d, 0x8b}, {0x4e, 0x03}, ++ {0x4c, 0x01}, {0x4d, 0xab}, {0x4e, 0x03}, ++ {0x4c, 0x01}, {0x4d, 0x8a}, {0x4e, 0x04}, ++ {0x4c, 0x01}, {0x4d, 0xaa}, {0x4e, 0x04}, ++ {0x4c, 0x01}, {0x4d, 0xca}, {0x4e, 0x04}, ++ {0x4c, 0x01}, {0x4d, 0xca}, {0x4e, 0x04}, ++ {0x4c, 0x01}, {0x4d, 0xc9}, {0x4e, 0x04}, ++ {0x4c, 0x01}, {0x4d, 0x8a}, {0x4e, 0x04}, ++ {0x4c, 0x01}, {0x4d, 0x89}, {0x4e, 0x04}, ++ {0x4c, 0x01}, {0x4d, 0xa9}, {0x4e, 0x04}, ++ {0x4c, 0x02}, {0x4d, 0x0b}, {0x4e, 0x05}, ++ {0x4c, 0x02}, {0x4d, 0x0a}, {0x4e, 0x05}, ++ {0x4c, 0x01}, {0x4d, 0xeb}, {0x4e, 0x05}, ++ {0x4c, 0x01}, {0x4d, 0xea}, {0x4e, 0x05}, ++ {0x4c, 0x02}, {0x4d, 0x09}, {0x4e, 0x05}, ++ {0x4c, 0x02}, {0x4d, 0x29}, {0x4e, 0x05}, ++ {0x4c, 0x02}, {0x4d, 0x2a}, {0x4e, 0x05}, ++ {0x4c, 0x02}, {0x4d, 0x4a}, {0x4e, 0x05}, ++ {0x4c, 0x02}, {0x4d, 0x8a}, {0x4e, 0x06}, ++ {0x4c, 0x02}, {0x4d, 0x49}, {0x4e, 0x06}, ++ {0x4c, 0x02}, {0x4d, 0x69}, {0x4e, 0x06}, ++ {0x4c, 0x02}, {0x4d, 0x89}, {0x4e, 0x06}, ++ {0x4c, 0x02}, {0x4d, 0xa9}, {0x4e, 0x06}, ++ {0x4c, 0x02}, {0x4d, 0x48}, {0x4e, 0x06}, ++ {0x4c, 0x02}, {0x4d, 0x68}, {0x4e, 0x06}, ++ {0x4c, 0x02}, {0x4d, 0x69}, {0x4e, 0x06}, ++ {0x4c, 0x02}, {0x4d, 0xca}, {0x4e, 0x07}, ++ {0x4c, 0x02}, {0x4d, 0xc9}, {0x4e, 0x07}, ++ {0x4c, 0x02}, {0x4d, 0xe9}, {0x4e, 0x07}, ++ {0x4c, 0x03}, {0x4d, 0x09}, {0x4e, 0x07}, ++ {0x4c, 0x02}, {0x4d, 0xc8}, {0x4e, 0x07}, ++ {0x4c, 0x02}, {0x4d, 0xe8}, {0x4e, 0x07}, ++ {0x4c, 0x02}, {0x4d, 0xa7}, {0x4e, 0x07}, ++ {0x4c, 0x02}, {0x4d, 0xc7}, {0x4e, 0x07}, ++ {0x4c, 0x02}, {0x4d, 0xe7}, {0x4e, 0x07}, ++ {0x4c, 0x03}, {0x4d, 0x07}, {0x4e, 0x07}, ++ {0x4f, 0x01}, ++ {0x50, 0x80}, {0x51, 0xa8}, {0x52, 0x47}, {0x53, 0x38}, ++ {0x54, 0xc7}, {0x56, 0x0e}, {0x58, 0x08}, {0x5b, 0x00}, ++ {0x5c, 0x74}, {0x5d, 0x8b}, {0x61, 0xdb}, {0x62, 0xb8}, ++ {0x63, 0x86}, {0x64, 0xc0}, {0x65, 0x04}, {0x67, 0xa8}, ++ {0x68, 0xb0}, {0x69, 0x00}, {0x6a, 0xa8}, {0x6b, 0xb0}, ++ {0x6c, 0xaf}, {0x6d, 0x8b}, {0x6e, 0x50}, {0x6f, 0x18}, ++ {0x73, 0xf0}, {0x70, 0x0d}, {0x71, 0x60}, {0x72, 0x80}, ++ {0x74, 0x01}, {0x75, 0x01}, {0x7f, 0x0c}, {0x76, 0x70}, ++ {0x77, 0x58}, {0x78, 0xa0}, {0x79, 0x5e}, {0x7a, 0x54}, ++ {0x7b, 0x58}, ++ {0xfe, 0x00}, ++}; ++ + static int gc2145_setup_awb(struct gc2145_dev *sensor, + u16 x1, u16 y1, u16 x2, u16 y2) + { + int ratio = 8; //XXX: manual for gc2035 FAE says 4 + +- gc2145_tx_start(sensor); +- + // disable awb +- gc2145_tx_update_bits(sensor, 0x82, BIT(1), 0); ++ gc2145_update_bits(sensor, 0x82, BIT(1), 0); + +- // reset white balance RGB gains +- gc2145_tx_write8(sensor, 0xb3, 0x40); +- gc2145_tx_write8(sensor, 0xb4, 0x40); +- gc2145_tx_write8(sensor, 0xb5, 0x40); ++ // load awb settings ++ gc2145_set_registers(sensor, (void*)gc2145_awb_regs, sizeof(gc2145_awb_regs)); + + // awb window +- gc2145_tx_write8(sensor, 0x1ec, x1 / ratio); +- gc2145_tx_write8(sensor, 0x1ed, y1 / ratio); +- gc2145_tx_write8(sensor, 0x1ee, x2 / ratio); +- gc2145_tx_write8(sensor, 0x1ef, y2 / ratio); ++ gc2145_write(sensor, 0x1ec, x1 / ratio); ++ gc2145_write(sensor, 0x1ed, y1 / ratio); ++ gc2145_write(sensor, 0x1ee, x2 / ratio); ++ gc2145_write(sensor, 0x1ef, y2 / ratio); + + // eanble awb +- gc2145_tx_update_bits(sensor, 0x82, BIT(1), BIT(1)); +- +- //1051 { 0xfe, 0x01 }, +- //1052 { 0x74, 0x01 }, ++ gc2145_update_bits(sensor, 0x82, BIT(1), BIT(1)); + +- return gc2145_tx_commit(sensor); ++ return 0; + } + + static int gc2145_setup_aec(struct gc2145_dev *sensor, +@@ -1599,11 +1680,6 @@ static int gc2145_setup_aec(struct gc2145_dev *sensor, + // disable AEC + gc2145_tx_write8(sensor, 0xb6, 0); + +- // set reasonable initial exposure and gains +- gc2145_tx_write16(sensor, 0x03, 1200); +- gc2145_tx_write8(sensor, 0xb1, 0x20); +- gc2145_tx_write8(sensor, 0xb2, 0xe0); +- + // setup measure window + gc2145_tx_write8(sensor, 0x101, x1 / x_ratio); + gc2145_tx_write8(sensor, 0x102, x2 / x_ratio); +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/media-gc2145-implement-system-suspend.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/media-gc2145-implement-system-suspend.patch new file mode 100644 index 000000000000..c3396c66bf4f --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/media-gc2145-implement-system-suspend.patch @@ -0,0 +1,72 @@ +From db757ea5f058b665f3435c42810ff35838f4a28d Mon Sep 17 00:00:00 2001 +From: Andrey Skvortsov +Date: Mon, 14 Aug 2023 13:27:08 +0300 +Subject: media: gc2145: implement system suspend + +If system was suspended while camera sensor was used, data and +interrupts were still coming from sensor and that caused unstable +system. Sometimes system hanged during a resume. + +Signed-off-by: Andrey Skvortsov +--- + drivers/media/i2c/gc2145.c | 37 +++++++++++++++++++++++++++++++++++++ + 1 file changed, 37 insertions(+) + +diff --git a/drivers/media/i2c/gc2145.c b/drivers/media/i2c/gc2145.c +index 749dce832aa0..429d86bf5f01 100644 +--- a/drivers/media/i2c/gc2145.c ++++ b/drivers/media/i2c/gc2145.c +@@ -2275,6 +2275,42 @@ static void gc2145_remove(struct i2c_client *client) + v4l2_ctrl_handler_free(&sensor->ctrls.handler); + } + ++static int gc2145_sensor_suspend(struct device *dev) ++{ ++ struct v4l2_subdev *sd = dev_get_drvdata(dev); ++ struct gc2145_dev *sensor = to_gc2145_dev(sd); ++ ++ mutex_lock(&sensor->lock); ++ if (sensor->streaming) ++ gc2145_set_stream(sensor, false); ++ mutex_unlock(&sensor->lock); ++ ++ return 0; ++} ++ ++static int gc2145_sensor_resume(struct device *dev) ++{ ++ struct v4l2_subdev *sd = dev_get_drvdata(dev); ++ struct gc2145_dev *sensor = to_gc2145_dev(sd); ++ int ret = 0; ++ ++ mutex_lock(&sensor->lock); ++ if (sensor->streaming) { ++ ret = gc2145_set_stream(sensor, true); ++ if (ret) { ++ gc2145_set_stream(sensor, false); ++ sensor->streaming = false; ++ } ++ } ++ mutex_unlock(&sensor->lock); ++ ++ return ret; ++} ++ ++static const struct dev_pm_ops gc2145_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(gc2145_sensor_suspend, gc2145_sensor_resume) ++}; ++ + static const struct i2c_device_id gc2145_id[] = { + {"gc2145", 0}, + {}, +@@ -2291,6 +2327,7 @@ static struct i2c_driver gc2145_i2c_driver = { + .driver = { + .name = "gc2145", + .of_match_table = gc2145_dt_ids, ++ .pm = &gc2145_pm_ops, + }, + .id_table = gc2145_id, + .probe = gc2145_probe, +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/media-hm5065-Add-subdev-driver-for-Himax-HM5065-camera-sensor.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/media-hm5065-Add-subdev-driver-for-Himax-HM5065-camera-sensor.patch new file mode 100644 index 000000000000..9b7294ef167c --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/media-hm5065-Add-subdev-driver-for-Himax-HM5065-camera-sensor.patch @@ -0,0 +1,2268 @@ +From b6f1f790b9f8dafcf115391cb36d80c8d7b75fb2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sat, 30 Sep 2017 02:39:48 +0200 +Subject: media: hm5065: Add subdev driver for Himax HM5065 camera sensor + +HM5065 is 5MP CMOS sensor. This driver implements support for +V4L2_MBUS_PARALLEL bus type only. The driver Other features: + +- External clock rates support from 6-27MHz (discrete values) +- Resolution support from 2592x1944 to 88x72 +- Frame rates are available depending on the PCLK frequency + (from VGA@120 to 2592x1944@8) +- Support for several YUV and RGB media bus formats + +Signed-off-by: Ondrej Jirman +--- + drivers/media/i2c/Kconfig | 10 + + drivers/media/i2c/Makefile | 1 + + drivers/media/i2c/hm5065.c | 2207 ++++++++++++++++++++++++++++++++++++ + 3 files changed, 2218 insertions(+) + create mode 100644 drivers/media/i2c/hm5065.c + +diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig +index 8ba096b8ebca..e7da18f3da3c 100644 +--- a/drivers/media/i2c/Kconfig ++++ b/drivers/media/i2c/Kconfig +@@ -701,6 +701,16 @@ config VIDEO_VGXY61 + source "drivers/media/i2c/ccs/Kconfig" + source "drivers/media/i2c/et8ek8/Kconfig" + ++config VIDEO_HM5065 ++ tristate "Himax HM5065 sensor support" ++ depends on I2C ++ select V4L2_FWNODE ++ select VIDEO_V4L2_SUBDEV_API ++ select MEDIA_CONTROLLER ++ help ++ This is a V4L2 sensor-level driver for Himax HM5065 ++ 5 Mpixel camera. ++ + endif + + menu "Camera ISPs" +diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile +index fbb988bd067a..b429d9f2e982 100644 +--- a/drivers/media/i2c/Makefile ++++ b/drivers/media/i2c/Makefile +@@ -157,3 +157,4 @@ obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o + obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o + obj-$(CONFIG_VIDEO_WM8739) += wm8739.o + obj-$(CONFIG_VIDEO_WM8775) += wm8775.o ++obj-$(CONFIG_VIDEO_HM5065) += hm5065.o +diff --git a/drivers/media/i2c/hm5065.c b/drivers/media/i2c/hm5065.c +new file mode 100644 +index 000000000000..a3500a4c1441 +--- /dev/null ++++ b/drivers/media/i2c/hm5065.c +@@ -0,0 +1,2207 @@ ++/* ++ * Himax HM5065 driver. ++ * Copyright (C) 2017-2019 OndÅ™ej Jirman . ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define HM5065_AF_FIRMWARE "hm5065-af.bin" ++#define HM5065_FIRMWARE_PARAMETERS "hm5065-init.bin" ++ ++#define HM5065_SENSOR_WIDTH 2592u ++#define HM5065_SENSOR_HEIGHT 1944u ++#define HM5065_CAPTURE_WIDTH_MIN 88u ++#define HM5065_CAPTURE_HEIGHT_MIN 72u ++ ++/* {{{ Register definitions */ ++ ++/* registers are assumed to be u8 unless otherwise specified */ ++ ++/* device parameters */ ++#define HM5065_REG_DEVICE_ID 0x0000 /* u16 */ ++#define HM5065_REG_DEVICE_ID_VALUE 0x039e ++#define HM5065_REG_FIRMWARE_VSN 0x0002 ++#define HM5065_REG_PATCH_VSN 0x0003 ++#define HM5065_REG_EXCLOCKLUT 0x0009 /* standby */ ++ ++#define HM5065_REG_INT_EVENT_FLAG 0x000a ++#define HM5065_REG_INT_EVENT_FLAG_OP_MODE BIT(0) ++#define HM5065_REG_INT_EVENT_FLAG_CAM_MODE BIT(1) ++#define HM5065_REG_INT_EVENT_FLAG_JPEG_STATUS BIT(2) ++#define HM5065_REG_INT_EVENT_FLAG_NUM_FRAMES BIT(3) ++#define HM5065_REG_INT_EVENT_FLAG_AF_LOCKED BIT(4) ++ ++/* mode manager */ ++#define HM5065_REG_USER_COMMAND 0x0010 ++#define HM5065_REG_USER_COMMAND_STOP 0x00 ++#define HM5065_REG_USER_COMMAND_RUN 0x01 ++#define HM5065_REG_USER_COMMAND_POWEROFF 0x02 ++ ++#define HM5065_REG_STATE 0x0011 ++#define HM5065_REG_STATE_RAW 0x10 ++#define HM5065_REG_STATE_IDLE 0x20 ++#define HM5065_REG_STATE_RUNNING 0x30 ++ ++#define HM5065_REG_ACTIVE_PIPE_SETUP_BANK 0x0012 ++#define HM5065_REG_ACTIVE_PIPE_SETUP_BANK_0 0x00 ++#define HM5065_REG_ACTIVE_PIPE_SETUP_BANK_1 0x01 ++ ++#define HM5065_REG_NUMBER_OF_FRAMES_STREAMED 0x0014 /* ro */ ++#define HM5065_REG_REQUIRED_STREAM_LENGTH 0x0015 ++ ++#define HM5065_REG_CSI_ENABLE 0x0016 /* standby */ ++#define HM5065_REG_CSI_ENABLE_DISABLE 0x00 ++#define HM5065_REG_CSI_ENABLE_CSI2_1LANE 0x01 ++#define HM5065_REG_CSI_ENABLE_CSI2_2LANE 0x02 ++ ++/* pipe setup bank 0 */ ++#define HM5065_REG_P0_SENSOR_MODE 0x0040 ++#define HM5065_REG_SENSOR_MODE_FULLSIZE 0x00 ++#define HM5065_REG_SENSOR_MODE_BINNING_2X2 0x01 ++#define HM5065_REG_SENSOR_MODE_BINNING_4X4 0x02 ++#define HM5065_REG_SENSOR_MODE_SUBSAMPLING_2X2 0x03 ++#define HM5065_REG_SENSOR_MODE_SUBSAMPLING_4X4 0x04 ++ ++#define HM5065_REG_P0_IMAGE_SIZE 0x0041 ++#define HM5065_REG_IMAGE_SIZE_5MP 0x00 ++#define HM5065_REG_IMAGE_SIZE_UXGA 0x01 ++#define HM5065_REG_IMAGE_SIZE_SXGA 0x02 ++#define HM5065_REG_IMAGE_SIZE_SVGA 0x03 ++#define HM5065_REG_IMAGE_SIZE_VGA 0x04 ++#define HM5065_REG_IMAGE_SIZE_CIF 0x05 ++#define HM5065_REG_IMAGE_SIZE_QVGA 0x06 ++#define HM5065_REG_IMAGE_SIZE_QCIF 0x07 ++#define HM5065_REG_IMAGE_SIZE_QQVGA 0x08 ++#define HM5065_REG_IMAGE_SIZE_QQCIF 0x09 ++#define HM5065_REG_IMAGE_SIZE_MANUAL 0x0a ++ ++#define HM5065_REG_P0_MANUAL_HSIZE 0x0042 /* u16 */ ++#define HM5065_REG_P0_MANUAL_VSIZE 0x0044 /* u16 */ ++ ++#define HM5065_REG_P0_DATA_FORMAT 0x0046 ++#define HM5065_REG_DATA_FORMAT_YCBCR_JFIF 0x00 ++#define HM5065_REG_DATA_FORMAT_YCBCR_REC601 0x01 ++#define HM5065_REG_DATA_FORMAT_YCBCR_CUSTOM 0x02 ++#define HM5065_REG_DATA_FORMAT_RGB_565 0x03 ++#define HM5065_REG_DATA_FORMAT_RGB_565_CUSTOM 0x04 ++#define HM5065_REG_DATA_FORMAT_RGB_444 0x05 ++#define HM5065_REG_DATA_FORMAT_RGB_555 0x06 ++#define HM5065_REG_DATA_FORMAT_RAW10ITU10 0x07 ++#define HM5065_REG_DATA_FORMAT_RAW10ITU8 0x08 ++#define HM5065_REG_DATA_FORMAT_JPEG 0x09 ++ ++#define HM5065_REG_P0_GAMMA_GAIN 0x0049 /* 0-31 */ ++#define HM5065_REG_P0_GAMMA_INTERPOLATION 0x004a /* 0-16 */ ++#define HM5065_REG_P0_PEAKING_GAIN 0x004c /* 0-63 */ ++ ++#define HM5065_REG_P0_JPEG_SQUEEZE_MODE 0x004d ++#define HM5065_REG_JPEG_SQUEEZE_MODE_USER 0x00 ++#define HM5065_REG_JPEG_SQUEEZE_MODE_AUTO 0x01 ++ ++#define HM5065_REG_P0_JPEG_TARGET_FILE_SIZE 0x004e /* u16, kB */ ++#define HM5065_REG_P0_JPEG_IMAGE_QUALITY 0x0050 ++#define HM5065_REG_JPEG_IMAGE_QUALITY_HIGH 0x00 ++#define HM5065_REG_JPEG_IMAGE_QUALITY_MEDIUM 0x01 ++#define HM5065_REG_JPEG_IMAGE_QUALITY_LOW 0x02 ++ ++/* pipe setup bank 1 (only register indexes) */ ++#define HM5065_REG_P1_SENSOR_MODE 0x0060 ++#define HM5065_REG_P1_IMAGE_SIZE 0x0061 ++#define HM5065_REG_P1_MANUAL_HSIZE 0x0062 /* u16 */ ++#define HM5065_REG_P1_MANUAL_VSIZE 0x0064 /* u16 */ ++#define HM5065_REG_P1_DATA_FORMAT 0x0066 ++#define HM5065_REG_P1_GAMMA_GAIN 0x0069 /* 0-31 */ ++#define HM5065_REG_P1_GAMMA_INTERPOLATION 0x006a /* 0-16 */ ++#define HM5065_REG_P1_PEAKING_GAIN 0x006c /* 0-63 */ ++#define HM5065_REG_P1_JPEG_SQUEEZE_MODE 0x006d ++#define HM5065_REG_P1_JPEG_TARGET_FILE_SIZE 0x006e /* u16, kB */ ++#define HM5065_REG_P1_JPEG_IMAGE_QUALITY 0x0070 ++ ++/* pipe setup - common registers */ ++#define HM5065_REG_CONTRAST 0x0080 /* 0-200 */ ++#define HM5065_REG_COLOR_SATURATION 0x0081 /* 0-200 */ ++#define HM5065_REG_BRIGHTNESS 0x0082 /* 0-200 */ ++#define HM5065_REG_HORIZONTAL_MIRROR 0x0083 /* 0,1 */ ++#define HM5065_REG_VERTICAL_FLIP 0x0084 /* 0,1 */ ++ ++#define HM5065_REG_YCRCB_ORDER 0x0085 ++#define HM5065_REG_YCRCB_ORDER_CB_Y_CR_Y 0x00 ++#define HM5065_REG_YCRCB_ORDER_CR_Y_CB_Y 0x01 ++#define HM5065_REG_YCRCB_ORDER_Y_CB_Y_CR 0x02 ++#define HM5065_REG_YCRCB_ORDER_Y_CR_Y_CB 0x03 ++ ++/* clock chain parameter inputs (floating point) */ ++#define HM5065_REG_EXTERNAL_CLOCK_FREQ_MHZ 0x00b0 /* fp16, 6-27, standby */ ++#define HM5065_REG_TARGET_PLL_OUTPUT 0x00b2 /* fp16, 450-1000, standby */ ++ ++/* static frame rate control */ ++#define HM5065_REG_DESIRED_FRAME_RATE_NUM 0x00c8 /* u16 */ ++#define HM5065_REG_DESIRED_FRAME_RATE_DEN 0x00ca ++ ++/* static frame rate status */ ++#define HM5065_REG_REQUESTED_FRAME_RATE_HZ 0x00d8 /* fp16 */ ++#define HM5065_REG_MAX_FRAME_RATE_HZ 0x00da /* fp16 */ ++#define HM5065_REG_MIN_FRAME_RATE_HZ 0x00dc /* fp16 */ ++ ++/* exposure controls */ ++#define HM5065_REG_EXPOSURE_MODE 0x0128 ++#define HM5065_REG_EXPOSURE_MODE_AUTO 0x00 ++#define HM5065_REG_EXPOSURE_MODE_COMPILED_MANUAL 0x01 ++#define HM5065_REG_EXPOSURE_MODE_DIRECT_MANUAL 0x02 ++ ++#define HM5065_REG_EXPOSURE_METERING 0x0129 ++#define HM5065_REG_EXPOSURE_METERING_FLAT 0x00 ++#define HM5065_REG_EXPOSURE_METERING_BACKLIT 0x01 ++#define HM5065_REG_EXPOSURE_METERING_CENTERED 0x02 ++ ++#define HM5065_REG_MANUAL_EXPOSURE_TIME_NUM 0x012a ++#define HM5065_REG_MANUAL_EXPOSURE_TIME_DEN 0x012b ++#define HM5065_REG_MANUAL_EXPOSURE_TIME_US 0x012c /* fp16 */ ++#define HM5065_REG_COLD_START_DESIRED_TIME_US 0x012e /* fp16, standby */ ++#define HM5065_REG_EXPOSURE_COMPENSATION 0x0130 /* s8, -7 - +7 */ ++ ++#define HM5065_REG_DIRECT_MODE_COARSE_INTEGRATION_LINES 0x0132 /* u16 */ ++#define HM5065_REG_DIRECT_MODE_FINE_INTEGRATION_PIXELS 0x0134 /* u16 */ ++#define HM5065_REG_DIRECT_MODE_CODED_ANALOG_GAIN 0x0136 /* u16 */ ++#define HM5065_REG_DIRECT_MODE_DIGITAL_GAIN 0x0138 /* fp16 */ ++#define HM5065_REG_FREEZE_AUTO_EXPOSURE 0x0142 /* 0,1 */ ++#define HM5065_REG_USER_MAXIMUM_INTEGRATION_TIME_US 0x0143 /* fp16 */ ++#define HM5065_REG_ANTI_FLICKER_MODE 0x0148 /* 0,1 */ ++ ++/* exposure algorithm controls */ ++#define HM5065_REG_DIGITAL_GAIN_FLOOR 0x015c /* fp16 */ ++#define HM5065_REG_DIGITAL_GAIN_CEILING 0x015e /* fp16 */ ++#define HM5065_REG_ANALOG_GAIN_FLOOR 0x02c0 /* u16 */ ++#define HM5065_REG_ANALOG_GAIN_CEILING 0x02c2 /* u16 */ ++ ++/* exposure status */ ++#define HM5065_REG_COARSE_INTEGRATION 0x017c /* u16 */ ++#define HM5065_REG_FINE_INTEGRATION_PENDING_PIXELS 0x017e /* u16 */ ++#define HM5065_REG_ANALOG_GAIN_PENDING 0x0180 /* fp16 */ ++#define HM5065_REG_DIGITAL_GAIN_PENDING 0x0182 /* fp16 */ ++#define HM5065_REG_DESIRED_EXPOSURE_TIME_US 0x0184 /* fp16 */ ++#define HM5065_REG_COMPILED_EXPOSURE_TIME_US 0x0186 /* fp16 */ ++#define HM5065_REG_USER_MAXIMUM_INTEGRATION_LINES 0x0189 /* u16 */ ++#define HM5065_REG_TOTAL_INTEGRATION_TIME_PENDING_US 0x018b /* fp16 */ ++#define HM5065_REG_CODED_ANALOG_GAIN_PENDING 0x018d /* u16 */ ++ ++/* flicker detect */ ++#define HM5065_REG_FD_ENABLE_DETECT 0x0190 /* 0,1 */ ++#define HM5065_REG_FD_DETECTION_START 0x0191 /* 0,1 */ ++#define HM5065_REG_FD_MAX_NUMBER_ATTEMP 0x0192 /* 0-255, 0 = continuous */ ++#define HM5065_REG_FD_FLICKER_IDENTIFICATION_THRESHOLD 0x0193 /* u16 */ ++#define HM5065_REG_FD_WIN_TIMES 0x0195 ++#define HM5065_REG_FD_FRAME_RATE_SHIFT_NUMBER 0x0196 ++#define HM5065_REG_FD_MANUAL_FREF_ENABLE 0x0197 /* 0,1 */ ++#define HM5065_REG_FD_MANU_FREF_100 0x0198 /* u16 */ ++#define HM5065_REG_FD_MANU_FREF_120 0x019a /* u16 */ ++#define HM5065_REG_FD_FLICKER_FREQUENCY 0x019c /* fp16 */ ++ ++/* white balance control */ ++#define HM5065_REG_WB_MODE 0x01a0 ++#define HM5065_REG_WB_MODE_OFF 0x00 ++#define HM5065_REG_WB_MODE_AUTOMATIC 0x01 ++#define HM5065_REG_WB_MODE_AUTO_INSTANT 0x02 ++#define HM5065_REG_WB_MODE_MANUAL_RGB 0x03 ++#define HM5065_REG_WB_MODE_CLOUDY_PRESET 0x04 ++#define HM5065_REG_WB_MODE_SUNNY_PRESET 0x05 ++#define HM5065_REG_WB_MODE_LED_PRESET 0x06 ++#define HM5065_REG_WB_MODE_FLUORESCENT_PRESET 0x07 ++#define HM5065_REG_WB_MODE_TUNGSTEN_PRESET 0x08 ++#define HM5065_REG_WB_MODE_HORIZON_PRESET 0x09 ++ ++#define HM5065_REG_WB_MANUAL_RED_GAIN 0x01a1 ++#define HM5065_REG_WB_MANUAL_GREEN_GAIN 0x01a2 ++#define HM5065_REG_WB_MANUAL_BLUE_GAIN 0x01a3 ++ ++#define HM5065_REG_WB_MISC_SETTINGS 0x01a4 ++#define HM5065_REG_WB_MISC_SETTINGS_FREEZE_ALGO BIT(2) ++ ++#define HM5065_REG_WB_HUE_R_BIAS 0x01a5 /* fp16 */ ++#define HM5065_REG_WB_HUE_B_BIAS 0x01a7 /* fp16 */ ++ ++#define HM5065_REG_WB_STATUS 0x01c0 ++#define HM5065_REG_WB_STATUS_STABLE BIT(0) ++ ++#define HM5065_REG_WB_NORM_RED_GAIN 0x01c8 /* fp16 */ ++#define HM5065_REG_WB_PART_RED_GAIN 0x01e0 /* fp16 */ ++#define HM5065_REG_WB_PART_GREEN_GAIN 0x01e2 /* fp16 */ ++#define HM5065_REG_WB_PART_BLUE_GAIN 0x01e4 /* fp16 */ ++ ++/* image stability status */ ++#define HM5065_REG_WHITE_BALANCE_STABLE 0x0291 /* 0,1 */ ++#define HM5065_REG_EXPOSURE_STABLE 0x0292 /* 0,1 */ ++#define HM5065_REG_STABLE 0x0294 /* 0,1 */ ++ ++/* special effects */ ++#define HM5065_REG_EFFECTS_NEGATIVE 0x0380 /* 0,1 */ ++#define HM5065_REG_EFFECTS_SOLARISING 0x0381 /* 0,1 */ ++#define HM5065_REG_EFFECTS_SKECTH 0x0382 /* 0,1 */ ++ ++#define HM5065_REG_EFFECTS_COLOR 0x0384 ++#define HM5065_REG_EFFECTS_COLOR_NORMAL 0x00 ++#define HM5065_REG_EFFECTS_COLOR_RED_ONLY 0x01 ++#define HM5065_REG_EFFECTS_COLOR_YELLOW_ONLY 0x02 ++#define HM5065_REG_EFFECTS_COLOR_GREEN_ONLY 0x03 ++#define HM5065_REG_EFFECTS_COLOR_BLUE_ONLY 0x04 ++#define HM5065_REG_EFFECTS_COLOR_BLACK_WHITE 0x05 ++#define HM5065_REG_EFFECTS_COLOR_SEPIA 0x06 ++#define HM5065_REG_EFFECTS_COLOR_ANTIQUE 0x07 ++#define HM5065_REG_EFFECTS_COLOR_AQUA 0x08 ++#define HM5065_REG_EFFECTS_COLOR_MANUAL_MATRIX 0x09 ++ ++/* anti-vignete, otp flash (skipped), page 79-89 */ ++ ++/* flash control */ ++#define HM5065_REG_FLASH_MODE 0x02d0 /* 0,1 */ ++#define HM5065_REG_FLASH_RECOMMENDED 0x02d1 /* 0,1 */ ++ ++/* test pattern */ ++#define HM5065_REG_ENABLE_TEST_PATTERN 0x05d8 /* 0,1 */ ++ ++#define HM5065_REG_TEST_PATTERN 0x05d9 ++#define HM5065_REG_TEST_PATTERN_NONE 0x00 ++#define HM5065_REG_TEST_PATTERN_HORIZONTAL_GREY_SCALE 0x01 ++#define HM5065_REG_TEST_PATTERN_VERTICAL_GREY_SCALE 0x02 ++#define HM5065_REG_TEST_PATTERN_DIAGONAL_GREY_SCALE 0x03 ++#define HM5065_REG_TEST_PATTERN_PN28 0x04 ++#define HM5065_REG_TEST_PATTERN_PN9 0x05 ++#define HM5065_REG_TEST_PATTERN_SOLID_COLOR 0x06 ++#define HM5065_REG_TEST_PATTERN_COLOR_BARS 0x07 ++#define HM5065_REG_TEST_PATTERN_GRADUATED_COLOR_BARS 0x08 ++ ++#define HM5065_REG_TESTDATA_RED 0x4304 /* u16, 0-1023 */ ++#define HM5065_REG_TESTDATA_GREEN_R 0x4308 /* u16, 0-1023 */ ++#define HM5065_REG_TESTDATA_BLUE 0x430c /* u16, 0-1023 */ ++#define HM5065_REG_TESTDATA_GREEN_B 0x4310 /* u16, 0-1023 */ ++ ++/* contrast stretch */ ++#define HM5065_REG_CS_ENABLE 0x05e8 /* 0,1 */ ++#define HM5065_REG_CS_GAIN_CEILING 0x05e9 /* fp16 */ ++#define HM5065_REG_CS_BLACK_OFFSET_CEILING 0x05eb ++#define HM5065_REG_CS_WHITE_PIX_TARGET 0x05ec /* fp16 */ ++#define HM5065_REG_CS_BLACK_PIX_TARGET 0x05ee /* fp16 */ ++#define HM5065_REG_CS_ENABLED 0x05f8 /* 0,1 */ ++#define HM5065_REG_CS_TOTAL_PIXEL 0x05f9 /* fp16 */ ++#define HM5065_REG_CS_W_TARGET 0x05fb /* u32 */ ++#define HM5065_REG_CS_B_TARGET 0x05ff /* u32 */ ++#define HM5065_REG_CS_GAIN 0x0603 /* fp16 */ ++#define HM5065_REG_CS_BLACK_OFFSET 0x0605 ++#define HM5065_REG_CS_WHITE_LIMIT 0x0606 ++ ++/* preset controls */ ++#define HM5065_REG_PRESET_LOADER_ENABLE 0x0638 /* 0,1, standby */ ++ ++#define HM5065_REG_INDIVIDUAL_PRESET 0x0639 /* standby */ ++#define HM5065_REG_INDIVIDUAL_PRESET_ANTIVIGNETTE BIT(0) ++#define HM5065_REG_INDIVIDUAL_PRESET_WHITE_BALANCE BIT(1) ++#define HM5065_REG_INDIVIDUAL_PRESET_VCM BIT(4) ++ ++/* jpeg control parameters*/ ++#define HM5065_REG_JPEG_STATUS 0x0649 ++#define HM5065_REG_JPEG_RESTART 0x064a ++#define HM5065_REG_JPEG_HI_SQUEEZE_VALUE 0x064b /* 5-255 (5 = max q.) */ ++#define HM5065_REG_JPEG_MED_SQUEEZE_VALUE 0x064c /* 5-255 */ ++#define HM5065_REG_JPEG_LOW_SQUEEZE_VALUE 0x064d /* 5-255 */ ++#define HM5065_REG_JPEG_LINE_LENGTH 0x064e /* u16, standby */ ++#define HM5065_REG_JPEG_CLOCK_RATIO 0x0650 /* 1-8, standby */ ++#define HM5065_REG_JPEG_THRES 0x0651 /* u16, standby */ ++#define HM5065_REG_JPEG_BYTE_SENT 0x0653 /* u32 */ ++ ++/* autofocus */ ++ ++#define HM5065_REG_AF_WINDOWS_SYSTEM 0x065a ++#define HM5065_REG_AF_WINDOWS_SYSTEM_7_ZONES 0x00 ++#define HM5065_REG_AF_WINDOWS_SYSTEM_1_ZONE 0x01 ++ ++#define HM5065_REG_AF_H_RATIO_NUM 0x065b ++#define HM5065_REG_AF_H_RATIO_DEN 0x065c ++#define HM5065_REG_AF_V_RATIO_NUM 0x065d ++#define HM5065_REG_AF_V_RATIO_DEN 0x065e ++ ++#define HM5065_REG_AF_RANGE 0x0709 ++#define HM5065_REG_AF_RANGE_FULL 0x00 ++#define HM5065_REG_AF_RANGE_LANDSCAPE 0x01 ++#define HM5065_REG_AF_RANGE_MACRO 0x02 ++ ++#define HM5065_REG_AF_MODE 0x070a ++#define HM5065_REG_AF_MODE_MANUAL 0x00 ++#define HM5065_REG_AF_MODE_CONTINUOUS 0x01 ++#define HM5065_REG_AF_MODE_SINGLE 0x03 ++ ++#define HM5065_REG_AF_MODE_STATUS 0x0720 ++ ++#define HM5065_REG_AF_COMMAND 0x070b ++#define HM5065_REG_AF_COMMAND_NULL 0x00 ++#define HM5065_REG_AF_COMMAND_RELEASED_BUTTON 0x01 ++#define HM5065_REG_AF_COMMAND_HALF_BUTTON 0x02 ++#define HM5065_REG_AF_COMMAND_TAKE_SNAPSHOT 0x03 ++#define HM5065_REG_AF_COMMAND_REFOCUS 0x04 ++ ++#define HM5065_REG_AF_LENS_COMMAND 0x070c ++#define HM5065_REG_AF_LENS_COMMAND_NULL 0x00 ++#define HM5065_REG_AF_LENS_COMMAND_MOVE_STEP_TO_INFINITY 0x01 ++#define HM5065_REG_AF_LENS_COMMAND_MOVE_STEP_TO_MACRO 0x02 ++#define HM5065_REG_AF_LENS_COMMAND_GOTO_INFINITY 0x03 ++#define HM5065_REG_AF_LENS_COMMAND_GOTO_MACRO 0x04 ++#define HM5065_REG_AF_LENS_COMMAND_GOTO_RECOVERY 0x05 ++#define HM5065_REG_AF_LENS_COMMAND_GOTO_TARGET_POSITION 0x07 ++#define HM5065_REG_AF_LENS_COMMAND_GOTO_HYPERFOCAL 0x0C ++ ++#define HM5065_REG_AF_MANUAL_STEP_SIZE 0x070d ++#define HM5065_REG_AF_FACE_LOCATION_CTRL_ENABLE 0x0714 ++#define HM5065_REG_AF_FACE_LOCATION_CTRL_ENABLE_AF BIT(0) ++#define HM5065_REG_AF_FACE_LOCATION_CTRL_ENABLE_AE BIT(1) ++#define HM5065_REG_AF_FACE_LOCATION_CTRL_ENABLE_AWB BIT(2) ++#define HM5065_REG_AF_FACE_LOCATION_X_START 0x0715 /* u16 */ ++#define HM5065_REG_AF_FACE_LOCATION_X_SIZE 0x0717 /* u16 */ ++#define HM5065_REG_AF_FACE_LOCATION_Y_START 0x0719 /* u16 */ ++#define HM5065_REG_AF_FACE_LOCATION_Y_SIZE 0x071b /* u16 */ ++ ++#define HM5065_REG_AF_IN_FOCUS 0x07ae /* ro 0,1 */ ++#define HM5065_REG_AF_IS_STABLE 0x0725 /* ro 0,1 */ ++ ++/* reverse engineered registers */ ++#define HM5065_REG_BUS_DATA_FORMAT 0x7000 ++#define HM5065_REG_COLORSPACE 0x5200 ++#define HM5065_REG_BUS_CONFIG 0x7101 ++#define HM5065_REG_BUS_CONFIG_BT656 0x24 ++#define HM5065_REG_BUS_CONFIG_PARALLEL_HH_VL 0x44 ++ ++/* }}} */ ++ ++struct reg_value { ++ u16 addr; ++ u8 value; ++} __packed; ++ ++/* ++ * Sensor has various pre-defined PLL configurations for a set of ++ * external clock frequencies. ++ */ ++struct hm5065_clk_lut { ++ unsigned long clk_freq; ++ u8 lut_id; ++}; ++ ++static const struct hm5065_clk_lut hm5065_clk_luts[] = { ++ { .clk_freq = 12000000, .lut_id = 0x10 }, ++ { .clk_freq = 13000000, .lut_id = 0x11 }, ++ { .clk_freq = 13500000, .lut_id = 0x12 }, ++ { .clk_freq = 14400000, .lut_id = 0x13 }, ++ { .clk_freq = 18000000, .lut_id = 0x14 }, ++ { .clk_freq = 19200000, .lut_id = 0x15 }, ++ { .clk_freq = 24000000, .lut_id = 0x16 }, ++ { .clk_freq = 26000000, .lut_id = 0x17 }, ++ { .clk_freq = 27000000, .lut_id = 0x18 }, ++}; ++ ++static const struct hm5065_clk_lut *hm5065_find_clk_lut(unsigned long freq) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(hm5065_clk_luts); i++) ++ if (hm5065_clk_luts[i].clk_freq == freq) ++ return &hm5065_clk_luts[i]; ++ ++ return NULL; ++} ++ ++struct hm5065_pixfmt { ++ u32 code; ++ u32 colorspace; ++ u8 data_fmt; ++ u8 ycbcr_order; ++ u8 fmt_setup; ++}; ++ ++//XXX: identify colrorspace correctly, see datasheet page 40 ++static const struct hm5065_pixfmt hm5065_formats[] = { ++ { ++ .code = MEDIA_BUS_FMT_UYVY8_2X8, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ .data_fmt = HM5065_REG_DATA_FORMAT_YCBCR_CUSTOM, ++ .ycbcr_order = HM5065_REG_YCRCB_ORDER_CB_Y_CR_Y, ++ .fmt_setup = 0x08 ++ }, ++ { ++ .code = MEDIA_BUS_FMT_VYUY8_2X8, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ .data_fmt = HM5065_REG_DATA_FORMAT_YCBCR_CUSTOM, ++ .ycbcr_order = HM5065_REG_YCRCB_ORDER_CR_Y_CB_Y, ++ .fmt_setup = 0x08 ++ }, ++ { ++ .code = MEDIA_BUS_FMT_YUYV8_2X8, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ .data_fmt = HM5065_REG_DATA_FORMAT_YCBCR_CUSTOM, ++ .ycbcr_order = HM5065_REG_YCRCB_ORDER_Y_CB_Y_CR, ++ .fmt_setup = 0x08 ++ }, ++ { ++ .code = MEDIA_BUS_FMT_YVYU8_2X8, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ .data_fmt = HM5065_REG_DATA_FORMAT_YCBCR_CUSTOM, ++ .ycbcr_order = HM5065_REG_YCRCB_ORDER_Y_CR_Y_CB, ++ .fmt_setup = 0x08 ++ }, ++ { ++ .code = MEDIA_BUS_FMT_RGB565_2X8_LE, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ .data_fmt = HM5065_REG_DATA_FORMAT_RGB_565, ++ .ycbcr_order = HM5065_REG_YCRCB_ORDER_Y_CR_Y_CB, ++ .fmt_setup = 0x02 ++ }, ++ { ++ .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ .data_fmt = HM5065_REG_DATA_FORMAT_RGB_555, ++ .ycbcr_order = HM5065_REG_YCRCB_ORDER_Y_CR_Y_CB, ++ .fmt_setup = 0x02 ++ }, ++}; ++ ++#define HM5065_NUM_FORMATS ARRAY_SIZE(hm5065_formats) ++ ++static const struct hm5065_pixfmt *hm5065_find_format(u32 code) ++{ ++ int i; ++ ++ for (i = 0; i < HM5065_NUM_FORMATS; i++) ++ if (hm5065_formats[i].code == code) ++ return &hm5065_formats[i]; ++ ++ return NULL; ++} ++ ++/* regulator supplies */ ++static const char * const hm5065_supply_name[] = { ++ "IOVDD", /* Digital I/O (2.8V) suppply */ ++ "AFVDD", /* Autofocus (2.8V) supply */ ++ "DVDD", /* Digital Core (1.8V) supply */ ++ "AVDD", /* Analog (2.8V) supply */ ++}; ++ ++#define HM5065_NUM_SUPPLIES ARRAY_SIZE(hm5065_supply_name) ++ ++struct hm5065_ctrls { ++ struct v4l2_ctrl_handler handler; ++ struct { ++ struct v4l2_ctrl *auto_exposure; ++ struct v4l2_ctrl *exposure; ++ struct v4l2_ctrl *d_gain; ++ struct v4l2_ctrl *a_gain; ++ }; ++ struct v4l2_ctrl *metering; ++ struct v4l2_ctrl *exposure_bias; ++ struct { ++ struct v4l2_ctrl *wb; ++ struct v4l2_ctrl *blue_balance; ++ struct v4l2_ctrl *red_balance; ++ }; ++ struct { ++ struct v4l2_ctrl *focus_auto; ++ struct v4l2_ctrl *af_start; ++ struct v4l2_ctrl *af_stop; ++ struct v4l2_ctrl *af_status; ++ struct v4l2_ctrl *af_distance; ++ struct v4l2_ctrl *focus_relative; ++ }; ++ struct v4l2_ctrl *aaa_lock; ++ struct v4l2_ctrl *hflip; ++ struct v4l2_ctrl *vflip; ++ struct v4l2_ctrl *pl_freq; ++ struct v4l2_ctrl *colorfx; ++ struct v4l2_ctrl *brightness; ++ struct v4l2_ctrl *saturation; ++ struct v4l2_ctrl *contrast; ++ struct v4l2_ctrl *gamma; ++ struct v4l2_ctrl *test_pattern; ++ struct v4l2_ctrl *test_data[4]; ++}; ++ ++struct hm5065_dev { ++ struct i2c_client *i2c_client; ++ struct v4l2_subdev sd; ++ struct media_pad pad; ++ struct v4l2_fwnode_endpoint ep; /* the parsed DT endpoint info */ ++ struct clk *xclk; /* external clock for HM5065 */ ++ ++ struct regulator_bulk_data supplies[HM5065_NUM_SUPPLIES]; ++ struct gpio_desc *reset_gpio; // nrst pin ++ struct gpio_desc *enable_gpio; // ce pin ++ ++ /* lock to protect all members below */ ++ struct mutex lock; ++ ++ struct v4l2_mbus_framefmt fmt; ++ struct v4l2_fract frame_interval; ++ struct hm5065_ctrls ctrls; ++ ++ bool pending_mode_change; ++ bool powered; ++ bool streaming; ++}; ++ ++static inline struct hm5065_dev *to_hm5065_dev(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct hm5065_dev, sd); ++} ++ ++static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) ++{ ++ return &container_of(ctrl->handler, struct hm5065_dev, ++ ctrls.handler)->sd; ++} ++ ++/* {{{ Register access helpers */ ++ ++static int hm5065_write_regs(struct hm5065_dev *sensor, u16 start_index, ++ u8 *data, int data_size) ++{ ++ struct i2c_client *client = sensor->i2c_client; ++ struct i2c_msg msg; ++ u8 buf[130]; ++ int ret; ++ ++ if (data_size > sizeof(buf) - 2) { ++ v4l2_err(&sensor->sd, "%s: oversized transfer (size=%d)\n", ++ __func__, data_size); ++ return -EINVAL; ++ } ++ ++ buf[0] = start_index >> 8; ++ buf[1] = start_index & 0xff; ++ memcpy(buf + 2, data, data_size); ++ ++ msg.addr = client->addr; ++ msg.flags = client->flags; ++ msg.buf = buf; ++ msg.len = data_size + 2; ++ ++ dev_dbg(&sensor->i2c_client->dev, "wr: %04x <= %*ph\n", ++ (u32)start_index, data_size, data); ++ ++ ret = i2c_transfer(client->adapter, &msg, 1); ++ if (ret < 0) { ++ v4l2_err(&sensor->sd, ++ "%s: error %d: start_index=%x, data=%*ph\n", ++ __func__, ret, (u32)start_index, data_size, data); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int hm5065_read_regs(struct hm5065_dev *sensor, u16 start_index, ++ u8 *data, int data_size) ++{ ++ struct i2c_client *client = sensor->i2c_client; ++ struct i2c_msg msg[2]; ++ u8 buf[2]; ++ int ret; ++ ++ buf[0] = start_index >> 8; ++ buf[1] = start_index & 0xff; ++ ++ msg[0].addr = client->addr; ++ msg[0].flags = client->flags; ++ msg[0].buf = buf; ++ msg[0].len = sizeof(buf); ++ ++ msg[1].addr = client->addr; ++ msg[1].flags = client->flags | I2C_M_RD; ++ msg[1].buf = data; ++ msg[1].len = data_size; ++ ++ ret = i2c_transfer(client->adapter, msg, 2); ++ if (ret < 0) { ++ v4l2_err(&sensor->sd, ++ "%s: error %d: start_index=%x, data_size=%d\n", ++ __func__, ret, (u32)start_index, data_size); ++ return ret; ++ } ++ ++ dev_dbg(&sensor->i2c_client->dev, "rd: %04x => %*ph\n", ++ (u32)start_index, data_size, data); ++ ++ return 0; ++} ++ ++static int hm5065_read(struct hm5065_dev *sensor, u16 reg, u8 *val) ++{ ++ return hm5065_read_regs(sensor, reg, val, 1); ++} ++ ++static int hm5065_write(struct hm5065_dev *sensor, u16 reg, u8 val) ++{ ++ return hm5065_write_regs(sensor, reg, &val, 1); ++} ++ ++static int hm5065_read16(struct hm5065_dev *sensor, u16 reg, u16 *val) ++{ ++ int ret; ++ ++ ret = hm5065_read_regs(sensor, reg, (u8 *)val, sizeof(*val)); ++ if (ret) ++ return ret; ++ ++ *val = be16_to_cpu(*val); ++ return 0; ++} ++ ++static int hm5065_write16(struct hm5065_dev *sensor, u16 reg, u16 val) ++{ ++ u16 tmp = cpu_to_be16(val); ++ ++ return hm5065_write_regs(sensor, reg, (u8 *)&tmp, sizeof(tmp)); ++} ++ ++/* ++ * The firmware format: ++ * , ..., ++ * "record" is a 2-byte register address (big endian) followed by 1-byte data ++ */ ++static int hm5065_load_firmware(struct hm5065_dev *sensor, const char *name) ++{ ++ int ret = 0, i = 0, list_size; ++ const struct firmware *fw; ++ struct reg_value *list; ++ u16 start, len; ++ u8 buf[128]; ++ ++ ret = request_firmware(&fw, name, sensor->sd.v4l2_dev->dev); ++ if (ret) { ++ v4l2_warn(&sensor->sd, ++ "Failed to read firmware %s, continuing anyway...\n", ++ name); ++ return 1; ++ } ++ ++ if (fw->size == 0) { ++ ret = 1; ++ goto err_release; ++ } ++ ++ if (fw->size % 3 != 0) { ++ v4l2_err(&sensor->sd, "Firmware image %s has invalid size\n", ++ name); ++ ret = -EINVAL; ++ goto err_release; ++ } ++ ++ list_size = fw->size / 3; ++ list = (struct reg_value *)fw->data; ++ ++ /* we speed up I2C communication via auto-increment functionality */ ++ while (i < list_size) { ++ start = be16_to_cpu(list[i].addr); ++ len = 0; ++ ++ while (i < list_size && ++ be16_to_cpu(list[i].addr) == (start + len) && ++ len < sizeof(buf)) ++ buf[len++] = list[i++].value; ++ ++ ret = hm5065_write_regs(sensor, start, buf, len); ++ if (ret) ++ goto err_release; ++ } ++ ++err_release: ++ release_firmware(fw); ++ return ret; ++} ++ ++/* ++ * Sensor uses ST Float900 format to represent floating point numbers. ++ * Binary floating point number: * (s ? -1 : 0) * 1.mmmmmmmmm * 2^eeeeee ++ * ++ * Following functions convert long value to and from the floating point format. ++ * ++ * Example: ++ * mili variant: val = 123456 => fp_val = 123.456 ++ * micro variant: val = -12345678 => fp_val = -12.345678 ++ */ ++static s64 hm5065_mili_from_fp16(u16 fp_val) ++{ ++ s64 val; ++ s64 mantisa = fp_val & 0x1ff; ++ int exp = (int)((fp_val >> 9) & 0x3f) - 31; ++ ++ val = (1000 * (mantisa | 0x200)); ++ if (exp > 0) ++ val <<= exp; ++ else if (exp < 0) ++ val >>= -exp; ++ val >>= 9; ++ ++ if (fp_val & 0x8000) ++ val = -val; ++ ++ return val; ++} ++ ++static u16 hm5065_mili_to_fp16(s32 val) ++{ ++ int fls; ++ u16 e, m, s = 0; ++ u64 v, rem; ++ ++ if (val == 0) ++ return 0; ++ ++ if (val < 0) { ++ val = -val; ++ s = 0x8000; ++ } ++ ++ v = (u64)val * 1024; ++ rem = do_div(v, 1000); ++ if (rem >= 500) ++ v++; ++ ++ fls = fls64(v) - 1; ++ e = 31 + fls - 10; ++ m = fls > 9 ? v >> (fls - 9) : v << (9 - fls); ++ ++ return s | (m & 0x1ff) | (e << 9); ++} ++ ++/* }}} */ ++/* {{{ Controls */ ++ ++static int hm5065_get_af_status(struct hm5065_dev *sensor) ++{ ++ struct hm5065_ctrls *ctrls = &sensor->ctrls; ++ u8 is_stable, mode; ++ int ret; ++ ++ ret = hm5065_read(sensor, HM5065_REG_AF_MODE_STATUS, &mode); ++ if (ret) ++ return ret; ++ ++ if (mode == HM5065_REG_AF_MODE_MANUAL) { ++ ctrls->af_status->val = V4L2_AUTO_FOCUS_STATUS_IDLE; ++ return 0; ++ } ++ ++ ret = hm5065_read(sensor, HM5065_REG_AF_IS_STABLE, &is_stable); ++ if (ret) ++ return ret; ++ ++ if (is_stable) ++ ctrls->af_status->val = V4L2_AUTO_FOCUS_STATUS_REACHED; ++ else if (!is_stable && mode == HM5065_REG_AF_MODE_CONTINUOUS) ++ ctrls->af_status->val = V4L2_AUTO_FOCUS_STATUS_BUSY; ++ else ++ ctrls->af_status->val = V4L2_AUTO_FOCUS_STATUS_IDLE; ++ ++ return 0; ++} ++ ++static int hm5065_get_exposure(struct hm5065_dev *sensor) ++{ ++ struct hm5065_ctrls *ctrls = &sensor->ctrls; ++ u16 again, dgain, exp; ++ int ret; ++ ++ ret = hm5065_read16(sensor, HM5065_REG_CODED_ANALOG_GAIN_PENDING, ++ &again); ++ if (ret) ++ return ret; ++ ++ ret = hm5065_read16(sensor, HM5065_REG_DIGITAL_GAIN_PENDING, &dgain); ++ if (ret) ++ return ret; ++ ++ ret = hm5065_read16(sensor, HM5065_REG_COARSE_INTEGRATION, &exp); ++ if (ret) ++ return ret; ++ ++ ctrls->exposure->val = exp; ++ ctrls->d_gain->val = clamp(hm5065_mili_from_fp16(dgain), 1000ll, ++ 4000ll); ++ ctrls->a_gain->val = again; ++ ++ return 0; ++} ++ ++static int hm5065_g_volatile_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd = ctrl_to_sd(ctrl); ++ struct hm5065_dev *sensor = to_hm5065_dev(sd); ++ int ret; ++ ++ /* v4l2_ctrl_lock() locks our own mutex */ ++ ++ if (!sensor->powered) ++ return -EIO; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_FOCUS_AUTO: ++ ret = hm5065_get_af_status(sensor); ++ if (ret) ++ return ret; ++ break; ++ case V4L2_CID_EXPOSURE_AUTO: ++ ret = hm5065_get_exposure(sensor); ++ if (ret) ++ return ret; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static const u8 hm5065_wb_opts[][2] = { ++ { V4L2_WHITE_BALANCE_MANUAL, HM5065_REG_WB_MODE_OFF }, ++ { V4L2_WHITE_BALANCE_INCANDESCENT, HM5065_REG_WB_MODE_TUNGSTEN_PRESET }, ++ { V4L2_WHITE_BALANCE_FLUORESCENT, ++ HM5065_REG_WB_MODE_FLUORESCENT_PRESET }, ++ { V4L2_WHITE_BALANCE_HORIZON, HM5065_REG_WB_MODE_HORIZON_PRESET }, ++ { V4L2_WHITE_BALANCE_CLOUDY, HM5065_REG_WB_MODE_CLOUDY_PRESET }, ++ { V4L2_WHITE_BALANCE_DAYLIGHT, HM5065_REG_WB_MODE_SUNNY_PRESET }, ++ { V4L2_WHITE_BALANCE_AUTO, HM5065_REG_WB_MODE_AUTOMATIC }, ++}; ++ ++static int hm5065_set_power_line_frequency(struct hm5065_dev *sensor, s32 val) ++{ ++ u16 freq; ++ int ret; ++ ++ switch (val) { ++ case V4L2_CID_POWER_LINE_FREQUENCY_DISABLED: ++ ret = hm5065_write(sensor, HM5065_REG_ANTI_FLICKER_MODE, 0); ++ if (ret) ++ return ret; ++ ++ return hm5065_write(sensor, HM5065_REG_FD_ENABLE_DETECT, 0); ++ case V4L2_CID_POWER_LINE_FREQUENCY_50HZ: ++ case V4L2_CID_POWER_LINE_FREQUENCY_60HZ: ++ ret = hm5065_write(sensor, HM5065_REG_ANTI_FLICKER_MODE, 1); ++ if (ret) ++ return ret; ++ ++ ret = hm5065_write(sensor, HM5065_REG_FD_ENABLE_DETECT, 0); ++ if (ret) ++ return ret; ++ ++ freq = (val == V4L2_CID_POWER_LINE_FREQUENCY_50HZ) ? ++ 0x4b20 : 0x4bc0; ++ ++ return hm5065_write16(sensor, HM5065_REG_FD_FLICKER_FREQUENCY, ++ freq); ++ case V4L2_CID_POWER_LINE_FREQUENCY_AUTO: ++ ret = hm5065_write(sensor, HM5065_REG_FD_ENABLE_DETECT, 1); ++ if (ret) ++ return ret; ++ ++ ret = hm5065_write(sensor, HM5065_REG_ANTI_FLICKER_MODE, 1); ++ if (ret) ++ return ret; ++ ++ ret = hm5065_write16(sensor, HM5065_REG_FD_MAX_NUMBER_ATTEMP, ++ 100); ++ if (ret) ++ return ret; ++ ++ ret = hm5065_write16(sensor, HM5065_REG_FD_FLICKER_FREQUENCY, ++ 0); ++ if (ret) ++ return ret; ++ ++ return hm5065_write(sensor, HM5065_REG_FD_DETECTION_START, 1); ++ default: ++ return -EINVAL; ++ } ++} ++ ++static int hm5065_set_colorfx(struct hm5065_dev *sensor, s32 val) ++{ ++ int ret; ++ ++ ret = hm5065_write(sensor, HM5065_REG_EFFECTS_COLOR, ++ HM5065_REG_EFFECTS_COLOR_NORMAL); ++ if (ret) ++ return ret; ++ ++ ret = hm5065_write(sensor, HM5065_REG_EFFECTS_NEGATIVE, 0); ++ if (ret) ++ return ret; ++ ++ ret = hm5065_write(sensor, HM5065_REG_EFFECTS_SOLARISING, 0); ++ if (ret) ++ return ret; ++ ++ ret = hm5065_write(sensor, HM5065_REG_EFFECTS_SKECTH, 0); ++ if (ret) ++ return ret; ++ ++ switch (val) { ++ case V4L2_COLORFX_NONE: ++ return 0; ++ case V4L2_COLORFX_NEGATIVE: ++ return hm5065_write(sensor, HM5065_REG_EFFECTS_NEGATIVE, 1); ++ case V4L2_COLORFX_SOLARIZATION: ++ return hm5065_write(sensor, HM5065_REG_EFFECTS_SOLARISING, 1); ++ case V4L2_COLORFX_SKETCH: ++ return hm5065_write(sensor, HM5065_REG_EFFECTS_SKECTH, 1); ++ case V4L2_COLORFX_ANTIQUE: ++ return hm5065_write(sensor, HM5065_REG_EFFECTS_COLOR, ++ HM5065_REG_EFFECTS_COLOR_ANTIQUE); ++ case V4L2_COLORFX_SEPIA: ++ return hm5065_write(sensor, HM5065_REG_EFFECTS_COLOR, ++ HM5065_REG_EFFECTS_COLOR_SEPIA); ++ case V4L2_COLORFX_AQUA: ++ return hm5065_write(sensor, HM5065_REG_EFFECTS_COLOR, ++ HM5065_REG_EFFECTS_COLOR_AQUA); ++ case V4L2_COLORFX_BW: ++ return hm5065_write(sensor, HM5065_REG_EFFECTS_COLOR, ++ HM5065_REG_EFFECTS_COLOR_BLACK_WHITE); ++ default: ++ return -EINVAL; ++ } ++} ++ ++#define AE_BIAS_MENU_DEFAULT_VALUE_INDEX 7 ++static const s64 ae_bias_menu_values[] = { ++ -2100, -1800, -1500, -1200, -900, -600, -300, ++ 0, 300, 600, 900, 1200, 1500, 1800, 2100 ++}; ++ ++static const s8 ae_bias_menu_reg_values[] = { ++ -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7 ++}; ++ ++static int hm5065_set_exposure(struct hm5065_dev *sensor) ++{ ++ struct hm5065_ctrls *ctrls = &sensor->ctrls; ++ bool is_auto = (ctrls->auto_exposure->val != V4L2_EXPOSURE_MANUAL); ++ int ret = 0; ++ ++ if (ctrls->auto_exposure->is_new) { ++ ret = hm5065_write(sensor, HM5065_REG_EXPOSURE_MODE, ++ is_auto ? ++ HM5065_REG_EXPOSURE_MODE_AUTO : ++ HM5065_REG_EXPOSURE_MODE_DIRECT_MANUAL); ++ if (ret) ++ return ret; ++ ++ if (ctrls->auto_exposure->cur.val != ctrls->auto_exposure->val && ++ !is_auto) { ++ /* ++ * Hack: At this point, there are current volatile ++ * values in val, but control framework will not ++ * update the cur values for our autocluster, as it ++ * should. I couldn't find the reason. This fixes ++ * it for our driver. Remove this after the kernel ++ * is fixed. ++ */ ++ ctrls->exposure->cur.val = ctrls->exposure->val; ++ ctrls->d_gain->cur.val = ctrls->d_gain->val; ++ ctrls->a_gain->cur.val = ctrls->a_gain->val; ++ } ++ } ++ ++ if (!is_auto && ctrls->exposure->is_new) { ++ ret = hm5065_write16(sensor, ++ HM5065_REG_DIRECT_MODE_COARSE_INTEGRATION_LINES, ++ ctrls->exposure->val); ++ if (ret) ++ return ret; ++ } ++ ++ if (!is_auto && ctrls->d_gain->is_new) { ++ ret = hm5065_write16(sensor, ++ HM5065_REG_DIRECT_MODE_DIGITAL_GAIN, ++ hm5065_mili_to_fp16(ctrls->d_gain->val)); ++ if (ret) ++ return ret; ++ } ++ ++ if (!is_auto && ctrls->a_gain->is_new) ++ ret = hm5065_write16(sensor, ++ HM5065_REG_DIRECT_MODE_CODED_ANALOG_GAIN, ++ ctrls->a_gain->val); ++ ++ return ret; ++} ++ ++static int hm5065_3a_lock(struct hm5065_dev *sensor, struct v4l2_ctrl *ctrl) ++{ ++ bool awb_lock = ctrl->val & V4L2_LOCK_WHITE_BALANCE; ++ bool ae_lock = ctrl->val & V4L2_LOCK_EXPOSURE; ++ int ret = 0; ++ ++ if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_EXPOSURE ++ && sensor->ctrls.auto_exposure->val == V4L2_EXPOSURE_AUTO) { ++ ret = hm5065_write(sensor, HM5065_REG_FREEZE_AUTO_EXPOSURE, ++ ae_lock); ++ if (ret) ++ return ret; ++ } ++ ++ if (((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_WHITE_BALANCE) ++ && sensor->ctrls.wb->val == V4L2_WHITE_BALANCE_AUTO) { ++ ret = hm5065_write(sensor, HM5065_REG_WB_MISC_SETTINGS, ++ awb_lock ? ++ HM5065_REG_WB_MISC_SETTINGS_FREEZE_ALGO : 0); ++ if (ret) ++ return ret; ++ } ++ ++ return ret; ++} ++ ++static int hm5065_set_auto_focus(struct hm5065_dev *sensor) ++{ ++ struct hm5065_ctrls *ctrls = &sensor->ctrls; ++ bool auto_focus = ctrls->focus_auto->val; ++ int ret = 0; ++ u8 range; ++ ++ if (auto_focus && ctrls->af_distance->is_new) { ++ switch (ctrls->af_distance->val) { ++ case V4L2_AUTO_FOCUS_RANGE_MACRO: ++ range = HM5065_REG_AF_RANGE_MACRO; ++ break; ++ case V4L2_AUTO_FOCUS_RANGE_AUTO: ++ range = HM5065_REG_AF_RANGE_FULL; ++ break; ++ case V4L2_AUTO_FOCUS_RANGE_INFINITY: ++ range = HM5065_REG_AF_RANGE_LANDSCAPE; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ ret = hm5065_write(sensor, HM5065_REG_AF_RANGE, range); ++ if (ret) ++ return ret; ++ } ++ ++ if (ctrls->focus_auto->is_new) { ++ v4l2_ctrl_activate(ctrls->af_start, !auto_focus); ++ v4l2_ctrl_activate(ctrls->af_stop, !auto_focus); ++ v4l2_ctrl_activate(ctrls->focus_relative, !auto_focus); ++ ++ ret = hm5065_write(sensor, HM5065_REG_AF_MODE, ++ auto_focus ? ++ HM5065_REG_AF_MODE_CONTINUOUS : ++ HM5065_REG_AF_MODE_SINGLE); ++ if (ret) ++ return ret; ++ ++ if (!auto_focus) { ++ ret = hm5065_write(sensor, HM5065_REG_AF_COMMAND, ++ HM5065_REG_AF_COMMAND_RELEASED_BUTTON); ++ if (ret) ++ return ret; ++ } ++ } ++ ++ if (!auto_focus && ctrls->af_start->is_new) { ++ ret = hm5065_write(sensor, HM5065_REG_AF_MODE, ++ HM5065_REG_AF_MODE_SINGLE); ++ if (ret) ++ return ret; ++ ++ ret = hm5065_write(sensor, HM5065_REG_AF_COMMAND, ++ HM5065_REG_AF_COMMAND_RELEASED_BUTTON); ++ if (ret) ++ return ret; ++ ++ usleep_range(190000, 200000); ++ ++ ret = hm5065_write(sensor, HM5065_REG_AF_COMMAND, ++ HM5065_REG_AF_COMMAND_HALF_BUTTON); ++ if (ret) ++ return ret; ++ } ++ ++ if (!auto_focus && ctrls->af_stop->is_new) { ++ ret = hm5065_write(sensor, HM5065_REG_AF_COMMAND, ++ HM5065_REG_AF_COMMAND_RELEASED_BUTTON); ++ if (ret) ++ return ret; ++ ++ ret = hm5065_write(sensor, HM5065_REG_AF_MODE, ++ HM5065_REG_AF_MODE_MANUAL); ++ if (ret) ++ return ret; ++ } ++ ++ if (!auto_focus && ctrls->focus_relative->is_new && ++ ctrls->focus_relative->val) { ++ u8 cmd = 0xff; ++ s32 step = ctrls->focus_relative->val; ++ ++ ctrls->focus_relative->val = 0; ++ ++ ret = hm5065_write(sensor, HM5065_REG_AF_MODE, ++ HM5065_REG_AF_MODE_MANUAL); ++ if (ret) ++ return ret; ++ ++ ret = hm5065_write(sensor, HM5065_REG_AF_MANUAL_STEP_SIZE, ++ abs(step)); ++ if (ret) ++ return ret; ++ ++ if (step < 0) ++ cmd = HM5065_REG_AF_LENS_COMMAND_MOVE_STEP_TO_INFINITY; ++ else if (step > 0) ++ cmd = HM5065_REG_AF_LENS_COMMAND_MOVE_STEP_TO_MACRO; ++ ++ if (cmd != 0xff) ++ ret = hm5065_write(sensor, HM5065_REG_AF_LENS_COMMAND, ++ cmd); ++ ++ if (ret) ++ return ret; ++ } ++ ++ return ret; ++} ++ ++static int hm5065_set_white_balance(struct hm5065_dev *sensor) ++{ ++ struct hm5065_ctrls *ctrls = &sensor->ctrls; ++ bool manual_wb = ctrls->wb->val == V4L2_WHITE_BALANCE_MANUAL; ++ int ret = 0, i; ++ s32 val; ++ ++ if (ctrls->wb->is_new) { ++ for (i = 0; i < ARRAY_SIZE(hm5065_wb_opts); i++) { ++ if (hm5065_wb_opts[i][0] != ctrls->wb->val) ++ continue; ++ ++ ret = hm5065_write(sensor, HM5065_REG_WB_MODE, ++ hm5065_wb_opts[i][1]); ++ if (ret) ++ return ret; ++ goto next; ++ } ++ ++ return -EINVAL; ++ } ++ ++next: ++ if (ctrls->wb->is_new || ctrls->blue_balance->is_new) { ++ val = manual_wb ? ctrls->blue_balance->val : 1000; ++ ret = hm5065_write16(sensor, HM5065_REG_WB_HUE_B_BIAS, ++ hm5065_mili_to_fp16(val)); ++ if (ret) ++ return ret; ++ } ++ ++ if (ctrls->wb->is_new || ctrls->red_balance->is_new) { ++ val = manual_wb ? ctrls->red_balance->val : 1000; ++ ret = hm5065_write16(sensor, HM5065_REG_WB_HUE_R_BIAS, ++ hm5065_mili_to_fp16(val)); ++ } ++ ++ return ret; ++} ++ ++static int hm5065_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd = ctrl_to_sd(ctrl); ++ struct hm5065_dev *sensor = to_hm5065_dev(sd); ++ struct hm5065_ctrls *ctrls = &sensor->ctrls; ++ s32 val = ctrl->val; ++ unsigned int i; ++ int ret; ++ u8 reg; ++ ++ /* v4l2_ctrl_lock() locks our own mutex */ ++ ++ /* ++ * If the device is not powered up by the host driver do ++ * not apply any controls to H/W at this time. Instead ++ * the controls will be restored right after power-up. ++ */ ++ if (!sensor->powered) ++ return 0; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_EXPOSURE_AUTO: ++ return hm5065_set_exposure(sensor); ++ ++ case V4L2_CID_EXPOSURE_METERING: ++ if (val == V4L2_EXPOSURE_METERING_AVERAGE) ++ reg = HM5065_REG_EXPOSURE_METERING_FLAT; ++ else if (val == V4L2_EXPOSURE_METERING_CENTER_WEIGHTED) ++ reg = HM5065_REG_EXPOSURE_METERING_CENTERED; ++ else ++ return -EINVAL; ++ ++ return hm5065_write(sensor, HM5065_REG_EXPOSURE_METERING, reg); ++ ++ case V4L2_CID_AUTO_EXPOSURE_BIAS: ++ if (val < 0 || val >= ARRAY_SIZE(ae_bias_menu_reg_values)) ++ return -EINVAL; ++ ++ return hm5065_write(sensor, HM5065_REG_EXPOSURE_COMPENSATION, ++ (u8)ae_bias_menu_reg_values[val]); ++ ++ case V4L2_CID_FOCUS_AUTO: ++ return hm5065_set_auto_focus(sensor); ++ ++ case V4L2_CID_CONTRAST: ++ return hm5065_write(sensor, HM5065_REG_CONTRAST, val); ++ ++ case V4L2_CID_SATURATION: ++ return hm5065_write(sensor, HM5065_REG_COLOR_SATURATION, val); ++ ++ case V4L2_CID_BRIGHTNESS: ++ return hm5065_write(sensor, HM5065_REG_BRIGHTNESS, val); ++ ++ case V4L2_CID_POWER_LINE_FREQUENCY: ++ return hm5065_set_power_line_frequency(sensor, val); ++ ++ case V4L2_CID_GAMMA: ++ return hm5065_write(sensor, HM5065_REG_P0_GAMMA_GAIN, val); ++ ++ case V4L2_CID_VFLIP: ++ return hm5065_write(sensor, HM5065_REG_VERTICAL_FLIP, ++ val ? 1 : 0); ++ ++ case V4L2_CID_HFLIP: ++ return hm5065_write(sensor, HM5065_REG_HORIZONTAL_MIRROR, ++ val ? 1 : 0); ++ ++ case V4L2_CID_COLORFX: ++ return hm5065_set_colorfx(sensor, val); ++ ++ case V4L2_CID_3A_LOCK: ++ return hm5065_3a_lock(sensor, ctrl); ++ ++ case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: ++ return hm5065_set_white_balance(sensor); ++ ++ case V4L2_CID_TEST_PATTERN_RED: ++ return hm5065_write16(sensor, HM5065_REG_TESTDATA_RED, val); ++ ++ case V4L2_CID_TEST_PATTERN_GREENR: ++ return hm5065_write16(sensor, HM5065_REG_TESTDATA_GREEN_R, val); ++ ++ case V4L2_CID_TEST_PATTERN_BLUE: ++ return hm5065_write16(sensor, HM5065_REG_TESTDATA_BLUE, val); ++ ++ case V4L2_CID_TEST_PATTERN_GREENB: ++ return hm5065_write16(sensor, HM5065_REG_TESTDATA_GREEN_B, val); ++ ++ case V4L2_CID_TEST_PATTERN: ++ for (i = 0; i < ARRAY_SIZE(ctrls->test_data); i++) ++ v4l2_ctrl_activate(ctrls->test_data[i], ++ val == 6); /* solid color */ ++ ++ ret = hm5065_write(sensor, HM5065_REG_ENABLE_TEST_PATTERN, ++ val == 0 ? 0 : 1); ++ if (ret) ++ return ret; ++ ++ return hm5065_write(sensor, HM5065_REG_TEST_PATTERN, val); ++ ++ default: ++ return -EINVAL; ++ } ++} ++ ++static const struct v4l2_ctrl_ops hm5065_ctrl_ops = { ++ .g_volatile_ctrl = hm5065_g_volatile_ctrl, ++ .s_ctrl = hm5065_s_ctrl, ++}; ++ ++static const char * const test_pattern_menu[] = { ++ "Disabled", ++ "Horizontal gray scale", ++ "Vertical gray scale", ++ "Diagonal gray scale", ++ "PN28", ++ "PN9 (bus test)", ++ "Solid color", ++ "Color bars", ++ "Graduated color bars", ++}; ++ ++static int hm5065_init_controls(struct hm5065_dev *sensor) ++{ ++ const struct v4l2_ctrl_ops *ops = &hm5065_ctrl_ops; ++ struct hm5065_ctrls *ctrls = &sensor->ctrls; ++ struct v4l2_ctrl_handler *hdl = &ctrls->handler; ++ u8 wb_max = 0; ++ u64 wb_mask = 0; ++ unsigned int i; ++ int ret; ++ ++ v4l2_ctrl_handler_init(hdl, 32); ++ ++ /* we can use our own mutex for the ctrl lock */ ++ hdl->lock = &sensor->lock; ++ ++ ctrls->auto_exposure = v4l2_ctrl_new_std_menu(hdl, ops, ++ V4L2_CID_EXPOSURE_AUTO, ++ V4L2_EXPOSURE_MANUAL, 0, ++ V4L2_EXPOSURE_AUTO); ++ ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, ++ V4L2_CID_EXPOSURE, ++ 1, HM5065_SENSOR_HEIGHT, 1, 30); ++ ctrls->d_gain = v4l2_ctrl_new_std(hdl, ops, ++ V4L2_CID_DIGITAL_GAIN, ++ 1000, 4000, 1, 1000); ++ ++ ctrls->a_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN, ++ 0, 0xf4, 1, 0); ++ ++ ctrls->metering = ++ v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_EXPOSURE_METERING, ++ V4L2_EXPOSURE_METERING_CENTER_WEIGHTED, ++ 0, V4L2_EXPOSURE_METERING_AVERAGE); ++ ctrls->exposure_bias = ++ v4l2_ctrl_new_int_menu(hdl, ops, ++ V4L2_CID_AUTO_EXPOSURE_BIAS, ++ ARRAY_SIZE(ae_bias_menu_values) - 1, ++ AE_BIAS_MENU_DEFAULT_VALUE_INDEX, ++ ae_bias_menu_values); ++ ++ for (i = 0; i < ARRAY_SIZE(hm5065_wb_opts); i++) { ++ if (wb_max < hm5065_wb_opts[i][0]) ++ wb_max = hm5065_wb_opts[i][0]; ++ wb_mask |= BIT(hm5065_wb_opts[i][0]); ++ } ++ ++ ctrls->wb = v4l2_ctrl_new_std_menu(hdl, ops, ++ V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, ++ wb_max, ~wb_mask, V4L2_WHITE_BALANCE_AUTO); ++ ++ ctrls->blue_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BLUE_BALANCE, ++ 0, 4000, 1, 1000); ++ ctrls->red_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_RED_BALANCE, ++ 0, 4000, 1, 1000); ++ ++ ctrls->gamma = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAMMA, ++ 0, 31, 1, 20); ++ ++ ctrls->colorfx = ++ v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_COLORFX, 15, ++ ~(BIT(V4L2_COLORFX_NONE) | ++ BIT(V4L2_COLORFX_NEGATIVE) | ++ BIT(V4L2_COLORFX_SOLARIZATION) | ++ BIT(V4L2_COLORFX_SKETCH) | ++ BIT(V4L2_COLORFX_SEPIA) | ++ BIT(V4L2_COLORFX_ANTIQUE) | ++ BIT(V4L2_COLORFX_AQUA) | ++ BIT(V4L2_COLORFX_BW)), ++ V4L2_COLORFX_NONE); ++ ++ ctrls->pl_freq = ++ v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_POWER_LINE_FREQUENCY, ++ V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0, ++ V4L2_CID_POWER_LINE_FREQUENCY_50HZ); ++ ++ ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, ++ V4L2_CID_HFLIP, 0, 1, 1, 0); ++ ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, ++ V4L2_CID_VFLIP, 0, 1, 1, 0); ++ ++ ctrls->focus_auto = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FOCUS_AUTO, ++ 0, 1, 1, 1); ++ ++ ctrls->af_start = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_FOCUS_START, ++ 0, 1, 1, 0); ++ ++ ctrls->af_stop = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_FOCUS_STOP, ++ 0, 1, 1, 0); ++ ++ ctrls->af_status = v4l2_ctrl_new_std(hdl, ops, ++ V4L2_CID_AUTO_FOCUS_STATUS, 0, ++ (V4L2_AUTO_FOCUS_STATUS_BUSY | ++ V4L2_AUTO_FOCUS_STATUS_REACHED | ++ V4L2_AUTO_FOCUS_STATUS_FAILED), ++ 0, V4L2_AUTO_FOCUS_STATUS_IDLE); ++ ++ ctrls->af_distance = ++ v4l2_ctrl_new_std_menu(hdl, ops, ++ V4L2_CID_AUTO_FOCUS_RANGE, ++ V4L2_AUTO_FOCUS_RANGE_MACRO, ++ ~(BIT(V4L2_AUTO_FOCUS_RANGE_AUTO) | ++ BIT(V4L2_AUTO_FOCUS_RANGE_INFINITY) | ++ BIT(V4L2_AUTO_FOCUS_RANGE_MACRO)), ++ V4L2_AUTO_FOCUS_RANGE_AUTO); ++ ++ ctrls->focus_relative = v4l2_ctrl_new_std(hdl, ops, ++ V4L2_CID_FOCUS_RELATIVE, ++ -100, 100, 1, 0); ++ ++ ctrls->brightness = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, ++ 0, 200, 1, 90); ++ ctrls->saturation = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION, ++ 0, 200, 1, 110); ++ ctrls->contrast = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, ++ 0, 200, 1, 108); ++ ++ ctrls->aaa_lock = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_3A_LOCK, ++ 0, 0x7, 0, 0); ++ ++ ctrls->test_pattern = ++ v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN, ++ ARRAY_SIZE(test_pattern_menu) - 1, ++ 0, 0, test_pattern_menu); ++ for (i = 0; i < ARRAY_SIZE(ctrls->test_data); i++) ++ ctrls->test_data[i] = ++ v4l2_ctrl_new_std(hdl, ops, ++ V4L2_CID_TEST_PATTERN_RED + i, ++ 0, 1023, 1, 0); ++ ++ if (hdl->error) { ++ ret = hdl->error; ++ goto free_ctrls; ++ } ++ ++ ctrls->af_status->flags |= V4L2_CTRL_FLAG_VOLATILE | ++ V4L2_CTRL_FLAG_READ_ONLY; ++ ++ v4l2_ctrl_auto_cluster(3, &ctrls->wb, V4L2_WHITE_BALANCE_MANUAL, false); ++ v4l2_ctrl_auto_cluster(4, &ctrls->auto_exposure, V4L2_EXPOSURE_MANUAL, ++ true); ++ v4l2_ctrl_cluster(6, &ctrls->focus_auto); ++ ++ sensor->sd.ctrl_handler = hdl; ++ return 0; ++ ++free_ctrls: ++ v4l2_ctrl_handler_free(hdl); ++ return ret; ++} ++ ++/* }}} */ ++/* {{{ Video ops */ ++ ++static int hm5065_get_frame_interval(struct v4l2_subdev *sd, ++ struct v4l2_subdev_state *sd_state, ++ struct v4l2_subdev_frame_interval *fi) ++{ ++ struct hm5065_dev *sensor = to_hm5065_dev(sd); ++ ++ if (fi->pad != 0) ++ return -EINVAL; ++ ++ mutex_lock(&sensor->lock); ++ fi->interval = sensor->frame_interval; ++ mutex_unlock(&sensor->lock); ++ ++ return 0; ++} ++ ++static int hm5065_get_max_binning(int width, int height) ++{ ++ if (width < HM5065_SENSOR_WIDTH / 4 && ++ height < HM5065_SENSOR_HEIGHT / 4) ++ return 4; ++ else if (width < HM5065_SENSOR_WIDTH / 2 && ++ height < HM5065_SENSOR_HEIGHT / 2) ++ return 2; ++ ++ return 1; ++} ++ ++static int hm5065_get_max_fps(int width, int height) ++{ ++ int max_fps, bin_factor; ++ ++ // more bining allows for faster readouts ++ bin_factor = hm5065_get_max_binning(width, height); ++ max_fps = 25000000 / (width * height * 2) * bin_factor; ++ ++ return clamp(max_fps, 1, 60); ++} ++ ++static int hm5065_set_frame_interval(struct v4l2_subdev *sd, ++ struct v4l2_subdev_state *sd_state, ++ struct v4l2_subdev_frame_interval *fi) ++{ ++ struct hm5065_dev *sensor = to_hm5065_dev(sd); ++ int ret = 0, fps, max_fps; ++ ++ if (fi->pad != 0) ++ return -EINVAL; ++ ++ mutex_lock(&sensor->lock); ++ ++ max_fps = hm5065_get_max_fps(sensor->fmt.width, sensor->fmt.height); ++ ++ /* user requested infinite frame rate */ ++ if (fi->interval.numerator == 0) ++ fps = max_fps; ++ else ++ fps = DIV_ROUND_CLOSEST(fi->interval.denominator, ++ fi->interval.numerator); ++ ++ fps = clamp(fps, 1, max_fps); ++ ++ sensor->frame_interval.numerator = 1; ++ sensor->frame_interval.denominator = fps; ++ fi->interval = sensor->frame_interval; ++ ++ if (sensor->streaming) { ++ ret = hm5065_write16(sensor, HM5065_REG_DESIRED_FRAME_RATE_NUM, ++ fps); ++ if (ret) ++ goto err_unlock; ++ ++ ret = hm5065_write(sensor, HM5065_REG_DESIRED_FRAME_RATE_DEN, ++ 1); ++ } ++ ++err_unlock: ++ mutex_unlock(&sensor->lock); ++ return ret; ++} ++ ++static int hm5065_setup_mode(struct hm5065_dev *sensor) ++{ ++ const struct hm5065_pixfmt *pix_fmt; ++ u8 sensor_mode; ++ int ret, fps; ++ ++ pix_fmt = hm5065_find_format(sensor->fmt.code); ++ if (!pix_fmt) { ++ dev_err(&sensor->i2c_client->dev, ++ "pixel format not supported %u\n", sensor->fmt.code); ++ return -EINVAL; ++ } ++ ++ ret = hm5065_write(sensor, HM5065_REG_USER_COMMAND, ++ HM5065_REG_USER_COMMAND_POWEROFF); ++ if (ret) ++ return ret; ++ ++ switch (hm5065_get_max_binning(sensor->fmt.width, sensor->fmt.height)) { ++ case 4: ++ sensor_mode = HM5065_REG_SENSOR_MODE_BINNING_4X4; ++ break; ++ case 2: ++ sensor_mode = HM5065_REG_SENSOR_MODE_BINNING_2X2; ++ break; ++ default: ++ sensor_mode = HM5065_REG_SENSOR_MODE_FULLSIZE; ++ } ++ ++ ret = hm5065_write(sensor, HM5065_REG_P0_SENSOR_MODE, sensor_mode); ++ if (ret) ++ return ret; ++ ++ ret = hm5065_write16(sensor, HM5065_REG_P0_MANUAL_HSIZE, ++ sensor->fmt.width); ++ if (ret) ++ return ret; ++ ++ ret = hm5065_write16(sensor, HM5065_REG_P0_MANUAL_VSIZE, ++ sensor->fmt.height); ++ if (ret) ++ return ret; ++ ++ ret = hm5065_write(sensor, HM5065_REG_P0_IMAGE_SIZE, ++ HM5065_REG_IMAGE_SIZE_MANUAL); ++ if (ret) ++ return ret; ++ ++ ret = hm5065_write(sensor, HM5065_REG_P0_DATA_FORMAT, ++ pix_fmt->data_fmt); ++ if (ret) ++ return ret; ++ ++ ret = hm5065_write(sensor, HM5065_REG_YCRCB_ORDER, ++ pix_fmt->ycbcr_order); ++ if (ret) ++ return ret; ++ ++ /* without this, brightness, contrast and saturation will not work */ ++ ret = hm5065_write(sensor, HM5065_REG_COLORSPACE, 9); ++ if (ret) ++ return ret; ++ ++ ret = hm5065_write(sensor, HM5065_REG_BUS_DATA_FORMAT, ++ pix_fmt->fmt_setup); ++ if (ret) ++ return ret; ++ ++ fps = hm5065_get_max_fps(sensor->fmt.width, sensor->fmt.height); ++ fps = clamp(fps, 1, (int)sensor->frame_interval.denominator); ++ ++ ret = hm5065_write16(sensor, HM5065_REG_DESIRED_FRAME_RATE_NUM, fps); ++ if (ret) ++ return ret; ++ ++ ret = hm5065_write(sensor, HM5065_REG_DESIRED_FRAME_RATE_DEN, 1); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static int hm5065_set_stream(struct hm5065_dev *sensor, int enable) ++{ ++ return hm5065_write(sensor, HM5065_REG_USER_COMMAND, enable ? ++ HM5065_REG_USER_COMMAND_RUN : ++ HM5065_REG_USER_COMMAND_STOP); ++} ++ ++static int hm5065_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ struct hm5065_dev *sensor = to_hm5065_dev(sd); ++ int ret = 0; ++ ++ mutex_lock(&sensor->lock); ++ ++ if (sensor->streaming == !enable) { ++ if (enable && sensor->pending_mode_change) { ++ ret = hm5065_setup_mode(sensor); ++ if (ret) ++ goto out; ++ } ++ ++ ret = hm5065_set_stream(sensor, enable); ++ if (ret) ++ goto out; ++ ++ if (enable && sensor->ctrls.focus_auto->cur.val) { ++ msleep(100); ++ ++ /* checking error here is not super important */ ++ hm5065_write(sensor, HM5065_REG_AF_MODE, ++ HM5065_REG_AF_MODE_CONTINUOUS); ++ } ++ ++ sensor->streaming = !!enable; ++ } ++ ++out: ++ mutex_unlock(&sensor->lock); ++ return ret; ++} ++ ++/* }}} */ ++/* {{{ Pad ops */ ++ ++static int hm5065_enum_mbus_code(struct v4l2_subdev *sd, ++ struct v4l2_subdev_state *sd_state, ++ struct v4l2_subdev_mbus_code_enum *code) ++{ ++ if (code->pad != 0) ++ return -EINVAL; ++ if (code->index >= HM5065_NUM_FORMATS) ++ return -EINVAL; ++ ++ code->code = hm5065_formats[code->index].code; ++ ++ return 0; ++} ++ ++static int hm5065_enum_frame_size(struct v4l2_subdev *sd, ++ struct v4l2_subdev_state *sd_state, ++ struct v4l2_subdev_frame_size_enum *fse) ++{ ++ if (fse->pad != 0) ++ return -EINVAL; ++ if (fse->index != 0) ++ return -EINVAL; ++ ++ fse->min_width = HM5065_CAPTURE_WIDTH_MIN; ++ fse->min_height = HM5065_CAPTURE_HEIGHT_MIN; ++ ++ fse->max_width = HM5065_SENSOR_WIDTH; ++ fse->max_height = HM5065_SENSOR_HEIGHT; ++ ++ return 0; ++} ++ ++static int hm5065_enum_frame_interval(struct v4l2_subdev *sd, ++ struct v4l2_subdev_state *sd_state, ++ struct v4l2_subdev_frame_interval_enum ++ *fie) ++{ ++ struct v4l2_fract tpf; ++ u32 max_fps, width, height; ++ ++ if (fie->pad != 0) ++ return -EINVAL; ++ ++ width = clamp(fie->width, HM5065_CAPTURE_WIDTH_MIN, ++ HM5065_SENSOR_WIDTH); ++ height = clamp(fie->height, HM5065_CAPTURE_HEIGHT_MIN, ++ HM5065_SENSOR_HEIGHT); ++ ++ max_fps = hm5065_get_max_fps(width, height); ++ ++ if (fie->index + 1 > max_fps) ++ return -EINVAL; ++ ++ tpf.numerator = 1; ++ tpf.denominator = fie->index + 1; ++ fie->interval = tpf; ++ ++ return 0; ++} ++ ++static int hm5065_get_fmt(struct v4l2_subdev *sd, ++ struct v4l2_subdev_state *sd_state, ++ struct v4l2_subdev_format *format) ++{ ++ struct hm5065_dev *sensor = to_hm5065_dev(sd); ++ struct v4l2_mbus_framefmt *mf; ++ ++ if (format->pad != 0) ++ return -EINVAL; ++ ++ if (format->which == V4L2_SUBDEV_FORMAT_TRY) { ++ mf = v4l2_subdev_state_get_format(sd_state, format->pad); ++ format->format = *mf; ++ return 0; ++ } ++ ++ mutex_lock(&sensor->lock); ++ format->format = sensor->fmt; ++ mutex_unlock(&sensor->lock); ++ ++ return 0; ++} ++ ++static int hm5065_set_fmt(struct v4l2_subdev *sd, ++ struct v4l2_subdev_state *sd_state, ++ struct v4l2_subdev_format *format) ++{ ++ struct hm5065_dev *sensor = to_hm5065_dev(sd); ++ struct v4l2_mbus_framefmt *mf = &format->format; ++ const struct hm5065_pixfmt *pixfmt; ++ int ret = 0; ++ ++ if (format->pad != 0) ++ return -EINVAL; ++ ++ /* check if we support requested mbus fmt */ ++ pixfmt = hm5065_find_format(mf->code); ++ if (!pixfmt) ++ pixfmt = &hm5065_formats[0]; ++ ++ mf->code = pixfmt->code; ++ mf->colorspace = pixfmt->colorspace; ++ mf->xfer_func = V4L2_XFER_FUNC_DEFAULT; ++ mf->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; ++ mf->quantization = V4L2_QUANTIZATION_DEFAULT; ++ mf->field = V4L2_FIELD_NONE; ++ ++ mutex_lock(&sensor->lock); ++ ++ mf->width = clamp(mf->width, HM5065_CAPTURE_WIDTH_MIN, ++ HM5065_SENSOR_WIDTH); ++ mf->height = clamp(mf->height, HM5065_CAPTURE_HEIGHT_MIN, ++ HM5065_SENSOR_HEIGHT); ++ ++ if (format->which == V4L2_SUBDEV_FORMAT_TRY) { ++ struct v4l2_mbus_framefmt *try_mf; ++ ++ try_mf = v4l2_subdev_state_get_format(sd_state, format->pad); ++ *try_mf = *mf; ++ goto out; ++ } ++ ++ if (sensor->streaming) { ++ ret = -EBUSY; ++ goto out; ++ } ++ ++ sensor->fmt = *mf; ++ sensor->pending_mode_change = true; ++out: ++ mutex_unlock(&sensor->lock); ++ return ret; ++} ++ ++/* }}} */ ++/* {{{ Core Ops */ ++ ++static void hm5065_chip_enable(struct hm5065_dev *sensor, bool enable) ++{ ++ gpiod_set_value(sensor->enable_gpio, enable ? 1 : 0); ++ gpiod_set_value(sensor->reset_gpio, enable ? 0 : 1); ++} ++ ++static int hm5065_configure(struct hm5065_dev *sensor) ++{ ++ int ret; ++ u16 device_id; ++ const struct hm5065_clk_lut *lut; ++ unsigned long xclk_freq; ++ ++ ret = hm5065_read16(sensor, HM5065_REG_DEVICE_ID, &device_id); ++ if (ret) ++ return ret; ++ ++ if (device_id != HM5065_REG_DEVICE_ID_VALUE) { ++ dev_err(&sensor->i2c_client->dev, ++ "unsupported device id: 0x%04x\n", ++ (unsigned int)device_id); ++ return -EINVAL; ++ } ++ ++ xclk_freq = clk_get_rate(sensor->xclk); ++ lut = hm5065_find_clk_lut(xclk_freq); ++ if (!lut) { ++ dev_err(&sensor->i2c_client->dev, ++ "xclk frequency out of range: %lu Hz\n", xclk_freq); ++ return -EINVAL; ++ } ++ ++ ret = hm5065_write(sensor, HM5065_REG_EXCLOCKLUT, lut->lut_id); ++ if (ret) ++ return ret; ++ ++ ret = hm5065_load_firmware(sensor, HM5065_AF_FIRMWARE); ++ if (ret < 0) ++ return ret; ++ ++ if (ret == 0) /* ret == 1 means firmware file missing */ ++ mdelay(200); ++ ++ ret = hm5065_load_firmware(sensor, HM5065_FIRMWARE_PARAMETERS); ++ if (ret < 0) ++ return ret; ++ ++ if (sensor->ep.bus_type == V4L2_MBUS_BT656) { ++ ret = hm5065_write(sensor, HM5065_REG_BUS_CONFIG, ++ HM5065_REG_BUS_CONFIG_BT656); ++ } else { ++ ret = hm5065_write(sensor, HM5065_REG_BUS_CONFIG, ++ HM5065_REG_BUS_CONFIG_PARALLEL_HH_VL); ++ } ++ ++ return ret; ++} ++ ++static int hm5065_set_power(struct hm5065_dev *sensor, bool on) ++{ ++ int ret = 0; ++ ++ if (on) { ++ ret = regulator_bulk_enable(HM5065_NUM_SUPPLIES, ++ sensor->supplies); ++ if (ret) ++ return ret; ++ ++ ret = clk_prepare_enable(sensor->xclk); ++ if (ret) ++ goto power_off; ++ ++ ret = clk_set_rate(sensor->xclk, 24000000); ++ if (ret) ++ goto xclk_off; ++ ++ usleep_range(1000, 2000); ++ hm5065_chip_enable(sensor, false); ++ usleep_range(1000, 2000); ++ hm5065_chip_enable(sensor, true); ++ usleep_range(50000, 70000); ++ ++ ret = hm5065_configure(sensor); ++ if (ret) ++ goto xclk_off; ++ ++ ret = hm5065_setup_mode(sensor); ++ if (ret) ++ goto xclk_off; ++ ++ return 0; ++ } ++ ++xclk_off: ++ clk_disable_unprepare(sensor->xclk); ++power_off: ++ hm5065_chip_enable(sensor, false); ++ regulator_bulk_disable(HM5065_NUM_SUPPLIES, sensor->supplies); ++ msleep(100); ++ return ret; ++} ++ ++static int hm5065_s_power(struct v4l2_subdev *sd, int on) ++{ ++ struct hm5065_dev *sensor = to_hm5065_dev(sd); ++ bool power_up, power_down; ++ int ret = 0; ++ ++ mutex_lock(&sensor->lock); ++ ++ power_up = on && !sensor->powered; ++ power_down = !on && sensor->powered; ++ ++ if (power_up || power_down) { ++ ret = hm5065_set_power(sensor, power_up); ++ if (!ret) ++ sensor->powered = on; ++ } ++ ++ mutex_unlock(&sensor->lock); ++ ++ if (!ret && power_up) { ++ /* restore controls */ ++ ret = v4l2_ctrl_handler_setup(&sensor->ctrls.handler); ++ if (ret) ++ hm5065_s_power(sd, 0); ++ } ++ ++ return ret; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int hm5065_g_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct hm5065_dev *sensor = to_hm5065_dev(sd); ++ int ret; ++ u8 val = 0; ++ ++ if (reg->reg > 0xffff) ++ return -EINVAL; ++ ++ reg->size = 1; ++ ++ mutex_lock(&sensor->lock); ++ ret = hm5065_read(sensor, reg->reg, &val); ++ mutex_unlock(&sensor->lock); ++ if (ret) ++ return -EIO; ++ ++ reg->val = val; ++ return 0; ++} ++ ++static int hm5065_s_register(struct v4l2_subdev *sd, ++ const struct v4l2_dbg_register *reg) ++{ ++ struct hm5065_dev *sensor = to_hm5065_dev(sd); ++ int ret; ++ ++ if (reg->reg > 0xffff || reg->val > 0xff) ++ return -EINVAL; ++ ++ mutex_lock(&sensor->lock); ++ ret = hm5065_write(sensor, reg->reg, reg->val); ++ mutex_unlock(&sensor->lock); ++ ++ return ret; ++} ++#endif ++ ++/* }}} */ ++ ++static const struct v4l2_subdev_core_ops hm5065_core_ops = { ++ .s_power = hm5065_s_power, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = hm5065_g_register, ++ .s_register = hm5065_s_register, ++#endif ++}; ++ ++static const struct v4l2_subdev_pad_ops hm5065_pad_ops = { ++ .enum_mbus_code = hm5065_enum_mbus_code, ++ .enum_frame_size = hm5065_enum_frame_size, ++ .enum_frame_interval = hm5065_enum_frame_interval, ++ .get_fmt = hm5065_get_fmt, ++ .set_fmt = hm5065_set_fmt, ++ .get_frame_interval = hm5065_get_frame_interval, ++ .set_frame_interval = hm5065_set_frame_interval, ++}; ++ ++static const struct v4l2_subdev_video_ops hm5065_video_ops = { ++ .s_stream = hm5065_s_stream, ++}; ++ ++static const struct v4l2_subdev_ops hm5065_subdev_ops = { ++ .core = &hm5065_core_ops, ++ .pad = &hm5065_pad_ops, ++ .video = &hm5065_video_ops, ++}; ++ ++static int hm5065_get_regulators(struct hm5065_dev *sensor) ++{ ++ int i; ++ ++ for (i = 0; i < HM5065_NUM_SUPPLIES; i++) ++ sensor->supplies[i].supply = hm5065_supply_name[i]; ++ ++ return devm_regulator_bulk_get(&sensor->i2c_client->dev, ++ HM5065_NUM_SUPPLIES, ++ sensor->supplies); ++} ++ ++#define HM5065_PARALLEL_SUPPORT_FLAGS \ ++ (V4L2_MBUS_HSYNC_ACTIVE_LOW | V4L2_MBUS_VSYNC_ACTIVE_HIGH | \ ++ V4L2_MBUS_PCLK_SAMPLE_FALLING | V4L2_MBUS_DATA_ACTIVE_HIGH) ++ ++static int hm5065_probe(struct i2c_client *client) ++{ ++ struct device *dev = &client->dev; ++ struct fwnode_handle *endpoint; ++ struct hm5065_dev *sensor; ++ int ret; ++ ++ sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); ++ if (!sensor) ++ return -ENOMEM; ++ ++ sensor->i2c_client = client; ++ ++ sensor->fmt.code = hm5065_formats[0].code; ++ sensor->fmt.width = 1280; ++ sensor->fmt.height = 720; ++ sensor->fmt.field = V4L2_FIELD_NONE; ++ sensor->frame_interval.numerator = 1; ++ sensor->frame_interval.denominator = 15; ++ sensor->pending_mode_change = true; ++ ++ endpoint = fwnode_graph_get_next_endpoint( ++ of_fwnode_handle(client->dev.of_node), NULL); ++ if (!endpoint) { ++ dev_err(dev, "endpoint node not found\n"); ++ return -EINVAL; ++ } ++ ++ ret = v4l2_fwnode_endpoint_parse(endpoint, &sensor->ep); ++ fwnode_handle_put(endpoint); ++ if (ret) { ++ dev_err(dev, "could not parse endpoint\n"); ++ return ret; ++ } ++ ++ /* ++ * We don't know how to configure the camera for any other parallel ++ * mode, yet. ++ */ ++ if (sensor->ep.bus_type != V4L2_MBUS_BT656 && ++ !(sensor->ep.bus_type == V4L2_MBUS_PARALLEL && ++ (sensor->ep.bus.parallel.flags & HM5065_PARALLEL_SUPPORT_FLAGS) == ++ HM5065_PARALLEL_SUPPORT_FLAGS)) { ++ dev_err(dev, "unsupported bus configuration %d/%08x\n", ++ sensor->ep.bus_type, sensor->ep.bus.parallel.flags); ++ return -EINVAL; ++ } ++ ++ /* get system clock (xclk) */ ++ sensor->xclk = devm_clk_get(dev, "xclk"); ++ if (IS_ERR(sensor->xclk)) { ++ dev_err(dev, "failed to get xclk\n"); ++ return PTR_ERR(sensor->xclk); ++ } ++ ++ sensor->enable_gpio = devm_gpiod_get_optional(dev, "enable", ++ GPIOD_OUT_LOW); ++ sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset", ++ GPIOD_OUT_HIGH); ++ ++ if (!sensor->enable_gpio && !sensor->reset_gpio) { ++ dev_err(dev, ++ "either chip enable or reset pin must be configured\n"); ++ return ret; ++ } ++ ++ v4l2_i2c_subdev_init(&sensor->sd, client, &hm5065_subdev_ops); ++ ++ sensor->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; ++ sensor->pad.flags = MEDIA_PAD_FL_SOURCE; ++ sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; ++ ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad); ++ if (ret) ++ return ret; ++ ++ ret = hm5065_get_regulators(sensor); ++ if (ret) ++ return ret; ++ ++ mutex_init(&sensor->lock); ++ ++ ret = hm5065_init_controls(sensor); ++ if (ret) ++ goto entity_cleanup; ++ ++ ret = v4l2_async_register_subdev(&sensor->sd); ++ if (ret) ++ goto free_ctrls; ++ ++ return 0; ++ ++free_ctrls: ++ v4l2_ctrl_handler_free(&sensor->ctrls.handler); ++entity_cleanup: ++ mutex_destroy(&sensor->lock); ++ media_entity_cleanup(&sensor->sd.entity); ++ return ret; ++} ++ ++static void hm5065_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct hm5065_dev *sensor = to_hm5065_dev(sd); ++ ++ v4l2_async_unregister_subdev(&sensor->sd); ++ mutex_destroy(&sensor->lock); ++ media_entity_cleanup(&sensor->sd.entity); ++ v4l2_ctrl_handler_free(&sensor->ctrls.handler); ++} ++ ++static const struct i2c_device_id hm5065_id[] = { ++ {"hm5065", 0}, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(i2c, hm5065_id); ++ ++static const struct of_device_id hm5065_dt_ids[] = { ++ { .compatible = "himax,hm5065" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, hm5065_dt_ids); ++ ++static struct i2c_driver hm5065_i2c_driver = { ++ .driver = { ++ .name = "hm5065", ++ .of_match_table = hm5065_dt_ids, ++ }, ++ .id_table = hm5065_id, ++ .probe = hm5065_probe, ++ .remove = hm5065_remove, ++}; ++ ++module_i2c_driver(hm5065_i2c_driver); ++ ++MODULE_AUTHOR("Ondrej Jirman "); ++MODULE_DESCRIPTION("HM5065 Camera Subdev Driver"); ++MODULE_LICENSE("GPL"); +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/media-i2c-gc2145-Move-upstream-driver-out-of-the-way.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/media-i2c-gc2145-Move-upstream-driver-out-of-the-way.patch new file mode 100644 index 000000000000..c808ad0e6693 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/media-i2c-gc2145-Move-upstream-driver-out-of-the-way.patch @@ -0,0 +1,52 @@ +From 430f13bc72248989cdf797c828aff616164e4ebf Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sat, 13 Jan 2024 21:00:27 +0100 +Subject: media: i2c: gc2145: Move upstream driver out of the way + +Upstream only implements MIPI CSI, but we already have a driver +with parallel bus support. + +Signed-off-by: Ondrej Jirman +--- + drivers/media/i2c/Kconfig | 4 ++-- + drivers/media/i2c/Makefile | 2 +- + drivers/media/i2c/{gc2145.c => gc2145-mipi.c} | 0 + 3 files changed, 3 insertions(+), 3 deletions(-) + rename drivers/media/i2c/{gc2145.c => gc2145-mipi.c} (100%) + +diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig +index e7da18f3da3c..43be07cc5b37 100644 +--- a/drivers/media/i2c/Kconfig ++++ b/drivers/media/i2c/Kconfig +@@ -90,9 +90,9 @@ config VIDEO_GC08A3 + To compile this driver as a module, choose M here: the + module will be called gc08a3. + +-config VIDEO_GC2145 ++config VIDEO_GC2145_MIPI + select V4L2_CCI_I2C +- tristate "GalaxyCore GC2145 sensor support" ++ tristate "GalaxyCore GC2145 sensor support - MIPI variant" + help + This is a V4L2 sensor-level driver for GalaxyCore GC2145 + 2 Mpixel camera. +diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile +index b429d9f2e982..08bd346db54e 100644 +--- a/drivers/media/i2c/Makefile ++++ b/drivers/media/i2c/Makefile +@@ -40,7 +40,7 @@ obj-$(CONFIG_VIDEO_ET8EK8) += et8ek8/ + obj-$(CONFIG_VIDEO_GC0308) += gc0308.o + obj-$(CONFIG_VIDEO_GC05A2) += gc05a2.o + obj-$(CONFIG_VIDEO_GC08A3) += gc08a3.o +-obj-$(CONFIG_VIDEO_GC2145) += gc2145.o ++obj-$(CONFIG_VIDEO_GC2145_MIPI) += gc2145-mipi.o + obj-$(CONFIG_VIDEO_HI556) += hi556.o + obj-$(CONFIG_VIDEO_HI846) += hi846.o + obj-$(CONFIG_VIDEO_HI847) += hi847.o +diff --git a/drivers/media/i2c/gc2145.c b/drivers/media/i2c/gc2145-mipi.c +similarity index 100% +rename from drivers/media/i2c/gc2145.c +rename to drivers/media/i2c/gc2145-mipi.c +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/media-i2c-gc2145-Parse-and-register-properties.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/media-i2c-gc2145-Parse-and-register-properties.patch new file mode 100644 index 000000000000..3c2e227a68ca --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/media-i2c-gc2145-Parse-and-register-properties.patch @@ -0,0 +1,43 @@ +From 4e13c92260d7cce43f7278488993cf4ed4b4c591 Mon Sep 17 00:00:00 2001 +From: Robert Mader +Date: Mon, 6 May 2024 16:36:05 -0700 +Subject: media: i2c: gc2145: Parse and register properties + +In order to propagate V4L2_CID_CAMERA_SENSOR_ROTATION and +V4L2_CID_CAMERA_ORIENTATION values from the DT to userspace. + +Signed-off-by: Robert Mader +--- + drivers/media/i2c/gc2145.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/drivers/media/i2c/gc2145.c b/drivers/media/i2c/gc2145.c +index b505b3f07387..d80e457f275b 100644 +--- a/drivers/media/i2c/gc2145.c ++++ b/drivers/media/i2c/gc2145.c +@@ -1272,6 +1272,7 @@ static int gc2145_init_controls(struct gc2145_dev *sensor) + const struct v4l2_ctrl_ops *ops = &gc2145_ctrl_ops; + struct gc2145_ctrls *ctrls = &sensor->ctrls; + struct v4l2_ctrl_handler *hdl = &ctrls->handler; ++ struct v4l2_fwnode_device_properties props; + //u8 wb_max = 0; + //u64 wb_mask = 0; + //unsigned int i; +@@ -1324,6 +1325,14 @@ static int gc2145_init_controls(struct gc2145_dev *sensor) + ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + ++ ret = v4l2_fwnode_device_parse(&sensor->i2c_client->dev, &props); ++ if (ret) ++ goto free_ctrls; ++ ++ ret = v4l2_ctrl_new_fwnode_properties(hdl, &gc2145_ctrl_ops, ++ &props); ++ if (ret) ++ goto free_ctrls; + + /* Test patterns */ + ctrls->test_pattern = +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-Add-read-only-property-for-vblank.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-Add-read-only-property-for-vblank.patch new file mode 100644 index 000000000000..c0c4296b5471 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-Add-read-only-property-for-vblank.patch @@ -0,0 +1,24 @@ +From 8f82ed74e5a0e9bc121429492cc09ff99b12aa9b Mon Sep 17 00:00:00 2001 +From: Adam Pigg +Date: Thu, 29 Dec 2022 11:10:41 +0000 +Subject: media: ov5640: Add read-only property for vblank + +--- + drivers/media/i2c/ov5640.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c +index 07e5af3073ec..5d69b74a5cd7 100644 +--- a/drivers/media/i2c/ov5640.c ++++ b/drivers/media/i2c/ov5640.c +@@ -3846,6 +3846,7 @@ static int ov5640_init_controls(struct ov5640_dev *sensor) + ctrls->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY; + ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + ctrls->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; ++ ctrls->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE; + ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE; + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-Don-t-powerup-the-sensor-during-driver-probe.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-Don-t-powerup-the-sensor-during-driver-probe.patch new file mode 100644 index 000000000000..3eda71ea0008 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-Don-t-powerup-the-sensor-during-driver-probe.patch @@ -0,0 +1,119 @@ +From 699b30b9278830c2ad7ae7331b3e6ee5016199da Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Wed, 8 Nov 2023 19:13:37 +0100 +Subject: media: ov5640: Don't powerup the sensor during driver probe + +It causes autofocus clicking during boot on some devices, and +it's enough to do it when turning on the sensor power by media +pipeline via s_power callback, later on. + +Signed-off-by: Ondrej Jirman +--- + drivers/media/i2c/ov5640.c | 55 +++++++++----------------------------- + 1 file changed, 12 insertions(+), 43 deletions(-) + +diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c +index 78a24f58888c..0e98ed8b3e08 100644 +--- a/drivers/media/i2c/ov5640.c ++++ b/drivers/media/i2c/ov5640.c +@@ -2497,6 +2497,7 @@ static void ov5640_powerup_sequence(struct ov5640_dev *sensor) + static int ov5640_set_power_on(struct ov5640_dev *sensor) + { + struct i2c_client *client = sensor->i2c_client; ++ u16 chip_id; + int ret; + + ret = clk_prepare_enable(sensor->xclk); +@@ -2520,6 +2521,13 @@ static int ov5640_set_power_on(struct ov5640_dev *sensor) + if (ret) + goto power_off; + ++ ret = ov5640_read_reg16(sensor, OV5640_REG_CHIP_ID, &chip_id); ++ if (ret) { ++ dev_err(&client->dev, "%s: failed to read chip identifier\n", ++ __func__); ++ goto power_off; ++ } ++ + return 0; + + power_off: +@@ -3841,28 +3849,6 @@ static int ov5640_get_regulators(struct ov5640_dev *sensor) + sensor->supplies); + } + +-static int ov5640_check_chip_id(struct ov5640_dev *sensor) +-{ +- struct i2c_client *client = sensor->i2c_client; +- int ret = 0; +- u16 chip_id; +- +- ret = ov5640_read_reg16(sensor, OV5640_REG_CHIP_ID, &chip_id); +- if (ret) { +- dev_err(&client->dev, "%s: failed to read chip identifier\n", +- __func__); +- return ret; +- } +- +- if (chip_id != 0x5640) { +- dev_err(&client->dev, "%s: wrong chip identifier, expected 0x5640, got 0x%x\n", +- __func__, chip_id); +- return -ENXIO; +- } +- +- return 0; +-} +- + static int ov5640_probe(struct i2c_client *client) + { + struct device *dev = &client->dev; +@@ -3963,35 +3949,16 @@ static int ov5640_probe(struct i2c_client *client) + if (ret) + goto entity_cleanup; + +- ret = ov5640_sensor_resume(dev); +- if (ret) { +- dev_err(dev, "failed to power on\n"); +- goto free_ctrls; +- } +- +- pm_runtime_set_active(dev); +- pm_runtime_get_noresume(dev); +- pm_runtime_enable(dev); +- +- ret = ov5640_check_chip_id(sensor); +- if (ret) +- goto err_pm_runtime; +- + ret = v4l2_async_register_subdev_sensor(&sensor->sd); + if (ret) +- goto err_pm_runtime; ++ goto free_ctrls; + ++ pm_runtime_enable(dev); + pm_runtime_set_autosuspend_delay(dev, 1000); + pm_runtime_use_autosuspend(dev); +- pm_runtime_mark_last_busy(dev); +- pm_runtime_put_autosuspend(dev); + + return 0; + +-err_pm_runtime: +- pm_runtime_put_noidle(dev); +- pm_runtime_disable(dev); +- ov5640_sensor_suspend(dev); + free_ctrls: + v4l2_ctrl_handler_free(&sensor->ctrls.handler); + entity_cleanup: +@@ -4006,6 +3973,8 @@ static void ov5640_remove(struct i2c_client *client) + struct ov5640_dev *sensor = to_ov5640_dev(sd); + struct device *dev = &client->dev; + ++ pm_runtime_dont_use_autosuspend(dev); ++ + pm_runtime_disable(dev); + if (!pm_runtime_status_suspended(dev)) + ov5640_sensor_suspend(dev); +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-Experiment-Try-to-disable-denoising-sharpening.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-Experiment-Try-to-disable-denoising-sharpening.patch new file mode 100644 index 000000000000..05abfd3fe916 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-Experiment-Try-to-disable-denoising-sharpening.patch @@ -0,0 +1,50 @@ +From 3fcdfc009b409cd64bb7926fecab2633486caf3b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Fri, 24 Jan 2020 18:25:59 +0100 +Subject: media: ov5640: [Experiment] Try to disable denoising/sharpening + +Not sure how this works exactly. More tests are needed. + +Signed-off-by: Ondrej Jirman +--- + drivers/media/i2c/ov5640.c | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c +index 0dae0438aa80..e408946782c6 100644 +--- a/drivers/media/i2c/ov5640.c ++++ b/drivers/media/i2c/ov5640.c +@@ -2327,6 +2327,7 @@ static int ov5640_set_mode(struct ov5640_dev *sensor) + bool auto_gain = sensor->ctrls.auto_gain->val == 1; + bool auto_exp = sensor->ctrls.auto_exp->val == V4L2_EXPOSURE_AUTO; + int ret; ++ u8 tmp; + + dn_mode = mode->dn_mode; + orig_dn_mode = orig_mode->dn_mode; +@@ -2390,6 +2391,22 @@ static int ov5640_set_mode(struct ov5640_dev *sensor) + if (ret < 0) + return ret; + ++ ret = ov5640_read_reg(sensor, 0x5308, &tmp); ++ if (ret) ++ return ret; ++ ++ ret = ov5640_write_reg(sensor, 0x5308, tmp | 0x10 | 0x40); ++ if (ret) ++ return ret; ++ ++ ret = ov5640_write_reg(sensor, 0x5306, 0); ++ if (ret) ++ return ret; ++ ++ ret = ov5640_write_reg(sensor, 0x5302, 0); ++ if (ret) ++ return ret; ++ + sensor->pending_mode_change = false; + sensor->last_mode = mode; + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-Fix-focus-commands-blocking-until-complete.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-Fix-focus-commands-blocking-until-complete.patch new file mode 100644 index 000000000000..e92f0af9181f --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-Fix-focus-commands-blocking-until-complete.patch @@ -0,0 +1,113 @@ +From eb9964740528f9aa25ee2c7591e92d3ed8f296a3 Mon Sep 17 00:00:00 2001 +From: Benjamin Schaaf +Date: Mon, 22 Nov 2021 23:38:26 +1100 +Subject: media: ov5640: Fix focus commands blocking until complete + +Previously setting the focus controls would block until the sensor +completed the focus routine. +--- + drivers/media/i2c/ov5640.c | 67 ++++++++++---------------------------- + 1 file changed, 18 insertions(+), 49 deletions(-) + +diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c +index 93cfd1b51713..07e5af3073ec 100644 +--- a/drivers/media/i2c/ov5640.c ++++ b/drivers/media/i2c/ov5640.c +@@ -2643,6 +2643,19 @@ static int ov5640_copy_fw_to_device(struct ov5640_dev *sensor, + return -ETIMEDOUT; + } + ++static int ov5640_fw_command(struct ov5640_dev *sensor, int command) ++{ ++ int ret; ++ ++ ret = ov5640_write_reg(sensor, OV5640_REG_FW_CMD_MAIN, command); ++ if(ret) ++ return ret; ++ ++ msleep(5); ++ ++ return 0; ++} ++ + static int ov5640_af_init(struct ov5640_dev *sensor) + { + struct i2c_client *client = sensor->i2c_client; +@@ -2680,6 +2693,11 @@ static int ov5640_af_init(struct ov5640_dev *sensor) + + // Set lens focus driver on + ret = ov5640_write_reg(sensor, OV5640_REG_VCM_CONTROL4, 0x3f); ++ if (ret) ++ return ret; ++ ++ // Set the default focus zone ++ ret = ov5640_fw_command(sensor, OV5640_FW_CMD_ZONE_CONFIG); + if (ret) + return ret; + return ret; +@@ -3304,35 +3322,6 @@ static int ov5640_set_framefmt(struct ov5640_dev *sensor, + is_jpeg ? (BIT(5) | BIT(3)) : 0); + } + +-static int ov5640_fw_command(struct ov5640_dev *sensor, int command) +-{ +- u8 fw_ack; +- int i; +- int ret; +- +- ret = ov5640_write_reg(sensor, OV5640_REG_FW_CMD_ACK, 0x01); +- if(ret) +- return ret; +- +- ret = ov5640_write_reg(sensor, OV5640_REG_FW_CMD_MAIN, command); +- if(ret) +- return ret; +- +- for (i = 0; i < 100; i++) { +- ret = ov5640_read_reg(sensor, OV5640_REG_FW_CMD_ACK, &fw_ack); +- if (ret) +- return ret; +- +- if (fw_ack == 0){ +- return ret; +- } +- +- msleep(50); +- } +- return -ETIMEDOUT; +-} +- +- + /* + * Sensor Controls. + */ +@@ -3468,26 +3457,6 @@ static int ov5640_set_ctrl_focus(struct ov5640_dev *sensor, int command) + return 0; + } + +- if (command == OV5640_FW_CMD_RELEASE_FOCUS) { +- dev_dbg(&client->dev, "%s: Releasing autofocus\n", +- __func__); +- return ov5640_fw_command(sensor, OV5640_FW_CMD_RELEASE_FOCUS); +- } +- +- // Restart zone config +- ret = ov5640_fw_command(sensor, OV5640_FW_CMD_ZONE_CONFIG); +- if (ret) +- return ret; +- +- // Set default focus zones +- ret = ov5640_fw_command(sensor, OV5640_FW_CMD_DEFAULT_ZONES); +- if (ret) +- return ret; +- +- dev_dbg(&client->dev, "%s: Triggering autofocus\n", +- __func__); +- +- // Start focussing + return ov5640_fw_command(sensor, command); + } + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-Implement-autofocus.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-Implement-autofocus.patch new file mode 100644 index 000000000000..ddfb542dc994 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-Implement-autofocus.patch @@ -0,0 +1,395 @@ +From e4f5aafa6a9584e382e62fe88386a58efc19ac29 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sun, 7 Aug 2022 15:17:09 +0200 +Subject: media: ov5640: Implement autofocus + +The autofocus functionality needs a firmware blob loaded into the +internal microcontroller. + +V4L2 doesn't have an api to control all autofocus functionality, but +this at least makes it possible to focus on the center of the sensor. + +Signed-off-by: Martijn Braam +--- + drivers/media/i2c/ov5640.c | 279 +++++++++++++++++++++++++++++++++++++ + 1 file changed, 279 insertions(+) + +diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c +index 0ff3a2c51d4d..83206c5b649a 100644 +--- a/drivers/media/i2c/ov5640.c ++++ b/drivers/media/i2c/ov5640.c +@@ -9,6 +9,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -119,6 +120,38 @@ + #define OV5640_REG_SDE_CTRL5 0x5585 + #define OV5640_REG_AVG_READOUT 0x56a1 + ++/* autofocus registers */ ++ ++#define OV5640_REG_SYS_RESET00 0x3000 ++#define OV5640_REG_SYS_RESET01 0x3001 ++#define OV5640_REG_SYS_CLOCK_ENABLE00 0x3004 ++#define OV5640_REG_SYS_CLOCK_ENABLE01 0x3005 ++ ++#define OV5640_REG_FW_CMD_MAIN 0x3022 ++#define OV5640_REG_FW_CMD_ACK 0x3023 ++#define OV5640_REG_FW_CMD_PARA0 0x3024 ++#define OV5640_REG_FW_CMD_PARA1 0x3025 ++#define OV5640_REG_FW_CMD_PARA2 0x3026 ++#define OV5640_REG_FW_CMD_PARA3 0x3027 ++#define OV5640_REG_FW_CMD_PARA4 0x3028 ++#define OV5640_REG_FW_STATUS 0x3029 ++ ++#define OV5640_REG_VCM_CONTROL4 0x3606 ++#define OV5640_REG_FIRMWARE_BASE 0x8000 ++ ++#define OV5640_FW_STATUS_S_FIRMWARE 0x7f ++#define OV5640_FW_STATUS_S_STARTUP 0x7e ++#define OV5640_FW_STATUS_S_IDLE 0x70 ++#define OV5640_FW_STATUS_S_FOCUSING 0x00 ++#define OV5640_FW_STATUS_S_FOCUSED 0x10 ++ ++#define OV5640_FW_CMD_TRIGGER_FOCUS 0x03 ++#define OV5640_FW_CMD_CONTINUOUS_FOCUS 0x04 ++#define OV5640_FW_CMD_GET_FOCUS_RESULT 0x07 ++#define OV5640_FW_CMD_RELEASE_FOCUS 0x08 ++#define OV5640_FW_CMD_ZONE_CONFIG 0x12 ++#define OV5640_FW_CMD_DEFAULT_ZONES 0x80 ++ + enum ov5640_mode_id { + OV5640_MODE_QQVGA_160_120 = 0, + OV5640_MODE_QCIF_176_144, +@@ -423,6 +456,12 @@ struct ov5640_ctrls { + struct v4l2_ctrl *auto_gain; + struct v4l2_ctrl *gain; + }; ++ struct { ++ struct v4l2_ctrl *focus_auto; ++ struct v4l2_ctrl *af_start; ++ struct v4l2_ctrl *af_stop; ++ struct v4l2_ctrl *af_status; ++ }; + struct v4l2_ctrl *brightness; + struct v4l2_ctrl *light_freq; + struct v4l2_ctrl *saturation; +@@ -465,6 +504,8 @@ struct ov5640_dev { + + bool pending_mode_change; + bool streaming; ++ ++ bool af_initialized; + }; + + static inline struct ov5640_dev *to_ov5640_dev(struct v4l2_subdev *sd) +@@ -2494,6 +2535,118 @@ static void ov5640_powerup_sequence(struct ov5640_dev *sensor) + OV5640_REG_SYS_CTRL0_SW_PWDN); + } + ++static int ov5640_copy_fw_to_device(struct ov5640_dev *sensor, ++ const struct firmware *fw) ++{ ++ struct i2c_client *client = sensor->i2c_client; ++ const u8 *data = (const u8 *)fw->data; ++ u8 fw_status; ++ int i; ++ int ret; ++ ++ // Putting MCU in reset state ++ ret = ov5640_write_reg(sensor, OV5640_REG_SYS_RESET00, 0x20); ++ if (ret) ++ return ret; ++ ++ // Write firmware ++ for (i = 0; i < fw->size / sizeof(u8); i++) ++ ov5640_write_reg(sensor, ++ OV5640_REG_FIRMWARE_BASE + i, ++ data[i]); ++ ++ // Reset MCU state ++ ov5640_write_reg(sensor, OV5640_REG_FW_CMD_MAIN, 0x00); ++ ov5640_write_reg(sensor, OV5640_REG_FW_CMD_ACK, 0x00); ++ ov5640_write_reg(sensor, OV5640_REG_FW_CMD_PARA0, 0x00); ++ ov5640_write_reg(sensor, OV5640_REG_FW_CMD_PARA1, 0x00); ++ ov5640_write_reg(sensor, OV5640_REG_FW_CMD_PARA2, 0x00); ++ ov5640_write_reg(sensor, OV5640_REG_FW_CMD_PARA3, 0x00); ++ ov5640_write_reg(sensor, OV5640_REG_FW_CMD_PARA4, 0x00); ++ ov5640_write_reg(sensor, OV5640_REG_FW_STATUS, 0x7f); ++ ++ // Start AF MCU ++ ret = ov5640_write_reg(sensor, OV5640_REG_SYS_RESET00, 0x00); ++ if (ret) ++ return ret; ++ ++ dev_info(&client->dev, "firmware upload success\n"); ++ ++ // Wait for firmware to be ready ++ for (i = 0; i < 5; i++) { ++ ret = ov5640_read_reg(sensor, OV5640_REG_FW_STATUS, &fw_status); ++ if (fw_status == OV5640_FW_STATUS_S_IDLE) { ++ dev_info(&client->dev, "fw started after %d ms\n", i * 50); ++ return ret; ++ } ++ msleep(50); ++ } ++ dev_err(&client->dev, "uploaded firmware didn't start, got to 0x%x, retrying...\n", fw_status); ++ ++ // Putting MCU in reset state ++ ret = ov5640_write_reg(sensor, OV5640_REG_SYS_RESET00, 0x20); ++ if (ret) ++ return ret; ++ // Start AF MCU ++ ret = ov5640_write_reg(sensor, OV5640_REG_SYS_RESET00, 0x00); ++ if (ret) ++ return ret; ++ // Wait for firmware to be ready ++ for (i = 0; i < 5; i++) { ++ ret = ov5640_read_reg(sensor, OV5640_REG_FW_STATUS, &fw_status); ++ if (fw_status == OV5640_FW_STATUS_S_IDLE) { ++ dev_info(&client->dev, "fw started after %d ms\n", i * 50); ++ return ret; ++ } ++ msleep(50); ++ } ++ dev_err(&client->dev, "uploaded firmware didn't start, got to 0x%x\n", fw_status); ++ return -ETIMEDOUT; ++} ++ ++static int ov5640_af_init(struct ov5640_dev *sensor) ++{ ++ struct i2c_client *client = sensor->i2c_client; ++ const char* fwname = "ov5640_af.bin"; ++ const struct firmware *fw; ++ int ret; ++ ++ if (sensor->af_initialized) { ++ return 0; ++ } ++ ++ if (firmware_request_nowarn(&fw, fwname, &client->dev) == 0) { ++ ret = ov5640_copy_fw_to_device(sensor, fw); ++ if (ret == 0) ++ sensor->af_initialized = 1; ++ } else { ++ dev_warn(&client->dev, "%s: no autofocus firmware available (%s)\n", ++ __func__, fwname); ++ ret = -1; ++ } ++ release_firmware(fw); ++ ++ if (ret) ++ return ret; ++ ++ // Enable AF systems ++ ret = ov5640_mod_reg(sensor, OV5640_REG_SYS_CLOCK_ENABLE00, ++ (BIT(6) | BIT(5)), (BIT(6) | BIT(5))); ++ if (ret) ++ return ret; ++ ret = ov5640_mod_reg(sensor, OV5640_REG_SYS_CLOCK_ENABLE01, ++ BIT(6), BIT(6)); ++ if (ret) ++ return ret; ++ ++ // Set lens focus driver on ++ ov5640_write_reg(sensor, OV5640_REG_VCM_CONTROL4, 0x3f); ++ if (ret) ++ return ret; ++ ++ return ret; ++} ++ + static int ov5640_set_power_on(struct ov5640_dev *sensor) + { + struct i2c_client *client = sensor->i2c_client; +@@ -2515,8 +2668,11 @@ static int ov5640_set_power_on(struct ov5640_dev *sensor) + goto xclk_off; + } + ++ sensor->af_initialized = 0; ++ + ov5640_powerup_sequence(sensor); + ++ + ret = ov5640_init_slave_id(sensor); + if (ret) + goto power_off; +@@ -3110,6 +3266,35 @@ static int ov5640_set_framefmt(struct ov5640_dev *sensor, + is_jpeg ? (BIT(5) | BIT(3)) : 0); + } + ++static int ov5640_fw_command(struct ov5640_dev *sensor, int command) ++{ ++ u8 fw_ack; ++ int i; ++ int ret; ++ ++ ret = ov5640_write_reg(sensor, OV5640_REG_FW_CMD_ACK, 0x01); ++ if(ret) ++ return ret; ++ ++ ret = ov5640_write_reg(sensor, OV5640_REG_FW_CMD_MAIN, command); ++ if(ret) ++ return ret; ++ ++ for (i = 0; i < 100; i++) { ++ ret = ov5640_read_reg(sensor, OV5640_REG_FW_CMD_ACK, &fw_ack); ++ if (ret) ++ return ret; ++ ++ if (fw_ack == 0){ ++ return ret; ++ } ++ ++ msleep(50); ++ } ++ return -ETIMEDOUT; ++} ++ ++ + /* + * Sensor Controls. + */ +@@ -3226,6 +3411,41 @@ static int ov5640_set_ctrl_exposure(struct ov5640_dev *sensor, + return ret; + } + ++static int ov5640_set_ctrl_focus(struct ov5640_dev *sensor, int command) ++{ ++ struct i2c_client *client = sensor->i2c_client; ++ int ret; ++ ++ ret = ov5640_af_init(sensor); ++ if (ret) { ++ dev_err(&client->dev, "%s: no autofocus firmware loaded\n", ++ __func__); ++ return 0; ++ } ++ ++ if (command == OV5640_FW_CMD_RELEASE_FOCUS) { ++ dev_dbg(&client->dev, "%s: Releasing autofocus\n", ++ __func__); ++ return ov5640_fw_command(sensor, OV5640_FW_CMD_RELEASE_FOCUS); ++ } ++ ++ // Restart zone config ++ ret = ov5640_fw_command(sensor, OV5640_FW_CMD_ZONE_CONFIG); ++ if (ret) ++ return ret; ++ ++ // Set default focus zones ++ ret = ov5640_fw_command(sensor, OV5640_FW_CMD_DEFAULT_ZONES); ++ if (ret) ++ return ret; ++ ++ dev_dbg(&client->dev, "%s: Triggering autofocus\n", ++ __func__); ++ ++ // Start focussing ++ return ov5640_fw_command(sensor, command); ++} ++ + static int ov5640_set_ctrl_gain(struct ov5640_dev *sensor, bool auto_gain) + { + struct ov5640_ctrls *ctrls = &sensor->ctrls; +@@ -3341,6 +3561,32 @@ static int ov5640_set_ctrl_vblank(struct ov5640_dev *sensor, int value) + mode->height + value); + } + ++static int ov5640_get_af_status(struct ov5640_dev *sensor) ++{ ++ u8 fw_status; ++ int ret; ++ ++ ret = ov5640_read_reg(sensor, OV5640_REG_FW_STATUS, &fw_status); ++ if (ret) ++ return ret; ++ ++ switch (fw_status) { ++ case OV5640_FW_STATUS_S_FIRMWARE: ++ case OV5640_FW_STATUS_S_STARTUP: ++ return V4L2_AUTO_FOCUS_STATUS_FAILED; ++ break; ++ case OV5640_FW_STATUS_S_IDLE: ++ return V4L2_AUTO_FOCUS_STATUS_IDLE; ++ break; ++ case OV5640_FW_STATUS_S_FOCUSED: ++ return V4L2_AUTO_FOCUS_STATUS_REACHED; ++ break; ++ default: ++ return V4L2_AUTO_FOCUS_STATUS_BUSY; ++ break; ++ } ++} ++ + static int ov5640_g_volatile_ctrl(struct v4l2_ctrl *ctrl) + { + struct v4l2_subdev *sd = ctrl_to_sd(ctrl); +@@ -3365,6 +3611,12 @@ static int ov5640_g_volatile_ctrl(struct v4l2_ctrl *ctrl) + return val; + sensor->ctrls.exposure->val = val; + break; ++ case V4L2_CID_FOCUS_AUTO: ++ val = ov5640_get_af_status(sensor); ++ if (val < 0) ++ return val; ++ sensor->ctrls.af_status->val = val; ++ break; + } + + pm_runtime_mark_last_busy(&sensor->i2c_client->dev); +@@ -3414,6 +3666,18 @@ static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl) + case V4L2_CID_AUTO_WHITE_BALANCE: + ret = ov5640_set_ctrl_white_balance(sensor, ctrl->val); + break; ++ case V4L2_CID_FOCUS_AUTO: ++ if (ctrl->val) ++ ret = ov5640_set_ctrl_focus(sensor, OV5640_FW_CMD_CONTINUOUS_FOCUS); ++ else ++ ret = ov5640_set_ctrl_focus(sensor, OV5640_FW_CMD_RELEASE_FOCUS); ++ break; ++ case V4L2_CID_AUTO_FOCUS_START: ++ ret = ov5640_set_ctrl_focus(sensor, OV5640_FW_CMD_TRIGGER_FOCUS); ++ break; ++ case V4L2_CID_AUTO_FOCUS_STOP: ++ ret = ov5640_set_ctrl_focus(sensor, OV5640_FW_CMD_RELEASE_FOCUS); ++ break; + case V4L2_CID_HUE: + ret = ov5640_set_ctrl_hue(sensor, ctrl->val); + break; +@@ -3514,6 +3778,20 @@ static int ov5640_init_controls(struct ov5640_dev *sensor) + ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN, + 0, 1023, 1, 0); + ++ /* Autofocus */ ++ ctrls->focus_auto = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FOCUS_AUTO, ++ 0, 1, 1, 0); ++ ctrls->af_start = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_FOCUS_START, ++ 0, 1, 1, 0); ++ ctrls->af_stop = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_FOCUS_STOP, ++ 0, 1, 1, 0); ++ ctrls->af_status = v4l2_ctrl_new_std(hdl, ops, ++ V4L2_CID_AUTO_FOCUS_STATUS, 0, ++ (V4L2_AUTO_FOCUS_STATUS_BUSY | ++ V4L2_AUTO_FOCUS_STATUS_REACHED | ++ V4L2_AUTO_FOCUS_STATUS_FAILED), ++ 0, V4L2_AUTO_FOCUS_STATUS_IDLE); ++ + ctrls->saturation = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION, + 0, 255, 1, 64); + ctrls->hue = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HUE, +@@ -3560,6 +3838,7 @@ static int ov5640_init_controls(struct ov5640_dev *sensor) + v4l2_ctrl_auto_cluster(3, &ctrls->auto_wb, 0, false); + v4l2_ctrl_auto_cluster(2, &ctrls->auto_gain, 0, true); + v4l2_ctrl_auto_cluster(2, &ctrls->auto_exp, 1, true); ++ v4l2_ctrl_cluster(4, &ctrls->focus_auto); + + sensor->sd.ctrl_handler = hdl; + return 0; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-Improve-error-reporting.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-Improve-error-reporting.patch new file mode 100644 index 000000000000..3cd68e2c425b --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-Improve-error-reporting.patch @@ -0,0 +1,43 @@ +From bba2d0b661c0d2e89df60ea31e93307367407e75 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Fri, 22 Oct 2021 19:52:49 +0200 +Subject: media: ov5640: Improve error reporting + +Some probe errors are currently not reported. Report them properly. + +Signed-off-by: Ondrej Jirman +--- + drivers/media/i2c/ov5640.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c +index 4e0803484fd4..0ff3a2c51d4d 100644 +--- a/drivers/media/i2c/ov5640.c ++++ b/drivers/media/i2c/ov5640.c +@@ -3940,8 +3940,10 @@ static int ov5640_probe(struct i2c_client *client) + return ret; + + ret = ov5640_get_regulators(sensor); +- if (ret) ++ if (ret) { ++ dev_err_probe(dev, ret, "Failed to get regulators\n"); + goto entity_cleanup; ++ } + + mutex_init(&sensor->lock); + +@@ -3950,8 +3952,10 @@ static int ov5640_probe(struct i2c_client *client) + goto entity_cleanup; + + ret = v4l2_async_register_subdev_sensor(&sensor->sd); +- if (ret) ++ if (ret) { ++ dev_err_probe(dev, ret, "Failed to register sensor\n"); + goto free_ctrls; ++ } + + pm_runtime_enable(dev); + pm_runtime_set_autosuspend_delay(dev, 1000); +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-Improve-firmware-load-time.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-Improve-firmware-load-time.patch new file mode 100644 index 000000000000..d35a05c60315 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-Improve-firmware-load-time.patch @@ -0,0 +1,177 @@ +From 20eee5b2d15278056cfddaddebbe2870b368ea1e Mon Sep 17 00:00:00 2001 +From: Benjamin Schaaf +Date: Mon, 22 Nov 2021 23:38:26 +1100 +Subject: media: ov5640: Improve firmware load time + +Downloading the firmware can be done in groups to minimize i2c packets. +The firmware status is set practically immediately upon successfully +loading it, so there's no need for a long timeout or retry. The +firmware can also not be loaded when the sensor isn't powered on, so +don't bother trying. +--- + drivers/media/i2c/ov5640.c | 97 ++++++++++++++++++++++++++++---------- + 1 file changed, 71 insertions(+), 26 deletions(-) + +diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c +index 83206c5b649a..93cfd1b51713 100644 +--- a/drivers/media/i2c/ov5640.c ++++ b/drivers/media/i2c/ov5640.c +@@ -1232,6 +1232,8 @@ static int ov5640_write_reg(struct ov5640_dev *sensor, u16 reg, u8 val) + msg.buf = buf; + msg.len = sizeof(buf); + ++ dev_dbg(&client->dev, "[wr %04x] <= %d\n", reg, val); ++ + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret < 0) { + dev_err(&client->dev, "%s: error: reg=%x, val=%x\n", +@@ -1242,6 +1244,42 @@ static int ov5640_write_reg(struct ov5640_dev *sensor, u16 reg, u8 val) + return 0; + } + ++static int ov5640_write_regs(struct ov5640_dev *sensor, u16 reg, ++ const u8 *data, int data_size) ++{ ++ struct i2c_client *client = sensor->i2c_client; ++ struct i2c_msg msg; ++ u8 buf[254 + 2]; ++ int ret; ++ ++ if (data_size > sizeof(buf) - 2) { ++ v4l2_err(&sensor->sd, "%s: oversized transfer (size=%d)\n", ++ __func__, data_size); ++ return -EINVAL; ++ } ++ ++ buf[0] = reg >> 8; ++ buf[1] = reg & 0xff; ++ memcpy(buf + 2, data, data_size); ++ ++ msg.addr = client->addr; ++ msg.flags = client->flags; ++ msg.buf = buf; ++ msg.len = data_size + 2; ++ ++ dev_dbg(&client->dev, "[wr %04x] <= %*ph\n", (u32)reg, data_size, data); ++ ++ ret = i2c_transfer(client->adapter, &msg, 1); ++ if (ret < 0) { ++ v4l2_err(&sensor->sd, ++ "%s: error %d: reg=%x, data=%*ph\n", ++ __func__, ret, (u32)reg, data_size, data); ++ return ret; ++ } ++ ++ return 0; ++} ++ + static int ov5640_read_reg(struct ov5640_dev *sensor, u16 reg, u8 *val) + { + struct i2c_client *client = sensor->i2c_client; +@@ -2543,6 +2581,7 @@ static int ov5640_copy_fw_to_device(struct ov5640_dev *sensor, + u8 fw_status; + int i; + int ret; ++ int num_groups, group_size = 254; + + // Putting MCU in reset state + ret = ov5640_write_reg(sensor, OV5640_REG_SYS_RESET00, 0x20); +@@ -2550,10 +2589,24 @@ static int ov5640_copy_fw_to_device(struct ov5640_dev *sensor, + return ret; + + // Write firmware +- for (i = 0; i < fw->size / sizeof(u8); i++) +- ov5640_write_reg(sensor, +- OV5640_REG_FIRMWARE_BASE + i, +- data[i]); ++ num_groups = fw->size / group_size; ++ for (i = 0; i < num_groups; i++) { ++ ret = ov5640_write_regs(sensor, ++ OV5640_REG_FIRMWARE_BASE + i * group_size, ++ data + i * group_size, ++ group_size); ++ if (ret) ++ return ret; ++ } ++ ++ if (i * group_size < fw->size) { ++ ret = ov5640_write_regs(sensor, ++ OV5640_REG_FIRMWARE_BASE + i * group_size, ++ data + i * group_size, ++ fw->size - i * group_size); ++ if (ret) ++ return ret; ++ } + + // Reset MCU state + ov5640_write_reg(sensor, OV5640_REG_FW_CMD_MAIN, 0x00); +@@ -2575,31 +2628,17 @@ static int ov5640_copy_fw_to_device(struct ov5640_dev *sensor, + // Wait for firmware to be ready + for (i = 0; i < 5; i++) { + ret = ov5640_read_reg(sensor, OV5640_REG_FW_STATUS, &fw_status); +- if (fw_status == OV5640_FW_STATUS_S_IDLE) { +- dev_info(&client->dev, "fw started after %d ms\n", i * 50); ++ if (ret) { + return ret; + } +- msleep(50); +- } +- dev_err(&client->dev, "uploaded firmware didn't start, got to 0x%x, retrying...\n", fw_status); + +- // Putting MCU in reset state +- ret = ov5640_write_reg(sensor, OV5640_REG_SYS_RESET00, 0x20); +- if (ret) +- return ret; +- // Start AF MCU +- ret = ov5640_write_reg(sensor, OV5640_REG_SYS_RESET00, 0x00); +- if (ret) +- return ret; +- // Wait for firmware to be ready +- for (i = 0; i < 5; i++) { +- ret = ov5640_read_reg(sensor, OV5640_REG_FW_STATUS, &fw_status); + if (fw_status == OV5640_FW_STATUS_S_IDLE) { +- dev_info(&client->dev, "fw started after %d ms\n", i * 50); +- return ret; ++ dev_info(&client->dev, "fw started after %d ms\n", i * 5); ++ return 0; + } +- msleep(50); ++ msleep(5); + } ++ + dev_err(&client->dev, "uploaded firmware didn't start, got to 0x%x\n", fw_status); + return -ETIMEDOUT; + } +@@ -2640,10 +2679,9 @@ static int ov5640_af_init(struct ov5640_dev *sensor) + return ret; + + // Set lens focus driver on +- ov5640_write_reg(sensor, OV5640_REG_VCM_CONTROL4, 0x3f); ++ ret = ov5640_write_reg(sensor, OV5640_REG_VCM_CONTROL4, 0x3f); + if (ret) + return ret; +- + return ret; + } + +@@ -3416,9 +3454,16 @@ static int ov5640_set_ctrl_focus(struct ov5640_dev *sensor, int command) + struct i2c_client *client = sensor->i2c_client; + int ret; + ++ // Don't attempt to do focus if the embedded controller is powered down ++ if (!sensor->streaming) { ++ dev_err(&client->dev, "%s: can't set focus when not powered\n", ++ __func__); ++ return 0; ++ } ++ + ret = ov5640_af_init(sensor); + if (ret) { +- dev_err(&client->dev, "%s: no autofocus firmware loaded\n", ++ dev_err(&client->dev, "%s: autofocus firmware load failed\n", + __func__); + return 0; + } +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-Sleep-after-poweroff-to-ensure-next-poweron-is-not.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-Sleep-after-poweroff-to-ensure-next-poweron-is-not.patch new file mode 100644 index 000000000000..98c89d238412 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-Sleep-after-poweroff-to-ensure-next-poweron-is-not.patch @@ -0,0 +1,29 @@ +From 8649c6af904ecf4098b858e6d622b5cee6ffd1cf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sun, 26 Jan 2020 00:19:40 +0100 +Subject: media: ov5640: Sleep after poweroff to ensure next poweron is not too + early + +It's easy to use v4l2 userspace api in such a way that user can trigger +a brownout on the sensor instead of a proper powerdown and powerup. + +Signed-off-by: Ondrej Jirman +--- + drivers/media/i2c/ov5640.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c +index e408946782c6..78a24f58888c 100644 +--- a/drivers/media/i2c/ov5640.c ++++ b/drivers/media/i2c/ov5640.c +@@ -2535,6 +2535,7 @@ static void ov5640_set_power_off(struct ov5640_dev *sensor) + ov5640_power(sensor, false); + regulator_bulk_disable(OV5640_NUM_SUPPLIES, sensor->supplies); + clk_disable_unprepare(sensor->xclk); ++ msleep(100); + } + + static int ov5640_set_power_mipi(struct ov5640_dev *sensor, bool on) +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-set-default-ae-target-lower.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-set-default-ae-target-lower.patch new file mode 100644 index 000000000000..ab30ecb068b2 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-set-default-ae-target-lower.patch @@ -0,0 +1,27 @@ +From 098ea461478192bdce1c8daf9dced8b2edba861b Mon Sep 17 00:00:00 2001 +From: Martijn Braam +Date: Wed, 7 Oct 2020 17:33:43 +0200 +Subject: media: ov5640: set default ae target lower + +The OV5640 tries to overexpose all photos by about 1 stop. This makes +the exposure target one stop lower. +--- + drivers/media/i2c/ov5640.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c +index 0e98ed8b3e08..4e0803484fd4 100644 +--- a/drivers/media/i2c/ov5640.c ++++ b/drivers/media/i2c/ov5640.c +@@ -3875,7 +3875,7 @@ static int ov5640_probe(struct i2c_client *client) + sensor->current_link_freq = + ov5640_csi2_link_freqs[OV5640_DEFAULT_LINK_FREQ]; + +- sensor->ae_target = 52; ++ sensor->ae_target = 28; + + endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), + NULL); +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-use-pm_runtime_force_suspend-resume-for-system-sus.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-use-pm_runtime_force_suspend-resume-for-system-sus.patch new file mode 100644 index 000000000000..edf72ed5e4d5 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5640-use-pm_runtime_force_suspend-resume-for-system-sus.patch @@ -0,0 +1,31 @@ +From b05dfe5ce59ad5c2bae740af6d2b44518a7754fd Mon Sep 17 00:00:00 2001 +From: Andrey Skvortsov +Date: Mon, 14 Aug 2023 13:25:07 +0300 +Subject: media: ov5640: use pm_runtime_force_suspend/resume for system suspend + +If system was suspended while camera sensor was used, data and +interrupts were still coming from sensor and that caused unstable +system. Sometimes system hanged during a resume. Use +pm_runtime_force_* helpers in order to support system suspend. + +Signed-off-by: Andrey Skvortsov +--- + drivers/media/i2c/ov5640.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c +index 5d69b74a5cd7..50177b44ed61 100644 +--- a/drivers/media/i2c/ov5640.c ++++ b/drivers/media/i2c/ov5640.c +@@ -4285,6 +4285,8 @@ static void ov5640_remove(struct i2c_client *client) + } + + static const struct dev_pm_ops ov5640_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, ++ pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(ov5640_sensor_suspend, ov5640_sensor_resume, NULL) + }; + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5648-Fix-call-to-pm_runtime_set_suspended.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5648-Fix-call-to-pm_runtime_set_suspended.patch new file mode 100644 index 000000000000..fe435c8ad2c5 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/media-ov5648-Fix-call-to-pm_runtime_set_suspended.patch @@ -0,0 +1,29 @@ +From 2f64c8745d4217a3375648c0e3af0f3ab0765a5d Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Wed, 24 Apr 2024 00:00:31 +0200 +Subject: media: ov5648: Fix call to pm_runtime_set_suspended + +It should be done when RPM is disabled. + +Signed-off-by: Ondrej Jirman +--- + drivers/media/i2c/ov5648.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/media/i2c/ov5648.c b/drivers/media/i2c/ov5648.c +index 4b86d2631bd1..c691e235388a 100644 +--- a/drivers/media/i2c/ov5648.c ++++ b/drivers/media/i2c/ov5648.c +@@ -2566,8 +2566,8 @@ static int ov5648_probe(struct i2c_client *client) + + /* Runtime PM */ + +- pm_runtime_enable(sensor->dev); + pm_runtime_set_suspended(sensor->dev); ++ pm_runtime_enable(sensor->dev); + + /* V4L2 subdev register */ + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/media-sun6i-csi-Add-multicamera-support-for-parallel-bus.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/media-sun6i-csi-Add-multicamera-support-for-parallel-bus.patch new file mode 100644 index 000000000000..62222bc51a88 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/media-sun6i-csi-Add-multicamera-support-for-parallel-bus.patch @@ -0,0 +1,166 @@ +From 9bdb57f474095a3e7ab25410a24d4b7dbf8028aa Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Fri, 6 Jan 2023 11:25:09 +0100 +Subject: media: sun6i-csi: Add multicamera support for parallel bus + +Multiple cameras may be connected to parallel bus eg. in a tablet. +Allow to register multiple parallel bus subdevices and switch between +them by enabling/disabling media graph links. + +Signed-off-by: Ondrej Jirman +--- + .../sunxi/sun6i-csi/sun6i_csi_bridge.c | 51 +++++++++++-------- + .../sunxi/sun6i-csi/sun6i_csi_bridge.h | 4 +- + 2 files changed, 33 insertions(+), 22 deletions(-) + +diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c +index 92290876a8b3..0ea1fae7352e 100644 +--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c ++++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c +@@ -207,12 +207,12 @@ static void sun6i_csi_bridge_disable(struct sun6i_csi_device *csi_dev) + + static void + sun6i_csi_bridge_configure_parallel(struct sun6i_csi_device *csi_dev, +- struct v4l2_subdev_state *state) ++ struct v4l2_subdev_state *state, ++ struct sun6i_csi_bridge_source *source) + { + struct device *dev = csi_dev->dev; + struct regmap *regmap = csi_dev->regmap; +- struct v4l2_fwnode_endpoint *endpoint = +- &csi_dev->bridge.source_parallel.endpoint; ++ struct v4l2_fwnode_endpoint *endpoint = &source->endpoint; + unsigned char bus_width = endpoint->bus.parallel.bus_width; + unsigned int flags = endpoint->bus.parallel.flags; + const struct v4l2_mbus_framefmt *sink_format; +@@ -377,10 +377,8 @@ static void sun6i_csi_bridge_configure(struct sun6i_csi_device *csi_dev, + struct sun6i_csi_bridge_source *source, + struct v4l2_subdev_state *state) + { +- struct sun6i_csi_bridge *bridge = &csi_dev->bridge; +- +- if (source == &bridge->source_parallel) +- sun6i_csi_bridge_configure_parallel(csi_dev, state); ++ if (source->endpoint.bus_type == V4L2_MBUS_PARALLEL) ++ sun6i_csi_bridge_configure_parallel(csi_dev, state, source); + else + sun6i_csi_bridge_configure_mipi_csi2(csi_dev, state); + +@@ -396,11 +394,11 @@ static int sun6i_csi_bridge_s_stream(struct v4l2_subdev *subdev, int on) + struct media_pad *local_pad = &bridge->pads[SUN6I_CSI_BRIDGE_PAD_SINK]; + bool capture_streaming = csi_dev->capture.state.streaming; + struct device *dev = csi_dev->dev; +- struct sun6i_csi_bridge_source *source; ++ struct sun6i_csi_bridge_source *source = NULL; + struct v4l2_subdev *source_subdev; + struct media_pad *remote_pad; + struct v4l2_subdev_state *state; +- int ret; ++ int ret, i; + + /* Source */ + +@@ -413,10 +411,20 @@ static int sun6i_csi_bridge_s_stream(struct v4l2_subdev *subdev, int on) + + source_subdev = media_entity_to_v4l2_subdev(remote_pad->entity); + +- if (source_subdev == bridge->source_parallel.subdev) +- source = &bridge->source_parallel; +- else ++ if (source_subdev == bridge->source_mipi_csi2.subdev) { + source = &bridge->source_mipi_csi2; ++ } else { ++ for (i = 0; i < SUN6I_CSI_SOURCE_PARALLEL_MAX; i++) { ++ if (source_subdev == bridge->source_parallel[i].subdev) { ++ source = &bridge->source_parallel[i]; ++ break; ++ } ++ } ++ } ++ if (!source) { ++ dev_err(dev, "bridge source not found\n"); ++ return -ENODEV; ++ } + + if (!on) { + v4l2_subdev_call(source_subdev, video, s_stream, 0); +@@ -647,10 +655,10 @@ sun6i_csi_bridge_notifier_bound(struct v4l2_async_notifier *notifier, + + switch (source->endpoint.base.port) { + case SUN6I_CSI_PORT_PARALLEL: +- enabled = true; ++ enabled = source->endpoint.base.id == 0; + break; + case SUN6I_CSI_PORT_MIPI_CSI2: +- enabled = !bridge->source_parallel.expected; ++ enabled = !bridge->source_parallel[0].expected; + break; + default: + return -EINVAL; +@@ -696,7 +704,7 @@ sun6i_csi_bridge_notifier_ops = { + + static int sun6i_csi_bridge_source_setup(struct sun6i_csi_device *csi_dev, + struct sun6i_csi_bridge_source *source, +- u32 port, ++ u32 port, u32 ep, + enum v4l2_mbus_type *bus_types) + { + struct device *dev = csi_dev->dev; +@@ -706,7 +714,7 @@ static int sun6i_csi_bridge_source_setup(struct sun6i_csi_device *csi_dev, + struct fwnode_handle *handle; + int ret; + +- handle = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), port, 0, 0); ++ handle = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), port, ep, 0); + if (!handle) + return -ENODEV; + +@@ -765,7 +773,7 @@ int sun6i_csi_bridge_setup(struct sun6i_csi_device *csi_dev) + V4L2_MBUS_BT656, + V4L2_MBUS_INVALID + }; +- int ret; ++ int ret, i; + + /* V4L2 Subdev */ + +@@ -817,11 +825,12 @@ int sun6i_csi_bridge_setup(struct sun6i_csi_device *csi_dev) + v4l2_async_nf_init(notifier, v4l2_dev); + notifier->ops = &sun6i_csi_bridge_notifier_ops; + +- sun6i_csi_bridge_source_setup(csi_dev, &bridge->source_parallel, +- SUN6I_CSI_PORT_PARALLEL, +- parallel_mbus_types); ++ for (i = 0; i < SUN6I_CSI_SOURCE_PARALLEL_MAX; i++) ++ sun6i_csi_bridge_source_setup(csi_dev, &bridge->source_parallel[i], ++ SUN6I_CSI_PORT_PARALLEL, i, ++ parallel_mbus_types); + sun6i_csi_bridge_source_setup(csi_dev, &bridge->source_mipi_csi2, +- SUN6I_CSI_PORT_MIPI_CSI2, NULL); ++ SUN6I_CSI_PORT_MIPI_CSI2, 0, NULL); + + ret = v4l2_async_nf_register(notifier); + if (ret) { +diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.h +index a5b0a6f064dd..e755b67a2db3 100644 +--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.h ++++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.h +@@ -38,12 +38,14 @@ struct sun6i_csi_bridge_async_subdev { + struct sun6i_csi_bridge_source *source; + }; + ++#define SUN6I_CSI_SOURCE_PARALLEL_MAX 2 ++ + struct sun6i_csi_bridge { + struct v4l2_subdev subdev; + struct v4l2_async_notifier notifier; + struct media_pad pads[2]; + +- struct sun6i_csi_bridge_source source_parallel; ++ struct sun6i_csi_bridge_source source_parallel[SUN6I_CSI_SOURCE_PARALLEL_MAX]; + struct sun6i_csi_bridge_source source_mipi_csi2; + }; + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/media-sun6i-csi-add-V4L2_CAP_IO_MC-capability.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/media-sun6i-csi-add-V4L2_CAP_IO_MC-capability.patch new file mode 100644 index 000000000000..c68600405109 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/media-sun6i-csi-add-V4L2_CAP_IO_MC-capability.patch @@ -0,0 +1,82 @@ +From 8f97c0cb4b2e06ba17b6c6d020ff22af2f9ed265 Mon Sep 17 00:00:00 2001 +From: Adam Pigg +Date: Tue, 3 Jan 2023 18:52:04 +0000 +Subject: media: sun6i-csi: add V4L2_CAP_IO_MC capability + +Advertise the V4L2_CAP_IO_MC flag and update +sun6i_csi_capture_enum_fmt to work with this capability. + +With the capability adverstised, the VIDIOC_ENUM_FMT ioctl +implementation is updated to enumerate pixel formats depending +on the supplied mbus_code. If the code is 0, all formats are +enumerated, if not, only pixel formats that match with the supplied +mbus_code are enumeratd. + +Signed-off-by: Adam Pigg +--- + .../sunxi/sun6i-csi/sun6i_csi_capture.c | 37 +++++++++++++++++-- + 1 file changed, 34 insertions(+), 3 deletions(-) + +diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c +index ad9d1fca338d..f63862fd4e76 100644 +--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c ++++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c +@@ -720,13 +720,43 @@ static int sun6i_csi_capture_enum_fmt(struct file *file, void *private, + struct v4l2_fmtdesc *fmtdesc) + { + u32 index = fmtdesc->index; ++ unsigned int i; + + if (index >= ARRAY_SIZE(sun6i_csi_capture_formats)) + return -EINVAL; + +- fmtdesc->pixelformat = sun6i_csi_capture_formats[index].pixelformat; ++ for (i = 0; i < ARRAY_SIZE(sun6i_csi_capture_formats); i++) { ++ const struct sun6i_csi_capture_format *format = ++ &sun6i_csi_capture_formats[i]; + +- return 0; ++ /* ++ * If a media bus code is specified, only consider formats that ++ * match it. ++ */ ++ if (fmtdesc->mbus_code) { ++ unsigned int j; ++ ++ if (!format->mbus_codes) ++ continue; ++ ++ for (j = 0; format->mbus_codes[j]; j++) { ++ if (fmtdesc->mbus_code == format->mbus_codes[j]) ++ break; ++ } ++ ++ if (!format->mbus_codes[j]) ++ continue; ++ } ++ ++ if (index == 0) { ++ fmtdesc->pixelformat = format->pixelformat; ++ return 0; ++ } ++ ++ index--; ++ } ++ ++ return -EINVAL; + } + + static int sun6i_csi_capture_g_fmt(struct file *file, void *private, +@@ -1032,7 +1062,8 @@ int sun6i_csi_capture_setup(struct sun6i_csi_device *csi_dev) + + strscpy(video_dev->name, SUN6I_CSI_CAPTURE_NAME, + sizeof(video_dev->name)); +- video_dev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; ++ video_dev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING ++ | V4L2_CAP_IO_MC; + video_dev->vfl_dir = VFL_DIR_RX; + video_dev->release = video_device_release_empty; + video_dev->fops = &sun6i_csi_capture_fops; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/media-sun6i-csi-capture-Use-subdev-operation-to-access-bridge-f.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/media-sun6i-csi-capture-Use-subdev-operation-to-access-bridge-f.patch new file mode 100644 index 000000000000..c3995fc04f7b --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/media-sun6i-csi-capture-Use-subdev-operation-to-access-bridge-f.patch @@ -0,0 +1,128 @@ +From 92fb79b20a3d74951ac19b53d684d6b5f9a284de Mon Sep 17 00:00:00 2001 +From: Laurent Pinchart +Date: Wed, 4 Jan 2023 12:29:56 +0200 +Subject: media: sun6i-csi: capture: Use subdev operation to access bridge + format + +Replace the sun6i_csi_bridge helper functions sun6i_csi_bridge_format() +and sun6i_csi_bridge_dimensions() that access the bridge fields from the +capture side with usage of the subdev .get_fmt() operation. This +decouples the bridge and capture side better by using abstractions +provided by the V4L2 subdev API, and prepares for usage of the subdev +active state in the bridge. + +The sun6i_csi_bridge_dimensions() helper is now unused and is removed. +The sun6i_csi_bridge_format() helper will be removed in a subsequent +change. + +Signed-off-by: Laurent Pinchart +(cherry picked from commit e728c6bbead7a10d9ffd1139e416e1199b5837a6) +--- + .../sunxi/sun6i-csi/sun6i_csi_bridge.c | 13 ++--------- + .../sunxi/sun6i-csi/sun6i_csi_bridge.h | 7 ------ + .../sunxi/sun6i-csi/sun6i_csi_capture.c | 23 ++++++++++++------- + 3 files changed, 17 insertions(+), 26 deletions(-) + +diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c +index d006d9dd0170..2303e5d2baee 100644 +--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c ++++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c +@@ -15,17 +15,8 @@ + + /* Helpers */ + +-void sun6i_csi_bridge_dimensions(struct sun6i_csi_device *csi_dev, +- unsigned int *width, unsigned int *height) +-{ +- if (width) +- *width = csi_dev->bridge.mbus_format.width; +- if (height) +- *height = csi_dev->bridge.mbus_format.height; +-} +- +-void sun6i_csi_bridge_format(struct sun6i_csi_device *csi_dev, +- u32 *mbus_code, u32 *field) ++static void sun6i_csi_bridge_format(struct sun6i_csi_device *csi_dev, ++ u32 *mbus_code, u32 *field) + { + if (mbus_code) + *mbus_code = csi_dev->bridge.mbus_format.code; +diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.h +index 44653b38f722..722b633b7893 100644 +--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.h ++++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.h +@@ -49,13 +49,6 @@ struct sun6i_csi_bridge { + struct sun6i_csi_bridge_source source_mipi_csi2; + }; + +-/* Helpers */ +- +-void sun6i_csi_bridge_dimensions(struct sun6i_csi_device *csi_dev, +- unsigned int *width, unsigned int *height); +-void sun6i_csi_bridge_format(struct sun6i_csi_device *csi_dev, +- u32 *mbus_code, u32 *field); +- + /* Format */ + + const struct sun6i_csi_bridge_format * +diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c +index 76356bc7f10e..4c43eaa42346 100644 +--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c ++++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c +@@ -891,11 +891,14 @@ static int sun6i_csi_capture_link_validate(struct media_link *link) + const struct sun6i_csi_capture_format *capture_format; + const struct sun6i_csi_bridge_format *bridge_format; + unsigned int capture_width, capture_height; +- unsigned int bridge_width, bridge_height; + const struct v4l2_format_info *format_info; + u32 pixelformat, capture_field; +- u32 mbus_code, bridge_field; ++ struct v4l2_subdev_format fmt = { ++ .which = V4L2_SUBDEV_FORMAT_ACTIVE, ++ .pad = SUN6I_CSI_BRIDGE_PAD_SOURCE, ++ }; + bool match; ++ int ret; + + sun6i_csi_capture_dimensions(csi_dev, &capture_width, &capture_height); + +@@ -904,18 +907,21 @@ static int sun6i_csi_capture_link_validate(struct media_link *link) + if (WARN_ON(!capture_format)) + return -EINVAL; + +- sun6i_csi_bridge_dimensions(csi_dev, &bridge_width, &bridge_height); ++ ret = v4l2_subdev_call_state_active(&csi_dev->bridge.subdev, pad, ++ get_fmt, &fmt); ++ if (ret) ++ return ret; + +- sun6i_csi_bridge_format(csi_dev, &mbus_code, &bridge_field); +- bridge_format = sun6i_csi_bridge_format_find(mbus_code); ++ bridge_format = sun6i_csi_bridge_format_find(fmt.format.code); + if (WARN_ON(!bridge_format)) + return -EINVAL; + + /* No cropping/scaling is supported. */ +- if (capture_width != bridge_width || capture_height != bridge_height) { ++ if (capture_width != fmt.format.width || ++ capture_height != fmt.format.height) { + v4l2_err(v4l2_dev, + "invalid input/output dimensions: %ux%u/%ux%u\n", +- bridge_width, bridge_height, capture_width, ++ fmt.format.width, fmt.format.height, capture_width, + capture_height); + return -EINVAL; + } +@@ -947,7 +953,8 @@ static int sun6i_csi_capture_link_validate(struct media_link *link) + /* With raw input mode, we need a 1:1 match between input and output. */ + if (bridge_format->input_format == SUN6I_CSI_INPUT_FMT_RAW || + capture_format->input_format_raw) { +- match = sun6i_csi_capture_format_match(pixelformat, mbus_code); ++ match = sun6i_csi_capture_format_match(pixelformat, ++ fmt.format.code); + if (!match) + goto invalid; + } +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/media-sun6i-csi-implement-vidioc_enum_framesizes.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/media-sun6i-csi-implement-vidioc_enum_framesizes.patch new file mode 100644 index 000000000000..ecfc5c5204fe --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/media-sun6i-csi-implement-vidioc_enum_framesizes.patch @@ -0,0 +1,65 @@ +From 5d32b5ed1d4592469ac598972ed467486a9c3641 Mon Sep 17 00:00:00 2001 +From: Adam Pigg +Date: Wed, 4 Jan 2023 21:18:51 +0000 +Subject: media: sun6i-csi: implement vidioc_enum_framesizes + +Create sun6i_csi_capture_enum_framesizes which defines the min +and max frame sizes. + +As per the specification of vidioc_enum_framesizes, if the supplied +index is >0 or no valid formats are found for the supplied format, +return -EINVAL. + +Set the type to V4L2_FRMSIZE_TYPE_CONTINUOUS and set the min and max +width and height from the approriate macros, 32x32 - 4800x4800. + +Signed-off-by: Adam Pigg +--- + .../sunxi/sun6i-csi/sun6i_csi_capture.c | 24 +++++++++++++++++++ + 1 file changed, 24 insertions(+) + +diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c +index f63862fd4e76..68560f451204 100644 +--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c ++++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c +@@ -793,6 +793,29 @@ static int sun6i_csi_capture_try_fmt(struct file *file, void *private, + return 0; + } + ++static int sun6i_csi_capture_enum_framesizes(struct file *file, void *fh, ++ struct v4l2_frmsizeenum *fsize) ++{ ++ const struct sun6i_csi_capture_format *format; ++ ++ if (fsize->index > 0) ++ return -EINVAL; ++ ++ format = sun6i_csi_capture_format_find(fsize->pixel_format); ++ if (!format) ++ return -EINVAL; ++ ++ fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; ++ fsize->stepwise.min_width = SUN6I_CSI_CAPTURE_WIDTH_MIN; ++ fsize->stepwise.max_width = SUN6I_CSI_CAPTURE_WIDTH_MAX; ++ fsize->stepwise.min_height = SUN6I_CSI_CAPTURE_HEIGHT_MIN; ++ fsize->stepwise.max_height = SUN6I_CSI_CAPTURE_HEIGHT_MAX; ++ fsize->stepwise.step_width = 1; ++ fsize->stepwise.step_height = 1; ++ ++ return 0; ++} ++ + static int sun6i_csi_capture_enum_input(struct file *file, void *private, + struct v4l2_input *input) + { +@@ -829,6 +852,7 @@ static const struct v4l2_ioctl_ops sun6i_csi_capture_ioctl_ops = { + .vidioc_g_fmt_vid_cap = sun6i_csi_capture_g_fmt, + .vidioc_s_fmt_vid_cap = sun6i_csi_capture_s_fmt, + .vidioc_try_fmt_vid_cap = sun6i_csi_capture_try_fmt, ++ .vidioc_enum_framesizes = sun6i_csi_capture_enum_framesizes, + + .vidioc_enum_input = sun6i_csi_capture_enum_input, + .vidioc_g_input = sun6i_csi_capture_g_input, +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/media-sun6i-csi-merge-sun6i_csi_formats-and-sun6i_csi_formats_m.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/media-sun6i-csi-merge-sun6i_csi_formats-and-sun6i_csi_formats_m.patch new file mode 100644 index 000000000000..3c35ff6faa51 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/media-sun6i-csi-merge-sun6i_csi_formats-and-sun6i_csi_formats_m.patch @@ -0,0 +1,431 @@ +From 4a94459f3c9f24e22331bb5e100ca885faa6a3bb Mon Sep 17 00:00:00 2001 +From: Adam Pigg +Date: Tue, 3 Jan 2023 18:20:21 +0000 +Subject: media: sun6i-csi: merge sun6i_csi_formats and sun6i_csi_formats_match + +Information about media bus formats and pixel formats supported by the +driver is split between the sun6i_csi_capture_formats and +sun6i_csi_capture_format_matches arrays. This makes it difficult to map +media bus formats to pixel formats when enumerating the supported pixel +formats by walking the sun6i_csi_capture_formats array. To prepare for +support of media bus format support in sun6i_csi_capture_enum_fmt(), +merge the two arrays toegether. + +To do this, sun6i_csi_capture_formats gains a const u32* to store the +mbus_codes previously from sun6i_csi_capture_format_matches and +sun6i_csi_capture_format_match is updated to iterate through this to +find the mbus code while iterating sun6i_csi_capture_formats. + +In addition to merging the arrys, to produce semi-planar or multi-planar +YUV formats, related mbus codes have been added to the *NV* and related +pixel formats. + +Signed-off-by: Adam Pigg +--- + .../sunxi/sun6i-csi/sun6i_csi_capture.c | 211 +++++++++--------- + .../sunxi/sun6i-csi/sun6i_csi_capture.h | 6 +- + 2 files changed, 102 insertions(+), 115 deletions(-) + +diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c +index 4c43eaa42346..ad9d1fca338d 100644 +--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c ++++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c +@@ -22,6 +22,8 @@ + + /* Helpers */ + ++#define SUN6I_BUS_FMTS(fmt...) (const u32[]) {fmt, 0} ++ + void sun6i_csi_capture_dimensions(struct sun6i_csi_device *csi_dev, + unsigned int *width, unsigned int *height) + { +@@ -49,72 +51,86 @@ static const struct sun6i_csi_capture_format sun6i_csi_capture_formats[] = { + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8, ++ .mbus_codes = SUN6I_BUS_FMTS(MEDIA_BUS_FMT_SBGGR8_1X8), + }, + { + .pixelformat = V4L2_PIX_FMT_SGBRG8, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8, ++ .mbus_codes = SUN6I_BUS_FMTS(MEDIA_BUS_FMT_SGBRG8_1X8), + }, + { + .pixelformat = V4L2_PIX_FMT_SGRBG8, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8, ++ .mbus_codes = SUN6I_BUS_FMTS(MEDIA_BUS_FMT_SGRBG8_1X8), + }, + { + .pixelformat = V4L2_PIX_FMT_SRGGB8, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8, ++ .mbus_codes = SUN6I_BUS_FMTS(MEDIA_BUS_FMT_SRGGB8_1X8), + }, + { + .pixelformat = V4L2_PIX_FMT_SBGGR10, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_10, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_10, ++ .mbus_codes = SUN6I_BUS_FMTS(MEDIA_BUS_FMT_SBGGR10_1X10), + }, + { + .pixelformat = V4L2_PIX_FMT_SGBRG10, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_10, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_10, ++ .mbus_codes = SUN6I_BUS_FMTS(MEDIA_BUS_FMT_SGBRG10_1X10), + }, + { + .pixelformat = V4L2_PIX_FMT_SGRBG10, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_10, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_10, ++ .mbus_codes = SUN6I_BUS_FMTS(MEDIA_BUS_FMT_SGRBG10_1X10), + }, + { + .pixelformat = V4L2_PIX_FMT_SRGGB10, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_10, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_10, ++ .mbus_codes = SUN6I_BUS_FMTS(MEDIA_BUS_FMT_SRGGB10_1X10), + }, + { + .pixelformat = V4L2_PIX_FMT_SBGGR12, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_12, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_12, ++ .mbus_codes = SUN6I_BUS_FMTS(MEDIA_BUS_FMT_SBGGR12_1X12), + }, + { + .pixelformat = V4L2_PIX_FMT_SGBRG12, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_12, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_12, ++ .mbus_codes = SUN6I_BUS_FMTS(MEDIA_BUS_FMT_SGBRG12_1X12), + }, + { + .pixelformat = V4L2_PIX_FMT_SGRBG12, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_12, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_12, ++ .mbus_codes = SUN6I_BUS_FMTS(MEDIA_BUS_FMT_SGRBG12_1X12), + }, + { + .pixelformat = V4L2_PIX_FMT_SRGGB12, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_12, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_12, ++ .mbus_codes = SUN6I_BUS_FMTS(MEDIA_BUS_FMT_SRGGB12_1X12), + }, + /* RGB */ + { + .pixelformat = V4L2_PIX_FMT_RGB565, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RGB565, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RGB565, ++ .mbus_codes = SUN6I_BUS_FMTS(MEDIA_BUS_FMT_RGB565_2X8_LE), + }, + { + .pixelformat = V4L2_PIX_FMT_RGB565X, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RGB565, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RGB565, ++ .mbus_codes = SUN6I_BUS_FMTS(MEDIA_BUS_FMT_RGB565_2X8_BE), + }, + /* YUV422 */ + { +@@ -123,6 +139,8 @@ static const struct sun6i_csi_capture_format sun6i_csi_capture_formats[] = { + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8, + .input_format_raw = true, + .hsize_len_factor = 2, ++ .mbus_codes = SUN6I_BUS_FMTS(MEDIA_BUS_FMT_YUYV8_2X8, ++ MEDIA_BUS_FMT_YUYV8_1X16), + }, + { + .pixelformat = V4L2_PIX_FMT_YVYU, +@@ -130,6 +148,8 @@ static const struct sun6i_csi_capture_format sun6i_csi_capture_formats[] = { + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8, + .input_format_raw = true, + .hsize_len_factor = 2, ++ .mbus_codes = SUN6I_BUS_FMTS(MEDIA_BUS_FMT_YVYU8_2X8, ++ MEDIA_BUS_FMT_YVYU8_1X16), + }, + { + .pixelformat = V4L2_PIX_FMT_UYVY, +@@ -137,6 +157,8 @@ static const struct sun6i_csi_capture_format sun6i_csi_capture_formats[] = { + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8, + .input_format_raw = true, + .hsize_len_factor = 2, ++ .mbus_codes = SUN6I_BUS_FMTS(MEDIA_BUS_FMT_UYVY8_2X8, ++ MEDIA_BUS_FMT_UYVY8_1X16), + }, + { + .pixelformat = V4L2_PIX_FMT_VYUY, +@@ -144,57 +166,124 @@ static const struct sun6i_csi_capture_format sun6i_csi_capture_formats[] = { + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8, + .input_format_raw = true, + .hsize_len_factor = 2, ++ .mbus_codes = SUN6I_BUS_FMTS(MEDIA_BUS_FMT_VYUY8_2X8, ++ MEDIA_BUS_FMT_VYUY8_1X16), + }, + { + .pixelformat = V4L2_PIX_FMT_NV16, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_YUV422SP, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_YUV422SP, ++ .mbus_codes = SUN6I_BUS_FMTS(MEDIA_BUS_FMT_UYVY8_2X8, ++ MEDIA_BUS_FMT_UYVY8_1X16, ++ MEDIA_BUS_FMT_VYUY8_2X8, ++ MEDIA_BUS_FMT_VYUY8_1X16, ++ MEDIA_BUS_FMT_YUYV8_2X8, ++ MEDIA_BUS_FMT_YUYV8_1X16, ++ MEDIA_BUS_FMT_YVYU8_2X8, ++ MEDIA_BUS_FMT_YVYU8_1X16), + }, + { + .pixelformat = V4L2_PIX_FMT_NV61, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_YUV422SP, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_YUV422SP, + .input_yuv_seq_invert = true, ++ .mbus_codes = SUN6I_BUS_FMTS(MEDIA_BUS_FMT_UYVY8_2X8, ++ MEDIA_BUS_FMT_UYVY8_1X16, ++ MEDIA_BUS_FMT_VYUY8_2X8, ++ MEDIA_BUS_FMT_VYUY8_1X16, ++ MEDIA_BUS_FMT_YUYV8_2X8, ++ MEDIA_BUS_FMT_YUYV8_1X16, ++ MEDIA_BUS_FMT_YVYU8_2X8, ++ MEDIA_BUS_FMT_YVYU8_1X16), + }, + { + .pixelformat = V4L2_PIX_FMT_YUV422P, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_YUV422P, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_YUV422P, ++ .mbus_codes = SUN6I_BUS_FMTS(MEDIA_BUS_FMT_UYVY8_2X8, ++ MEDIA_BUS_FMT_UYVY8_1X16, ++ MEDIA_BUS_FMT_VYUY8_2X8, ++ MEDIA_BUS_FMT_VYUY8_1X16, ++ MEDIA_BUS_FMT_YUYV8_2X8, ++ MEDIA_BUS_FMT_YUYV8_1X16, ++ MEDIA_BUS_FMT_YVYU8_2X8, ++ MEDIA_BUS_FMT_YVYU8_1X16), + }, + /* YUV420 */ + { + .pixelformat = V4L2_PIX_FMT_NV12_16L16, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_YUV420MB, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_YUV420MB, ++ .mbus_codes = SUN6I_BUS_FMTS(MEDIA_BUS_FMT_UYVY8_2X8, ++ MEDIA_BUS_FMT_UYVY8_1X16, ++ MEDIA_BUS_FMT_VYUY8_2X8, ++ MEDIA_BUS_FMT_VYUY8_1X16, ++ MEDIA_BUS_FMT_YUYV8_2X8, ++ MEDIA_BUS_FMT_YUYV8_1X16, ++ MEDIA_BUS_FMT_YVYU8_2X8, ++ MEDIA_BUS_FMT_YVYU8_1X16), + }, + { + .pixelformat = V4L2_PIX_FMT_NV12, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_YUV420SP, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_YUV420SP, ++ .mbus_codes = SUN6I_BUS_FMTS(MEDIA_BUS_FMT_UYVY8_2X8, ++ MEDIA_BUS_FMT_UYVY8_1X16, ++ MEDIA_BUS_FMT_VYUY8_2X8, ++ MEDIA_BUS_FMT_VYUY8_1X16, ++ MEDIA_BUS_FMT_YUYV8_2X8, ++ MEDIA_BUS_FMT_YUYV8_1X16, ++ MEDIA_BUS_FMT_YVYU8_2X8, ++ MEDIA_BUS_FMT_YVYU8_1X16), + }, + { + .pixelformat = V4L2_PIX_FMT_NV21, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_YUV420SP, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_YUV420SP, + .input_yuv_seq_invert = true, ++ .mbus_codes = SUN6I_BUS_FMTS(MEDIA_BUS_FMT_UYVY8_2X8, ++ MEDIA_BUS_FMT_UYVY8_1X16, ++ MEDIA_BUS_FMT_VYUY8_2X8, ++ MEDIA_BUS_FMT_VYUY8_1X16, ++ MEDIA_BUS_FMT_YUYV8_2X8, ++ MEDIA_BUS_FMT_YUYV8_1X16, ++ MEDIA_BUS_FMT_YVYU8_2X8, ++ MEDIA_BUS_FMT_YVYU8_1X16), + }, + + { + .pixelformat = V4L2_PIX_FMT_YUV420, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_YUV420P, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_YUV420P, ++ .mbus_codes = SUN6I_BUS_FMTS(MEDIA_BUS_FMT_UYVY8_2X8, ++ MEDIA_BUS_FMT_UYVY8_1X16, ++ MEDIA_BUS_FMT_VYUY8_2X8, ++ MEDIA_BUS_FMT_VYUY8_1X16, ++ MEDIA_BUS_FMT_YUYV8_2X8, ++ MEDIA_BUS_FMT_YUYV8_1X16, ++ MEDIA_BUS_FMT_YVYU8_2X8, ++ MEDIA_BUS_FMT_YVYU8_1X16), + }, + { + .pixelformat = V4L2_PIX_FMT_YVU420, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_YUV420P, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_YUV420P, + .input_yuv_seq_invert = true, ++ .mbus_codes = SUN6I_BUS_FMTS(MEDIA_BUS_FMT_UYVY8_2X8, ++ MEDIA_BUS_FMT_UYVY8_1X16, ++ MEDIA_BUS_FMT_VYUY8_2X8, ++ MEDIA_BUS_FMT_VYUY8_1X16, ++ MEDIA_BUS_FMT_YUYV8_2X8, ++ MEDIA_BUS_FMT_YUYV8_1X16, ++ MEDIA_BUS_FMT_YVYU8_2X8, ++ MEDIA_BUS_FMT_YVYU8_1X16), + }, + /* Compressed */ + { + .pixelformat = V4L2_PIX_FMT_JPEG, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8, ++ .mbus_codes = SUN6I_BUS_FMTS(MEDIA_BUS_FMT_JPEG_1X8), + }, + }; + +@@ -210,118 +299,20 @@ struct sun6i_csi_capture_format *sun6i_csi_capture_format_find(u32 pixelformat) + return NULL; + } + +-/* RAW formats need an exact match between pixel and mbus formats. */ +-static const +-struct sun6i_csi_capture_format_match sun6i_csi_capture_format_matches[] = { +- /* YUV420 */ +- { +- .pixelformat = V4L2_PIX_FMT_YUYV, +- .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, +- }, +- { +- .pixelformat = V4L2_PIX_FMT_YUYV, +- .mbus_code = MEDIA_BUS_FMT_YUYV8_1X16, +- }, +- { +- .pixelformat = V4L2_PIX_FMT_YVYU, +- .mbus_code = MEDIA_BUS_FMT_YVYU8_2X8, +- }, +- { +- .pixelformat = V4L2_PIX_FMT_YVYU, +- .mbus_code = MEDIA_BUS_FMT_YVYU8_1X16, +- }, +- { +- .pixelformat = V4L2_PIX_FMT_UYVY, +- .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8, +- }, +- { +- .pixelformat = V4L2_PIX_FMT_UYVY, +- .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16, +- }, +- { +- .pixelformat = V4L2_PIX_FMT_VYUY, +- .mbus_code = MEDIA_BUS_FMT_VYUY8_2X8, +- }, +- { +- .pixelformat = V4L2_PIX_FMT_VYUY, +- .mbus_code = MEDIA_BUS_FMT_VYUY8_1X16, +- }, +- /* RGB */ +- { +- .pixelformat = V4L2_PIX_FMT_RGB565, +- .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE, +- }, +- { +- .pixelformat = V4L2_PIX_FMT_RGB565X, +- .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_BE, +- }, +- /* Bayer */ +- { +- .pixelformat = V4L2_PIX_FMT_SBGGR8, +- .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, +- }, +- { +- .pixelformat = V4L2_PIX_FMT_SGBRG8, +- .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8, +- }, +- { +- .pixelformat = V4L2_PIX_FMT_SGRBG8, +- .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, +- }, +- { +- .pixelformat = V4L2_PIX_FMT_SRGGB8, +- .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8, +- }, +- { +- .pixelformat = V4L2_PIX_FMT_SBGGR10, +- .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, +- }, +- { +- .pixelformat = V4L2_PIX_FMT_SGBRG10, +- .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, +- }, +- { +- .pixelformat = V4L2_PIX_FMT_SGRBG10, +- .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, +- }, +- { +- .pixelformat = V4L2_PIX_FMT_SRGGB10, +- .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, +- }, +- { +- .pixelformat = V4L2_PIX_FMT_SBGGR12, +- .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12, +- }, +- { +- .pixelformat = V4L2_PIX_FMT_SGBRG12, +- .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12, +- }, +- { +- .pixelformat = V4L2_PIX_FMT_SGRBG12, +- .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12, +- }, +- { +- .pixelformat = V4L2_PIX_FMT_SRGGB12, +- .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12, +- }, +- /* Compressed */ +- { +- .pixelformat = V4L2_PIX_FMT_JPEG, +- .mbus_code = MEDIA_BUS_FMT_JPEG_1X8, +- }, +-}; +- + static bool sun6i_csi_capture_format_match(u32 pixelformat, u32 mbus_code) + { +- unsigned int i; +- +- for (i = 0; i < ARRAY_SIZE(sun6i_csi_capture_format_matches); i++) { +- const struct sun6i_csi_capture_format_match *match = +- &sun6i_csi_capture_format_matches[i]; +- +- if (match->pixelformat == pixelformat && +- match->mbus_code == mbus_code) +- return true; ++ unsigned int i, j; ++ ++ for (i = 0; i < ARRAY_SIZE(sun6i_csi_capture_formats); i++) { ++ const struct sun6i_csi_capture_format *format = ++ &sun6i_csi_capture_formats[i]; ++ ++ if (format->pixelformat == pixelformat) { ++ for (j = 0; format->mbus_codes[j]; j++) { ++ if (mbus_code == format->mbus_codes[j]) ++ return true; ++ } ++ } + } + + return false; +diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.h +index 3ee5ccefbd10..0484942834e3 100644 +--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.h ++++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.h +@@ -27,11 +27,7 @@ struct sun6i_csi_capture_format { + bool input_yuv_seq_invert; + bool input_format_raw; + u32 hsize_len_factor; +-}; +- +-struct sun6i_csi_capture_format_match { +- u32 pixelformat; +- u32 mbus_code; ++ const u32 *mbus_codes; + }; + + #undef current +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/media-sun6i-csi-subdev-Use-subdev-active-state-to-store-active-.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/media-sun6i-csi-subdev-Use-subdev-active-state-to-store-active-.patch new file mode 100644 index 000000000000..60b52676a2fd --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/media-sun6i-csi-subdev-Use-subdev-active-state-to-store-active-.patch @@ -0,0 +1,346 @@ +From 7ca3f841e2a66d2145bedc06dbcce5044725f622 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sat, 13 Jan 2024 21:24:24 +0100 +Subject: media: sun6i-csi: subdev: Use subdev active state to store active + format + +The sun6i_csi_bridge structure stores the active format as a struct +v4l2_mbus_framefmt instance. Now that the V4L2 subdev core supports +managing the active state, use it to replace the manually-managed +format. This simplifies the implementation of the subdev operations by +removing the need for a custom .get_fmt() handler. + +The subdev state now needs to be passed manually from the .s_stream() +operation to internal functions that need to access the format. As a +result, the sun6i_csi_bridge_format() helper function is now unused and +can be dropped. + +The .init_state() and .set_fmt() operations also need to be updated +accordingly. As a result, .set_fmt() now correctly propagates the format +from the sink pad to the source pad. + +Signed-off-by: Laurent Pinchart +(cherry picked from commit b14038f024ad84473ed79f3baecc2da9ca16d9a5) +--- + .../sunxi/sun6i-csi/sun6i_csi_bridge.c | 149 +++++++++--------- + .../sunxi/sun6i-csi/sun6i_csi_bridge.h | 2 - + 2 files changed, 73 insertions(+), 78 deletions(-) + +diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c +index 2303e5d2baee..92290876a8b3 100644 +--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c ++++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c +@@ -13,17 +13,6 @@ + #include "sun6i_csi_bridge.h" + #include "sun6i_csi_reg.h" + +-/* Helpers */ +- +-static void sun6i_csi_bridge_format(struct sun6i_csi_device *csi_dev, +- u32 *mbus_code, u32 *field) +-{ +- if (mbus_code) +- *mbus_code = csi_dev->bridge.mbus_format.code; +- if (field) +- *field = csi_dev->bridge.mbus_format.field; +-} +- + /* Format */ + + static const struct sun6i_csi_bridge_format sun6i_csi_bridge_formats[] = { +@@ -217,7 +206,8 @@ static void sun6i_csi_bridge_disable(struct sun6i_csi_device *csi_dev) + } + + static void +-sun6i_csi_bridge_configure_parallel(struct sun6i_csi_device *csi_dev) ++sun6i_csi_bridge_configure_parallel(struct sun6i_csi_device *csi_dev, ++ struct v4l2_subdev_state *state) + { + struct device *dev = csi_dev->dev; + struct regmap *regmap = csi_dev->regmap; +@@ -225,14 +215,14 @@ sun6i_csi_bridge_configure_parallel(struct sun6i_csi_device *csi_dev) + &csi_dev->bridge.source_parallel.endpoint; + unsigned char bus_width = endpoint->bus.parallel.bus_width; + unsigned int flags = endpoint->bus.parallel.flags; +- u32 field; ++ const struct v4l2_mbus_framefmt *sink_format; + u32 value = SUN6I_CSI_IF_CFG_IF_CSI; + +- sun6i_csi_bridge_format(csi_dev, NULL, &field); ++ sink_format = v4l2_subdev_state_get_format(state, SUN6I_CSI_BRIDGE_PAD_SINK); + +- if (field == V4L2_FIELD_INTERLACED || +- field == V4L2_FIELD_INTERLACED_TB || +- field == V4L2_FIELD_INTERLACED_BT) ++ if (sink_format->field == V4L2_FIELD_INTERLACED || ++ sink_format->field == V4L2_FIELD_INTERLACED_TB || ++ sink_format->field == V4L2_FIELD_INTERLACED_BT) + value |= SUN6I_CSI_IF_CFG_SRC_TYPE_INTERLACED | + SUN6I_CSI_IF_CFG_FIELD_DT_PCLK_SHIFT(1) | + SUN6I_CSI_IF_CFG_FIELD_DT_FIELD_VSYNC; +@@ -308,17 +298,18 @@ sun6i_csi_bridge_configure_parallel(struct sun6i_csi_device *csi_dev) + } + + static void +-sun6i_csi_bridge_configure_mipi_csi2(struct sun6i_csi_device *csi_dev) ++sun6i_csi_bridge_configure_mipi_csi2(struct sun6i_csi_device *csi_dev, ++ struct v4l2_subdev_state *state) + { ++ const struct v4l2_mbus_framefmt *sink_format; + struct regmap *regmap = csi_dev->regmap; + u32 value = SUN6I_CSI_IF_CFG_IF_MIPI; +- u32 field; + +- sun6i_csi_bridge_format(csi_dev, NULL, &field); ++ sink_format = v4l2_subdev_state_get_format(state, SUN6I_CSI_BRIDGE_PAD_SINK); + +- if (field == V4L2_FIELD_INTERLACED || +- field == V4L2_FIELD_INTERLACED_TB || +- field == V4L2_FIELD_INTERLACED_BT) ++ if (sink_format->field == V4L2_FIELD_INTERLACED || ++ sink_format->field == V4L2_FIELD_INTERLACED_TB || ++ sink_format->field == V4L2_FIELD_INTERLACED_BT) + value |= SUN6I_CSI_IF_CFG_SRC_TYPE_INTERLACED; + else + value |= SUN6I_CSI_IF_CFG_SRC_TYPE_PROGRESSIVE; +@@ -326,19 +317,20 @@ sun6i_csi_bridge_configure_mipi_csi2(struct sun6i_csi_device *csi_dev) + regmap_write(regmap, SUN6I_CSI_IF_CFG_REG, value); + } + +-static void sun6i_csi_bridge_configure_format(struct sun6i_csi_device *csi_dev) ++static void sun6i_csi_bridge_configure_format(struct sun6i_csi_device *csi_dev, ++ struct v4l2_subdev_state *state) + { + struct regmap *regmap = csi_dev->regmap; + bool capture_streaming = csi_dev->capture.state.streaming; + const struct sun6i_csi_bridge_format *bridge_format; + const struct sun6i_csi_capture_format *capture_format; +- u32 mbus_code, field, pixelformat; ++ const struct v4l2_mbus_framefmt *sink_format; ++ u32 pixelformat; + u8 input_format, input_yuv_seq, output_format; + u32 value = 0; + +- sun6i_csi_bridge_format(csi_dev, &mbus_code, &field); +- +- bridge_format = sun6i_csi_bridge_format_find(mbus_code); ++ sink_format = v4l2_subdev_state_get_format(state, SUN6I_CSI_BRIDGE_PAD_SINK); ++ bridge_format = sun6i_csi_bridge_format_find(sink_format->code); + if (WARN_ON(!bridge_format)) + return; + +@@ -358,9 +350,9 @@ static void sun6i_csi_bridge_configure_format(struct sun6i_csi_device *csi_dev) + if (capture_format->input_yuv_seq_invert) + input_yuv_seq = bridge_format->input_yuv_seq_invert; + +- if (field == V4L2_FIELD_INTERLACED || +- field == V4L2_FIELD_INTERLACED_TB || +- field == V4L2_FIELD_INTERLACED_BT) ++ if (sink_format->field == V4L2_FIELD_INTERLACED || ++ sink_format->field == V4L2_FIELD_INTERLACED_TB || ++ sink_format->field == V4L2_FIELD_INTERLACED_BT) + output_format = capture_format->output_format_field; + else + output_format = capture_format->output_format_frame; +@@ -371,9 +363,9 @@ static void sun6i_csi_bridge_configure_format(struct sun6i_csi_device *csi_dev) + value |= SUN6I_CSI_CH_CFG_INPUT_FMT(input_format); + value |= SUN6I_CSI_CH_CFG_INPUT_YUV_SEQ(input_yuv_seq); + +- if (field == V4L2_FIELD_TOP) ++ if (sink_format->field == V4L2_FIELD_TOP) + value |= SUN6I_CSI_CH_CFG_FIELD_SEL_FIELD0; +- else if (field == V4L2_FIELD_BOTTOM) ++ else if (sink_format->field == V4L2_FIELD_BOTTOM) + value |= SUN6I_CSI_CH_CFG_FIELD_SEL_FIELD1; + else + value |= SUN6I_CSI_CH_CFG_FIELD_SEL_EITHER; +@@ -382,16 +374,17 @@ static void sun6i_csi_bridge_configure_format(struct sun6i_csi_device *csi_dev) + } + + static void sun6i_csi_bridge_configure(struct sun6i_csi_device *csi_dev, +- struct sun6i_csi_bridge_source *source) ++ struct sun6i_csi_bridge_source *source, ++ struct v4l2_subdev_state *state) + { + struct sun6i_csi_bridge *bridge = &csi_dev->bridge; + + if (source == &bridge->source_parallel) +- sun6i_csi_bridge_configure_parallel(csi_dev); ++ sun6i_csi_bridge_configure_parallel(csi_dev, state); + else +- sun6i_csi_bridge_configure_mipi_csi2(csi_dev); ++ sun6i_csi_bridge_configure_mipi_csi2(csi_dev, state); + +- sun6i_csi_bridge_configure_format(csi_dev); ++ sun6i_csi_bridge_configure_format(csi_dev, state); + } + + /* V4L2 Subdev */ +@@ -406,6 +399,7 @@ static int sun6i_csi_bridge_s_stream(struct v4l2_subdev *subdev, int on) + struct sun6i_csi_bridge_source *source; + struct v4l2_subdev *source_subdev; + struct media_pad *remote_pad; ++ struct v4l2_subdev_state *state; + int ret; + + /* Source */ +@@ -441,8 +435,9 @@ static int sun6i_csi_bridge_s_stream(struct v4l2_subdev *subdev, int on) + sun6i_csi_bridge_irq_clear(csi_dev); + + /* Configure */ +- +- sun6i_csi_bridge_configure(csi_dev, source); ++ state = v4l2_subdev_lock_and_get_active_state(subdev); ++ sun6i_csi_bridge_configure(csi_dev, source, state); ++ v4l2_subdev_unlock_state(state); + + if (capture_streaming) + sun6i_csi_capture_configure(csi_dev); +@@ -495,13 +490,9 @@ sun6i_csi_bridge_mbus_format_prepare(struct v4l2_mbus_framefmt *mbus_format) + static int sun6i_csi_bridge_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state) + { +- struct sun6i_csi_device *csi_dev = v4l2_get_subdevdata(subdev); + unsigned int pad = SUN6I_CSI_BRIDGE_PAD_SINK; + struct v4l2_mbus_framefmt *mbus_format = + v4l2_subdev_state_get_format(state, pad); +- struct mutex *lock = &csi_dev->bridge.lock; +- +- mutex_lock(lock); + + mbus_format->code = sun6i_csi_bridge_formats[0].mbus_code; + mbus_format->width = 1280; +@@ -509,8 +500,6 @@ static int sun6i_csi_bridge_init_state(struct v4l2_subdev *subdev, + + sun6i_csi_bridge_mbus_format_prepare(mbus_format); + +- mutex_unlock(lock); +- + return 0; + } + +@@ -527,53 +516,60 @@ sun6i_csi_bridge_enum_mbus_code(struct v4l2_subdev *subdev, + return 0; + } + +-static int sun6i_csi_bridge_get_fmt(struct v4l2_subdev *subdev, +- struct v4l2_subdev_state *state, +- struct v4l2_subdev_format *format) +-{ +- struct sun6i_csi_device *csi_dev = v4l2_get_subdevdata(subdev); +- struct v4l2_mbus_framefmt *mbus_format = &format->format; +- struct mutex *lock = &csi_dev->bridge.lock; +- +- mutex_lock(lock); +- +- if (format->which == V4L2_SUBDEV_FORMAT_TRY) +- *mbus_format = *v4l2_subdev_state_get_format(state, +- format->pad); +- else +- *mbus_format = csi_dev->bridge.mbus_format; +- +- mutex_unlock(lock); +- +- return 0; +-} +- + static int sun6i_csi_bridge_set_fmt(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) + { +- struct sun6i_csi_device *csi_dev = v4l2_get_subdevdata(subdev); +- struct v4l2_mbus_framefmt *mbus_format = &format->format; +- struct mutex *lock = &csi_dev->bridge.lock; ++ struct v4l2_mbus_framefmt *sink_format; ++ struct v4l2_mbus_framefmt *src_format; + +- mutex_lock(lock); ++ /* ++ * As the source format can't change independently of the sink format, ++ * a set operation on the source is a get. ++ */ ++ if (format->pad == SUN6I_CSI_BRIDGE_PAD_SOURCE) ++ return v4l2_subdev_get_fmt(subdev, state, format); + +- sun6i_csi_bridge_mbus_format_prepare(mbus_format); ++ sink_format = v4l2_subdev_state_get_format(state, SUN6I_CSI_BRIDGE_PAD_SINK); + ++ /* + if (format->which == V4L2_SUBDEV_FORMAT_TRY) + *v4l2_subdev_state_get_format(state, format->pad) = + *mbus_format; ++ */ ++ ++ if (!sun6i_csi_bridge_format_find(format->format.code)) ++ sink_format->code = sun6i_csi_bridge_formats[0].mbus_code; + else +- csi_dev->bridge.mbus_format = *mbus_format; ++ sink_format->code = format->format.code; ++ ++ sink_format->width = format->format.width; ++ sink_format->height = format->format.height; ++ ++ /* Accept any colorspace as the CSI bridge isn't colorspace-aware. */ ++ sink_format->colorspace = format->format.colorspace; ++ sink_format->xfer_func = format->format.xfer_func; ++ sink_format->ycbcr_enc = format->format.ycbcr_enc; ++ sink_format->quantization = format->format.quantization; ++ ++ sink_format->field = V4L2_FIELD_NONE; + +- mutex_unlock(lock); ++ format->format = *sink_format; ++ ++ /* ++ * Propagate the format to the source pad. The CSI bridge can't ++ * transcode, the format on the source pad always matches the sink pad ++ * exactly. ++ */ ++ src_format = v4l2_subdev_state_get_format(state, SUN6I_CSI_BRIDGE_PAD_SOURCE); ++ *src_format = *sink_format; + + return 0; + } + + static const struct v4l2_subdev_pad_ops sun6i_csi_bridge_pad_ops = { + .enum_mbus_code = sun6i_csi_bridge_enum_mbus_code, +- .get_fmt = sun6i_csi_bridge_get_fmt, ++ .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = sun6i_csi_bridge_set_fmt, + }; + +@@ -771,8 +767,6 @@ int sun6i_csi_bridge_setup(struct sun6i_csi_device *csi_dev) + }; + int ret; + +- mutex_init(&bridge->lock); +- + /* V4L2 Subdev */ + + v4l2_subdev_init(subdev, &sun6i_csi_bridge_subdev_ops); +@@ -801,6 +795,9 @@ int sun6i_csi_bridge_setup(struct sun6i_csi_device *csi_dev) + return ret; + + /* V4L2 Subdev */ ++ ret = v4l2_subdev_init_finalize(subdev); ++ if (ret) ++ goto error_media_entity; + + if (csi_dev->isp_available) + ret = v4l2_async_register_subdev(subdev); +diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.h +index 722b633b7893..a5b0a6f064dd 100644 +--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.h ++++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.h +@@ -42,8 +42,6 @@ struct sun6i_csi_bridge { + struct v4l2_subdev subdev; + struct v4l2_async_notifier notifier; + struct media_pad pads[2]; +- struct v4l2_mbus_framefmt mbus_format; +- struct mutex lock; /* Mbus format lock. */ + + struct sun6i_csi_bridge_source source_parallel; + struct sun6i_csi_bridge_source source_mipi_csi2; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/mfd-axp20x-Add-battery-IRQ-resources.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/mfd-axp20x-Add-battery-IRQ-resources.patch new file mode 100644 index 000000000000..0321218d50c2 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/mfd-axp20x-Add-battery-IRQ-resources.patch @@ -0,0 +1,102 @@ +From 183f5386ee44033042a23dcb7051572cb5a229a9 Mon Sep 17 00:00:00 2001 +From: Samuel Holland +Date: Sat, 29 Feb 2020 01:01:58 -0600 +Subject: mfd: axp20x: Add battery IRQ resources + +Signed-off-by: Samuel Holland +--- + drivers/mfd/axp20x.c | 36 ++++++++++++++++++++++++++++++++++++ + 1 file changed, 36 insertions(+) + +diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c +index 4e52c79f8f50..8ff8bc4a22b2 100644 +--- a/drivers/mfd/axp20x.c ++++ b/drivers/mfd/axp20x.c +@@ -311,6 +311,15 @@ static const struct resource axp20x_ac_power_supply_resources[] = { + DEFINE_RES_IRQ_NAMED(AXP20X_IRQ_ACIN_OVER_V, "ACIN_OVER_V"), + }; + ++static const struct resource axp20x_battery_resources[] = { ++ DEFINE_RES_IRQ_NAMED(AXP20X_IRQ_BATT_PLUGIN, "BATT_PLUGIN"), ++ DEFINE_RES_IRQ_NAMED(AXP20X_IRQ_BATT_REMOVAL, "BATT_REMOVAL"), ++ DEFINE_RES_IRQ_NAMED(AXP20X_IRQ_BATT_ENT_ACT_MODE, "BATT_HEALTH_DEAD"), ++ DEFINE_RES_IRQ_NAMED(AXP20X_IRQ_BATT_EXIT_ACT_MODE, "BATT_HEALTH_GOOD"), ++ DEFINE_RES_IRQ_NAMED(AXP20X_IRQ_CHARG, "BATT_CHARGING"), ++ DEFINE_RES_IRQ_NAMED(AXP20X_IRQ_CHARG_DONE, "BATT_CHARGING_DONE"), ++}; ++ + static const struct resource axp20x_pek_resources[] = { + DEFINE_RES_IRQ_NAMED(AXP20X_IRQ_PEK_RIS_EDGE, "PEK_DBR"), + DEFINE_RES_IRQ_NAMED(AXP20X_IRQ_PEK_FAL_EDGE, "PEK_DBF"), +@@ -369,6 +378,23 @@ static const struct resource axp717_pek_resources[] = { + DEFINE_RES_IRQ_NAMED(AXP717_IRQ_PEK_FAL_EDGE, "PEK_DBF"), + }; + ++static const struct resource axp803_battery_resources[] = { ++ DEFINE_RES_IRQ_NAMED(AXP803_IRQ_BATT_PLUGIN, "BATT_PLUGIN"), ++ DEFINE_RES_IRQ_NAMED(AXP803_IRQ_BATT_REMOVAL, "BATT_REMOVAL"), ++ DEFINE_RES_IRQ_NAMED(AXP803_IRQ_BATT_ENT_ACT_MODE, "BATT_HEALTH_DEAD"), ++ DEFINE_RES_IRQ_NAMED(AXP803_IRQ_BATT_EXIT_ACT_MODE, "BATT_HEALTH_GOOD"), ++ DEFINE_RES_IRQ_NAMED(AXP803_IRQ_CHARG, "BATT_CHARGING"), ++ DEFINE_RES_IRQ_NAMED(AXP803_IRQ_CHARG_DONE, "BATT_CHARGING_DONE"), ++ DEFINE_RES_IRQ_NAMED(AXP803_IRQ_BATT_CHG_TEMP_HIGH, "BATT_CHG_TEMP_HIGH"), ++ DEFINE_RES_IRQ_NAMED(AXP803_IRQ_BATT_CHG_TEMP_HIGH_END, "BATT_CHG_TEMP_HIGH_END"), ++ DEFINE_RES_IRQ_NAMED(AXP803_IRQ_BATT_CHG_TEMP_LOW, "BATT_CHG_TEMP_LOW"), ++ DEFINE_RES_IRQ_NAMED(AXP803_IRQ_BATT_CHG_TEMP_LOW_END, "BATT_CHG_TEMP_LOW_END"), ++ DEFINE_RES_IRQ_NAMED(AXP803_IRQ_BATT_ACT_TEMP_HIGH, "BATT_ACT_TEMP_HIGH"), ++ DEFINE_RES_IRQ_NAMED(AXP803_IRQ_BATT_ACT_TEMP_HIGH_END, "BATT_ACT_TEMP_HIGH_END"), ++ DEFINE_RES_IRQ_NAMED(AXP803_IRQ_BATT_ACT_TEMP_LOW, "BATT_ACT_TEMP_LOW"), ++ DEFINE_RES_IRQ_NAMED(AXP803_IRQ_BATT_ACT_TEMP_LOW_END, "BATT_ACT_TEMP_LOW_END"), ++}; ++ + static const struct resource axp803_pek_resources[] = { + DEFINE_RES_IRQ_NAMED(AXP803_IRQ_PEK_RIS_EDGE, "PEK_DBR"), + DEFINE_RES_IRQ_NAMED(AXP803_IRQ_PEK_FAL_EDGE, "PEK_DBF"), +@@ -972,6 +998,8 @@ static const struct mfd_cell axp20x_cells[] = { + }, { + .name = "axp20x-battery-power-supply", + .of_compatible = "x-powers,axp209-battery-power-supply", ++ .num_resources = ARRAY_SIZE(axp20x_battery_resources), ++ .resources = axp20x_battery_resources, + }, { + .name = "axp20x-ac-power-supply", + .of_compatible = "x-powers,axp202-ac-power-supply", +@@ -1006,6 +1034,8 @@ static const struct mfd_cell axp221_cells[] = { + }, { + .name = "axp20x-battery-power-supply", + .of_compatible = "x-powers,axp221-battery-power-supply", ++ .num_resources = ARRAY_SIZE(axp20x_battery_resources), ++ .resources = axp20x_battery_resources, + }, { + .name = "axp20x-usb-power-supply", + .of_compatible = "x-powers,axp221-usb-power-supply", +@@ -1028,6 +1058,8 @@ static const struct mfd_cell axp223_cells[] = { + }, { + .name = "axp20x-battery-power-supply", + .of_compatible = "x-powers,axp221-battery-power-supply", ++ .num_resources = ARRAY_SIZE(axp20x_battery_resources), ++ .resources = axp20x_battery_resources, + }, { + .name = "axp20x-regulator", + }, { +@@ -1144,6 +1176,8 @@ static const struct mfd_cell axp803_cells[] = { + }, { + .name = "axp20x-battery-power-supply", + .of_compatible = "x-powers,axp813-battery-power-supply", ++ .num_resources = ARRAY_SIZE(axp803_battery_resources), ++ .resources = axp803_battery_resources, + }, { + .name = "axp20x-ac-power-supply", + .of_compatible = "x-powers,axp813-ac-power-supply", +@@ -1204,6 +1238,8 @@ static const struct mfd_cell axp813_cells[] = { + }, { + .name = "axp20x-battery-power-supply", + .of_compatible = "x-powers,axp813-battery-power-supply", ++ .num_resources = ARRAY_SIZE(axp803_battery_resources), ++ .resources = axp803_battery_resources, + }, { + .name = "axp20x-ac-power-supply", + .of_compatible = "x-powers,axp813-ac-power-supply", +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/misc-modem-power-Power-manager-for-modems.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/misc-modem-power-Power-manager-for-modems.patch new file mode 100644 index 000000000000..ca2f31770057 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/misc-modem-power-Power-manager-for-modems.patch @@ -0,0 +1,2048 @@ +From ec41719fff6c6b38703993687e10ee49d795092e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sun, 12 Nov 2017 02:10:15 +0100 +Subject: misc: modem-power: Power manager for modems + +This driver allows for powering/resetting devices that are otherwise +handled by other subsytems (USB). It also has mechanismsm for polling +from userspace on device->SoC wakeup events via GPIO. + +This is mostly useful for controling modems. The supported modems are: + +- Quectel EG25 +- ZTE MG3732 + +Signed-off-by: Ondrej Jirman +--- + drivers/misc/Kconfig | 7 + + drivers/misc/Makefile | 1 + + drivers/misc/modem-power.c | 1990 ++++++++++++++++++++++++++++++++++++ + 3 files changed, 1998 insertions(+) + create mode 100644 drivers/misc/modem-power.c + +diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig +index 56bc72c7ce4a..c0efb3129661 100644 +--- a/drivers/misc/Kconfig ++++ b/drivers/misc/Kconfig +@@ -632,6 +632,13 @@ config MCHP_LAN966X_PCI + - lan966x-miim (MDIO_MSCC_MIIM) + - lan966x-switch (LAN966X_SWITCH) + ++config MODEM_POWER ++ tristate "Modem power/wakeup support for EG25, MG3732, etc." ++ depends on OF && SERIAL_DEV_BUS && RFKILL ++ help ++ This driver provides support for powering up and handling ++ wakeup signals for various modems. ++ + source "drivers/misc/c2port/Kconfig" + source "drivers/misc/eeprom/Kconfig" + source "drivers/misc/cb710/Kconfig" +diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile +index 545aad06d088..9b20ffa44835 100644 +--- a/drivers/misc/Makefile ++++ b/drivers/misc/Makefile +@@ -75,3 +75,4 @@ lan966x-pci-objs := lan966x_pci.o + lan966x-pci-objs += lan966x_pci.dtbo.o + obj-$(CONFIG_MCHP_LAN966X_PCI) += lan966x-pci.o + obj-y += keba/ ++obj-$(CONFIG_MODEM_POWER) += modem-power.o +diff --git a/drivers/misc/modem-power.c b/drivers/misc/modem-power.c +new file mode 100644 +index 000000000000..069790db953d +--- /dev/null ++++ b/drivers/misc/modem-power.c +@@ -0,0 +1,1990 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++/* ++ * Modem power control driver. ++ * ++ * Ondrej Jirman ++ * ++ * How this works ++ * -------------- ++ * ++ * The driver: ++ * - can be registered as a platform or serial device ++ * - will use gpios, regulator and (optionally) serial port to control the modem ++ * - exposes a character device to control the modem power and receive various ++ * events ++ * - exposes sysfs interface to control modem power and wakeup ++ * - supports multiple modem types and instances ++ * ++ * Power up/power down: ++ * - may take a lot of time (eg. ~13-22s powerup, >22s powerdown) ++ * - happens on a private workqueue under a lock ++ * - may happen from shutdown hook ++ * - prevents suspend when powerup/powerdown is in progress ++ * - is serialized and there's no abort of in-progress operations ++ * - for specific power sequence see comments in the section for each ++ * supported modem variant ++ * - the driver monitors the power status of the modem (optionally) ++ * and tries to complete the powerdown initiated via AT command ++ * - the driver tries to detect when the modem is killswitched off ++ * and updates the driver status to reflect that ++ * ++ * Suspend/resume: ++ * - suspend is blocked if powerup/down is in progress ++ * - modem can wakeup the host over gpio based IRQ (RI signal) ++ * - the driver will assert ap_ready after resume finishes ++ * ++ * Rfkill: ++ * - the driver implements a rfkill interface if rfkill gpio is available ++ */ ++ ++//#define DEBUG ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define DRIVER_NAME "modem-power" ++ ++enum { ++ MPWR_REQ_NONE = 0, ++ MPWR_REQ_RESET, ++ MPWR_REQ_PWDN, ++ MPWR_REQ_PWUP, ++}; ++ ++enum { ++ MPWR_MODE_NORMAL = 1, ++ MPWR_MODE_DUMB, ++ MPWR_MODE_FASTBOOT, ++ MPWR_MODE_ALT1, ++ MPWR_MODE_ALT2, ++}; ++ ++struct mpwr_dev; ++ ++struct mpwr_gpio { ++ const char* name; ++ unsigned desc_off; ++ int flags; ++ bool required; ++ int irq_flags; ++ unsigned irq_off; ++}; ++ ++#define MPWR_GPIO_DEF(_name, _flags, _req) \ ++ { .name = #_name, \ ++ .desc_off = offsetof(struct mpwr_dev, _name##_gpio), \ ++ .flags = _flags, \ ++ .required = _req, \ ++ } ++ ++#define MPWR_GPIO_DEF_IRQ(_name, _flags, _req, _irq_flags) \ ++ { .name = #_name, \ ++ .desc_off = offsetof(struct mpwr_dev, _name##_gpio), \ ++ .flags = _flags, \ ++ .required = _req, \ ++ .irq_flags = _irq_flags, \ ++ .irq_off = offsetof(struct mpwr_dev, _name##_irq), \ ++ } ++ ++struct mpwr_variant { ++ int (*power_init)(struct mpwr_dev* mpwr); ++ int (*power_up)(struct mpwr_dev* mpwr); ++ int (*power_down)(struct mpwr_dev* mpwr); ++ int (*reset)(struct mpwr_dev* mpwr); ++ void (*recv_msg)(struct mpwr_dev *mpwr, const char *msg); ++ int (*suspend)(struct mpwr_dev *mpwr); ++ int (*resume)(struct mpwr_dev *mpwr); ++ const struct mpwr_gpio* gpios; ++ bool regulator_required; ++ bool monitor_wakeup; ++}; ++ ++struct mpwr_dev { ++ struct device *dev; ++ const struct mpwr_variant* variant; ++ ++ wait_queue_head_t wait; ++ ++ /* serdev */ ++ struct serdev_device *serdev; ++ char rcvbuf[4096]; ++ size_t rcvbuf_fill; ++ char msg[4096]; ++ int msg_len; ++ int msg_ok; ++ //struct kfifo kfifo; ++ DECLARE_KFIFO(kfifo, unsigned char, 4096); ++ ++ /* power */ ++ struct regulator *regulator; ++ struct regulator *regulator_vbus; ++ ++ /* outputs */ ++ struct gpio_desc *enable_gpio; ++ struct gpio_desc *reset_gpio; ++ struct gpio_desc *pwrkey_gpio; ++ struct gpio_desc *sleep_gpio; ++ struct gpio_desc *dtr_gpio; ++ struct gpio_desc *host_ready_gpio; ++ struct gpio_desc *cts_gpio; ++ struct gpio_desc *rts_gpio; ++ ++ /* inputs */ ++ struct gpio_desc *status_gpio; ++ struct gpio_desc *wakeup_gpio; ++ int wakeup_irq; ++ bool status_pwrkey_multiplexed; ++ ++ /* config */ ++ struct cdev cdev; ++ dev_t major; ++ ++ /* rfkill */ ++ struct rfkill *rfkill; ++ ++ /* powerup/dn work queue */ ++ struct workqueue_struct *wq; ++ struct work_struct power_work; ++ struct work_struct finish_pdn_work; ++ struct mutex modem_lock; ++ ++ // change ++ spinlock_t lock; /* protects last_request */ ++ int last_request; ++ int powerup_mode; ++ ktime_t last_wakeup; ++ ++ struct timer_list wd_timer; ++ struct delayed_work host_ready_work; ++ ++ unsigned long flags[1]; ++}; ++ ++enum { ++ /* modem is powered */ ++ MPWR_F_POWERED, ++ MPWR_F_POWER_CHANGE_INPROGRESS, ++ MPWR_F_KILLSWITCHED, ++ /* we got a wakeup from the modem */ ++ MPWR_F_GOT_WAKEUP, ++ /* serdev */ ++ MPWR_F_RECEIVING_MSG, ++ /* eg25 */ ++ MPWR_F_GOT_PDN, ++ /* config options */ ++ MPWR_F_BLOCKED, ++ /* file */ ++ MPWR_F_OPEN, ++ MPWR_F_OVERFLOW, ++}; ++ ++static struct class* mpwr_class; ++ ++static int mpwr_serdev_at_cmd(struct mpwr_dev *mpwr, const char *msg, int timeout_ms); ++static int mpwr_serdev_at_cmd_with_retry(struct mpwr_dev *mpwr, const char *msg, ++ int timeout_ms, int tries); ++static int mpwr_serdev_at_cmd_with_retry_ignore_timeout(struct mpwr_dev *mpwr, const char *msg, ++ int timeout_ms, int tries); ++ ++// {{{ mg2723 variant ++ ++static int mpwr_mg2723_power_init(struct mpwr_dev* mpwr) ++{ ++ // if the device has power applied or doesn't have regulator ++ // configured (we assume it's always powered) initialize GPIO ++ // to shut it down initially ++ if (!mpwr->regulator || regulator_is_enabled(mpwr->regulator)) { ++ gpiod_set_value(mpwr->enable_gpio, 0); ++ gpiod_set_value(mpwr->reset_gpio, 1); ++ } else { ++ // device is not powered, don't drive the gpios ++ gpiod_direction_input(mpwr->enable_gpio); ++ gpiod_direction_input(mpwr->reset_gpio); ++ } ++ ++ return 0; ++} ++ ++static int mpwr_mg2723_power_up(struct mpwr_dev* mpwr) ++{ ++ int ret; ++ ++ // power up ++ if (mpwr->regulator) { ++ ret = regulator_enable(mpwr->regulator); ++ if (ret < 0) { ++ dev_err(mpwr->dev, ++ "can't enable power supply err=%d", ret); ++ return ret; ++ } ++ } ++ ++ gpiod_direction_output(mpwr->enable_gpio, 1); ++ gpiod_direction_output(mpwr->reset_gpio, 1); ++ msleep(300); ++ gpiod_set_value(mpwr->reset_gpio, 0); ++ ++ return 0; ++} ++ ++static int mpwr_mg2723_power_down(struct mpwr_dev* mpwr) ++{ ++ gpiod_set_value(mpwr->enable_gpio, 0); ++ msleep(50); ++ ++ if (mpwr->regulator) { ++ regulator_disable(mpwr->regulator); ++ ++ gpiod_direction_input(mpwr->enable_gpio); ++ gpiod_direction_input(mpwr->reset_gpio); ++ } else { ++ gpiod_set_value(mpwr->reset_gpio, 1); ++ } ++ ++ return 0; ++} ++ ++static int mpwr_mg2723_reset(struct mpwr_dev* mpwr) ++{ ++ gpiod_set_value(mpwr->reset_gpio, 1); ++ msleep(300); ++ gpiod_set_value(mpwr->reset_gpio, 0); ++ ++ return 0; ++} ++ ++static const struct mpwr_gpio mpwr_mg2723_gpios[] = { ++ MPWR_GPIO_DEF(enable, GPIOD_IN, true), ++ MPWR_GPIO_DEF(reset, GPIOD_IN, true), ++ MPWR_GPIO_DEF_IRQ(wakeup, GPIOD_IN, true, IRQF_TRIGGER_FALLING), ++ { }, ++}; ++ ++static const struct mpwr_variant mpwr_mg2723_variant = { ++ .power_init = mpwr_mg2723_power_init, ++ .power_up = mpwr_mg2723_power_up, ++ .power_down = mpwr_mg2723_power_down, ++ .reset = mpwr_mg2723_reset, ++ .gpios = mpwr_mg2723_gpios, ++}; ++ ++// }}} ++// {{{ eg25 variant ++ ++static bool mpwr_eg25_qcfg_airplanecontrol_is_ok(const char* v) ++{ ++ return strstarts(v, "1,"); ++} ++ ++struct mpwr_eg25_qcfg { ++ const char* name; ++ const char* val; ++ bool (*is_ok)(const char* val); ++}; ++ ++#define EG25G_LATEST_KNOWN_FIRMWARE "EG25GGBR07A08M2G_01.002.07" ++ ++static const struct mpwr_eg25_qcfg mpwr_eg25_qcfgs[] = { ++ //{ "risignaltype", "\"respective\"", }, ++ { "risignaltype", "\"physical\"", }, ++ { "urc/ri/ring", "\"pulse\",1,1000,5000,\"off\",1", }, ++ { "urc/ri/smsincoming", "\"pulse\",1,1", }, ++ { "urc/ri/other", "\"off\",1,1", }, ++ { "urc/ri/pin", "uart_ri", }, ++ { "urc/delay", "0", }, ++ ++ //{ "sleep/datactrl", "0,300,1", }, ++ ++ { "sleepind/level", "0", }, ++ { "wakeupin/level", "0", }, ++ ++ { "ApRstLevel", "0", }, ++ { "ModemRstLevel", "0", }, ++ ++ // in EG25-G this tries to modify file in /etc (read-only) ++ // and fails ++ //{ "dbgctl", "0", }, ++ ++ // we don't need AP_READY ++ { "apready", "0,0,500", }, ++ ++ { "airplanecontrol", "1", mpwr_eg25_qcfg_airplanecontrol_is_ok }, ++ ++ // available since firmware R07A08_01.002.01.002 ++ { "fast/poweroff", "1" }, ++}; ++ ++static char* mpwr_serdev_get_response_value(struct mpwr_dev *mpwr, ++ const char* prefix) ++{ ++ int off; ++ ++ for (off = 0; off < mpwr->msg_len; off += strlen(mpwr->msg + off) + 1) ++ if (strstarts(mpwr->msg + off, prefix)) ++ return mpwr->msg + off + strlen(prefix); ++ ++ return NULL; ++} ++ ++static struct gpio_desc *mpwr_eg25_get_pwrkey_gpio(struct mpwr_dev *mpwr) ++{ ++ if (mpwr->status_pwrkey_multiplexed) ++ return mpwr->status_gpio; ++ ++ return mpwr->pwrkey_gpio; ++} ++ ++/* ++ * Gpio meanings ++ * ------------- ++ * ++ * enable_gpio - 1 = enables RF, 0 = disables RF ++ * sleep_gpio - 1 = puts modem to sleep, 0 = wakes up the modem (must be 0 ++ * during poweron) ++ * reset_gpio - accepts 150-460ms reset pulse (high __|^|__) ++ * pwrkey_gpio - accepts 100ms-650ms pulse for powerup (high __|^|__) ++ * 650ms+ pulse for powerdown ++ * (initiated after pulse ends, pulse may have indefinite ++ * duration) ++ * status_gpio - modem power status 0 = powered 1 = unpowered ++ * wakeup_gpio - "ring indicator" output from the modem ++ * host_ready_gpio - AP_READY pin - host is ready to receive URCs ++ * ++ * (pwrkey may be multiplexed with status_gpio) ++ * ++ * Modem behavior ++ * -------------- ++ * ++ * wakeup_gpio (RI): ++ * - goes high shortly after power is applied (~15ms) ++ * - goes low when RDY is sent ++ * ++ * dtr_gpio ++ * - when high, modem can sleep if requested ++ * - H->L will wake up a sleeping modem ++ * - internal pull-up ++ * ++ * ri ++ * - pulled low when there's URC ++ * - modem wakes up on URC automatically ++ * ++ * - AT+QURCCFG ++ * - AT+QINDCFG="csq",1 ++ * - AT+QINDCFG="ring",1 ++ * - AT+QINDCFG="smsincoming",1 ++ * - AT+CGREG=0 ++ * - AT+CREG=0 ++ * ++ * - AT+QURCCFG="urcport","uart1" ++ */ ++static int mpwr_eg25_power_up(struct mpwr_dev* mpwr) ++{ ++ struct gpio_desc *pwrkey_gpio = mpwr_eg25_get_pwrkey_gpio(mpwr); ++ bool wakeup_ok, status_ok; ++ bool needs_restart = false; ++ u32 speed = 115200; ++ int ret, i, off; ++ ktime_t start; ++ int mode = mpwr->powerup_mode; ++ ++ if (regulator_is_enabled(mpwr->regulator)) ++ dev_warn(mpwr->dev, ++ "regulator was already enabled during powerup"); ++ ++ /* Enable the modem power. */ ++ ret = regulator_enable(mpwr->regulator); ++ if (ret < 0) { ++ dev_err(mpwr->dev, ++ "can't enable power supply err=%d", ret); ++ return ret; ++ } ++ ++ ret = regulator_enable(mpwr->regulator_vbus); ++ if (ret < 0) { ++ dev_err(mpwr->dev, ++ "can't enable vbus power supply err=%d", ret); ++ regulator_disable(mpwr->regulator); ++ return ret; ++ } ++ ++ /* Drive default gpio signals during powerup */ ++ /* host_ready_gpio should be 1 during normal powerup */ ++ gpiod_direction_output(mpwr->host_ready_gpio, mode != MPWR_MODE_ALT2); ++ /* #W_DISABLE must be left pulled up during modem power up ++ * early on, because opensource bootloader uses this signal to enter ++ * fastboot mode when it's pulled down. ++ * ++ * This should be 1 for normal powerup and 0 for fastboot mode with ++ * special Biktor's firmware. ++ */ ++ gpiod_direction_output(mpwr->enable_gpio, mode != MPWR_MODE_FASTBOOT); ++ gpiod_direction_output(mpwr->sleep_gpio, 0); ++ gpiod_direction_output(mpwr->reset_gpio, 0); ++ gpiod_direction_output(pwrkey_gpio, 0); ++ /* dtr_gpio should be 0 during normal powerup */ ++ gpiod_direction_output(mpwr->dtr_gpio, mode == MPWR_MODE_ALT1); ++ ++ /* Wait for powerup. (30ms min. according to datasheet) */ ++ msleep(50); ++ ++ /* Send 200ms pwrkey pulse to initiate poweron */ ++ gpiod_set_value(pwrkey_gpio, 1); ++ msleep(200); ++ gpiod_set_value(pwrkey_gpio, 0); ++ ++ /* skip modem killswitch status checks in fastboot bootloader entry mode */ ++ if (mode != MPWR_MODE_NORMAL) ++ goto open_serdev; ++ ++ /* Switch status key to input, in case it's multiplexed with pwrkey. */ ++ gpiod_direction_input(mpwr->status_gpio); ++ ++ /* ++ * Wait for status/wakeup change, assume good values, if CTS/status ++ * signals, are not configured. ++ */ ++ status_ok = mpwr->status_gpio ? false : true; ++ wakeup_ok = mpwr->wakeup_gpio ? false : true; ++ ++ /* wait up to 10s for status */ ++ start = ktime_get(); ++ while (ktime_ms_delta(ktime_get(), start) < 10000) { ++ if (!wakeup_ok && mpwr->wakeup_gpio && gpiod_get_value(mpwr->wakeup_gpio)) { ++ dev_info(mpwr->dev, "wakeup ok\n"); ++ wakeup_ok = true; ++ } ++ ++ if (!status_ok && mpwr->status_gpio && !gpiod_get_value(mpwr->status_gpio)) { ++ dev_info(mpwr->dev, "status ok\n"); ++ status_ok = true; ++ } ++ ++ /* modem is ready */ ++ if (wakeup_ok && status_ok) ++ break; ++ ++ msleep(50); ++ } ++ ++ if (!wakeup_ok) { ++ dev_err(mpwr->dev, "The modem looks kill-switched\n"); ++ if (!test_and_set_bit(MPWR_F_KILLSWITCHED, mpwr->flags)) ++ sysfs_notify(&mpwr->dev->kobj, NULL, "killswitched"); ++ goto err_shutdown_noclose; ++ } ++ ++ if (!status_ok) { ++ dev_err(mpwr->dev, "The modem didn't report powerup success in time\n"); ++ goto err_shutdown_noclose; ++ } ++ ++ if (test_and_clear_bit(MPWR_F_KILLSWITCHED, mpwr->flags)) ++ sysfs_notify(&mpwr->dev->kobj, NULL, "killswitched"); ++ ++open_serdev: ++ /* open serial console */ ++ ret = serdev_device_open(mpwr->serdev); ++ if (ret) { ++ dev_err(mpwr->dev, "error opening serdev (%d)\n", ret); ++ goto err_shutdown_noclose; ++ } ++ ++ of_property_read_u32(mpwr->dev->of_node, "current-speed", &speed); ++ serdev_device_set_baudrate(mpwr->serdev, speed); ++ serdev_device_set_flow_control(mpwr->serdev, false); ++ ret = serdev_device_set_parity(mpwr->serdev, SERDEV_PARITY_NONE); ++ if (ret) { ++ dev_err(mpwr->dev, "error setting serdev parity (%d)\n", ret); ++ goto err_shutdown; ++ } ++ ++ if (mode != MPWR_MODE_NORMAL) ++ goto powered_up; ++ ++ ret = mpwr_serdev_at_cmd_with_retry_ignore_timeout(mpwr, "AT&FE0", 1000, 30); ++ if (ret) ++ goto err_shutdown; ++ ++ /* print firmware version */ ++ ret = mpwr_serdev_at_cmd_with_retry(mpwr, "AT+QVERSION;+QSUBSYSVER", 1000, 15); ++ if (ret == 0 && mpwr->msg_len > 0) { ++ bool outdated = false; ++ ++ dev_info(mpwr->dev, "===================================================\n"); ++ for (off = 0; off < mpwr->msg_len; off += strlen(mpwr->msg + off) + 1) { ++ if (strstr(mpwr->msg + off, "Project Rev") && !strstr(mpwr->msg + off, EG25G_LATEST_KNOWN_FIRMWARE)) ++ outdated = true; ++ ++ dev_info(mpwr->dev, "%s\n", mpwr->msg + off); ++ } ++ dev_info(mpwr->dev, "===================================================\n"); ++ ++ if (outdated) ++ dev_warn(mpwr->dev, "Your modem has an outdated firmware. Latest know version is %s. Consider updating.\n", EG25G_LATEST_KNOWN_FIRMWARE); ++ } ++ ++ /* print ADB key to dmesg */ ++ ret = mpwr_serdev_at_cmd_with_retry(mpwr, "AT+QADBKEY?", 1000, 15); ++ if (ret == 0) { ++ const char *val = mpwr_serdev_get_response_value(mpwr, "+QADBKEY: "); ++ if (val) ++ dev_info(mpwr->dev, "ADB KEY is '%s' (you can use it to unlock ADB access to the modem, see https://xnux.eu/devices/feature/modem-pp.html)\n", val); ++ } ++ ++ // check DAI config ++ ret = mpwr_serdev_at_cmd_with_retry(mpwr, "AT+QDAI?", 1000, 15); ++ if (ret == 0) { ++ const char *val = mpwr_serdev_get_response_value(mpwr, "+QDAI: "); ++ const char *needed_val = NULL; ++ char buf[128]; ++ ++ if (val) { ++ of_property_read_string(mpwr->dev->of_node, "quectel,qdai", &needed_val); ++ ++ if (needed_val && strcmp(needed_val, val)) { ++ dev_warn(mpwr->dev, "QDAI is '%s' (changing to '%s')\n", val, needed_val); ++ ++ /* update qdai */ ++ snprintf(buf, sizeof buf, "AT+QDAI=%s", needed_val); ++ ret = mpwr_serdev_at_cmd(mpwr, buf, 5000); ++ if (ret == 0) ++ needs_restart = true; ++ } else { ++ dev_info(mpwr->dev, "QDAI is '%s'\n", val); ++ } ++ } ++ } ++ ++ /* reset the modem, to apply QDAI config if necessary */ ++ if (needs_restart) { ++ dev_info(mpwr->dev, "Restarting modem\n"); ++ ++ /* reboot is broken with fastboot enabled */ ++ mpwr_serdev_at_cmd(mpwr, "AT+QCFG=\"fast/poweroff\",0", 5000); ++ ++ ret = mpwr_serdev_at_cmd(mpwr, "AT+CFUN=1,1", 5000); ++ if (ret) ++ goto err_shutdown; ++ ++ /* wait a bit before starting to probe the modem again */ ++ msleep(6000); ++ ++ ret = mpwr_serdev_at_cmd_with_retry_ignore_timeout(mpwr, "AT&FE0", 1000, 30); ++ if (ret) ++ goto err_shutdown; ++ ++ // wait until QDAI starts succeeding (then the modem is ready ++ // to accept the following QCFGs) ++ ret = mpwr_serdev_at_cmd_with_retry(mpwr, "AT+QDAI?", 1000, 15); ++ if (ret) ++ goto err_shutdown; ++ } ++ ++ /* check and update important QCFGs */ ++ for (i = 0; i < ARRAY_SIZE(mpwr_eg25_qcfgs); i++) { ++ const char* name = mpwr_eg25_qcfgs[i].name; ++ const char* needed_val = mpwr_eg25_qcfgs[i].val; ++ bool (*is_ok)(const char* val) = mpwr_eg25_qcfgs[i].is_ok; ++ const char *val; ++ char buf[128]; ++ ++ snprintf(buf, sizeof buf, "AT+QCFG=\"%s\"", name); ++ ret = mpwr_serdev_at_cmd(mpwr, buf, 1000); ++ if (ret) ++ continue; ++ ++ snprintf(buf, sizeof buf, "+QCFG: \"%s\",", name); ++ val = mpwr_serdev_get_response_value(mpwr, buf); ++ if (val) { ++ if (needed_val && (is_ok ? !is_ok(val) : strcmp(needed_val, val))) { ++ dev_info(mpwr->dev, "QCFG '%s' is '%s' (changing to '%s')\n", name, val, needed_val); ++ ++ /* update qcfg */ ++ snprintf(buf, sizeof buf, "AT+QCFG=\"%s\",%s", name, needed_val); ++ ret = mpwr_serdev_at_cmd(mpwr, buf, 1000); ++ if (ret) ++ break; /* go to next QCFG */ ++ } else { ++ dev_info(mpwr->dev, "QCFG '%s' is '%s'\n", name, val); ++ } ++ } ++ } ++ ++ /* setup URC port */ ++ ret = mpwr_serdev_at_cmd(mpwr, "AT+QURCCFG=\"urcport\",\"all\"", 2000); ++ if (ret) { ++ dev_info(mpwr->dev, "Your modem doesn't support AT+QURCCFG=\"urcport\",\"all\", consider upgrading the firmware.\n"); ++ ++ ret = mpwr_serdev_at_cmd(mpwr, "AT+QURCCFG=\"urcport\",\"usbat\"", 2000); ++ if (ret) ++ dev_err(mpwr->dev, "Modem may not report URCs to the right port!\n"); ++ } ++ ++ /* enable the modem to go to sleep when DTR is low */ ++ ret = mpwr_serdev_at_cmd(mpwr, "AT+QSCLK=1", 2000); ++ if (ret) ++ dev_err(mpwr->dev, "Modem will probably not sleep!\n"); ++ ++powered_up: ++ // if we're signaling some alternate boot mode via GPIO, we need to ++ // sleep here so that modem's boot script notices the gpio ++ if (mode == MPWR_MODE_ALT1 || mode == MPWR_MODE_FASTBOOT || mode == MPWR_MODE_ALT2) ++ msleep(12000); ++ ++ gpiod_direction_output(mpwr->dtr_gpio, 1); ++ ++ return 0; ++ ++err_shutdown: ++ serdev_device_close(mpwr->serdev); ++err_shutdown_noclose: ++ dev_warn(mpwr->dev, ++ "Forcibly cutting off power, data loss may occur.\n"); ++ gpiod_direction_input(mpwr->enable_gpio); ++ gpiod_direction_input(mpwr->reset_gpio); ++ gpiod_direction_input(mpwr->sleep_gpio); ++ gpiod_direction_input(pwrkey_gpio); ++ gpiod_direction_input(mpwr->host_ready_gpio); ++ gpiod_direction_input(mpwr->dtr_gpio); ++ ++ regulator_disable(mpwr->regulator_vbus); ++ regulator_disable(mpwr->regulator); ++ return -ENODEV; ++} ++ ++static int mpwr_eg25_power_down_finish(struct mpwr_dev* mpwr) ++{ ++ struct gpio_desc *pwrkey_gpio = mpwr_eg25_get_pwrkey_gpio(mpwr); ++ ktime_t start = ktime_get(); ++ int ret; ++ ++ serdev_device_close(mpwr->serdev); ++ ++ /* ++ * This function is called right after POWERED DOWN message is received. ++ * ++ * In case of fast/poweroff == 1, no POWERED DOWN message is sent. ++ * Fast power off times are around 1s since the end of 800ms ++ * POK pulse. ++ * ++ * When the modem powers down RI (wakeup) goes low and STATUS goes ++ * high at the same time. Status is not connected on some boards. ++ * RI should be inactive during poweroff, but we don't know for sure. ++ * ++ * Therfore: ++ * - wait for STATUS going low ++ * - in case that's not available wait for RI going low ++ * - in case timings seem off, warn the user ++ * ++ * In addition, some boards have PWRKEY multiplexed with STATUS signal. ++ * In that case we need to switch STATUS to output high level, as soon ++ * as it goes low in order to prevent a power-up signal being registered ++ * by the modem. ++ */ ++ ++ if (mpwr->status_gpio) { ++ /* wait up to 30s for status going high */ ++ while (ktime_ms_delta(ktime_get(), start) < 30000) { ++ if (gpiod_get_value(mpwr->status_gpio)) { ++ if (ktime_ms_delta(ktime_get(), start) < 500) ++ dev_warn(mpwr->dev, ++ "STATUS signal is high too soon during powerdown. Modem is already off?\n"); ++ goto powerdown; ++ } ++ ++ msleep(20); ++ } ++ ++ dev_warn(mpwr->dev, ++ "STATUS signal didn't go high during shutdown. Modem is still on?\n"); ++ goto force_powerdown; ++ } else { ++ clear_bit(MPWR_F_GOT_WAKEUP, mpwr->flags); ++ ++ if (!gpiod_get_value(mpwr->wakeup_gpio)) { ++ dev_warn(mpwr->dev, ++ "RI signal is low too soon during powerdown. Modem is already off, or spurious wakeup?\n"); ++ msleep(2000); ++ goto powerdown; ++ } ++ ++ ret = wait_event_timeout(mpwr->wait, ++ test_bit(MPWR_F_GOT_WAKEUP, mpwr->flags), ++ msecs_to_jiffies(30000)); ++ if (ret <= 0) { ++ dev_warn(mpwr->dev, ++ "RI signal didn't go low during shutdown, is modem really powering down?\n"); ++ goto force_powerdown; ++ } ++ ++ if (ktime_ms_delta(ktime_get(), start) < 500) { ++ dev_warn(mpwr->dev, ++ "RI signal is low too soon during powerdown. Modem is already off, or spurious wakeup?\n"); ++ msleep(2000); ++ goto powerdown; ++ } ++ } ++ ++powerdown: ++ gpiod_direction_input(mpwr->enable_gpio); ++ gpiod_direction_input(mpwr->reset_gpio); ++ gpiod_direction_input(mpwr->sleep_gpio); ++ gpiod_direction_input(pwrkey_gpio); ++ gpiod_direction_input(mpwr->host_ready_gpio); ++ gpiod_direction_input(mpwr->dtr_gpio); ++ ++ regulator_disable(mpwr->regulator_vbus); ++ regulator_disable(mpwr->regulator); ++ ++ return 0; ++ ++force_powerdown: ++ dev_warn(mpwr->dev, ++ "Forcibly cutting off power, data loss may occur.\n"); ++ goto powerdown; ++} ++ ++static int mpwr_eg25_power_down(struct mpwr_dev* mpwr) ++{ ++ struct gpio_desc *pwrkey_gpio = mpwr_eg25_get_pwrkey_gpio(mpwr); ++ //int ret; ++ ++ /* Send 800ms pwrkey pulse to initiate powerdown. */ ++ gpiod_direction_output(pwrkey_gpio, 1); ++ msleep(800); ++ gpiod_set_value(pwrkey_gpio, 0); ++ ++ /* Switch status key to input, in case it's multiplexed with pwrkey. */ ++ gpiod_direction_input(mpwr->status_gpio); ++ ++ msleep(20); ++ ++#if 0 ++ // wait for POWERED DOWN message ++ clear_bit(MPWR_F_GOT_PDN, mpwr->flags); ++ ret = wait_event_timeout(mpwr->wait, ++ test_bit(MPWR_F_GOT_PDN, mpwr->flags), ++ msecs_to_jiffies(7000)); ++ if (ret <= 0) ++ dev_warn(mpwr->dev, ++ "POWERED DOWN message not received, is modem really powering down?\n"); ++#endif ++ ++ return mpwr_eg25_power_down_finish(mpwr); ++} ++ ++static void mpwr_finish_pdn_work(struct work_struct *work) ++{ ++ /* ++ struct mpwr_dev *mpwr = container_of(work, struct mpwr_dev, power_work); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&mpwr->lock, flags); ++ spin_unlock_irqrestore(&mpwr->lock, flags); ++ ++ pm_stay_awake(mpwr->dev); ++ ++ mutex_lock(&mpwr->modem_lock); ++ ++ mutex_unlock(&mpwr->modem_lock); ++ ++ pm_relax(mpwr->dev); ++ */ ++} ++ ++static void mpwr_eg25_receive_msg(struct mpwr_dev *mpwr, const char *msg) ++{ ++ unsigned int msg_len; ++ ++ if (!strcmp(msg, "POWERED DOWN")) { ++ // system is powering down ++ set_bit(MPWR_F_GOT_PDN, mpwr->flags); ++ wake_up(&mpwr->wait); ++ ++ /* ++ if (mutex_trylock(&mpwr->modem_lock)) { ++ // if no power op is in progress, this means userspace ++ // tried to shut the modem down via AT command, finish up ++ // the job ++ ++ pm_stay_awake(mpwr->dev); ++ ++ queue_work(mpwr->wq, &mpwr->power_work); ++ dev_warn(mpwr->dev, "userspace shut down the modem via AT command, finishing the job\n"); ++ mpwr_eg25_power_down_finish(mpwr); ++ mutex_unlock(&mpwr->modem_lock); ++ ++ pm_relax(mpwr->dev); ++ } ++ */ ++ return; ++ } ++ ++ if (!strcmp(msg, "RDY")) { ++ // system is ready after powerup ++ return; ++ } ++ ++ if (!test_bit(MPWR_F_OPEN, mpwr->flags)) ++ return; ++ ++ msg_len = strlen(msg); ++ ++ if (msg_len + 1 > kfifo_avail(&mpwr->kfifo)) { ++ if (!test_and_set_bit(MPWR_F_OVERFLOW, mpwr->flags)) ++ wake_up(&mpwr->wait); ++ return; ++ } ++ ++ kfifo_in(&mpwr->kfifo, msg, msg_len); ++ kfifo_in(&mpwr->kfifo, "\n", 1); ++ wake_up(&mpwr->wait); ++} ++ ++static void mpwr_host_ready_work(struct work_struct *work) ++{ ++ struct mpwr_dev *mpwr = container_of(work, struct mpwr_dev, host_ready_work.work); ++ int ret; ++ ++ mutex_lock(&mpwr->modem_lock); ++ gpiod_direction_output(mpwr->dtr_gpio, 0); ++ ++ /* ++ * We need to give the modem some time to wake up. ++ */ ++ msleep(5); ++ ++ ret = mpwr_serdev_at_cmd(mpwr, "AT+QCFG=\"urc/cache\",0", 500); ++ if (ret) ++ dev_warn(mpwr->dev, ++ "Failed to disable urc/cache, you may not be able to see URCs\n"); ++ ++ gpiod_direction_output(mpwr->dtr_gpio, 1); ++ mutex_unlock(&mpwr->modem_lock); ++ ++ gpiod_direction_output(mpwr->host_ready_gpio, 1); ++} ++ ++static int mpwr_eg25_suspend(struct mpwr_dev *mpwr) ++{ ++ int ret; ++ ++ cancel_delayed_work_sync(&mpwr->host_ready_work); ++ ++ gpiod_direction_output(mpwr->host_ready_gpio, 0); ++ ++ mutex_lock(&mpwr->modem_lock); ++ gpiod_direction_output(mpwr->dtr_gpio, 0); ++ ++ msleep(5); ++ ++ ret = mpwr_serdev_at_cmd(mpwr, "AT+QCFG=\"urc/cache\",1", 500); ++ if (ret) ++ dev_warn(mpwr->dev, ++ "Failed to enable urc/cache, you may lose URCs during suspend\n"); ++ ++ gpiod_direction_output(mpwr->dtr_gpio, 1); ++ mutex_unlock(&mpwr->modem_lock); ++ ++ return 0; ++} ++ ++static int mpwr_eg25_resume(struct mpwr_dev *mpwr) ++{ ++ //gpiod_direction_output(mpwr->dtr_gpio, 0); ++ ++ // delay disabling URC cache until the whole system is hopefully resumed... ++ schedule_delayed_work(&mpwr->host_ready_work, msecs_to_jiffies(1000)); ++ ++ return 0; ++} ++ ++static const struct mpwr_gpio mpwr_eg25_gpios[] = { ++ MPWR_GPIO_DEF(enable, GPIOD_OUT_HIGH, true), ++ MPWR_GPIO_DEF(reset, GPIOD_OUT_LOW, true), ++ MPWR_GPIO_DEF(pwrkey, GPIOD_OUT_LOW, false), ++ MPWR_GPIO_DEF(dtr, GPIOD_OUT_LOW, true), ++ MPWR_GPIO_DEF(status, GPIOD_IN, false), ++ MPWR_GPIO_DEF_IRQ(wakeup, GPIOD_IN, true, ++ IRQF_TRIGGER_FALLING), ++ ++ // XXX: not really needed... ++ MPWR_GPIO_DEF(sleep, GPIOD_OUT_LOW, false), ++ MPWR_GPIO_DEF(host_ready, GPIOD_OUT_HIGH, false), ++ MPWR_GPIO_DEF(cts, GPIOD_IN, false), ++ MPWR_GPIO_DEF(rts, GPIOD_OUT_LOW, false), ++ { }, ++}; ++ ++static const struct mpwr_variant mpwr_eg25_variant = { ++ .power_up = mpwr_eg25_power_up, ++ .power_down = mpwr_eg25_power_down, ++ .recv_msg = mpwr_eg25_receive_msg, ++ .suspend = mpwr_eg25_suspend, ++ .resume = mpwr_eg25_resume, ++ .gpios = mpwr_eg25_gpios, ++ .regulator_required = true, ++ .monitor_wakeup = true, ++}; ++ ++// }}} ++// {{{ generic helpers ++ ++static void mpwr_reset(struct mpwr_dev* mpwr) ++{ ++ struct device *dev = mpwr->dev; ++ int ret; ++ ++ if (!test_bit(MPWR_F_POWERED, mpwr->flags)) { ++ dev_err(dev, "reset requested but device is not enabled"); ++ return; ++ } ++ ++ if (!mpwr->reset_gpio) { ++ dev_err(dev, "reset is not configured for this device"); ++ return; ++ } ++ ++ if (!mpwr->variant->reset) { ++ dev_err(dev, "reset requested but not implemented"); ++ return; ++ } ++ ++ dev_info(dev, "resetting"); ++ ret = mpwr->variant->reset(mpwr); ++ if (ret) { ++ dev_err(dev, "reset failed"); ++ } ++} ++ ++static void mpwr_power_down(struct mpwr_dev* mpwr) ++{ ++ struct device *dev = mpwr->dev; ++ ktime_t start = ktime_get(); ++ int ret; ++ ++ if (!test_bit(MPWR_F_POWERED, mpwr->flags)) ++ return; ++ ++ if (!mpwr->variant->power_down) { ++ dev_err(dev, "power down requested but not implemented"); ++ return; ++ } ++ ++ dev_info(dev, "powering down"); ++ ++ ret = mpwr->variant->power_down(mpwr); ++ if (ret) { ++ dev_err(dev, "power down failed"); ++ } else { ++ clear_bit(MPWR_F_POWERED, mpwr->flags); ++ sysfs_notify(&mpwr->dev->kobj, NULL, "powered"); ++ dev_info(mpwr->dev, "powered down in %lld ms\n", ++ ktime_ms_delta(ktime_get(), start)); ++ } ++} ++ ++static void mpwr_power_up(struct mpwr_dev* mpwr) ++{ ++ struct device *dev = mpwr->dev; ++ ktime_t start = ktime_get(); ++ int ret; ++ ++ if (test_bit(MPWR_F_POWERED, mpwr->flags)) ++ return; ++ ++ if (!mpwr->variant->power_up) { ++ dev_err(dev, "power up requested but not implemented"); ++ return; ++ } ++ ++ dev_info(dev, "powering up"); ++ ++ ret = mpwr->variant->power_up(mpwr); ++ if (ret) { ++ dev_err(dev, "power up failed"); ++ } else { ++ set_bit(MPWR_F_POWERED, mpwr->flags); ++ sysfs_notify(&mpwr->dev->kobj, NULL, "powered"); ++ dev_info(mpwr->dev, "powered up in %lld ms\n", ++ ktime_ms_delta(ktime_get(), start)); ++ } ++} ++ ++// }}} ++// {{{ chardev ++ ++static int mpwr_release(struct inode *ip, struct file *fp) ++{ ++ struct mpwr_dev* mpwr = fp->private_data; ++ ++ clear_bit(MPWR_F_OPEN, mpwr->flags); ++ ++ return 0; ++} ++ ++static int mpwr_open(struct inode *ip, struct file *fp) ++{ ++ struct mpwr_dev* mpwr = container_of(ip->i_cdev, struct mpwr_dev, cdev); ++ ++ fp->private_data = mpwr; ++ ++ if (test_and_set_bit(MPWR_F_OPEN, mpwr->flags)) ++ return -EBUSY; ++ ++ nonseekable_open(ip, fp); ++ return 0; ++} ++ ++static ssize_t mpwr_read(struct file *fp, char __user *buf, size_t len, ++ loff_t *off) ++{ ++ struct mpwr_dev* mpwr = fp->private_data; ++ int non_blocking = fp->f_flags & O_NONBLOCK; ++ unsigned int copied; ++ int ret; ++ ++ if (non_blocking && kfifo_is_empty(&mpwr->kfifo)) ++ return -EWOULDBLOCK; ++ ++ ret = wait_event_interruptible(mpwr->wait, ++ !kfifo_is_empty(&mpwr->kfifo) ++ || test_bit(MPWR_F_OVERFLOW, mpwr->flags)); ++ if (ret) ++ return ret; ++ ++ if (test_and_clear_bit(MPWR_F_OVERFLOW, mpwr->flags)) { ++ if (len < 9) ++ return -E2BIG; ++ if (copy_to_user(buf, "OVERFLOW\n", 9)) ++ return -EFAULT; ++ return 9; ++ } ++ ++ ret = kfifo_to_user(&mpwr->kfifo, buf, len, &copied); ++ ++ return ret ? ret : copied; ++} ++ ++static unsigned int mpwr_poll(struct file *fp, poll_table *wait) ++{ ++ struct mpwr_dev* mpwr = fp->private_data; ++ ++ poll_wait(fp, &mpwr->wait, wait); ++ ++ if (!kfifo_is_empty(&mpwr->kfifo)) ++ return EPOLLIN | EPOLLRDNORM; ++ ++ return 0; ++} ++ ++static const struct file_operations mpwr_fops = { ++ .owner = THIS_MODULE, ++ .open = mpwr_open, ++ .release = mpwr_release, ++ .llseek = noop_llseek, ++ .read = mpwr_read, ++ .poll = mpwr_poll, ++}; ++ ++// }}} ++ ++static void mpwr_work_handler(struct work_struct *work) ++{ ++ struct mpwr_dev *mpwr = container_of(work, struct mpwr_dev, power_work); ++ unsigned long flags; ++ int last_request; ++ ++ spin_lock_irqsave(&mpwr->lock, flags); ++ last_request = mpwr->last_request; ++ mpwr->last_request = 0; ++ spin_unlock_irqrestore(&mpwr->lock, flags); ++ ++ pm_stay_awake(mpwr->dev); ++ ++ mutex_lock(&mpwr->modem_lock); ++ ++ if (last_request == MPWR_REQ_RESET) { ++ mpwr_reset(mpwr); ++ } else if (last_request == MPWR_REQ_PWDN) { ++ mpwr_power_down(mpwr); ++ } else if (last_request == MPWR_REQ_PWUP) { ++ mpwr_power_up(mpwr); ++ } ++ ++ mutex_unlock(&mpwr->modem_lock); ++ ++ clear_bit(MPWR_F_POWER_CHANGE_INPROGRESS, mpwr->flags); ++ sysfs_notify(&mpwr->dev->kobj, NULL, "is_busy"); ++ wake_up(&mpwr->wait); ++ ++ pm_relax(mpwr->dev); ++} ++ ++static void mpwr_request_power_change(struct mpwr_dev* mpwr, int request, int mode) ++{ ++ unsigned long flags; ++ ++ set_bit(MPWR_F_POWER_CHANGE_INPROGRESS, mpwr->flags); ++ sysfs_notify(&mpwr->dev->kobj, NULL, "is_busy"); ++ ++ spin_lock_irqsave(&mpwr->lock, flags); ++ mpwr->last_request = request; ++ if (mode >= 0) ++ mpwr->powerup_mode = mode; ++ spin_unlock_irqrestore(&mpwr->lock, flags); ++ ++ queue_work(mpwr->wq, &mpwr->power_work); ++} ++ ++static irqreturn_t mpwr_gpio_isr(int irq, void *dev_id) ++{ ++ struct mpwr_dev *mpwr = dev_id; ++ ++ if (irq == mpwr->wakeup_irq) { ++ dev_dbg(mpwr->dev, "wakeup irq\n"); ++ ++ if (device_can_wakeup(mpwr->dev)) ++ pm_wakeup_event(mpwr->dev, 2000); ++ ++ set_bit(MPWR_F_GOT_WAKEUP, mpwr->flags); ++ spin_lock(&mpwr->lock); ++ mpwr->last_wakeup = ktime_get(); ++ spin_unlock(&mpwr->lock); ++ wake_up(&mpwr->wait); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static void mpwr_wd_timer_fn(struct timer_list *t) ++{ ++ struct mpwr_dev *mpwr = from_timer(mpwr, t, wd_timer); ++ ++ if (!mpwr->variant->monitor_wakeup || !test_bit(MPWR_F_POWERED, mpwr->flags)) ++ return; ++ ++ /* ++ * Monitor wakeup status: ++ * ++ * If RI signal is low for too long we assume the user killswitched ++ * the modem at runtime. ++ */ ++ spin_lock(&mpwr->lock); ++ if (!gpiod_get_value(mpwr->wakeup_gpio)) { ++ if (ktime_ms_delta(ktime_get(), mpwr->last_wakeup) > 5000) { ++ if (!test_and_set_bit(MPWR_F_KILLSWITCHED, mpwr->flags)) ++ sysfs_notify(&mpwr->dev->kobj, NULL, "killswitched"); ++ wake_up(&mpwr->wait); ++ dev_warn(mpwr->dev, "modem looks killswitched at runtime!\n"); ++ } ++ } ++ spin_unlock(&mpwr->lock); ++ ++ mod_timer(t, jiffies + msecs_to_jiffies(1000)); ++} ++ ++// {{{ sysfs ++ ++static ssize_t powered_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct mpwr_dev *mpwr = platform_get_drvdata(to_platform_device(dev)); ++ ++ return scnprintf(buf, PAGE_SIZE, "%u\n", ++ !!test_bit(MPWR_F_POWERED, mpwr->flags)); ++} ++ ++static ssize_t powered_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t len) ++{ ++ struct mpwr_dev *mpwr = platform_get_drvdata(to_platform_device(dev)); ++ unsigned status; ++ int ret; ++ ++ if (test_bit(MPWR_F_BLOCKED, mpwr->flags)) ++ return -EPERM; ++ ++ ret = kstrtouint(buf, 10, &status); ++ if (ret) ++ return ret; ++ ++ mpwr_request_power_change(mpwr, status ? MPWR_REQ_PWUP : MPWR_REQ_PWDN, status); ++ ++ return len; ++} ++ ++static ssize_t powered_blocking_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t len) ++{ ++ struct mpwr_dev *mpwr = platform_get_drvdata(to_platform_device(dev)); ++ unsigned status; ++ int ret; ++ ++ if (test_bit(MPWR_F_BLOCKED, mpwr->flags)) ++ return -EPERM; ++ ++ ret = kstrtouint(buf, 10, &status); ++ if (ret) ++ return ret; ++ ++ mpwr_request_power_change(mpwr, status ? MPWR_REQ_PWUP : MPWR_REQ_PWDN, status); ++ ++ ret = wait_event_interruptible_timeout(mpwr->wait, ++ !test_bit(MPWR_F_POWER_CHANGE_INPROGRESS, mpwr->flags), ++ msecs_to_jiffies(60000)); ++ if (ret <= 0) { ++ dev_err(mpwr->dev, "Power state change timeout\n"); ++ return -EIO; ++ } ++ ++ if (!!status != !!test_bit(MPWR_F_POWERED, mpwr->flags)) ++ return -EIO; ++ ++ return len; ++} ++ ++static ssize_t help_show(struct device *dev, struct device_attribute *attr, char *buf) ++{ ++ return scnprintf(buf, PAGE_SIZE, ++ "echo N > powered, where N can be:\n" ++ "0: power off\n" ++ "1: normal powerup\n" ++ "2: dumb powerup (no AT commands and little error checking during powerup)\n" ++ "3: fastboot powerup (with biktor's patched aboot - #W_DISABLE held low during powerup)\n" ++ "4: alternate powerup (megi's userspace - DTR held high during powerup)\n\n" ++ "echo N > powered_blocking can be used for the write to block until power status transition completes\n"); ++} ++ ++static ssize_t killswitched_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct mpwr_dev *mpwr = platform_get_drvdata(to_platform_device(dev)); ++ ++ return scnprintf(buf, PAGE_SIZE, "%u\n", ++ !!test_bit(MPWR_F_KILLSWITCHED, mpwr->flags)); ++} ++ ++static ssize_t is_busy_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct mpwr_dev *mpwr = platform_get_drvdata(to_platform_device(dev)); ++ ++ return scnprintf(buf, PAGE_SIZE, "%u\n", ++ !!test_bit(MPWR_F_POWER_CHANGE_INPROGRESS, mpwr->flags)); ++} ++ ++static ssize_t hard_reset_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t len) ++{ ++ struct mpwr_dev *mpwr = platform_get_drvdata(to_platform_device(dev)); ++ bool val; ++ int ret; ++ ++ if (test_bit(MPWR_F_BLOCKED, mpwr->flags)) ++ return -EPERM; ++ ++ ret = kstrtobool(buf, &val); ++ if (ret) ++ return ret; ++ if (val) ++ mpwr_request_power_change(mpwr, MPWR_REQ_RESET, -1); ++ ++ return len; ++} ++ ++static ssize_t debug_pins_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t len) ++{ ++ struct mpwr_dev *mpwr = platform_get_drvdata(to_platform_device(dev)); ++ unsigned val; ++ int ret; ++ ++ if (test_bit(MPWR_F_BLOCKED, mpwr->flags)) ++ return -EPERM; ++ ++ ret = kstrtouint(buf, 16, &val); ++ if (ret) ++ return ret; ++ ++ gpiod_direction_output(mpwr->host_ready_gpio, val & BIT(0)); ++ gpiod_direction_output(mpwr->enable_gpio, val & BIT(1)); ++ gpiod_direction_output(mpwr->dtr_gpio, val & BIT(2)); ++ gpiod_direction_output(mpwr->rts_gpio, val & BIT(3)); ++ ++ return len; ++} ++ ++static ssize_t debug_pins_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct mpwr_dev *mpwr = platform_get_drvdata(to_platform_device(dev)); ++ ++ return scnprintf(buf, PAGE_SIZE, "CTS=%u RI=%u\n", ++ gpiod_get_value(mpwr->cts_gpio), gpiod_get_value(mpwr->wakeup_gpio)); ++} ++ ++static DEVICE_ATTR_RW(powered); ++static DEVICE_ATTR_WO(powered_blocking); ++static DEVICE_ATTR_RO(killswitched); ++static DEVICE_ATTR_RO(is_busy); ++static DEVICE_ATTR_RO(help); ++static DEVICE_ATTR_WO(hard_reset); ++static DEVICE_ATTR_RW(debug_pins); ++ ++static struct attribute *mpwr_attrs[] = { ++ &dev_attr_powered.attr, ++ &dev_attr_powered_blocking.attr, ++ &dev_attr_killswitched.attr, ++ &dev_attr_is_busy.attr, ++ &dev_attr_help.attr, ++ &dev_attr_hard_reset.attr, ++ &dev_attr_debug_pins.attr, ++ NULL, ++}; ++ ++static const struct attribute_group mpwr_group = { ++ .attrs = mpwr_attrs, ++}; ++ ++// }}} ++// {{{ rfkill ++ ++static int mpwr_rfkill_set(void *data, bool blocked) ++{ ++ struct mpwr_dev *mpwr = data; ++ ++ gpiod_set_value(mpwr->enable_gpio, !blocked); ++ return 0; ++} ++ ++static void mpwr_rfkill_query(struct rfkill *rfkill, void *data) ++{ ++ struct mpwr_dev *mpwr = data; ++ ++ rfkill_set_sw_state(rfkill, !gpiod_get_value(mpwr->enable_gpio)); ++} ++ ++static const struct rfkill_ops mpwr_rfkill_ops = { ++ .set_block = mpwr_rfkill_set, ++ .query = mpwr_rfkill_query, ++}; ++ ++// }}} ++// {{{ probe ++ ++static int mpwr_probe_generic(struct device *dev, struct mpwr_dev **mpwr_out) ++{ ++ struct mpwr_dev *mpwr; ++ struct device_node *np = dev->of_node; ++ struct device *sdev; ++ const char* cdev_name = NULL; ++ int ret, i; ++ ++ mpwr = devm_kzalloc(dev, sizeof(*mpwr), GFP_KERNEL); ++ if (!mpwr) ++ return -ENOMEM; ++ ++ mpwr->variant = of_device_get_match_data(dev); ++ if (!mpwr->variant) ++ return -EINVAL; ++ ++ mpwr->dev = dev; ++ init_waitqueue_head(&mpwr->wait); ++ mutex_init(&mpwr->modem_lock); ++ spin_lock_init(&mpwr->lock); ++ INIT_WORK(&mpwr->power_work, &mpwr_work_handler); ++ INIT_WORK(&mpwr->finish_pdn_work, &mpwr_finish_pdn_work); ++ INIT_DELAYED_WORK(&mpwr->host_ready_work, mpwr_host_ready_work); ++ INIT_KFIFO(mpwr->kfifo); ++ ++ ret = of_property_read_string(np, "char-device-name", &cdev_name); ++ if (ret) { ++ dev_err(dev, "char-device-name is not configured"); ++ return -EINVAL; ++ } ++ ++ if (of_property_read_bool(np, "blocked")) ++ set_bit(MPWR_F_BLOCKED, mpwr->flags); ++ ++ mpwr->status_pwrkey_multiplexed = ++ of_property_read_bool(np, "status-pwrkey-multiplexed"); ++ ++ mpwr->regulator = devm_regulator_get_optional(dev, "power"); ++ if (IS_ERR(mpwr->regulator)) { ++ ret = PTR_ERR(mpwr->regulator); ++ if (ret != -ENODEV) { ++ dev_err(dev, "can't get power supply err=%d", ret); ++ return ret; ++ } ++ ++ mpwr->regulator = NULL; ++ } ++ ++ if (!mpwr->regulator && mpwr->variant->regulator_required) { ++ dev_err(dev, "can't get power supply err=%d", -ENODEV); ++ return -ENODEV; ++ } ++ ++ mpwr->regulator_vbus = devm_regulator_get(dev, "vbus"); ++ if (IS_ERR(mpwr->regulator_vbus)) ++ return dev_err_probe(dev, PTR_ERR(mpwr->regulator_vbus), ++ "can't get vbus power supply\n"); ++ ++ for (i = 0; mpwr->variant->gpios[i].name; i++) { ++ const struct mpwr_gpio *io = &mpwr->variant->gpios[i]; ++ struct gpio_desc **desc = (struct gpio_desc **)((u8*)mpwr + ++ io->desc_off); ++ int *irq = (int*)((u8*)mpwr + io->irq_off); ++ char buf[64]; ++ ++ if (io->required) ++ *desc = devm_gpiod_get(dev, io->name, io->flags); ++ else ++ *desc = devm_gpiod_get_optional(dev, io->name, io->flags); ++ ++ if (IS_ERR(*desc)) { ++ dev_err(dev, "can't get %s gpio err=%ld", io->name, ++ PTR_ERR(*desc)); ++ return PTR_ERR(*desc); ++ } ++ ++ if (!*desc) ++ continue; ++ ++ if (io->irq_flags == 0 || io->irq_off == 0) ++ continue; ++ ++ *irq = gpiod_to_irq(*desc); ++ if (*irq <= 0) { ++ dev_err(dev, "error converting %s gpio to irq: %d", ++ io->name, ret); ++ return *irq; ++ } ++ ++ snprintf(buf, sizeof buf, "modem-%s-gpio", io->name); ++ ret = devm_request_irq(dev, *irq, mpwr_gpio_isr, io->irq_flags, ++ devm_kstrdup(dev, buf, GFP_KERNEL), mpwr); ++ if (ret) { ++ dev_err(dev, "error requesting %s irq: %d", ++ io->name, ret); ++ return ret; ++ } ++ } ++ ++ if (mpwr->status_pwrkey_multiplexed && mpwr->pwrkey_gpio) { ++ dev_err(dev, "status and pwrkey are multiplexed, but pwrkey defined\n"); ++ return -EINVAL; ++ } ++ ++ if (mpwr->status_pwrkey_multiplexed && !mpwr->status_gpio) { ++ dev_err(dev, "status and pwrkey are multiplexed, but status is not defined\n"); ++ return -EINVAL; ++ } ++ ++ ret = devm_device_add_group(dev, &mpwr_group); ++ if (ret) ++ return ret; ++ ++ // create char device ++ ret = alloc_chrdev_region(&mpwr->major, 0, 1, "modem-power"); ++ if (ret) { ++ dev_err(dev, "can't allocate chrdev region"); ++ goto err_disable_regulator; ++ } ++ ++ cdev_init(&mpwr->cdev, &mpwr_fops); ++ mpwr->cdev.owner = THIS_MODULE; ++ ret = cdev_add(&mpwr->cdev, mpwr->major, 1); ++ if (ret) { ++ dev_err(dev, "can't add cdev"); ++ goto err_unreg_chrev_region; ++ } ++ ++ sdev = device_create(mpwr_class, dev, mpwr->major, mpwr, cdev_name); ++ if (IS_ERR(sdev)) { ++ ret = PTR_ERR(sdev); ++ goto err_del_cdev; ++ } ++ ++ if (mpwr->wakeup_irq > 0) { ++ ret = device_init_wakeup(dev, true); ++ if (ret) { ++ dev_err(dev, "failed to init wakeup (%d)\n", ret); ++ goto err_free_dev; ++ } ++ } ++ ++ if (mpwr->enable_gpio) { ++ mpwr->rfkill = rfkill_alloc("modem", dev, RFKILL_TYPE_WWAN, ++ &mpwr_rfkill_ops, mpwr); ++ if (!mpwr->rfkill) { ++ dev_err(dev, "failed to alloc rfkill\n"); ++ ret = -ENOMEM; ++ goto err_deinit_wakeup; ++ } ++ ++ rfkill_init_sw_state(mpwr->rfkill, false); ++ ++ ret = rfkill_register(mpwr->rfkill); ++ if (ret) { ++ dev_err(dev, "failed to register rfkill (%d)\n", ret); ++ goto err_free_rfkill; ++ } ++ } ++ ++ mpwr->wq = alloc_ordered_workqueue("modem-power", 0); ++ if (!mpwr->wq) { ++ ret = -ENOMEM; ++ dev_err(dev, "failed to allocate workqueue\n"); ++ goto err_unreg_rfkill; ++ } ++ ++ if (mpwr->variant->power_init) ++ mpwr->variant->power_init(mpwr); ++ ++ timer_setup(&mpwr->wd_timer, mpwr_wd_timer_fn, 0); ++ mod_timer(&mpwr->wd_timer, jiffies + msecs_to_jiffies(50)); ++ ++ dev_info(dev, "modem power manager ready"); ++ *mpwr_out = mpwr; ++ ++ return 0; ++ ++err_unreg_rfkill: ++ if (mpwr->rfkill) ++ rfkill_unregister(mpwr->rfkill); ++err_free_rfkill: ++ if (mpwr->rfkill) ++ rfkill_destroy(mpwr->rfkill); ++err_deinit_wakeup: ++ if (mpwr->wakeup_irq > 0) ++ device_init_wakeup(dev, false); ++err_free_dev: ++ device_destroy(mpwr_class, mpwr->major); ++err_del_cdev: ++ cdev_del(&mpwr->cdev); ++err_unreg_chrev_region: ++ unregister_chrdev_region(mpwr->major, 1); ++err_disable_regulator: ++ cancel_work_sync(&mpwr->power_work); ++ return ret; ++} ++ ++static void mpwr_remove_generic(struct mpwr_dev *mpwr) ++{ ++ if (mpwr->rfkill) { ++ rfkill_unregister(mpwr->rfkill); ++ rfkill_destroy(mpwr->rfkill); ++ } ++ ++ if (mpwr->wakeup_irq > 0) ++ device_init_wakeup(mpwr->dev, false); ++ ++ del_timer_sync(&mpwr->wd_timer); ++ cancel_delayed_work_sync(&mpwr->host_ready_work); ++ ++ cancel_work_sync(&mpwr->power_work); ++ destroy_workqueue(mpwr->wq); ++ ++ mutex_lock(&mpwr->modem_lock); ++ mpwr_power_down(mpwr); ++ mutex_unlock(&mpwr->modem_lock); ++ ++ device_destroy(mpwr_class, mpwr->major); ++ cdev_del(&mpwr->cdev); ++ unregister_chrdev_region(mpwr->major, 1); ++} ++ ++static void mpwr_shutdown_generic(struct mpwr_dev *mpwr) ++{ ++ cancel_work_sync(&mpwr->power_work); ++ cancel_delayed_work_sync(&mpwr->host_ready_work); ++ ++ mutex_lock(&mpwr->modem_lock); ++ mpwr_power_down(mpwr); ++ mutex_unlock(&mpwr->modem_lock); ++} ++ ++// }}} ++// {{{ suspend/resume ++ ++static int __maybe_unused mpwr_suspend(struct device *dev) ++{ ++ struct mpwr_dev *mpwr = dev_get_drvdata(dev); ++ int ret = 0; ++ ++ if (!test_bit(MPWR_F_POWERED, mpwr->flags)) ++ return 0; ++ ++ //if (mpwr->sleep_gpio) ++ //gpiod_direction_output(mpwr->sleep_gpio, 1); ++ ++ if (mpwr->variant->suspend) ++ mpwr->variant->suspend(mpwr); ++ ++ if (mpwr->wakeup_irq && device_may_wakeup(mpwr->dev)) ++ enable_irq_wake(mpwr->wakeup_irq); ++ ++ return ret; ++} ++ ++static int __maybe_unused mpwr_resume(struct device *dev) ++{ ++ struct mpwr_dev *mpwr = dev_get_drvdata(dev); ++ int ret = 0; ++ ++ if (!test_bit(MPWR_F_POWERED, mpwr->flags)) ++ return 0; ++ ++ //if (mpwr->sleep_gpio) ++ //gpiod_direction_output(mpwr->sleep_gpio, 0); ++ ++ if (mpwr->variant->resume) ++ mpwr->variant->resume(mpwr); ++ ++ if (mpwr->wakeup_irq && device_may_wakeup(mpwr->dev)) ++ disable_irq_wake(mpwr->wakeup_irq); ++ ++ return ret; ++} ++ ++static const struct dev_pm_ops mpwr_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(mpwr_suspend, mpwr_resume) ++}; ++ ++// }}} ++// {{{ serdev ++ ++static int mpwr_serdev_send_msg(struct mpwr_dev *mpwr, const char *msg) ++{ ++ int ret, len; ++ char buf[128]; ++ ++ if (!mpwr->serdev) ++ return -ENODEV; ++ ++ len = snprintf(buf, sizeof buf, "%s\r\n", msg); ++ if (len >= sizeof buf) ++ return -E2BIG; ++ ++ ret = serdev_device_write(mpwr->serdev, buf, len, msecs_to_jiffies(3000)); ++ if (ret < len) ++ return -EIO; ++ ++ serdev_device_wait_until_sent(mpwr->serdev, msecs_to_jiffies(3000)); ++ ++ return 0; ++} ++ ++static int __mpwr_serdev_at_cmd(struct mpwr_dev *mpwr, const char *msg, ++ int timeout_ms, bool report_error, bool report_timeout) ++{ ++ int ret; ++ ++ if (test_and_set_bit(MPWR_F_RECEIVING_MSG, mpwr->flags)) ++ return -EBUSY; ++ ++ mpwr->msg_len = 0; ++ ++ dev_dbg(mpwr->dev, "SEND: %s\n", msg); ++ ++ ret = mpwr_serdev_send_msg(mpwr, msg); ++ if (ret) { ++ clear_bit(MPWR_F_RECEIVING_MSG, mpwr->flags); ++ dev_err(mpwr->dev, "AT command '%s' can't be sent (%d)\n", msg, ret); ++ return ret; ++ } ++ ++ ret = wait_event_interruptible_timeout(mpwr->wait, ++ !test_bit(MPWR_F_RECEIVING_MSG, mpwr->flags), ++ msecs_to_jiffies(timeout_ms)); ++ if (ret <= 0) { ++ clear_bit(MPWR_F_RECEIVING_MSG, mpwr->flags); ++ if (report_timeout) ++ dev_err(mpwr->dev, "AT command '%s' timed out\n", msg); ++ return ret ? ret : -ETIMEDOUT; ++ } ++ ++ if (!mpwr->msg_ok) { ++ if (report_error) ++ dev_err(mpwr->dev, "AT command '%s' returned ERROR\n", msg); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int mpwr_serdev_at_cmd(struct mpwr_dev *mpwr, const char *msg, int timeout_ms) ++{ ++ return __mpwr_serdev_at_cmd(mpwr, msg, timeout_ms, true, true); ++} ++ ++static int __mpwr_serdev_at_cmd_with_retry(struct mpwr_dev *mpwr, const char *msg, ++ int timeout_ms, int tries, bool ignore_timeout) ++{ ++ int ret = 0; ++ ++ if (tries < 1) ++ tries = 1; ++ ++ while (tries-- > 0) { ++ ret = __mpwr_serdev_at_cmd(mpwr, msg, timeout_ms, false, !ignore_timeout); ++ if (ret != -EINVAL && (!ignore_timeout || ret != -ETIMEDOUT)) ++ return ret; ++ ++ if (ret != -ETIMEDOUT) ++ msleep(1000); ++ } ++ ++ dev_err(mpwr->dev, "AT command '%s' returned ERROR\n", msg); ++ return ret; ++} ++ ++static int mpwr_serdev_at_cmd_with_retry(struct mpwr_dev *mpwr, const char *msg, ++ int timeout_ms, int tries) ++{ ++ return __mpwr_serdev_at_cmd_with_retry(mpwr, msg, timeout_ms, tries, false); ++} ++ ++static int mpwr_serdev_at_cmd_with_retry_ignore_timeout(struct mpwr_dev *mpwr, const char *msg, ++ int timeout_ms, int tries) ++{ ++ return __mpwr_serdev_at_cmd_with_retry(mpwr, msg, timeout_ms, tries, true); ++} ++ ++static void mpwr_serdev_receive_msg(struct mpwr_dev *mpwr, const char *msg) ++{ ++ dev_dbg(mpwr->dev, "RECV: %s\n", msg); ++ ++ if (mpwr->variant->recv_msg) ++ mpwr->variant->recv_msg(mpwr, msg); ++ ++ if (!test_bit(MPWR_F_RECEIVING_MSG, mpwr->flags)) ++ return; ++ ++ if (!strcmp(msg, "OK")) { ++ clear_bit(MPWR_F_RECEIVING_MSG, mpwr->flags); ++ mpwr->msg_ok = true; ++ wake_up(&mpwr->wait); ++ return; ++ } else if (!strcmp(msg, "ERROR")) { ++ clear_bit(MPWR_F_RECEIVING_MSG, mpwr->flags); ++ mpwr->msg_ok = false; ++ wake_up(&mpwr->wait); ++ return; ++ } else { ++ int len = strlen(msg); ++ ++ if (mpwr->msg_len + len + 1 > sizeof(mpwr->msg)) { ++ dev_warn(mpwr->dev, "message buffer overflow, ignoring message\n"); ++ return; ++ } ++ ++ memcpy(mpwr->msg + mpwr->msg_len, msg, len + 1); ++ mpwr->msg_len += len + 1; ++ } ++} ++ ++static size_t mpwr_serdev_receive_buf(struct serdev_device *serdev, ++ const u8 *buf, size_t count) ++{ ++ struct mpwr_dev *mpwr = serdev_device_get_drvdata(serdev); ++ size_t avail = sizeof(mpwr->rcvbuf) - mpwr->rcvbuf_fill; ++ char* p; ++ ++ if (avail < count) ++ count = avail; ++ ++ if (avail > 0) { ++ memcpy(mpwr->rcvbuf + mpwr->rcvbuf_fill, buf, count); ++ mpwr->rcvbuf_fill += count; ++ } ++ ++ while (true) { ++ p = strnstr(mpwr->rcvbuf, "\r\n", mpwr->rcvbuf_fill); ++ if (p) { ++ if (p > mpwr->rcvbuf) { ++ *p = 0; ++ mpwr_serdev_receive_msg(mpwr, mpwr->rcvbuf); ++ } ++ ++ mpwr->rcvbuf_fill -= (p - mpwr->rcvbuf) + 2; ++ memmove(mpwr->rcvbuf, p + 2, mpwr->rcvbuf_fill); ++ } else { ++ if (sizeof(mpwr->rcvbuf) - mpwr->rcvbuf_fill == 0) { ++ mpwr->rcvbuf_fill = 0; ++ dev_warn(mpwr->dev, "rcvbuf overflow\n"); ++ } ++ ++ break; ++ } ++ } ++ ++ return count; ++} ++ ++static const struct serdev_device_ops mpwr_serdev_ops = { ++ .receive_buf = mpwr_serdev_receive_buf, ++ .write_wakeup = serdev_device_write_wakeup, ++}; ++ ++static int mpwr_serdev_probe(struct serdev_device *serdev) ++{ ++ struct device *dev = &serdev->dev; ++ struct mpwr_dev* mpwr; ++ int ret; ++ ++ ret = mpwr_probe_generic(dev, &mpwr); ++ if (ret) ++ return ret; ++ ++ serdev_device_set_drvdata(serdev, mpwr); ++ serdev_device_set_client_ops(serdev, &mpwr_serdev_ops); ++ mpwr->serdev = serdev; ++ ++ return 0; ++} ++ ++static void mpwr_serdev_remove(struct serdev_device *serdev) ++{ ++ struct mpwr_dev *mpwr = serdev_device_get_drvdata(serdev); ++ ++ mpwr_remove_generic(mpwr); ++} ++ ++static const struct of_device_id mpwr_of_match_serdev[] = { ++ { .compatible = "quectel,eg25", ++ .data = &mpwr_eg25_variant }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, mpwr_of_match_serdev); ++ ++static void mpwr_serdev_shutdown(struct device *dev) ++{ ++ struct mpwr_dev *mpwr = dev_get_drvdata(dev); ++ ++ mpwr_shutdown_generic(mpwr); ++} ++ ++static struct serdev_device_driver mpwr_serdev_driver = { ++ .probe = mpwr_serdev_probe, ++ .remove = mpwr_serdev_remove, ++ .driver = { ++ .name = DRIVER_NAME, ++ .of_match_table = mpwr_of_match_serdev, ++ .pm = &mpwr_pm_ops, ++ .shutdown = mpwr_serdev_shutdown, ++ }, ++}; ++ ++// }}} ++// {{{ platdev ++ ++static int mpwr_pdev_probe(struct platform_device *pdev) ++{ ++ struct mpwr_dev* mpwr; ++ int ret; ++ ++ ret = mpwr_probe_generic(&pdev->dev, &mpwr); ++ if (ret) ++ return ret; ++ ++ platform_set_drvdata(pdev, mpwr); ++ return 0; ++} ++ ++static void mpwr_pdev_remove(struct platform_device *pdev) ++{ ++ struct mpwr_dev *mpwr = platform_get_drvdata(pdev); ++ ++ mpwr_remove_generic(mpwr); ++} ++ ++static void mpwr_pdev_shutdown(struct platform_device *pdev) ++{ ++ struct mpwr_dev *mpwr = platform_get_drvdata(pdev); ++ ++ mpwr_shutdown_generic(mpwr); ++} ++ ++static const struct of_device_id mpwr_of_match_plat[] = { ++ { .compatible = "zte,mg3732", ++ .data = &mpwr_mg2723_variant }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, mpwr_of_match_plat); ++ ++static struct platform_driver mpwr_platform_driver = { ++ .probe = mpwr_pdev_probe, ++ .remove = mpwr_pdev_remove, ++ .shutdown = mpwr_pdev_shutdown, ++ .driver = { ++ .name = DRIVER_NAME, ++ .of_match_table = mpwr_of_match_plat, ++ .pm = &mpwr_pm_ops, ++ }, ++}; ++ ++// }}} ++// {{{ driver init ++ ++static int __init mpwr_driver_init(void) ++{ ++ int ret; ++ ++ mpwr_class = class_create("modem-power"); ++ if (IS_ERR(mpwr_class)) ++ return PTR_ERR(mpwr_class); ++ ++ ret = serdev_device_driver_register(&mpwr_serdev_driver); ++ if (ret) ++ goto err_class; ++ ++ ret = platform_driver_register(&mpwr_platform_driver); ++ if (ret) ++ goto err_serdev; ++ ++ return ret; ++ ++err_serdev: ++ serdev_device_driver_unregister(&mpwr_serdev_driver); ++err_class: ++ class_destroy(mpwr_class); ++ return ret; ++} ++ ++static void __exit mpwr_driver_exit(void) ++{ ++ serdev_device_driver_unregister(&mpwr_serdev_driver); ++ platform_driver_unregister(&mpwr_platform_driver); ++ class_destroy(mpwr_class); ++} ++ ++module_init(mpwr_driver_init); ++module_exit(mpwr_driver_exit); ++ ++MODULE_DESCRIPTION("Modem power manager"); ++MODULE_AUTHOR("Ondrej Jirman "); ++MODULE_LICENSE("GPL v2"); ++ ++// }}} +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/mmc-add-delay-after-power-class-selection.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/mmc-add-delay-after-power-class-selection.patch new file mode 100644 index 000000000000..cfb802902e4f --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/mmc-add-delay-after-power-class-selection.patch @@ -0,0 +1,38 @@ +From e943000632398ac560d05492d23665c8ea0113cf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Fri, 16 Mar 2018 20:31:55 +0100 +Subject: mmc: add delay after power class selection + +This seems to fix issue with sporadic ETIMEOUT in eMMC cache +initialization on second generation TBS A711 tablet. + +Signed-off-by: Ondrej Jirman +--- + drivers/mmc/core/mmc.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c +index 6a23be214543..8ef20ffc9828 100644 +--- a/drivers/mmc/core/mmc.c ++++ b/drivers/mmc/core/mmc.c +@@ -1840,6 +1840,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, + */ + mmc_select_powerclass(card); + ++ msleep(20); ++ + /* + * Enable HPI feature (if supported) + */ +@@ -1858,6 +1860,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, + } + } + ++ msleep(20); ++ + /* + * If cache size is higher than 0, this indicates the existence of cache + * and it can be turned on. Note that some eMMCs from Micron has been +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/mmc-dw-mmc-rockchip-fix-sdmmc-after-soft-reboot.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/mmc-dw-mmc-rockchip-fix-sdmmc-after-soft-reboot.patch new file mode 100644 index 000000000000..01b6d0777ad2 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/mmc-dw-mmc-rockchip-fix-sdmmc-after-soft-reboot.patch @@ -0,0 +1,52 @@ +From a276625b2b8b781c6e50fb66ddc747a8c761c076 Mon Sep 17 00:00:00 2001 +From: Peter Geis +Date: Sat, 22 Jan 2022 20:35:39 +0000 +Subject: mmc: dw-mmc-rockchip: fix sdmmc after soft reboot + +During shutdown the dw-mmc driver shuts down the vqmmc regulator. +This leads to situations where the sdmmc is unavailable after a soft +reboot. +Fix this by ensuring the vqmmc regulator is powered up on kernel +shutdown. + +Signed-off-by: Peter Geis +--- + drivers/mmc/host/dw_mmc-rockchip.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c +index baa23b517731..42edf219623b 100644 +--- a/drivers/mmc/host/dw_mmc-rockchip.c ++++ b/drivers/mmc/host/dw_mmc-rockchip.c +@@ -10,6 +10,7 @@ + #include + #include + #include ++#include + #include + + #include "dw_mmc.h" +@@ -575,9 +576,20 @@ static const struct dev_pm_ops dw_mci_rockchip_dev_pm_ops = { + NULL) + }; + ++static void dw_mci_rockchip_shutdown(struct platform_device *pdev) ++{ ++ struct dw_mci *host = dev_get_drvdata(&pdev->dev); ++ struct mmc_host *mmc = host->slot->mmc; ++ ++ if (!IS_ERR(mmc->supply.vqmmc) && !(host->vqmmc_enabled)) ++ if(!regulator_enable(mmc->supply.vqmmc)) ++ host->vqmmc_enabled = true; ++} ++ + static struct platform_driver dw_mci_rockchip_pltfm_driver = { + .probe = dw_mci_rockchip_probe, + .remove = dw_mci_rockchip_remove, ++ .shutdown = dw_mci_rockchip_shutdown, + .driver = { + .name = "dwmmc_rockchip", + .probe_type = PROBE_PREFER_ASYNCHRONOUS, +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/mmc-sunxi-mmc-Remove-runtime-PM.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/mmc-sunxi-mmc-Remove-runtime-PM.patch new file mode 100644 index 000000000000..808b333b01fc --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/mmc-sunxi-mmc-Remove-runtime-PM.patch @@ -0,0 +1,143 @@ +From 2cdaf7e64d4df7a1f4cc2be5f04d0831cc87b0a6 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Mon, 15 Feb 2021 09:11:16 +0100 +Subject: mmc: sunxi-mmc: Remove runtime-PM + +With runtime PM I get a lot of errors like (on H5/H6 f2fs formatted +rootfs): + +[81360.499781] mmc_erase: group start error -110, status 0x0 +[81360.550703] mmc_erase: group start error -110, status 0x0 +[81360.599594] sunxi-mmc 4020000.mmc: data error, sending stop command +[81360.599611] sunxi-mmc 4020000.mmc: send stop command failed +[83474.053131] mmc_erase: group start error -110, status 0x0 +[83474.104043] mmc_erase: group start error -110, status 0x0 +[83850.888258] mmc_erase: group start error -110, status 0x0 +[83850.938810] mmc_erase: group start error -110, status 0x0 +[83850.989808] mmc_erase: group start error -110, status 0x0 +[83851.040888] mmc_erase: group start error -110, status 0x0 +[83851.091908] mmc_erase: group start error -110, status 0x0 +[83851.142938] mmc_erase: group start error -110, status 0x0 +[83851.193806] sunxi-mmc 4020000.mmc: data error, sending stop command +[83851.193825] sunxi-mmc 4020000.mmc: send stop command failed +[83851.201040] sunxi-mmc 4020000.mmc: data error, sending stop command +[83851.201062] sunxi-mmc 4020000.mmc: send stop command failed +[83851.720183] mmc_erase: group start error -110, status 0x0 +[83851.770866] mmc_erase: group start error -110, status 0x0 +[83851.821418] mmc_erase: group start error -110, status 0x0 +[83851.872081] mmc_erase: group start error -110, status 0x0 +[83851.922757] mmc_erase: group start error -110, status 0x0 +[83851.999497] mmc_erase: group start error -110, status 0x0 +[83852.050151] mmc_erase: group start error -110, status 0x0 +[83852.101319] mmc_erase: group start error -110, status 0x0 +[83852.152189] mmc_erase: group start error -110, status 0x0 +[83852.202866] mmc_erase: group start error -110, status 0x0 + +Signed-off-by: Ondrej Jirman +--- + drivers/mmc/host/sunxi-mmc.c | 35 +++++++++++++++++++---------------- + 1 file changed, 19 insertions(+), 16 deletions(-) + +diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c +index 1508eead5d01..30b596562281 100644 +--- a/drivers/mmc/host/sunxi-mmc.c ++++ b/drivers/mmc/host/sunxi-mmc.c +@@ -949,9 +949,15 @@ static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) + { + struct sunxi_mmc_host *host = mmc_priv(mmc); + ++ if (ios->power_mode == MMC_POWER_OFF) ++ sunxi_mmc_reset_host(host); ++ + sunxi_mmc_card_power(host, ios); + sunxi_mmc_set_bus_width(host, ios->bus_width); + sunxi_mmc_set_clk(host, ios); ++ ++ if (ios->power_mode == MMC_POWER_UP) ++ sunxi_mmc_init_host(host); + } + + static int sunxi_mmc_volt_switch(struct mmc_host *mmc, struct mmc_ios *ios) +@@ -977,8 +983,8 @@ static void sunxi_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) + unsigned long flags; + u32 imask; + +- if (enable) +- pm_runtime_get_noresume(host->dev); ++ //if (enable) ++ //pm_runtime_get_noresume(host->dev); + + spin_lock_irqsave(&host->lock, flags); + +@@ -993,8 +999,8 @@ static void sunxi_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) + mmc_writel(host, REG_IMASK, imask); + spin_unlock_irqrestore(&host->lock, flags); + +- if (!enable) +- pm_runtime_put_noidle(host->mmc->parent); ++ //if (!enable) ++ //pm_runtime_put_noidle(host->mmc->parent); + } + + static void sunxi_mmc_hw_reset(struct mmc_host *mmc) +@@ -1464,10 +1470,10 @@ static int sunxi_mmc_probe(struct platform_device *pdev) + if (ret) + goto error_free_dma; + +- pm_runtime_set_active(&pdev->dev); +- pm_runtime_set_autosuspend_delay(&pdev->dev, 50); +- pm_runtime_use_autosuspend(&pdev->dev); +- pm_runtime_enable(&pdev->dev); ++ //pm_runtime_set_active(&pdev->dev); ++ //pm_runtime_set_autosuspend_delay(&pdev->dev, 50); ++ //pm_runtime_use_autosuspend(&pdev->dev); ++ //pm_runtime_enable(&pdev->dev); + + ret = mmc_add_host(mmc); + if (ret) +@@ -1492,11 +1498,11 @@ static void sunxi_mmc_remove(struct platform_device *pdev) + struct sunxi_mmc_host *host = mmc_priv(mmc); + + mmc_remove_host(mmc); +- pm_runtime_disable(&pdev->dev); +- if (!pm_runtime_status_suspended(&pdev->dev)) { ++ //pm_runtime_disable(&pdev->dev); ++ //if (!pm_runtime_status_suspended(&pdev->dev)) { + disable_irq(host->irq); + sunxi_mmc_disable(host); +- } ++ //} + dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); + mmc_free_host(mmc); + } +@@ -1531,7 +1537,6 @@ static int sunxi_mmc_runtime_suspend(struct device *dev) + * Disabling the irq will prevent this. + */ + disable_irq(host->irq); +- sunxi_mmc_reset_host(host); + sunxi_mmc_disable(host); + + return 0; +@@ -1539,8 +1544,6 @@ static int sunxi_mmc_runtime_suspend(struct device *dev) + #endif + + static const struct dev_pm_ops sunxi_mmc_pm_ops = { +- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, +- pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(sunxi_mmc_runtime_suspend, + sunxi_mmc_runtime_resume, + NULL) +@@ -1550,8 +1553,8 @@ static struct platform_driver sunxi_mmc_driver = { + .driver = { + .name = "sunxi-mmc", + .probe_type = PROBE_PREFER_ASYNCHRONOUS, +- .of_match_table = sunxi_mmc_of_match, +- .pm = &sunxi_mmc_pm_ops, ++ .of_match_table = of_match_ptr(sunxi_mmc_of_match), ++ //.pm = &sunxi_mmc_pm_ops, + }, + .probe = sunxi_mmc_probe, + .remove = sunxi_mmc_remove, +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/mtd-spi-nor-Add-Alliance-memory-support.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/mtd-spi-nor-Add-Alliance-memory-support.patch new file mode 100644 index 000000000000..2030dbbc2e54 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/mtd-spi-nor-Add-Alliance-memory-support.patch @@ -0,0 +1,85 @@ +From 3b39764173b1cd726267af2c4510f1ac145c1def Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Tue, 1 Aug 2023 14:56:33 +0200 +Subject: mtd: spi-nor: Add Alliance memory support + +- 128MBit flash: as25f1128mq + +Signed-off-by: Ondrej Jirman +--- + drivers/mtd/spi-nor/Makefile | 1 + + drivers/mtd/spi-nor/alliance.c | 24 ++++++++++++++++++++++++ + drivers/mtd/spi-nor/core.c | 1 + + drivers/mtd/spi-nor/core.h | 1 + + 4 files changed, 27 insertions(+) + create mode 100644 drivers/mtd/spi-nor/alliance.c + +diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile +index 5dd9c35f6b6f..998876920319 100644 +--- a/drivers/mtd/spi-nor/Makefile ++++ b/drivers/mtd/spi-nor/Makefile +@@ -1,6 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0 + + spi-nor-objs := core.o sfdp.o swp.o otp.o sysfs.o ++spi-nor-objs += alliance.o + spi-nor-objs += atmel.o + spi-nor-objs += eon.o + spi-nor-objs += esmt.o +diff --git a/drivers/mtd/spi-nor/alliance.c b/drivers/mtd/spi-nor/alliance.c +new file mode 100644 +index 000000000000..f3f03d458a5c +--- /dev/null ++++ b/drivers/mtd/spi-nor/alliance.c +@@ -0,0 +1,24 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) 2023, Ondrej Jirman ++ */ ++ ++#include ++ ++#include "core.h" ++ ++static const struct flash_info alliance_nor_parts[] = { ++ { ++ .id = SNOR_ID(0x52, 0x42, 0x18), ++ .name = "as25f1128mq", ++ .size = SZ_16M, ++ .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, ++ .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, ++ }, ++}; ++ ++const struct spi_nor_manufacturer spi_nor_alliance = { ++ .name = "alliance", ++ .parts = alliance_nor_parts, ++ .nparts = ARRAY_SIZE(alliance_nor_parts), ++}; +diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c +index 7bb44fabe093..12c3b844e135 100644 +--- a/drivers/mtd/spi-nor/core.c ++++ b/drivers/mtd/spi-nor/core.c +@@ -1967,6 +1967,7 @@ int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor) + } + + static const struct spi_nor_manufacturer *manufacturers[] = { ++ &spi_nor_alliance, + &spi_nor_atmel, + &spi_nor_eon, + &spi_nor_esmt, +diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h +index ceff412f7d65..fff7ee7c504e 100644 +--- a/drivers/mtd/spi-nor/core.h ++++ b/drivers/mtd/spi-nor/core.h +@@ -584,6 +584,7 @@ struct sfdp { + }; + + /* Manufacturer drivers. */ ++extern const struct spi_nor_manufacturer spi_nor_alliance; + extern const struct spi_nor_manufacturer spi_nor_atmel; + extern const struct spi_nor_manufacturer spi_nor_eon; + extern const struct spi_nor_manufacturer spi_nor_esmt; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/mtd-spi-nor-Add-vdd-regulator-support.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/mtd-spi-nor-Add-vdd-regulator-support.patch new file mode 100644 index 000000000000..851e4b30a2d6 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/mtd-spi-nor-Add-vdd-regulator-support.patch @@ -0,0 +1,33 @@ +From 397be32cffa8fe179989e754f15cdfd42d32d325 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Mon, 30 Sep 2019 11:49:54 +0200 +Subject: mtd: spi-nor: Add vdd regulator support + +On some boards, SPI NOR needs a regulator be enabled. + +Signed-off-by: Ondrej Jirman +--- + drivers/mtd/spi-nor/core.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c +index 19eb98bd6821..7bb44fabe093 100644 +--- a/drivers/mtd/spi-nor/core.c ++++ b/drivers/mtd/spi-nor/core.c +@@ -3596,6 +3596,13 @@ static int spi_nor_probe(struct spi_mem *spimem) + if (!nor) + return -ENOMEM; + ++ ret = devm_regulator_get_enable(&spi->dev, "vdd"); ++ if (ret) ++ return dev_err_probe(&spi->dev, ret, ++ "unable to get vdd regulator\n"); ++ ++ msleep(5); ++ + nor->spimem = spimem; + nor->dev = dev; + spi_nor_set_flash_node(nor, dev->of_node); +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/net-stmmac-sun8i-Add-support-for-enabling-a-regulator-for-PHY-I.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/net-stmmac-sun8i-Add-support-for-enabling-a-regulator-for-PHY-I.patch new file mode 100644 index 000000000000..90ae368e2223 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/net-stmmac-sun8i-Add-support-for-enabling-a-regulator-for-PHY-I.patch @@ -0,0 +1,89 @@ +From 7b4eda27c79bedf2ce5ed68df717b53e84bf15e2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Tue, 20 Aug 2019 14:31:38 +0200 +Subject: net: stmmac: sun8i: Add support for enabling a regulator for PHY I/O + pins + +Orange Pi 3 has two regulators that power the Realtek RTL8211E. According +to the phy datasheet, both regulators need to be enabled at the same time. + +Add support for the second optional regulator, "phy-io", to the glue +driver. + +Signed-off-by: Ondrej Jirman +--- + .../net/ethernet/stmicro/stmmac/dwmac-sun8i.c | 20 ++++++++++++++++++- + 1 file changed, 19 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c +index 9c2685cb72d2..6e82638d314a 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c +@@ -61,6 +61,8 @@ struct emac_variant { + /* struct sunxi_priv_data - hold all sunxi private data + * @ephy_clk: reference to the optional EPHY clock for the internal PHY + * @regulator_phy: reference to the optional regulator ++ * @regulator_phy_io: reference to the optional regulator for ++ * PHY I/O pins + * @rst_ephy: reference to the optional EPHY reset for the internal PHY + * @variant: reference to the current board variant + * @regmap: regmap for using the syscon +@@ -71,6 +73,7 @@ struct emac_variant { + struct sunxi_priv_data { + struct clk *ephy_clk; + struct regulator *regulator_phy; ++ struct regulator *regulator_phy_io; + struct reset_control *rst_ephy; + const struct emac_variant *variant; + struct regmap_field *regmap_field; +@@ -588,10 +591,16 @@ static int sun8i_dwmac_init(struct platform_device *pdev, void *priv) + struct sunxi_priv_data *gmac = priv; + int ret; + ++ ret = regulator_enable(gmac->regulator_phy_io); ++ if (ret) { ++ dev_err(&pdev->dev, "Fail to enable PHY I/O regulator\n"); ++ return ret; ++ } ++ + ret = regulator_enable(gmac->regulator_phy); + if (ret) { + dev_err(&pdev->dev, "Fail to enable PHY regulator\n"); +- return ret; ++ goto err_disable_regulator_phy_io; + } + + if (gmac->use_internal_phy) { +@@ -604,6 +613,8 @@ static int sun8i_dwmac_init(struct platform_device *pdev, void *priv) + + err_disable_regulator: + regulator_disable(gmac->regulator_phy); ++err_disable_regulator_phy_io: ++ regulator_disable(gmac->regulator_phy_io); + + return ret; + } +@@ -1049,6 +1060,7 @@ static void sun8i_dwmac_exit(struct platform_device *pdev, void *priv) + sun8i_dwmac_unpower_internal_phy(gmac); + + regulator_disable(gmac->regulator_phy); ++ regulator_disable(gmac->regulator_phy_io); + } + + static void sun8i_dwmac_set_mac_loopback(void __iomem *ioaddr, bool enable) +@@ -1177,6 +1189,12 @@ static int sun8i_dwmac_probe(struct platform_device *pdev) + return dev_err_probe(dev, PTR_ERR(gmac->regulator_phy), + "Failed to get PHY regulator\n"); + ++ /* Optional regulator for PHY I/O pins */ ++ gmac->regulator_phy_io = devm_regulator_get(dev, "phy-io"); ++ if (IS_ERR(gmac->regulator_phy_io)) ++ return dev_err_probe(dev, PTR_ERR(gmac->regulator_phy_io), ++ "Failed to get PHY I/O regulator\n"); ++ + /* The "GMAC clock control" register might be located in the + * CCU address range (on the R40), or the system control address + * range (on most other sun8i and later SoCs). +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/net-stmmac-sun8i-Rename-PHY-regulator-variable-to-regulator_phy.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/net-stmmac-sun8i-Rename-PHY-regulator-variable-to-regulator_phy.patch new file mode 100644 index 000000000000..21f3ba8b2c06 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/net-stmmac-sun8i-Rename-PHY-regulator-variable-to-regulator_phy.patch @@ -0,0 +1,81 @@ +From e75d0648a45ace112ac64688f87b05b23b8bb1c5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Tue, 20 Aug 2019 14:29:29 +0200 +Subject: net: stmmac: sun8i: Rename PHY regulator variable to regulator_phy + +We'll be adding further optional regulators, and this makes it clearer +what the regulator is for. + +Signed-off-by: Ondrej Jirman +--- + .../net/ethernet/stmicro/stmmac/dwmac-sun8i.c | 18 +++++++++--------- + 1 file changed, 9 insertions(+), 9 deletions(-) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c +index 7983df0d0eaa..9c2685cb72d2 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c +@@ -60,7 +60,7 @@ struct emac_variant { + + /* struct sunxi_priv_data - hold all sunxi private data + * @ephy_clk: reference to the optional EPHY clock for the internal PHY +- * @regulator: reference to the optional regulator ++ * @regulator_phy: reference to the optional regulator + * @rst_ephy: reference to the optional EPHY reset for the internal PHY + * @variant: reference to the current board variant + * @regmap: regmap for using the syscon +@@ -70,7 +70,7 @@ struct emac_variant { + */ + struct sunxi_priv_data { + struct clk *ephy_clk; +- struct regulator *regulator; ++ struct regulator *regulator_phy; + struct reset_control *rst_ephy; + const struct emac_variant *variant; + struct regmap_field *regmap_field; +@@ -588,9 +588,9 @@ static int sun8i_dwmac_init(struct platform_device *pdev, void *priv) + struct sunxi_priv_data *gmac = priv; + int ret; + +- ret = regulator_enable(gmac->regulator); ++ ret = regulator_enable(gmac->regulator_phy); + if (ret) { +- dev_err(&pdev->dev, "Fail to enable regulator\n"); ++ dev_err(&pdev->dev, "Fail to enable PHY regulator\n"); + return ret; + } + +@@ -603,7 +603,7 @@ static int sun8i_dwmac_init(struct platform_device *pdev, void *priv) + return 0; + + err_disable_regulator: +- regulator_disable(gmac->regulator); ++ regulator_disable(gmac->regulator_phy); + + return ret; + } +@@ -1048,7 +1048,7 @@ static void sun8i_dwmac_exit(struct platform_device *pdev, void *priv) + if (gmac->variant->soc_has_internal_phy) + sun8i_dwmac_unpower_internal_phy(gmac); + +- regulator_disable(gmac->regulator); ++ regulator_disable(gmac->regulator_phy); + } + + static void sun8i_dwmac_set_mac_loopback(void __iomem *ioaddr, bool enable) +@@ -1172,9 +1172,9 @@ static int sun8i_dwmac_probe(struct platform_device *pdev) + } + + /* Optional regulator for PHY */ +- gmac->regulator = devm_regulator_get(dev, "phy"); +- if (IS_ERR(gmac->regulator)) +- return dev_err_probe(dev, PTR_ERR(gmac->regulator), ++ gmac->regulator_phy = devm_regulator_get(dev, "phy"); ++ if (IS_ERR(gmac->regulator_phy)) ++ return dev_err_probe(dev, PTR_ERR(gmac->regulator_phy), + "Failed to get PHY regulator\n"); + + /* The "GMAC clock control" register might be located in the +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/net-stmmac-sun8i-Use-devm_regulator_get-for-PHY-regulator.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/net-stmmac-sun8i-Use-devm_regulator_get-for-PHY-regulator.patch new file mode 100644 index 000000000000..f7b3898ce43f --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/net-stmmac-sun8i-Use-devm_regulator_get-for-PHY-regulator.patch @@ -0,0 +1,84 @@ +From e79f7a9f2ca1b767b63f924b43ee363e850a911c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Wed, 27 Mar 2019 13:21:06 +0100 +Subject: net: stmmac: sun8i: Use devm_regulator_get for PHY regulator + +Use devm_regulator_get instead of devm_regulator_get_optional and rely +on dummy supply. This avoids NULL checks before regulator_enable/disable +calls. + +This path also improves error reporting, because we now report both +use of dummy supply and error during registration with more detail, +instead of generic info level message "No regulator found" that +was reported previously on errors and lack of regulator property in DT. + +Finally, we'll be adding further optional regulators, and the overall +code will be simpler. + +Signed-off-by: Ondrej Jirman +--- + .../net/ethernet/stmicro/stmmac/dwmac-sun8i.c | 27 +++++++------------ + 1 file changed, 10 insertions(+), 17 deletions(-) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c +index 4b7b2582a120..7983df0d0eaa 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c +@@ -588,12 +588,10 @@ static int sun8i_dwmac_init(struct platform_device *pdev, void *priv) + struct sunxi_priv_data *gmac = priv; + int ret; + +- if (gmac->regulator) { +- ret = regulator_enable(gmac->regulator); +- if (ret) { +- dev_err(&pdev->dev, "Fail to enable regulator\n"); +- return ret; +- } ++ ret = regulator_enable(gmac->regulator); ++ if (ret) { ++ dev_err(&pdev->dev, "Fail to enable regulator\n"); ++ return ret; + } + + if (gmac->use_internal_phy) { +@@ -605,8 +603,7 @@ static int sun8i_dwmac_init(struct platform_device *pdev, void *priv) + return 0; + + err_disable_regulator: +- if (gmac->regulator) +- regulator_disable(gmac->regulator); ++ regulator_disable(gmac->regulator); + + return ret; + } +@@ -1051,8 +1048,7 @@ static void sun8i_dwmac_exit(struct platform_device *pdev, void *priv) + if (gmac->variant->soc_has_internal_phy) + sun8i_dwmac_unpower_internal_phy(gmac); + +- if (gmac->regulator) +- regulator_disable(gmac->regulator); ++ regulator_disable(gmac->regulator); + } + + static void sun8i_dwmac_set_mac_loopback(void __iomem *ioaddr, bool enable) +@@ -1176,13 +1172,10 @@ static int sun8i_dwmac_probe(struct platform_device *pdev) + } + + /* Optional regulator for PHY */ +- gmac->regulator = devm_regulator_get_optional(dev, "phy"); +- if (IS_ERR(gmac->regulator)) { +- if (PTR_ERR(gmac->regulator) == -EPROBE_DEFER) +- return -EPROBE_DEFER; +- dev_info(dev, "No regulator found\n"); +- gmac->regulator = NULL; +- } ++ gmac->regulator = devm_regulator_get(dev, "phy"); ++ if (IS_ERR(gmac->regulator)) ++ return dev_err_probe(dev, PTR_ERR(gmac->regulator), ++ "Failed to get PHY regulator\n"); + + /* The "GMAC clock control" register might be located in the + * CCU address range (on the R40), or the system control address +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/nfc-pn544-Add-support-for-VBAT-PVDD-regulators.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/nfc-pn544-Add-support-for-VBAT-PVDD-regulators.patch new file mode 100644 index 000000000000..dc481d76c7ab --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/nfc-pn544-Add-support-for-VBAT-PVDD-regulators.patch @@ -0,0 +1,106 @@ +From 1734c610a0f7ac87fbbad98ffc8fc13b5b84d066 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Fri, 10 Nov 2017 14:29:26 +0100 +Subject: nfc: pn544: Add support for VBAT/PVDD regulators + +Regulators are required, so this can't go into mainline as is. + +Signed-off-by: Ondrej Jirman +--- + drivers/nfc/pn544/i2c.c | 31 +++++++++++++++++++++++++++++-- + 1 file changed, 29 insertions(+), 2 deletions(-) + +diff --git a/drivers/nfc/pn544/i2c.c b/drivers/nfc/pn544/i2c.c +index a0dfb3f98d5a..4b980d87f7ae 100644 +--- a/drivers/nfc/pn544/i2c.c ++++ b/drivers/nfc/pn544/i2c.c +@@ -16,6 +16,7 @@ + #include + #include + #include ++#include + + #include + +@@ -59,6 +60,14 @@ MODULE_DEVICE_TABLE(acpi, pn544_hci_i2c_acpi_match); + + #define PN544_HCI_I2C_DRIVER_NAME "pn544_hci_i2c" + ++/* regulator supplies */ ++static const char * const pn544_supply_names[] = { ++ "PVDD", /* Digital Core (1.8V) supply */ ++ "VBAT", /* Analog (2.9V-5.5V) supply */ ++}; ++ ++#define PN544_NUM_SUPPLIES ARRAY_SIZE(pn544_supply_names) ++ + /* + * Exposed through the 4 most significant bytes + * from the HCI SW_VERSION first byte, a.k.a. +@@ -148,6 +157,7 @@ struct pn544_i2c_phy { + struct i2c_client *i2c_dev; + struct nfc_hci_dev *hdev; + ++ struct regulator_bulk_data supplies[PN544_NUM_SUPPLIES]; + struct gpio_desc *gpiod_en; + struct gpio_desc *gpiod_fw; + +@@ -238,6 +248,13 @@ static void pn544_hci_i2c_enable_mode(struct pn544_i2c_phy *phy, int run_mode) + static int pn544_hci_i2c_enable(void *phy_id) + { + struct pn544_i2c_phy *phy = phy_id; ++ int ret; ++ ++ pr_info("%s\n", __func__); ++ ++ ret = regulator_bulk_enable(PN544_NUM_SUPPLIES, phy->supplies); ++ if (ret) ++ return ret; + + pn544_hci_i2c_enable_mode(phy, PN544_HCI_MODE); + +@@ -260,6 +277,8 @@ static void pn544_hci_i2c_disable(void *phy_id) + gpiod_set_value_cansleep(phy->gpiod_en, !phy->en_polarity); + usleep_range(10000, 15000); + ++ regulator_bulk_disable(PN544_NUM_SUPPLIES, phy->supplies); ++ + phy->powered = 0; + } + +@@ -366,7 +385,7 @@ static int pn544_hci_i2c_read(struct pn544_i2c_phy *phy, struct sk_buff **skb) + + if ((len < (PN544_HCI_I2C_LLC_MIN_SIZE - 1)) || + (len > (PN544_HCI_I2C_LLC_MAX_SIZE - 1))) { +- nfc_err(&client->dev, "invalid len byte\n"); ++ nfc_err(&client->dev, "invalid len byte %hhx\n", len); + r = -EBADMSG; + goto flush; + } +@@ -868,7 +887,7 @@ static int pn544_hci_i2c_probe(struct i2c_client *client) + { + struct device *dev = &client->dev; + struct pn544_i2c_phy *phy; +- int r = 0; ++ int r = 0, i; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + nfc_err(&client->dev, "Need I2C_FUNC_I2C\n"); +@@ -890,6 +909,14 @@ static int pn544_hci_i2c_probe(struct i2c_client *client) + if (r) + dev_dbg(dev, "Unable to add GPIO mapping table\n"); + ++ for (i = 0; i < PN544_NUM_SUPPLIES; i++) ++ phy->supplies[i].supply = pn544_supply_names[i]; ++ ++ r = devm_regulator_bulk_get(&client->dev, PN544_NUM_SUPPLIES, ++ phy->supplies); ++ if (r) ++ return r; ++ + /* Get EN GPIO */ + phy->gpiod_en = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); + if (IS_ERR(phy->gpiod_en)) { +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/of-property-fw_devlink-Support-allwinner-sram-links.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/of-property-fw_devlink-Support-allwinner-sram-links.patch new file mode 100644 index 000000000000..228851a4fd49 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/of-property-fw_devlink-Support-allwinner-sram-links.patch @@ -0,0 +1,58 @@ +From ffba28a2260e995a9170bea3a008d84a130fa583 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sat, 15 May 2021 21:43:44 +0200 +Subject: of: property: fw_devlink: Support allwinner,sram links + +allwinner,sram property points to a node representing section of SRAM, +which is implemented by its ancestor (syscon), so we link to the +parent of parent of the actual SRAM section node that the link points +to. + +Signed-off-by: Ondrej Jirman +--- + drivers/of/property.c | 22 ++++++++++++++++++++++ + 1 file changed, 22 insertions(+) + +diff --git a/drivers/of/property.c b/drivers/of/property.c +index 208d922cc24c..8c9f3ea663a6 100644 +--- a/drivers/of/property.c ++++ b/drivers/of/property.c +@@ -1455,6 +1455,27 @@ static struct device_node *parse_remote_endpoint(struct device_node *np, + return of_graph_get_remote_port_parent(np); + } + ++static struct device_node *parse_allwinner_sram(struct device_node *np, ++ const char *prop_name, int index) ++{ ++ struct device_node *sram_node; ++ ++ if (!IS_ENABLED(CONFIG_SUNXI_SRAM)) ++ return NULL; ++ ++ if (strcmp(prop_name, "allwinner,sram")) ++ return NULL; ++ ++ if (index > 0) ++ return NULL; ++ ++ sram_node = of_parse_phandle(np, prop_name, 0); ++ sram_node = of_get_parent(sram_node); ++ sram_node = of_get_parent(sram_node); ++ ++ return sram_node; ++} ++ + static const struct supplier_bindings of_supplier_bindings[] = { + { .parse_prop = parse_clocks, }, + { .parse_prop = parse_interconnects, }, +@@ -1502,6 +1523,7 @@ static const struct supplier_bindings of_supplier_bindings[] = { + .parse_prop = parse_post_init_providers, + .fwlink_flags = FWLINK_FLAG_IGNORE, + }, ++ { .parse_prop = parse_allwinner_sram, }, + {} + }; + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/opp-core-Avoid-confusing-error-when-no-regulator-is-defined-in-.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/opp-core-Avoid-confusing-error-when-no-regulator-is-defined-in-.patch new file mode 100644 index 000000000000..bf907bfae41c --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/opp-core-Avoid-confusing-error-when-no-regulator-is-defined-in-.patch @@ -0,0 +1,31 @@ +From c0d7f75b2beb43824f91642088643e90e5eeb19e Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sat, 29 Oct 2022 23:16:51 +0200 +Subject: opp: core: Avoid confusing error when no regulator is defined in DT + +A64 GPU doesn't have a separate regulator. Avoid useless error. + +Signed-off-by: Ondrej Jirman +--- + drivers/opp/core.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/drivers/opp/core.c b/drivers/opp/core.c +index 73e9a3b2f29b..388d2622cf69 100644 +--- a/drivers/opp/core.c ++++ b/drivers/opp/core.c +@@ -2240,6 +2240,11 @@ static int _opp_set_regulators(struct opp_table *opp_table, struct device *dev, + for (i = 0; i < count; i++) { + reg = regulator_get_optional(dev, names[i]); + if (IS_ERR(reg)) { ++ if (PTR_ERR(reg) == -ENODEV) { ++ ret = -ENODEV; ++ goto free_regulators; ++ } ++ + ret = dev_err_probe(dev, PTR_ERR(reg), + "%s: no regulator (%s) found\n", + __func__, names[i]); +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/pci-Workaround-ITS-timeouts-on-poweroff-reboot-on-Orange-Pi-5-P.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/pci-Workaround-ITS-timeouts-on-poweroff-reboot-on-Orange-Pi-5-P.patch new file mode 100644 index 000000000000..2fb7a14ffb04 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/pci-Workaround-ITS-timeouts-on-poweroff-reboot-on-Orange-Pi-5-P.patch @@ -0,0 +1,32 @@ +From a624070758979300cfc1b684162a2fb881773cae Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Wed, 14 Jun 2023 00:48:21 +0200 +Subject: pci: Workaround ITS timeouts on poweroff/reboot on Orange Pi 5 Plus + +Call to pci_free_irq_vectors on PCIE2x ports causes ITS timeout messages +and delayed shutdown/reboot (by up to a minute). + +The root cause will be elsewhere. This is not for upstream. Anyway, +there should be no harm from this, since we're shutting down anyway. + +Signed-off-by: Ondrej Jirman +--- + drivers/pci/pcie/portdrv.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/pci/pcie/portdrv.c b/drivers/pci/pcie/portdrv.c +index e8318fd5f6ed..22d65c83396e 100644 +--- a/drivers/pci/pcie/portdrv.c ++++ b/drivers/pci/pcie/portdrv.c +@@ -743,7 +743,7 @@ static void pcie_portdrv_shutdown(struct pci_dev *dev) + pm_runtime_dont_use_autosuspend(&dev->dev); + } + +- pcie_port_device_remove(dev); ++ device_for_each_child(&dev->dev, NULL, remove_iter); + } + + static pci_ers_result_t pcie_portdrv_error_detected(struct pci_dev *dev, +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/phy-allwinner-sun4i-usb-Add-support-for-usb_role_switch.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/phy-allwinner-sun4i-usb-Add-support-for-usb_role_switch.patch new file mode 100644 index 000000000000..bb0c22ce8b44 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/phy-allwinner-sun4i-usb-Add-support-for-usb_role_switch.patch @@ -0,0 +1,146 @@ +From 0179ed04b8454eca07abc91a41c213423f4d1b1d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Mon, 8 Jun 2020 00:11:06 +0200 +Subject: phy: allwinner: sun4i-usb: Add support for usb_role_switch + +This allow controlling the usb0 data role from a typec port driver. + +Signed-off-by: Ondrej Jirman +--- + drivers/phy/allwinner/Kconfig | 1 + + drivers/phy/allwinner/phy-sun4i-usb.c | 51 ++++++++++++++++++++++++++- + 2 files changed, 51 insertions(+), 1 deletion(-) + +diff --git a/drivers/phy/allwinner/Kconfig b/drivers/phy/allwinner/Kconfig +index fb584518b2d0..e93a53139460 100644 +--- a/drivers/phy/allwinner/Kconfig ++++ b/drivers/phy/allwinner/Kconfig +@@ -12,6 +12,7 @@ config PHY_SUN4I_USB + depends on USB_SUPPORT + select GENERIC_PHY + select USB_COMMON ++ select USB_ROLE_SWITCH + help + Enable this to support the transceiver that is part of Allwinner + sunxi SoCs. +diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c +index 29b8fd4b9351..3a42865f9a5b 100644 +--- a/drivers/phy/allwinner/phy-sun4i-usb.c ++++ b/drivers/phy/allwinner/phy-sun4i-usb.c +@@ -31,6 +31,7 @@ + #include + #include + #include ++#include + #include + + #define REG_ISCR 0x00 +@@ -139,6 +140,9 @@ struct sun4i_usb_phy_data { + int id_det; + int vbus_det; + struct delayed_work detect; ++ struct usb_role_switch_desc switch_desc; ++ struct usb_role_switch *role_switch; ++ int usb_role; + }; + + #define to_sun4i_usb_phy_data(phy) \ +@@ -401,6 +405,9 @@ static int sun4i_usb_phy_exit(struct phy *_phy) + + static int sun4i_usb_phy0_get_id_det(struct sun4i_usb_phy_data *data) + { ++ if (data->usb_role >= 0) ++ return data->usb_role == USB_ROLE_HOST ? 0 : 1; ++ + switch (data->dr_mode) { + case USB_DR_MODE_OTG: + if (data->id_det_gpio) +@@ -417,6 +424,9 @@ static int sun4i_usb_phy0_get_id_det(struct sun4i_usb_phy_data *data) + + static int sun4i_usb_phy0_get_vbus_det(struct sun4i_usb_phy_data *data) + { ++ if (data->usb_role >= 0) ++ return data->usb_role == USB_ROLE_NONE ? 0 : 1; ++ + if (data->vbus_det_gpio) + return gpiod_get_value_cansleep(data->vbus_det_gpio); + +@@ -436,7 +446,7 @@ static int sun4i_usb_phy0_get_vbus_det(struct sun4i_usb_phy_data *data) + + static bool sun4i_usb_phy0_have_vbus_det(struct sun4i_usb_phy_data *data) + { +- return data->vbus_det_gpio || data->vbus_power_supply; ++ return data->usb_role >= 0 || data->vbus_det_gpio || data->vbus_power_supply; + } + + static bool sun4i_usb_phy0_poll(struct sun4i_usb_phy_data *data) +@@ -695,6 +705,24 @@ static struct phy *sun4i_usb_phy_xlate(struct device *dev, + return data->phys[args->args[0]].phy; + } + ++static int sun4i_usb_role_set(struct usb_role_switch *sw, enum usb_role role) ++{ ++ struct sun4i_usb_phy_data *data = usb_role_switch_get_drvdata(sw); ++ ++ data->usb_role = role; ++ queue_delayed_work(system_wq, &data->detect, 0); ++ ++ return 0; ++} ++ ++static enum usb_role sun4i_usb_role_get(struct usb_role_switch *sw) ++{ ++ struct sun4i_usb_phy_data *data = usb_role_switch_get_drvdata(sw); ++ int role = sun4i_usb_phy0_get_id_det(data) ? USB_ROLE_DEVICE : USB_ROLE_HOST; ++ ++ return data->usb_role >= 0 ? data->usb_role : role; ++} ++ + static void sun4i_usb_phy_remove(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; +@@ -708,6 +736,8 @@ static void sun4i_usb_phy_remove(struct platform_device *pdev) + devm_free_irq(dev, data->vbus_det_irq, data); + + cancel_delayed_work_sync(&data->detect); ++ ++ usb_role_switch_unregister(data->role_switch); + } + + static const unsigned int sun4i_usb_phy0_cable[] = { +@@ -735,6 +765,8 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) + if (!data->cfg) + return -EINVAL; + ++ data->usb_role = -1; ++ + data->base = devm_platform_ioremap_resource_byname(pdev, "phy_ctrl"); + if (IS_ERR(data->base)) + return PTR_ERR(data->base); +@@ -895,6 +927,23 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) + return PTR_ERR(phy_provider); + } + ++ /* setup role switcher */ ++ data->switch_desc.name = "usb0"; ++ data->switch_desc.fwnode = dev_fwnode(dev); ++ data->switch_desc.set = sun4i_usb_role_set; ++ data->switch_desc.get = sun4i_usb_role_get; ++ data->switch_desc.driver_data = data; ++ ++ /* ++ * Don't interfere with the default behavior of this driver until ++ * the consumer of the role switch uses the switch for the first time. ++ */ ++ data->role_switch = usb_role_switch_register(dev, &data->switch_desc); ++ if (IS_ERR(data->role_switch)) { ++ dev_warn(dev, "Unable to register Role Switch\n"); ++ data->role_switch = NULL; ++ } ++ + dev_dbg(dev, "successfully loaded\n"); + + return 0; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/power-axp20x_battery-Allow-to-set-target-voltage-to-4.35V.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/power-axp20x_battery-Allow-to-set-target-voltage-to-4.35V.patch new file mode 100644 index 000000000000..173af001b73d --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/power-axp20x_battery-Allow-to-set-target-voltage-to-4.35V.patch @@ -0,0 +1,31 @@ +From aa242f8de59b3c71dd8541e635ddcb3fbe319ff2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Thu, 27 Feb 2020 00:53:17 +0100 +Subject: power: axp20x_battery: Allow to set target voltage to 4.35V + +Some devices have batteries that allow that. + +Signed-off-by: Ondrej Jirman +--- + drivers/power/supply/axp20x_battery.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/drivers/power/supply/axp20x_battery.c b/drivers/power/supply/axp20x_battery.c +index 3c3158f31a48..96d2856ef318 100644 +--- a/drivers/power/supply/axp20x_battery.c ++++ b/drivers/power/supply/axp20x_battery.c +@@ -629,6 +629,11 @@ static int axp20x_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt, + val = AXP20X_CHRG_CTRL1_TGT_4_2V; + break; + ++ case 4350000: ++ case 4360000: ++ val = AXP20X_CHRG_CTRL1_TGT_4_36V; ++ break; ++ + default: + /* + * AXP20x max voltage can be set to 4.36V and AXP22X max voltage +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/power-axp803-Add-interrupts-for-low-battery-power-condition.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/power-axp803-Add-interrupts-for-low-battery-power-condition.patch new file mode 100644 index 000000000000..b5db132dfb13 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/power-axp803-Add-interrupts-for-low-battery-power-condition.patch @@ -0,0 +1,43 @@ +From 3128559dbbdafac2ca2b68bf52d1472b349dd330 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sun, 14 Mar 2021 15:49:34 +0100 +Subject: power: axp803: Add interrupts for low battery power condition + +These are necessary so that the device is woken up by interrupt +when low on battery, to handle the condition. + +Signed-off-by: Ondrej Jirman +--- + drivers/mfd/axp20x.c | 2 ++ + drivers/power/supply/axp20x_battery.c | 2 ++ + 2 files changed, 4 insertions(+) + +diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c +index 8ff8bc4a22b2..5da865469679 100644 +--- a/drivers/mfd/axp20x.c ++++ b/drivers/mfd/axp20x.c +@@ -393,6 +393,8 @@ static const struct resource axp803_battery_resources[] = { + DEFINE_RES_IRQ_NAMED(AXP803_IRQ_BATT_ACT_TEMP_HIGH_END, "BATT_ACT_TEMP_HIGH_END"), + DEFINE_RES_IRQ_NAMED(AXP803_IRQ_BATT_ACT_TEMP_LOW, "BATT_ACT_TEMP_LOW"), + DEFINE_RES_IRQ_NAMED(AXP803_IRQ_BATT_ACT_TEMP_LOW_END, "BATT_ACT_TEMP_LOW_END"), ++ DEFINE_RES_IRQ_NAMED(AXP803_IRQ_LOW_PWR_LVL1, "BATT_LOW_PWR_LVL1"), ++ DEFINE_RES_IRQ_NAMED(AXP803_IRQ_LOW_PWR_LVL2, "BATT_LOW_PWR_LVL2"), + }; + + static const struct resource axp803_pek_resources[] = { +diff --git a/drivers/power/supply/axp20x_battery.c b/drivers/power/supply/axp20x_battery.c +index 3a5f77219d83..988741b7d3be 100644 +--- a/drivers/power/supply/axp20x_battery.c ++++ b/drivers/power/supply/axp20x_battery.c +@@ -1098,6 +1098,8 @@ static const struct axp_irq_data axp813_irqs[] = { + { "BATT_HEALTH_GOOD", axp20x_battery_changed_irq }, + { "BATT_CHARGING", axp20x_battery_changed_irq }, + { "BATT_CHARGING_DONE", axp20x_battery_changed_irq }, ++ { "BATT_LOW_PWR_LVL1", axp20x_battery_changed_irq }, ++ { "BATT_LOW_PWR_LVL2", axp20x_battery_changed_irq }, + { "BATT_CHG_TEMP_HIGH", axp20x_battery_temp_hot_irq }, + { "BATT_CHG_TEMP_HIGH_END", axp20x_battery_temp_normal_irq }, + { "BATT_CHG_TEMP_LOW", axp20x_battery_temp_cold_irq }, +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-Add-support-for-USB_BC_ENABLED-and-USB_DCP_INPUT_C.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-Add-support-for-USB_BC_ENABLED-and-USB_DCP_INPUT_C.patch new file mode 100644 index 000000000000..ed3390245414 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-Add-support-for-USB_BC_ENABLED-and-USB_DCP_INPUT_C.patch @@ -0,0 +1,45 @@ +From d997e3a57c6e83fa651b6f9daa80ac308b17c51f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Mon, 22 Jun 2020 02:28:01 +0200 +Subject: power: supply: Add support for USB_BC_ENABLED and + USB_DCP_INPUT_CURRENT_LIMIT + +- USB_BC_ENABLED - enabling/disabling BC 1.2 USB type detection +- USB_DCP_INPUT_CURRENT_LIMIT - setting a default current limit for DCP/CDP + ports + +Signed-off-by: Ondrej Jirman +--- + drivers/power/supply/power_supply_sysfs.c | 2 ++ + include/linux/power_supply.h | 2 ++ + 2 files changed, 4 insertions(+) + +diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c +index edb058c19c9c..430d64b974c0 100644 +--- a/drivers/power/supply/power_supply_sysfs.c ++++ b/drivers/power/supply/power_supply_sysfs.c +@@ -213,6 +213,8 @@ static struct power_supply_attr power_supply_attrs[] __ro_after_init = { + POWER_SUPPLY_ATTR(TIME_TO_FULL_AVG), + POWER_SUPPLY_ENUM_ATTR(TYPE), + POWER_SUPPLY_ENUM_ATTR(USB_TYPE), ++ POWER_SUPPLY_ATTR(USB_BC_ENABLED), ++ POWER_SUPPLY_ATTR(USB_DCP_INPUT_CURRENT_LIMIT), + POWER_SUPPLY_ENUM_ATTR(SCOPE), + POWER_SUPPLY_ATTR(PRECHARGE_CURRENT), + POWER_SUPPLY_ATTR(CHARGE_TERM_CURRENT), +diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h +index 6ed53b292162..dec890d882c2 100644 +--- a/include/linux/power_supply.h ++++ b/include/linux/power_supply.h +@@ -166,6 +166,8 @@ enum power_supply_property { + POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, + POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, + POWER_SUPPLY_PROP_TYPE, /* use power_supply.type instead */ ++ POWER_SUPPLY_PROP_USB_BC_ENABLED, ++ POWER_SUPPLY_PROP_USB_DCP_INPUT_CURRENT_LIMIT, + POWER_SUPPLY_PROP_USB_TYPE, + POWER_SUPPLY_PROP_SCOPE, + POWER_SUPPLY_PROP_PRECHARGE_CURRENT, +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x-battery-Add-support-for-POWER_SUPPLY_PROP_E.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x-battery-Add-support-for-POWER_SUPPLY_PROP_E.patch new file mode 100644 index 000000000000..f33ff736a084 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x-battery-Add-support-for-POWER_SUPPLY_PROP_E.patch @@ -0,0 +1,90 @@ +From fcfd714cac86026d77c397e3f52740ab2eb76566 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Thu, 10 Nov 2022 20:05:58 +0100 +Subject: power: supply: axp20x-battery: Add support for + POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN + +Report total battery capacity. + +Signed-off-by: Ondrej Jirman +--- + drivers/power/supply/axp20x_battery.c | 30 +++++++++++++++++++++++---- + 1 file changed, 26 insertions(+), 4 deletions(-) + +diff --git a/drivers/power/supply/axp20x_battery.c b/drivers/power/supply/axp20x_battery.c +index ba7c45e4bf10..9b4b1b8d09b1 100644 +--- a/drivers/power/supply/axp20x_battery.c ++++ b/drivers/power/supply/axp20x_battery.c +@@ -127,6 +127,7 @@ struct axp20x_batt_ps { + /* Maximum constant charge current */ + unsigned int max_ccc; + const struct axp_data *data; ++ struct power_supply_battery_info *info; + }; + + static int axp20x_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt, +@@ -460,6 +461,16 @@ static int axp20x_battery_get_prop(struct power_supply *psy, + + break; + ++ case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: ++ val->intval = 0; ++ if (axp20x_batt->info) ++ val->intval = axp20x_batt->info->energy_full_design_uwh; ++ return 0; ++ ++ case POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN: ++ val->intval = 0; ++ return 0; ++ + default: + return -EINVAL; + } +@@ -914,6 +925,8 @@ static enum power_supply_property axp20x_battery_props[] = { + POWER_SUPPLY_PROP_VOLTAGE_MIN, + POWER_SUPPLY_PROP_VOLTAGE_OCV, + POWER_SUPPLY_PROP_CAPACITY, ++ POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, ++ POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN, + }; + + static enum power_supply_property axp717_battery_props[] = { +@@ -1213,12 +1226,18 @@ static const struct of_device_id axp20x_battery_ps_id[] = { + }; + MODULE_DEVICE_TABLE(of, axp20x_battery_ps_id); + ++static void axp20x_put_battery_info(void *data) ++{ ++ struct axp20x_batt_ps *axp20x_batt = data; ++ ++ power_supply_put_battery_info(axp20x_batt->batt, axp20x_batt->info); ++} ++ + static int axp20x_power_probe(struct platform_device *pdev) + { + struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); + struct axp20x_batt_ps *axp20x_batt; + struct power_supply_config psy_cfg = {}; +- struct power_supply_battery_info *info; + struct device *dev = &pdev->dev; + const struct axp_irq_data *irq_data; + int irq, ret; +@@ -1256,9 +1275,12 @@ static int axp20x_power_probe(struct platform_device *pdev) + + axp20x_batt->health = POWER_SUPPLY_HEALTH_GOOD; + +- if (!power_supply_get_battery_info(axp20x_batt->batt, &info)) { +- axp20x_batt->data->set_bat_info(pdev, axp20x_batt, info); +- power_supply_put_battery_info(axp20x_batt->batt, info); ++ if (!power_supply_get_battery_info(axp20x_batt->batt, &axp20x_batt->info)) { ++ axp20x_batt->data->set_bat_info(pdev, axp20x_batt, axp20x_batt->info); ++ ++ ret = devm_add_action_or_reset(&pdev->dev, axp20x_put_battery_info, axp20x_batt); ++ if (ret) ++ return ret; + } + + /* Request irqs after registering, as irqs may trigger immediately */ +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x-battery-Enable-poweron-by-RTC-alarm.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x-battery-Enable-poweron-by-RTC-alarm.patch new file mode 100644 index 000000000000..82a1d06936ca --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x-battery-Enable-poweron-by-RTC-alarm.patch @@ -0,0 +1,35 @@ +From 1f7bdbc2a3c8d4b6deb77a4ef6b7bf5dada6a662 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sat, 27 Aug 2022 20:50:43 +0200 +Subject: power: supply: axp20x-battery: Enable poweron by RTC alarm + +For the Pinephone to be able to poweron on RTC alarm, some bits in +PMIC need to be enabled. This will cause PMIC to be wakeable by +SoC pulling the interrupt line low. + +This may need some coordination from a bootloader. + +Signed-off-by: Ondrej Jirman +--- + drivers/power/supply/axp20x_battery.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/drivers/power/supply/axp20x_battery.c b/drivers/power/supply/axp20x_battery.c +index 1899af1810fd..ba7c45e4bf10 100644 +--- a/drivers/power/supply/axp20x_battery.c ++++ b/drivers/power/supply/axp20x_battery.c +@@ -1333,6 +1333,11 @@ static int axp20x_power_probe(struct platform_device *pdev) + ret = regmap_update_bits(axp20x_batt->regmap, 0x84, 0x37, 0x31); + if (ret) + goto warn_bat; ++ ++ // enable poweron by RTC ++ ret = regmap_update_bits(axp20x_batt->regmap, 0x8f, BIT(7), BIT(7)); ++ if (ret) ++ goto warn_bat; + } + + return 0; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x-battery-Improve-probe-error-reporting.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x-battery-Improve-probe-error-reporting.patch new file mode 100644 index 000000000000..8da1d25fce75 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x-battery-Improve-probe-error-reporting.patch @@ -0,0 +1,69 @@ +From e452c5a4a3d43d07132fcd9eaf2b041bddafc3fb Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Tue, 7 Jan 2025 18:53:23 +0100 +Subject: power: supply: axp20x-battery: Improve probe error reporting + +Use dev_err_probe in places... + +Signed-off-by: Ondrej Jirman +--- + drivers/power/supply/axp20x_battery.c | 28 ++++++++++++--------------- + 1 file changed, 12 insertions(+), 16 deletions(-) + +diff --git a/drivers/power/supply/axp20x_battery.c b/drivers/power/supply/axp20x_battery.c +index 9b4b1b8d09b1..bab9b4a8f623 100644 +--- a/drivers/power/supply/axp20x_battery.c ++++ b/drivers/power/supply/axp20x_battery.c +@@ -1262,16 +1262,15 @@ static int axp20x_power_probe(struct platform_device *pdev) + + ret = axp20x_batt->data->cfg_iio_chan(pdev, axp20x_batt); + if (ret) +- return ret; ++ return dev_err_probe(&pdev->dev, ret, ++ "failed to configure iio channels\n"); + + axp20x_batt->batt = devm_power_supply_register(&pdev->dev, + axp20x_batt->data->bat_ps_desc, + &psy_cfg); +- if (IS_ERR(axp20x_batt->batt)) { +- dev_err(&pdev->dev, "failed to register power supply: %ld\n", +- PTR_ERR(axp20x_batt->batt)); +- return PTR_ERR(axp20x_batt->batt); +- } ++ if (IS_ERR(axp20x_batt->batt)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(axp20x_batt->batt), ++ "failed to register power supply\n"); + + axp20x_batt->health = POWER_SUPPLY_HEALTH_GOOD; + +@@ -1286,20 +1285,17 @@ static int axp20x_power_probe(struct platform_device *pdev) + /* Request irqs after registering, as irqs may trigger immediately */ + for (irq_data = axp20x_batt->data->irqs; irq_data->name; irq_data++) { + irq = platform_get_irq_byname(pdev, irq_data->name); +- if (irq < 0) { +- dev_err(&pdev->dev, "No IRQ for %s: %d\n", +- irq_data->name, irq); +- return irq; +- } ++ if (irq < 0) ++ return dev_err_probe(&pdev->dev, irq, ++ "No IRQ for %s\n", irq_data->name); ++ + irq = regmap_irq_get_virq(axp20x->regmap_irqc, irq); + ret = devm_request_any_context_irq(&pdev->dev, irq, + irq_data->handler, 0, + DRVNAME, axp20x_batt); +- if (ret < 0) { +- dev_err(&pdev->dev, "Error requesting %s IRQ: %d\n", +- irq_data->name, ret); +- return ret; +- } ++ if (ret < 0) ++ return dev_err_probe(&pdev->dev, ret, ++ "Error requesting %s IRQ\n", irq_data->name); + } + + /* +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x-battery-Support-POWER_SUPPLY_PROP_CHARGE_BE.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x-battery-Support-POWER_SUPPLY_PROP_CHARGE_BE.patch new file mode 100644 index 000000000000..c5cd63b88fec --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x-battery-Support-POWER_SUPPLY_PROP_CHARGE_BE.patch @@ -0,0 +1,86 @@ +From 69e253126549eb8af47a0145651f4fc575b1332c Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sat, 2 Apr 2022 02:50:14 +0200 +Subject: power: supply: axp20x-battery: Support + POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR + +Support force disabling the charger in a more standardized way. + +Signed-off-by: Ondrej Jirman +--- + drivers/power/supply/axp20x_battery.c | 31 +++++++++++++++++++++++++++ + 1 file changed, 31 insertions(+) + +diff --git a/drivers/power/supply/axp20x_battery.c b/drivers/power/supply/axp20x_battery.c +index 988741b7d3be..1899af1810fd 100644 +--- a/drivers/power/supply/axp20x_battery.c ++++ b/drivers/power/supply/axp20x_battery.c +@@ -317,6 +317,19 @@ static int axp20x_battery_get_prop(struct power_supply *psy, + val->intval = !!(reg & AXP20X_PWR_OP_BATT_PRESENT); + break; + ++ case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: ++ ret = regmap_read(axp20x_batt->regmap, AXP20X_CHRG_CTRL1, ++ ®); ++ if (ret) ++ return ret; ++ ++ if (reg & AXP20X_CHRG_CTRL1_ENABLE) ++ val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO; ++ else ++ val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE; ++ ++ return 0; ++ + case POWER_SUPPLY_PROP_STATUS: + ret = regmap_read(axp20x_batt->regmap, AXP20X_PWR_OP_MODE, + ®); +@@ -819,6 +832,21 @@ static int axp20x_battery_set_prop(struct power_supply *psy, + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: + return axp20x_set_max_constant_charge_current(axp20x_batt, + val->intval); ++ case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: ++ switch (val->intval) { ++ case POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO: ++ return regmap_update_bits(axp20x_batt->regmap, ++ AXP20X_CHRG_CTRL1, ++ AXP20X_CHRG_CTRL1_ENABLE, ++ AXP20X_CHRG_CTRL1_ENABLE); ++ case POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE: ++ return regmap_update_bits(axp20x_batt->regmap, ++ AXP20X_CHRG_CTRL1, ++ AXP20X_CHRG_CTRL1_ENABLE, 0); ++ default: ++ return -EINVAL; ++ } ++ + case POWER_SUPPLY_PROP_STATUS: + switch (val->intval) { + case POWER_SUPPLY_STATUS_CHARGING: +@@ -876,6 +904,7 @@ static enum power_supply_property axp20x_battery_props[] = { + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_STATUS, ++ POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, +@@ -907,6 +936,7 @@ static int axp20x_battery_prop_writeable(struct power_supply *psy, + return psp == POWER_SUPPLY_PROP_STATUS || + psp == POWER_SUPPLY_PROP_VOLTAGE_MIN || + psp == POWER_SUPPLY_PROP_VOLTAGE_MAX || ++ psp == POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR || + psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT || + psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX; + } +@@ -928,6 +958,7 @@ static const struct power_supply_desc axp209_batt_ps_desc = { + .property_is_writeable = axp20x_battery_prop_writeable, + .get_property = axp20x_battery_get_prop, + .set_property = axp20x_battery_set_prop, ++ .charge_behaviours = BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO) | BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE), + }; + + static const struct power_supply_desc axp717_batt_ps_desc = { +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x-usb-power-Add-missing-interrupts.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x-usb-power-Add-missing-interrupts.patch new file mode 100644 index 000000000000..a7196f1c6a2e --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x-usb-power-Add-missing-interrupts.patch @@ -0,0 +1,59 @@ +From c5a7633f0da393dd7ba0d1ea7e7c205470c3c72d Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Fri, 15 Mar 2024 21:48:34 +0100 +Subject: power: supply: axp20x-usb-power: Add missing interrupts + +Notify power_supply_change on BC detection interrupts for the consumers +to get BC results ASAP. + +Signed-off-by: Ondrej Jirman +--- + drivers/mfd/axp20x.c | 2 ++ + drivers/power/supply/axp20x_usb_power.c | 11 +++++++++-- + 2 files changed, 11 insertions(+), 2 deletions(-) + +diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c +index 5da865469679..94651380ecdb 100644 +--- a/drivers/mfd/axp20x.c ++++ b/drivers/mfd/axp20x.c +@@ -347,6 +347,8 @@ static const struct resource axp717_usb_power_supply_resources[] = { + static const struct resource axp803_usb_power_supply_resources[] = { + DEFINE_RES_IRQ_NAMED(AXP803_IRQ_VBUS_PLUGIN, "VBUS_PLUGIN"), + DEFINE_RES_IRQ_NAMED(AXP803_IRQ_VBUS_REMOVAL, "VBUS_REMOVAL"), ++ DEFINE_RES_IRQ_NAMED(AXP803_IRQ_BC_USB_CHNG, "BC_USB_CHNG"), ++ DEFINE_RES_IRQ_NAMED(AXP803_IRQ_MV_CHNG, "MV_CHNG"), + }; + + static const struct resource axp22x_pek_resources[] = { +diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c +index b555ba9bf6ad..44cbd429cb91 100644 +--- a/drivers/power/supply/axp20x_usb_power.c ++++ b/drivers/power/supply/axp20x_usb_power.c +@@ -728,6 +728,13 @@ static const char * const axp717_irq_names[] = { + "VBUS_OVER_V", + }; + ++static const char * const axp813_irq_names[] = { ++ "VBUS_PLUGIN", ++ "VBUS_REMOVAL", ++ "BC_USB_CHNG", ++ "MV_CHNG", ++}; ++ + static int axp192_usb_curr_lim_table[] = { + -1, + -1, +@@ -831,8 +838,8 @@ static const struct axp_data axp717_data = { + + static const struct axp_data axp813_data = { + .power_desc = &axp813_usb_power_desc, +- .irq_names = axp22x_irq_names, +- .num_irq_names = ARRAY_SIZE(axp22x_irq_names), ++ .irq_names = axp813_irq_names, ++ .num_irq_names = ARRAY_SIZE(axp813_irq_names), + .curr_lim_table = axp813_usb_curr_lim_table, + .curr_lim_table_size = ARRAY_SIZE(axp813_usb_curr_lim_table), + .curr_lim_fld = REG_FIELD(AXP22X_CHRG_CTRL3, 4, 7), +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x-usb-power-Change-Vbus-hold-voltage-to-4.5V.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x-usb-power-Change-Vbus-hold-voltage-to-4.5V.patch new file mode 100644 index 000000000000..7ab6ac65ec69 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x-usb-power-Change-Vbus-hold-voltage-to-4.5V.patch @@ -0,0 +1,38 @@ +From 66616b654c38ff3cb0b34b6b676fa13425c40d4e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Mon, 14 Dec 2020 07:42:08 +0100 +Subject: power: supply: axp20x-usb-power: Change Vbus hold voltage to 4.5V + +This fixes USB-PD charger resets. + +Signed-off-by: Ondrej Jirman +--- + drivers/power/supply/axp20x_usb_power.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c +index 9722912268fe..b555ba9bf6ad 100644 +--- a/drivers/power/supply/axp20x_usb_power.c ++++ b/drivers/power/supply/axp20x_usb_power.c +@@ -1011,6 +1011,18 @@ static int axp20x_usb_power_probe(struct platform_device *pdev) + return ret; + } + ++ /*TODO: Re-work this into a supply property with OF based default value */ ++ if (of_machine_is_compatible("pine64,pinephone-1.2") > 0 || ++ of_machine_is_compatible("pine64,pinephone-1.1") > 0 || ++ of_machine_is_compatible("pine64,pinephone-1.0") > 0) { ++ ++ dev_info(&pdev->dev, "Increasing Vbus hold voltage to 4.5V\n"); ++ ++ ret = regmap_update_bits(axp20x->regmap, 0x30, 0x7 << 3, 0x5 << 3); ++ if (ret) ++ return ret; ++ } ++ + psy_cfg.of_node = pdev->dev.of_node; + psy_cfg.drv_data = power; + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x_battery-Add-support-for-reporting-OCV.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x_battery-Add-support-for-reporting-OCV.patch new file mode 100644 index 000000000000..b90a7396da0d --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x_battery-Add-support-for-reporting-OCV.patch @@ -0,0 +1,63 @@ +From 008e3cc97745fbba5e3273a5d9395d1791ff8df7 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Fri, 28 Feb 2020 19:16:46 +0100 +Subject: power: supply: axp20x_battery: Add support for reporting OCV + +Export OCV reported by PMIC via sysfs. + +Signed-off-by: Ondrej Jirman +--- + drivers/power/supply/axp20x_battery.c | 23 +++++++++++++++++++++++ + 1 file changed, 23 insertions(+) + +diff --git a/drivers/power/supply/axp20x_battery.c b/drivers/power/supply/axp20x_battery.c +index 96d2856ef318..f19ab493245b 100644 +--- a/drivers/power/supply/axp20x_battery.c ++++ b/drivers/power/supply/axp20x_battery.c +@@ -270,6 +270,25 @@ static int axp717_get_constant_charge_current(struct axp20x_batt_ps *axp, + return 0; + } + ++static int axp20x_get_ocv_voltage(struct axp20x_batt_ps *axp, int *val) ++{ ++ int ret; ++ unsigned int ocvh, ocvl, ocv; ++ ++ ret = regmap_read(axp->regmap, AXP288_FG_OCVH_REG, &ocvh); ++ if (ret) ++ return ret; ++ ++ ret = regmap_read(axp->regmap, AXP288_FG_OCVL_REG, &ocvl); ++ if (ret) ++ return ret; ++ ++ ocv = ocvh << 4 | (ocvl & 0xf); ++ ++ *val = ocv * 1100; ++ return 0; ++} ++ + static int axp20x_battery_get_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +@@ -368,6 +387,9 @@ static int axp20x_battery_get_prop(struct power_supply *psy, + + break; + ++ case POWER_SUPPLY_PROP_VOLTAGE_OCV: ++ return axp20x_get_ocv_voltage(axp20x_batt, &val->intval); ++ + case POWER_SUPPLY_PROP_CAPACITY: + /* When no battery is present, return capacity is 100% */ + ret = regmap_read(axp20x_batt->regmap, AXP20X_PWR_OP_MODE, +@@ -851,6 +873,7 @@ static enum power_supply_property axp20x_battery_props[] = { + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_VOLTAGE_MAX, + POWER_SUPPLY_PROP_VOLTAGE_MIN, ++ POWER_SUPPLY_PROP_VOLTAGE_OCV, + POWER_SUPPLY_PROP_CAPACITY, + }; + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x_battery-Fix-charging-done-detection.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x_battery-Fix-charging-done-detection.patch new file mode 100644 index 000000000000..b55a095a05be --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x_battery-Fix-charging-done-detection.patch @@ -0,0 +1,40 @@ +From 1159cc7534f19ea3fad016e3651cce77df029e6f Mon Sep 17 00:00:00 2001 +From: Samuel Holland +Date: Sat, 29 Feb 2020 00:55:09 -0600 +Subject: power: supply: axp20x_battery: Fix charging done detection + +Signed-off-by: Samuel Holland +--- + drivers/power/supply/axp20x_battery.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/drivers/power/supply/axp20x_battery.c b/drivers/power/supply/axp20x_battery.c +index 1bac1a934614..74a5d53d7cf2 100644 +--- a/drivers/power/supply/axp20x_battery.c ++++ b/drivers/power/supply/axp20x_battery.c +@@ -38,6 +38,7 @@ + #define AXP717_PWR_STATUS_BAT_CHRG 1 + #define AXP717_PWR_STATUS_BAT_DISCHRG 2 + ++#define AXP20X_PWR_OP_BATT_CHARGING BIT(6) + #define AXP20X_PWR_OP_BATT_PRESENT BIT(5) + #define AXP20X_PWR_OP_BATT_ACTIVATED BIT(3) + #define AXP717_PWR_OP_BATT_PRESENT BIT(3) +@@ -308,12 +309,12 @@ static int axp20x_battery_get_prop(struct power_supply *psy, + break; + + case POWER_SUPPLY_PROP_STATUS: +- ret = regmap_read(axp20x_batt->regmap, AXP20X_PWR_INPUT_STATUS, ++ ret = regmap_read(axp20x_batt->regmap, AXP20X_PWR_OP_MODE, + ®); + if (ret) + return ret; + +- if (reg & AXP20X_PWR_STATUS_BAT_CHARGING) { ++ if (reg & AXP20X_PWR_OP_BATT_CHARGING) { + val->intval = POWER_SUPPLY_STATUS_CHARGING; + return 0; + } +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x_battery-Monitor-battery-health.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x_battery-Monitor-battery-health.patch new file mode 100644 index 000000000000..f063c5ea9611 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x_battery-Monitor-battery-health.patch @@ -0,0 +1,117 @@ +From fad659e393bcf094adcd4cd5661a8c19bcb1bdfd Mon Sep 17 00:00:00 2001 +From: Samuel Holland +Date: Sat, 29 Feb 2020 01:04:33 -0600 +Subject: power: supply: axp20x_battery: Monitor battery health + +Signed-off-by: Samuel Holland +--- + drivers/power/supply/axp20x_battery.c | 58 ++++++++++++++++++++++++++- + 1 file changed, 56 insertions(+), 2 deletions(-) + +diff --git a/drivers/power/supply/axp20x_battery.c b/drivers/power/supply/axp20x_battery.c +index 1c4d5f2eac62..3a5f77219d83 100644 +--- a/drivers/power/supply/axp20x_battery.c ++++ b/drivers/power/supply/axp20x_battery.c +@@ -123,6 +123,7 @@ struct axp20x_batt_ps { + struct iio_channel *batt_chrg_i; + struct iio_channel *batt_dischrg_i; + struct iio_channel *batt_v; ++ int health; + /* Maximum constant charge current */ + unsigned int max_ccc; + const struct axp_data *data; +@@ -362,7 +363,7 @@ static int axp20x_battery_get_prop(struct power_supply *psy, + return 0; + } + +- val->intval = POWER_SUPPLY_HEALTH_GOOD; ++ val->intval = axp20x_batt->health; + break; + + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: +@@ -1047,6 +1048,39 @@ static irqreturn_t axp20x_battery_changed_irq(int irq, void *devid) + return IRQ_HANDLED; + } + ++static irqreturn_t axp20x_battery_temp_cold_irq(int irq, void *devid) ++{ ++ struct axp20x_batt_ps *axp20x_batt = devid; ++ ++ axp20x_batt->health = POWER_SUPPLY_HEALTH_COLD; ++ ++ power_supply_changed(axp20x_batt->batt); ++ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t axp20x_battery_temp_hot_irq(int irq, void *devid) ++{ ++ struct axp20x_batt_ps *axp20x_batt = devid; ++ ++ axp20x_batt->health = POWER_SUPPLY_HEALTH_OVERHEAT; ++ ++ power_supply_changed(axp20x_batt->batt); ++ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t axp20x_battery_temp_normal_irq(int irq, void *devid) ++{ ++ struct axp20x_batt_ps *axp20x_batt = devid; ++ ++ axp20x_batt->health = POWER_SUPPLY_HEALTH_GOOD; ++ ++ power_supply_changed(axp20x_batt->batt); ++ ++ return IRQ_HANDLED; ++} ++ + static const struct axp_irq_data axp20x_irqs[] = { + { "BATT_PLUGIN", axp20x_battery_changed_irq }, + { "BATT_REMOVAL", axp20x_battery_changed_irq }, +@@ -1057,6 +1091,24 @@ static const struct axp_irq_data axp20x_irqs[] = { + {} + }; + ++static const struct axp_irq_data axp813_irqs[] = { ++ { "BATT_PLUGIN", axp20x_battery_changed_irq }, ++ { "BATT_REMOVAL", axp20x_battery_changed_irq }, ++ { "BATT_HEALTH_DEAD", axp20x_battery_changed_irq }, ++ { "BATT_HEALTH_GOOD", axp20x_battery_changed_irq }, ++ { "BATT_CHARGING", axp20x_battery_changed_irq }, ++ { "BATT_CHARGING_DONE", axp20x_battery_changed_irq }, ++ { "BATT_CHG_TEMP_HIGH", axp20x_battery_temp_hot_irq }, ++ { "BATT_CHG_TEMP_HIGH_END", axp20x_battery_temp_normal_irq }, ++ { "BATT_CHG_TEMP_LOW", axp20x_battery_temp_cold_irq }, ++ { "BATT_CHG_TEMP_LOW_END", axp20x_battery_temp_normal_irq }, ++ { "BATT_ACT_TEMP_HIGH", axp20x_battery_temp_hot_irq }, ++ { "BATT_ACT_TEMP_HIGH_END", axp20x_battery_temp_normal_irq }, ++ { "BATT_ACT_TEMP_LOW", axp20x_battery_temp_cold_irq }, ++ { "BATT_ACT_TEMP_LOW_END", axp20x_battery_temp_normal_irq }, ++ {} ++}; ++ + static const struct axp_data axp209_data = { + .ccc_scale = 100000, + .ccc_offset = 300000, +@@ -1108,7 +1160,7 @@ static const struct axp_data axp813_data = { + .set_max_voltage = axp20x_battery_set_max_voltage, + .cfg_iio_chan = axp209_bat_cfg_iio_channels, + .set_bat_info = axp209_set_battery_info, +- .irqs = axp20x_irqs, ++ .irqs = axp813_irqs, + }; + + static const struct of_device_id axp20x_battery_ps_id[] = { +@@ -1169,6 +1221,8 @@ static int axp20x_power_probe(struct platform_device *pdev) + return PTR_ERR(axp20x_batt->batt); + } + ++ axp20x_batt->health = POWER_SUPPLY_HEALTH_GOOD; ++ + if (!power_supply_get_battery_info(axp20x_batt->batt, &info)) { + axp20x_batt->data->set_bat_info(pdev, axp20x_batt, info); + power_supply_put_battery_info(axp20x_batt->batt, info); +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x_battery-Send-uevents-for-status-changes.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x_battery-Send-uevents-for-status-changes.patch new file mode 100644 index 000000000000..12f0738a1d37 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x_battery-Send-uevents-for-status-changes.patch @@ -0,0 +1,149 @@ +From 3370f271bac47c366689c2372d201bc48cb99b15 Mon Sep 17 00:00:00 2001 +From: Samuel Holland +Date: Sat, 29 Feb 2020 01:04:17 -0600 +Subject: power: supply: axp20x_battery: Send uevents for status changes + +Signed-off-by: Samuel Holland +--- + drivers/power/supply/axp20x_battery.c | 56 ++++++++++++++++++++++++++- + 1 file changed, 54 insertions(+), 2 deletions(-) + +diff --git a/drivers/power/supply/axp20x_battery.c b/drivers/power/supply/axp20x_battery.c +index 74a5d53d7cf2..1c4d5f2eac62 100644 +--- a/drivers/power/supply/axp20x_battery.c ++++ b/drivers/power/supply/axp20x_battery.c +@@ -90,8 +90,15 @@ + #define AXP717_BAT_CC_MIN_UA 0 + #define AXP717_BAT_CC_MAX_UA 3008000 + ++#define DRVNAME "axp20x-battery-power-supply" ++ + struct axp20x_batt_ps; + ++struct axp_irq_data { ++ const char *name; ++ irq_handler_t handler; ++}; ++ + struct axp_data { + int ccc_scale; + int ccc_offset; +@@ -106,6 +113,7 @@ struct axp_data { + void (*set_bat_info)(struct platform_device *pdev, + struct axp20x_batt_ps *axp_batt, + struct power_supply_battery_info *info); ++ const struct axp_irq_data *irqs; + }; + + struct axp20x_batt_ps { +@@ -1030,6 +1038,25 @@ static void axp717_set_battery_info(struct platform_device *pdev, + } + } + ++static irqreturn_t axp20x_battery_changed_irq(int irq, void *devid) ++{ ++ struct axp20x_batt_ps *axp20x_batt = devid; ++ ++ power_supply_changed(axp20x_batt->batt); ++ ++ return IRQ_HANDLED; ++} ++ ++static const struct axp_irq_data axp20x_irqs[] = { ++ { "BATT_PLUGIN", axp20x_battery_changed_irq }, ++ { "BATT_REMOVAL", axp20x_battery_changed_irq }, ++ { "BATT_HEALTH_DEAD", axp20x_battery_changed_irq }, ++ { "BATT_HEALTH_GOOD", axp20x_battery_changed_irq }, ++ { "BATT_CHARGING", axp20x_battery_changed_irq }, ++ { "BATT_CHARGING_DONE", axp20x_battery_changed_irq }, ++ {} ++}; ++ + static const struct axp_data axp209_data = { + .ccc_scale = 100000, + .ccc_offset = 300000, +@@ -1040,6 +1067,7 @@ static const struct axp_data axp209_data = { + .set_max_voltage = axp20x_battery_set_max_voltage, + .cfg_iio_chan = axp209_bat_cfg_iio_channels, + .set_bat_info = axp209_set_battery_info, ++ .irqs = axp20x_irqs, + }; + + static const struct axp_data axp221_data = { +@@ -1053,6 +1081,7 @@ static const struct axp_data axp221_data = { + .set_max_voltage = axp22x_battery_set_max_voltage, + .cfg_iio_chan = axp209_bat_cfg_iio_channels, + .set_bat_info = axp209_set_battery_info, ++ .irqs = axp20x_irqs, + }; + + static const struct axp_data axp717_data = { +@@ -1065,6 +1094,7 @@ static const struct axp_data axp717_data = { + .set_max_voltage = axp717_battery_set_max_voltage, + .cfg_iio_chan = axp717_bat_cfg_iio_channels, + .set_bat_info = axp717_set_battery_info, ++ .irqs = axp20x_irqs, + }; + + static const struct axp_data axp813_data = { +@@ -1078,6 +1108,7 @@ static const struct axp_data axp813_data = { + .set_max_voltage = axp20x_battery_set_max_voltage, + .cfg_iio_chan = axp209_bat_cfg_iio_channels, + .set_bat_info = axp209_set_battery_info, ++ .irqs = axp20x_irqs, + }; + + static const struct of_device_id axp20x_battery_ps_id[] = { +@@ -1099,11 +1130,13 @@ MODULE_DEVICE_TABLE(of, axp20x_battery_ps_id); + + static int axp20x_power_probe(struct platform_device *pdev) + { ++ struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); + struct axp20x_batt_ps *axp20x_batt; + struct power_supply_config psy_cfg = {}; + struct power_supply_battery_info *info; + struct device *dev = &pdev->dev; +- int ret; ++ const struct axp_irq_data *irq_data; ++ int irq, ret; + + if (!of_device_is_available(pdev->dev.of_node)) + return -ENODEV; +@@ -1141,6 +1174,25 @@ static int axp20x_power_probe(struct platform_device *pdev) + power_supply_put_battery_info(axp20x_batt->batt, info); + } + ++ /* Request irqs after registering, as irqs may trigger immediately */ ++ for (irq_data = axp20x_batt->data->irqs; irq_data->name; irq_data++) { ++ irq = platform_get_irq_byname(pdev, irq_data->name); ++ if (irq < 0) { ++ dev_err(&pdev->dev, "No IRQ for %s: %d\n", ++ irq_data->name, irq); ++ return irq; ++ } ++ irq = regmap_irq_get_virq(axp20x->regmap_irqc, irq); ++ ret = devm_request_any_context_irq(&pdev->dev, irq, ++ irq_data->handler, 0, ++ DRVNAME, axp20x_batt); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "Error requesting %s IRQ: %d\n", ++ irq_data->name, ret); ++ return ret; ++ } ++ } ++ + /* + * Update max CCC to a valid value if battery info is present or set it + * to current register value by default. +@@ -1206,7 +1258,7 @@ static int axp20x_power_probe(struct platform_device *pdev) + static struct platform_driver axp20x_batt_driver = { + .probe = axp20x_power_probe, + .driver = { +- .name = "axp20x-battery-power-supply", ++ .name = DRVNAME, + .of_match_table = axp20x_battery_ps_id, + }, + }; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x_battery-Setup-thermal-regulation-experiment.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x_battery-Setup-thermal-regulation-experiment.patch new file mode 100644 index 000000000000..6886f02f3223 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/power-supply-axp20x_battery-Setup-thermal-regulation-experiment.patch @@ -0,0 +1,82 @@ +From c402714c643950ffe020804c98bb8e23e31ae050 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Wed, 23 Sep 2020 14:59:41 +0200 +Subject: power: supply: axp20x_battery: Setup thermal regulation + (experimental) + +Values for NTC resistances were pulled out of thin air! + +None of this is tested to actually work. + +Signed-off-by: Ondrej Jirman +--- + drivers/power/supply/axp20x_battery.c | 53 +++++++++++++++++++++++++++ + 1 file changed, 53 insertions(+) + +diff --git a/drivers/power/supply/axp20x_battery.c b/drivers/power/supply/axp20x_battery.c +index f19ab493245b..1bac1a934614 100644 +--- a/drivers/power/supply/axp20x_battery.c ++++ b/drivers/power/supply/axp20x_battery.c +@@ -1146,6 +1146,59 @@ static int axp20x_power_probe(struct platform_device *pdev) + */ + axp20x_get_constant_charge_current(axp20x_batt, &axp20x_batt->max_ccc); + ++ if (of_machine_is_compatible("pine64,pinephone-1.2") > 0 || ++ of_machine_is_compatible("pine64,pinephone-1.1") > 0 || ++ of_machine_is_compatible("pine64,pinephone-1.0") > 0) { ++ // 3kOhm NTC inside PinePhone batery ++ // --------------------------------- ++ // ++ // Charging: ++ // 0 - 15 °C: Max 0.2C CC to 4.35V : 9750 Ohm - 4710 Ohm ++ // 15 - 50 °C: Max 0.5C CC to 4.35V : 4710 Ohm - 1080 Ohm ++ // ++ // Discharging: ++ // -10 °C : 16500 Ohm ++ // 55 °C : 896 Ohm ++ // enable TS pin input to ADC ++ ++ dev_info(dev, "Configuring battery thermal regulation for Pinephone\n"); ++ ++ ret = regmap_update_bits(axp20x_batt->regmap, 0x82, BIT(0), BIT(0)); ++ if (ret) ++ goto warn_bat; ++ ++ // safety thresholds: ++ ++ // voltage = reg_val * 12800 uV (range is 0 - 3.264V) ++ ret = regmap_write(axp20x_batt->regmap, 0x38, 9750 * 80 / 12800); // V_LTF-charge ++ if (ret) ++ goto warn_bat; ++ ++ ret = regmap_write(axp20x_batt->regmap, 0x39, 1080 * 80 / 12800); // V_HTF-charge ++ if (ret) ++ goto warn_bat; ++ ++ ret = regmap_write(axp20x_batt->regmap, 0x3c, 16500 * 80 / 12800); // V_LTF-work ++ if (ret) ++ goto warn_bat; ++ ++ ret = regmap_write(axp20x_batt->regmap, 0x3d, 896 * 80 / 12800); // V_HTF-work ++ if (ret) ++ goto warn_bat; ++ ++ // There is a hysteresis of 460.8 mV(refer to TS pin voltage) for UTP ++ // threshold, and there is a hysteresis of 57.6 mV for OTP threshold. ++ ++ // use TS pin only when charging, make it affect the charger, I = 80uA ++ ret = regmap_update_bits(axp20x_batt->regmap, 0x84, 0x37, 0x31); ++ if (ret) ++ goto warn_bat; ++ } ++ ++ return 0; ++ ++warn_bat: ++ dev_err(dev, "Failed to configure battery thermal regulation\n"); + return 0; + } + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/regulator-Add-simple-driver-for-enabling-a-regulator-from-users.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/regulator-Add-simple-driver-for-enabling-a-regulator-from-users.patch new file mode 100644 index 000000000000..35711ff4fbdb --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/regulator-Add-simple-driver-for-enabling-a-regulator-from-users.patch @@ -0,0 +1,192 @@ +From b6d9a904952e90d848cb7dc88a4f1e2a9ae8b438 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sat, 5 Oct 2019 15:10:11 +0200 +Subject: regulator: Add simple driver for enabling a regulator from userspace + +This is just like userspace-consumer, but can be used from device +tree. + +Signed-off-by: Ondrej Jirman +--- + drivers/regulator/Kconfig | 9 ++ + drivers/regulator/Makefile | 1 + + drivers/regulator/userspace-consumer-of.c | 135 ++++++++++++++++++++++ + 3 files changed, 145 insertions(+) + create mode 100644 drivers/regulator/userspace-consumer-of.c + +diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig +index 39297f7d8177..db7f70e9a015 100644 +--- a/drivers/regulator/Kconfig ++++ b/drivers/regulator/Kconfig +@@ -66,6 +66,15 @@ config REGULATOR_NETLINK_EVENTS + + If unsure, say no. + ++config REGULATOR_USERSPACE_CONSUMER_OF ++ tristate "Userspace regulator consumer support (OF)" ++ help ++ There are some classes of devices that are controlled entirely ++ from user space. Userspace consumer driver provides ability to ++ control power supplies for such devices. ++ ++ If unsure, say no. ++ + config REGULATOR_88PG86X + tristate "Marvell 88PG86X voltage regulators" + depends on I2C +diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile +index 3d5a803dce8a..267a09e14295 100644 +--- a/drivers/regulator/Makefile ++++ b/drivers/regulator/Makefile +@@ -10,6 +10,7 @@ obj-$(CONFIG_OF) += of_regulator.o + obj-$(CONFIG_REGULATOR_FIXED_VOLTAGE) += fixed.o + obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o + obj-$(CONFIG_REGULATOR_USERSPACE_CONSUMER) += userspace-consumer.o ++obj-$(CONFIG_REGULATOR_USERSPACE_CONSUMER_OF) += userspace-consumer-of.o + + obj-$(CONFIG_REGULATOR_88PG86X) += 88pg86x.o + obj-$(CONFIG_REGULATOR_88PM800) += 88pm800-regulator.o +diff --git a/drivers/regulator/userspace-consumer-of.c b/drivers/regulator/userspace-consumer-of.c +new file mode 100644 +index 000000000000..bcc9433c352d +--- /dev/null ++++ b/drivers/regulator/userspace-consumer-of.c +@@ -0,0 +1,135 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct userspace_consumer_data { ++ struct mutex lock; ++ bool enabled; ++ struct regulator *supply; ++}; ++ ++static ssize_t reg_show_state(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct userspace_consumer_data *data = dev_get_drvdata(dev); ++ ++ if (data->enabled) ++ return sprintf(buf, "enabled\n"); ++ ++ return sprintf(buf, "disabled\n"); ++} ++ ++static ssize_t reg_set_state(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct userspace_consumer_data *data = dev_get_drvdata(dev); ++ bool enabled; ++ int ret; ++ ++ /* ++ * sysfs_streq() doesn't need the \n's, but we add them so the strings ++ * will be shared with show_state(), above. ++ */ ++ if (sysfs_streq(buf, "enabled\n") || sysfs_streq(buf, "1")) ++ enabled = true; ++ else if (sysfs_streq(buf, "disabled\n") || sysfs_streq(buf, "0")) ++ enabled = false; ++ else { ++ dev_err(dev, "Configuring invalid mode\n"); ++ return count; ++ } ++ ++ mutex_lock(&data->lock); ++ if (enabled != data->enabled) { ++ if (enabled) ++ ret = regulator_enable(data->supply); ++ else ++ ret = regulator_disable(data->supply); ++ ++ if (ret == 0) ++ data->enabled = enabled; ++ else ++ dev_err(dev, "Failed to configure state: %d\n", ret); ++ } ++ mutex_unlock(&data->lock); ++ ++ return count; ++} ++ ++static DEVICE_ATTR(state, 0644, reg_show_state, reg_set_state); ++ ++static struct attribute *attributes[] = { ++ &dev_attr_state.attr, ++ NULL, ++}; ++ ++static const struct attribute_group attr_group = { ++ .attrs = attributes, ++}; ++ ++static int regulator_userspace_consumer_probe(struct platform_device *pdev) ++{ ++ struct userspace_consumer_data *drvdata; ++ int ret; ++ ++ drvdata = devm_kzalloc(&pdev->dev, ++ sizeof(struct userspace_consumer_data), ++ GFP_KERNEL); ++ if (drvdata == NULL) ++ return -ENOMEM; ++ ++ mutex_init(&drvdata->lock); ++ ++ drvdata->supply = devm_regulator_get(&pdev->dev, "controlled"); ++ if (IS_ERR(drvdata->supply)) { ++ ret = PTR_ERR(drvdata->supply); ++ if (ret != -EPROBE_DEFER) ++ dev_err(&pdev->dev, "Failed to get supply: %d\n", ret); ++ return ret; ++ } ++ ++ ret = sysfs_create_group(&pdev->dev.kobj, &attr_group); ++ if (ret != 0) ++ return ret; ++ ++ platform_set_drvdata(pdev, drvdata); ++ ++ return 0; ++} ++ ++static void regulator_userspace_consumer_remove(struct platform_device *pdev) ++{ ++ struct userspace_consumer_data *data = platform_get_drvdata(pdev); ++ ++ sysfs_remove_group(&pdev->dev.kobj, &attr_group); ++ ++ if (data->enabled) ++ regulator_disable(data->supply); ++} ++ ++static const struct of_device_id ids_of_match[] = { ++ { .compatible = "custom,reg-userspace-consumer", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, ids_of_match); ++ ++static struct platform_driver regulator_userspace_consumer_driver = { ++ .probe = regulator_userspace_consumer_probe, ++ .remove = regulator_userspace_consumer_remove, ++ .driver = { ++ .name = "reg-userspace-consumer-of", ++ .of_match_table = of_match_ptr(ids_of_match), ++ }, ++}; ++ ++module_platform_driver(regulator_userspace_consumer_driver); ++ ++MODULE_AUTHOR("Mike Rapoport "); ++MODULE_DESCRIPTION("Userspace consumer for voltage and current regulators"); ++MODULE_LICENSE("GPL"); +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/regulator-axp20x-Add-support-for-vin-supply-for-drivevbus.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/regulator-axp20x-Add-support-for-vin-supply-for-drivevbus.patch new file mode 100644 index 000000000000..968e1a579f32 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/regulator-axp20x-Add-support-for-vin-supply-for-drivevbus.patch @@ -0,0 +1,61 @@ +From 8a7a8e2c300da9d317bc12cad53e4f495978b476 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Mon, 8 Jun 2020 00:15:04 +0200 +Subject: regulator: axp20x: Add support for vin-supply for drivevbus + +When drivevbus is used to control some regulator via the N_VBUSEN +pin of the PMIC, this regulator may have a source supply. Allow to +specify the supply using the vin-supply property. + +Signed-off-by: Ondrej Jirman +--- + drivers/regulator/axp20x-regulator.c | 30 +++++++++++++++++++++++++--- + 1 file changed, 27 insertions(+), 3 deletions(-) + +diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c +index dca99cfb7cbb..0f2480316d5a 100644 +--- a/drivers/regulator/axp20x-regulator.c ++++ b/drivers/regulator/axp20x-regulator.c +@@ -1734,12 +1734,36 @@ static int axp20x_regulator_probe(struct platform_device *pdev) + } + + if (drivevbus) { ++ struct regulator_desc *new_desc; ++ bool drivevbus_vin = false; ++ struct device_node *nr, *nd; ++ ++ nr = of_get_child_by_name(pdev->dev.parent->of_node, "regulators"); ++ if (nr) { ++ nd = of_get_child_by_name(nr, "drivevbus"); ++ if (nd) { ++ drivevbus_vin = !!of_find_property(nd, "vin-supply", NULL); ++ of_node_put(nd); ++ } ++ ++ of_node_put(nr); ++ } ++ ++ new_desc = devm_kzalloc(&pdev->dev, sizeof(*new_desc), GFP_KERNEL); ++ if (!new_desc) ++ return -ENOMEM; ++ ++ *new_desc = axp22x_drivevbus_regulator; ++ ++ if (drivevbus_vin) { ++ new_desc->supply_name = "vin"; ++ dev_info(&pdev->dev, "drivevbus has vin\n"); ++ } ++ + /* Change N_VBUSEN sense pin to DRIVEVBUS output pin */ + regmap_update_bits(axp20x->regmap, AXP20X_OVER_TMP, + AXP22X_MISC_N_VBUSEN_FUNC, 0); +- rdev = devm_regulator_register(&pdev->dev, +- &axp22x_drivevbus_regulator, +- &config); ++ rdev = devm_regulator_register(&pdev->dev, new_desc, &config); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, "Failed to register drivevbus\n"); + return PTR_ERR(rdev); +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/regulator-axp20x-Enable-over-temperature-protection-and-16s-res.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/regulator-axp20x-Enable-over-temperature-protection-and-16s-res.patch new file mode 100644 index 000000000000..6a74bb5bfdc6 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/regulator-axp20x-Enable-over-temperature-protection-and-16s-res.patch @@ -0,0 +1,41 @@ +From 3071728a895acee0ad98869a26eac2d25376718d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Tue, 18 Feb 2020 23:57:20 +0100 +Subject: regulator: axp20x: Enable over-temperature protection and 16s reset + function + +Why not? Also why here, but whatever. + +Signed-off-by: Ondrej Jirman +--- + drivers/regulator/axp20x-regulator.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c +index f304d393f5e0..95bb3ffd76b9 100644 +--- a/drivers/regulator/axp20x-regulator.c ++++ b/drivers/regulator/axp20x-regulator.c +@@ -92,6 +92,8 @@ + #define AXP22X_WORKMODE_DCDCX_MASK(x) BIT_MASK(x) + + #define AXP22X_MISC_N_VBUSEN_FUNC BIT(4) ++#define AXP22X_MISC_16S_RESET_FUNC BIT(3) ++#define AXP22X_MISC_OTP BIT(2) + + #define AXP22X_DCDC1_V_OUT_MASK GENMASK(4, 0) + #define AXP22X_DCDC2_V_OUT_MASK GENMASK(5, 0) +@@ -1778,6 +1780,11 @@ static int axp20x_regulator_probe(struct platform_device *pdev) + } + } + ++ // enable 16s power-on reset and over-temperature protection ++ regmap_update_bits(axp20x->regmap, AXP20X_OVER_TMP, ++ AXP22X_MISC_16S_RESET_FUNC | AXP22X_MISC_OTP, ++ AXP22X_MISC_16S_RESET_FUNC | AXP22X_MISC_OTP); ++ + return 0; + } + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/regulator-axp20x-Turn-N_VBUSEN-to-input-on-x-powers-sense-vbus-.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/regulator-axp20x-Turn-N_VBUSEN-to-input-on-x-powers-sense-vbus-.patch new file mode 100644 index 000000000000..64cc3bce3267 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/regulator-axp20x-Turn-N_VBUSEN-to-input-on-x-powers-sense-vbus-.patch @@ -0,0 +1,53 @@ +From 161f933ab81767414c88de145df9220a8a66ae2e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Tue, 9 Jun 2020 22:00:36 +0200 +Subject: regulator: axp20x: Turn N_VBUSEN to input on x-powers,sense-vbus-en + +When x-powers,sense-vbus-en is present on the PMIC DT node, force +the N_VBUSEN to be an input. This is necessary on boards that +have N_VBUSEN connected to VBUS enabling switch to make PMIC +not charge when the board is generating VBUS voltage itself. + +Signed-off-by: Ondrej Jirman +--- + .../arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.2.dts | 4 ++++ + drivers/regulator/axp20x-regulator.c | 8 ++++++++ + 2 files changed, 12 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.2.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.2.dts +index c4e3547e02e9..ec4927dfe9dc 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.2.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.2.dts +@@ -15,6 +15,10 @@ wifi_pwrseq: wifi-pwrseq { + }; + }; + ++&axp803 { ++ x-powers,sense-vbus-en; ++}; ++ + &backlight { + power-supply = <®_ldo_io0>; + lth-brightness = <10>; +diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c +index 0f2480316d5a..f304d393f5e0 100644 +--- a/drivers/regulator/axp20x-regulator.c ++++ b/drivers/regulator/axp20x-regulator.c +@@ -1733,6 +1733,14 @@ static int axp20x_regulator_probe(struct platform_device *pdev) + &aldo1_name); + } + ++ if (!drivevbus && ++ of_property_read_bool(pdev->dev.parent->of_node, "x-powers,sense-vbus-en")) { ++ /* make N_VBUSEN an input */ ++ regmap_update_bits(axp20x->regmap, AXP20X_OVER_TMP, ++ AXP22X_MISC_N_VBUSEN_FUNC, ++ AXP22X_MISC_N_VBUSEN_FUNC); ++ } ++ + if (drivevbus) { + struct regulator_desc *new_desc; + bool drivevbus_vin = false; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/regulator-tp65185-Add-hwmon-device-for-reading-temperature.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/regulator-tp65185-Add-hwmon-device-for-reading-temperature.patch new file mode 100644 index 000000000000..11e8579f6fdf --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/regulator-tp65185-Add-hwmon-device-for-reading-temperature.patch @@ -0,0 +1,235 @@ +From 6a9067668d70903cdeb7420f6e759c0f175390bb Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Thu, 17 Oct 2019 23:23:17 +0200 +Subject: regulator: tp65185: Add hwmon device for reading temperature + +Temperature for the display waveforms compensation is read +via a special NTC measurement hardware inside the tp65185x. + +Signed-off-by: Ondrej Jirman +--- + drivers/regulator/tp65185x.c | 134 +++++++++++++++++++++++++++++++++-- + 1 file changed, 127 insertions(+), 7 deletions(-) + +diff --git a/drivers/regulator/tp65185x.c b/drivers/regulator/tp65185x.c +index 8b57a11ff2f9..08245b50e334 100644 +--- a/drivers/regulator/tp65185x.c ++++ b/drivers/regulator/tp65185x.c +@@ -7,10 +7,12 @@ + #include + #include + #include ++#include + #include + #include + #include + #include ++#include + #include + + #define REG_TMST_VALUE 0x00 +@@ -31,6 +33,9 @@ + #define REG_PG 0x0F + #define REG_REVID 0x10 + ++#define REG_TMST1_READ_THERM 0x80 ++#define REG_TMST1_CONV_END 0x20 ++ + struct tp65185x { + struct device* dev; + struct gpio_desc* wakeup_gpio; +@@ -39,6 +44,8 @@ struct tp65185x { + struct gpio_desc* powergood_gpio; + struct regmap *regmap; + bool is_enabled; ++ struct mutex wakeup_mutex; ++ int wake_refs; + int vcom; + }; + +@@ -192,6 +199,30 @@ static int wait_for_power_good(struct tp65185x *tp) + return -ETIMEDOUT; + } + ++static void wakeup_regulator(struct tp65185x *tp, int wake) ++{ ++ mutex_lock(&tp->wakeup_mutex); ++ ++ if (wake) { ++ tp->wake_refs++; ++ if (tp->wake_refs > 1) ++ goto out_unlock; ++ ++ gpiod_set_value(tp->wakeup_gpio, 1); ++ usleep_range(3000, 4000); ++ } else { ++ tp->wake_refs--; ++ if (tp->wake_refs > 0) ++ goto out_unlock; ++ ++ gpiod_set_value(tp->wakeup_gpio, 0); ++ usleep_range(100, 200); ++ } ++ ++out_unlock: ++ mutex_unlock(&tp->wakeup_mutex); ++} ++ + static int enable_supply(struct regulator_dev *rdev) + { + struct tp65185x* tp = rdev_get_drvdata(rdev); +@@ -200,9 +231,7 @@ static int enable_supply(struct regulator_dev *rdev) + if (tp->is_enabled) + return 0; + +- // wake up the regulator +- gpiod_set_value(tp->wakeup_gpio, 1); +- usleep_range(3000, 4000); ++ wakeup_regulator(tp, 1); + + ret = apply_voltage(tp, tp->vcom); + if (ret) { +@@ -247,7 +276,7 @@ static int enable_supply(struct regulator_dev *rdev) + usleep_range(2000, 3000); + gpiod_set_value(tp->powerup_gpio, 0); + msleep(100); +- gpiod_set_value(tp->wakeup_gpio, 0); ++ wakeup_regulator(tp, 0); + return ret; + } + +@@ -273,7 +302,7 @@ static int disable_supply(struct regulator_dev *rdev) + show_power_status(tp, "power down"); + + // this will power down the V3P3 switch too +- gpiod_set_value(tp->wakeup_gpio, 0); ++ wakeup_regulator(tp, 0); + + tp->is_enabled = false; + +@@ -309,8 +338,85 @@ static const struct regulator_desc tp65185x_reg = { + .owner = THIS_MODULE, + }; + +-static int tp65185x_i2c_probe(struct i2c_client *i2c, +- const struct i2c_device_id *id) ++static int tp65185x_ntc_read_temperature(struct tp65185x* tp, long *val) ++{ ++ int ret; ++ unsigned int reg; ++ ++ wakeup_regulator(tp, 1); ++ ++ ret = regmap_update_bits(tp->regmap, REG_TMST1, ++ REG_TMST1_READ_THERM, ++ REG_TMST1_READ_THERM); ++ if (ret) ++ goto err_sleep; ++ ++ ret = regmap_read_poll_timeout(tp->regmap, REG_TMST1, reg, ++ reg & REG_TMST1_CONV_END, ++ 2000, 100000); ++ if (ret) ++ goto err_sleep; ++ ++ ret = regmap_read(tp->regmap, REG_TMST_VALUE, ®); ++ if (ret) ++ goto err_sleep; ++ ++ *val = (s8)(u8)reg; ++ ++err_sleep: ++ wakeup_regulator(tp, 0); ++ return ret; ++} ++ ++static int tp65185x_ntc_read(struct device *dev, enum hwmon_sensor_types type, ++ u32 attr, int channel, long *val) ++{ ++ struct tp65185x *tp = dev_get_drvdata(dev); ++ ++ if (type == hwmon_temp && attr == hwmon_temp_input) { ++ return tp65185x_ntc_read_temperature(tp, val); ++ } else if (type == hwmon_temp && attr == hwmon_temp_type) { ++ *val = 4; ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++ ++static umode_t tp65185x_ntc_is_visible(const void *data, ++ enum hwmon_sensor_types type, ++ u32 attr, int channel) ++{ ++ if (type == hwmon_temp) { ++ switch (attr) { ++ case hwmon_temp_input: ++ case hwmon_temp_type: ++ return 0444; ++ default: ++ break; ++ } ++ } ++ ++ return 0; ++} ++ ++static const struct hwmon_channel_info *tp65185x_ntc_info[] = { ++ HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), ++ HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_TYPE), ++ NULL ++}; ++ ++static const struct hwmon_ops tp65185x_ntc_hwmon_ops = { ++ .is_visible = tp65185x_ntc_is_visible, ++ .read = tp65185x_ntc_read, ++}; ++ ++static const struct hwmon_chip_info tp65185x_ntc_chip_info = { ++ .ops = &tp65185x_ntc_hwmon_ops, ++ .info = tp65185x_ntc_info, ++}; ++ ++static int tp65185x_i2c_probe(struct i2c_client *i2c) + { + struct device *dev = &i2c->dev; + struct regulator_dev *rdev; +@@ -318,6 +424,7 @@ static int tp65185x_i2c_probe(struct i2c_client *i2c, + unsigned int reg; + struct tp65185x* tp; + const char* rev = NULL; ++ struct device *hwmon_dev; + int ret; + + tp = devm_kzalloc(dev, sizeof(*tp), GFP_KERNEL); +@@ -325,6 +432,7 @@ static int tp65185x_i2c_probe(struct i2c_client *i2c, + return -ENOMEM; + + tp->dev = dev; ++ mutex_init(&tp->wakeup_mutex); + + tp->powergood_gpio = devm_gpiod_get(dev, "powergood", GPIOD_IN); + if (IS_ERR(tp->powergood_gpio)) { +@@ -404,6 +512,18 @@ static int tp65185x_i2c_probe(struct i2c_client *i2c, + return ret; + } + ++ hwmon_dev = devm_hwmon_device_register_with_info(&i2c->dev, ++ "tps65185", ++ tp, ++ &tp65185x_ntc_chip_info, ++ NULL); ++ if (IS_ERR(hwmon_dev)) { ++ ret = PTR_ERR(hwmon_dev); ++ dev_err(dev, "unable to register tmst as hwmon device (%d)\n", ++ ret); ++ return ret; ++ } ++ + return 0; + } + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/regulator-tp65185x-Add-tp65185x-eInk-panel-regulator-driver.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/regulator-tp65185x-Add-tp65185x-eInk-panel-regulator-driver.patch new file mode 100644 index 000000000000..6a69fc03c784 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/regulator-tp65185x-Add-tp65185x-eInk-panel-regulator-driver.patch @@ -0,0 +1,487 @@ +From 654021b95149522cd06ce47f7378a5ca0025aef4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sat, 5 Oct 2019 15:12:51 +0200 +Subject: regulator: tp65185x: Add tp65185x eInk panel regulator driver + +This is a very simple driver that doesn't try to expose individual +regulators, and implements the specific power sequencing necessary +for the eInk panel power up directly. + +All the 6 regulators are thus represented as one logical supply. +VCOM output is the only voltage that needs setting, and it can +be changed by setting the voltage of the regulator exported by +the driver. + +Signed-off-by: Ondrej Jirman +--- + drivers/regulator/Kconfig | 8 + + drivers/regulator/Makefile | 1 + + drivers/regulator/tp65185x.c | 428 +++++++++++++++++++++++++++++++++++ + 3 files changed, 437 insertions(+) + create mode 100644 drivers/regulator/tp65185x.c + +diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig +index db7f70e9a015..887d8a78a1ba 100644 +--- a/drivers/regulator/Kconfig ++++ b/drivers/regulator/Kconfig +@@ -1729,4 +1729,12 @@ config REGULATOR_QCOM_LABIBB + boost regulator and IBB can be used as a negative boost regulator + for LCD display panel. + ++config REGULATOR_TP65185X ++ tristate "eInk display regulator tp65185x" ++ depends on I2C ++ select REGMAP_I2C ++ help ++ This driver provides support for the voltage regulators for ++ eInk displays on various e-book readers. ++ + endif +diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile +index 267a09e14295..c0266414e3e4 100644 +--- a/drivers/regulator/Makefile ++++ b/drivers/regulator/Makefile +@@ -202,5 +202,6 @@ obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o + obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o + obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o + obj-$(CONFIG_REGULATOR_WM8994) += wm8994-regulator.o ++obj-$(CONFIG_REGULATOR_TP65185X) += tp65185x.o + + ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG +diff --git a/drivers/regulator/tp65185x.c b/drivers/regulator/tp65185x.c +new file mode 100644 +index 000000000000..8b57a11ff2f9 +--- /dev/null ++++ b/drivers/regulator/tp65185x.c +@@ -0,0 +1,428 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++// ++// Regulator device driver for tp65185x (eInk panel regulator) ++// ++// Copyright (C) 2019 OndÅ™ej Jirman ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define REG_TMST_VALUE 0x00 ++#define REG_ENABLE 0x01 ++#define REG_VADJ 0x02 ++#define REG_VCOM1 0x03 ++#define REG_VCOM2 0x04 ++#define REG_INT_EN1 0x05 ++#define REG_INT_EN2 0x06 ++#define REG_INT1 0x07 ++#define REG_INT2 0x08 ++#define REG_UPSEQ0 0x09 ++#define REG_UPSEQ1 0x0A ++#define REG_DWNSEQ0 0x0B ++#define REG_DWNSEQ1 0x0C ++#define REG_TMST1 0x0D ++#define REG_TMST2 0x0E ++#define REG_PG 0x0F ++#define REG_REVID 0x10 ++ ++struct tp65185x { ++ struct device* dev; ++ struct gpio_desc* wakeup_gpio; ++ struct gpio_desc* vcom_gpio; ++ struct gpio_desc* powerup_gpio; ++ struct gpio_desc* powergood_gpio; ++ struct regmap *regmap; ++ bool is_enabled; ++ int vcom; ++}; ++ ++static const struct regmap_config tp65185x_regmap_config = { ++ .reg_bits = 8, ++ .val_bits = 8, ++ .max_register = 0x10, ++ .cache_type = REGCACHE_NONE, ++}; ++ ++struct voltage_rail { ++ const char* name; ++ int bit; ++ int bit_en; ++}; ++ ++static const struct voltage_rail rails[] = { ++ { "VB", 7, -1 }, ++ { "VDDH", 6, 3 }, ++ { "VN", 5, -1 }, ++ { "VPOS", 4, 2 }, ++ { "VEE", 3, 1 }, ++ { "VNEG", 1, 0 }, ++ { "VCOM", -1, 4 }, ++ { "V3P3", -1, 5 }, ++}; ++ ++static int apply_voltage(struct tp65185x *tp, int vcom) ++{ ++ int ret; ++ ++ ret = regmap_write(tp->regmap, REG_VCOM1, vcom & 0xff); ++ if (ret) ++ return ret; ++ ++ return regmap_write(tp->regmap, REG_VCOM2, (vcom >> 8) & 1); ++} ++ ++static int set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV, ++ unsigned *selector) ++{ ++ struct tp65185x* tp = rdev_get_drvdata(rdev); ++ unsigned int vcom = min_uV / 10000; ++ ++ if (vcom > 511 || min_uV < 0) ++ return -EINVAL; ++ ++ tp->vcom = vcom; ++ ++ // setup VCOM ++ ++ if (!tp->is_enabled) ++ return 0; ++ ++ return apply_voltage(tp, vcom); ++} ++ ++static int get_voltage(struct regulator_dev *rdev) ++{ ++ struct tp65185x* tp = rdev_get_drvdata(rdev); ++ unsigned int lsb, msb; ++ int ret; ++ ++ if (tp->is_enabled) { ++ ret = regmap_read(rdev->regmap, REG_VCOM1, &lsb); ++ if (ret) ++ return ret; ++ ++ ret = regmap_read(rdev->regmap, REG_VCOM2, &msb); ++ if (ret) ++ return ret; ++ ++ return (lsb | ((msb & 1) << 8)) * 10000; ++ } ++ ++ return tp->vcom * 10000; ++} ++ ++#ifdef DEBUG ++static int show_power_status(struct tp65185x* tp, const char* state) ++{ ++ unsigned int reg; ++ int i, ret; ++ ++ ret = regmap_read(tp->regmap, REG_PG, ®); ++ if (ret) ++ return ret; ++ ++ dev_warn(tp->dev, "%s:\n", state); ++ ++ dev_warn(tp->dev, " voltage rail power good:\n"); ++ for (i = 0; i < ARRAY_SIZE(rails); i++) ++ if (rails[i].bit >= 0) ++ dev_warn(tp->dev, " %s %s\n", rails[i].name, ++ reg & BIT(rails[i].bit) ? "good" : "fail"); ++ ++ ret = regmap_read(tp->regmap, REG_ENABLE, ®); ++ if (ret) ++ return ret; ++ ++ dev_warn(tp->dev, " voltage rail enable status:\n"); ++ for (i = 0; i < ARRAY_SIZE(rails); i++) ++ if (rails[i].bit_en >= 0) ++ dev_warn(tp->dev, " %s %s\n", rails[i].name, ++ reg & BIT(rails[i].bit_en) ++ ? "enabled" : "disabled"); ++ ++ return 0; ++} ++#else ++static int show_power_status(struct tp65185x* tp, const char* state) ++{ ++ return 0; ++} ++#endif ++ ++static int show_power_bad(struct tp65185x* tp) ++{ ++ unsigned int reg; ++ int i, ret; ++ ++ ret = regmap_read(tp->regmap, REG_PG, ®); ++ if (ret) ++ return ret; ++ ++ dev_warn(tp->dev, "Voltage rail failures:\n"); ++ for (i = 0; i < ARRAY_SIZE(rails); i++) ++ if (rails[i].bit >= 0 && !(reg & BIT(rails[i].bit))) ++ dev_warn(tp->dev, " %s failed\n", rails[i].name); ++ ++ return 0; ++} ++ ++static int wait_for_power_good(struct tp65185x *tp) ++{ ++ int ret, loops = 10; ++ ++ // wait for power good ++ while (loops-- > 0) { ++ ret = gpiod_get_value(tp->powergood_gpio); ++ if (ret < 0) ++ return ret; ++ ++ if (ret) ++ return 0; ++ ++ msleep(5); ++ } ++ ++ show_power_bad(tp); ++ return -ETIMEDOUT; ++} ++ ++static int enable_supply(struct regulator_dev *rdev) ++{ ++ struct tp65185x* tp = rdev_get_drvdata(rdev); ++ int ret; ++ ++ if (tp->is_enabled) ++ return 0; ++ ++ // wake up the regulator ++ gpiod_set_value(tp->wakeup_gpio, 1); ++ usleep_range(3000, 4000); ++ ++ ret = apply_voltage(tp, tp->vcom); ++ if (ret) { ++ dev_err(tp->dev, "vcom restore failed (%d)\n", ret); ++ goto err; ++ } ++ ++ show_power_status(tp, "pre-powerup"); ++ ++ // enable the VDD on the panel (V3P3) first ++ ret = regmap_write(tp->regmap, REG_ENABLE, 0x20); ++ if (ret) { ++ dev_err(tp->dev, "vdd enable failed (%d)\n", ret); ++ goto err; ++ } ++ ++ usleep_range(2000, 2200); ++ ++ show_power_status(tp, "V3P3 en"); ++ ++ // powerup by default takes about 20ms ++ gpiod_set_value(tp->powerup_gpio, 1); ++ usleep_range(22000, 24000); ++ ++ ret = wait_for_power_good(tp); ++ if (ret) ++ goto err; ++ ++ show_power_status(tp, "powerup done"); ++ ++ // enable VCOM last ++ gpiod_set_value(tp->vcom_gpio, 1); ++ usleep_range(4000, 5000); ++ ++ show_power_status(tp, "powerup vcom"); ++ ++ tp->is_enabled = true; ++ return 0; ++ ++err: ++ gpiod_set_value(tp->vcom_gpio, 0); ++ usleep_range(2000, 3000); ++ gpiod_set_value(tp->powerup_gpio, 0); ++ msleep(100); ++ gpiod_set_value(tp->wakeup_gpio, 0); ++ return ret; ++} ++ ++static int disable_supply(struct regulator_dev *rdev) ++{ ++ struct tp65185x* tp = rdev_get_drvdata(rdev); ++ ++ if (!tp->is_enabled) ++ return 0; ++ ++ show_power_status(tp, "pre-poweroff"); ++ ++ gpiod_set_value(tp->vcom_gpio, 0); ++ usleep_range(5000, 6000); ++ ++ show_power_status(tp, "vcom down"); ++ ++ gpiod_set_value(tp->powerup_gpio, 0); ++ ++ // it may take up to 100ms to power off all high voltage rails ++ msleep(100); ++ ++ show_power_status(tp, "power down"); ++ ++ // this will power down the V3P3 switch too ++ gpiod_set_value(tp->wakeup_gpio, 0); ++ ++ tp->is_enabled = false; ++ ++ return 0; ++} ++ ++static int is_supply_enabled(struct regulator_dev *rdev) ++{ ++ struct tp65185x* tp = rdev_get_drvdata(rdev); ++ //int ret; ++ ++ //ret = gpiod_get_value(tp->powergood_gpio); ++ //if (ret < 0) ++ //return ret; ++ ++ return tp->is_enabled; ++} ++ ++static const struct regulator_ops tp65185x_ops = { ++ .is_enabled = is_supply_enabled, ++ .enable = enable_supply, ++ .disable = disable_supply, ++ .set_voltage = set_voltage, ++ .get_voltage = get_voltage, ++}; ++ ++static const struct regulator_desc tp65185x_reg = { ++ .name = "tp65185x", ++ .id = 0, ++ .continuous_voltage_range = 1, ++ .ops = &tp65185x_ops, ++ .type = REGULATOR_VOLTAGE, ++ .owner = THIS_MODULE, ++}; ++ ++static int tp65185x_i2c_probe(struct i2c_client *i2c, ++ const struct i2c_device_id *id) ++{ ++ struct device *dev = &i2c->dev; ++ struct regulator_dev *rdev; ++ struct regulator_config config = { }; ++ unsigned int reg; ++ struct tp65185x* tp; ++ const char* rev = NULL; ++ int ret; ++ ++ tp = devm_kzalloc(dev, sizeof(*tp), GFP_KERNEL); ++ if (!tp) ++ return -ENOMEM; ++ ++ tp->dev = dev; ++ ++ tp->powergood_gpio = devm_gpiod_get(dev, "powergood", GPIOD_IN); ++ if (IS_ERR(tp->powergood_gpio)) { ++ ret = PTR_ERR(tp->powergood_gpio); ++ dev_err(dev, "Can't get wakeup gpio (%d)\n", ret); ++ return ret; ++ } ++ ++ tp->powerup_gpio = devm_gpiod_get(dev, "powerup", GPIOD_OUT_LOW); ++ if (IS_ERR(tp->powerup_gpio)) { ++ ret = PTR_ERR(tp->powerup_gpio); ++ dev_err(dev, "Can't get wakeup gpio (%d)\n", ret); ++ return ret; ++ } ++ ++ tp->vcom_gpio = devm_gpiod_get(dev, "vcom", GPIOD_OUT_LOW); ++ if (IS_ERR(tp->vcom_gpio)) { ++ ret = PTR_ERR(tp->vcom_gpio); ++ dev_err(dev, "Can't get wakeup gpio (%d)\n", ret); ++ return ret; ++ } ++ ++ tp->wakeup_gpio = devm_gpiod_get(dev, "wakeup", GPIOD_OUT_HIGH); ++ if (IS_ERR(tp->wakeup_gpio)) { ++ ret = PTR_ERR(tp->wakeup_gpio); ++ dev_err(dev, "Can't get wakeup gpio (%d)\n", ret); ++ return ret; ++ } ++ ++ // wait for wakeup ++ usleep_range(10000, 12000); ++ ++ i2c_set_clientdata(i2c, tp); ++ ++ tp->regmap = devm_regmap_init_i2c(i2c, &tp65185x_regmap_config); ++ if (IS_ERR(tp->regmap)) { ++ ret = PTR_ERR(tp->regmap); ++ dev_err(dev, "Failed to allocate register map: %d\n", ret); ++ return ret; ++ } ++ ++ ret = regmap_read(tp->regmap, REG_REVID, ®); ++ if (ret) { ++ dev_err(dev, "chip id read failed (%d)\n", ret); ++ return ret; ++ } ++ ++ switch (reg) { ++ case 0x45: rev = "TPS65185 1p0"; break; ++ case 0x55: rev = "TPS65185 1p1"; break; ++ case 0x65: rev = "TPS65185 1p2"; break; ++ case 0x66: rev = "TPS651851 1p0"; break; ++ default: ++ dev_err(dev, "reading chip id\n"); ++ break; ++ ++ } ++ ++ dev_info(dev, "detected chip id 0x%02x (%s)\n", (int)reg, rev); ++ ++ // disable regulators, move to sleep ++ gpiod_set_value(tp->wakeup_gpio, 0); ++ ++ config.driver_data = tp; ++ config.dev = &i2c->dev; ++ config.regmap = tp->regmap; ++ config.of_node = dev->of_node; ++ config.init_data = of_get_regulator_init_data(dev, dev->of_node, ++ &tp65185x_reg); ++ if (!config.init_data) ++ return -ENOMEM; ++ ++ rdev = devm_regulator_register(&i2c->dev, &tp65185x_reg, &config); ++ if (IS_ERR(rdev)) { ++ ret = PTR_ERR(rdev); ++ dev_err(&i2c->dev, "Failed to register egulator (%d)\n", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static const struct i2c_device_id tp65185x_i2c_id[] = { ++ { "tp65185x", 0 }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(i2c, tp65185x_i2c_id); ++ ++static struct i2c_driver tp65185x_regulator_driver = { ++ .driver = { ++ .name = "tp65185x", ++ }, ++ .probe = tp65185x_i2c_probe, ++ .id_table = tp65185x_i2c_id, ++}; ++ ++module_i2c_driver(tp65185x_regulator_driver); ++ ++MODULE_AUTHOR("OndÅ™ej Jirman "); ++MODULE_DESCRIPTION("Regulator device driver for tp65185x"); ++MODULE_LICENSE("GPL"); +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/rtc-Print-which-error-caused-RTC-read-failure.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/rtc-Print-which-error-caused-RTC-read-failure.patch new file mode 100644 index 000000000000..59e01a9eeaeb --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/rtc-Print-which-error-caused-RTC-read-failure.patch @@ -0,0 +1,31 @@ +From 8952f8ddf5ac1e61907aed9d5ec6f061175efd36 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sat, 2 Mar 2024 15:08:57 +0100 +Subject: rtc: Print which error caused RTC read failure + +Currently we can't know from kernel log, whether the read failed due +to I2C bus error, or invalid time value, etc. Print the error in error +message. + +Signed-off-by: Ondrej Jirman +--- + drivers/rtc/class.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c +index e31fa0ad127e..2f14aecf0e24 100644 +--- a/drivers/rtc/class.c ++++ b/drivers/rtc/class.c +@@ -66,7 +66,8 @@ static void rtc_hctosys(struct rtc_device *rtc) + err = rtc_read_time(rtc, &tm); + if (err) { + dev_err(rtc->dev.parent, +- "hctosys: unable to read the hardware clock\n"); ++ "hctosys: unable to read the hardware clock %pe\n", ++ ERR_PTR(err)); + goto err_read; + } + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/rtc-sun6i-Allow-RTC-wakeup-after-shutdown.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/rtc-sun6i-Allow-RTC-wakeup-after-shutdown.patch new file mode 100644 index 000000000000..1327813df4b3 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/rtc-sun6i-Allow-RTC-wakeup-after-shutdown.patch @@ -0,0 +1,69 @@ +From 78d0c26df5a45af3c229534210c1a73b91a7f2fd Mon Sep 17 00:00:00 2001 +From: Samuel Holland +Date: Sat, 2 Jan 2021 15:52:27 -0600 +Subject: rtc: sun6i: Allow RTC wakeup after shutdown + +Only IRQs that have enable_irq_wake() called on them can wake the system +from sleep or after it has been shut down. Currently, the RTC alarm can +only wake the system from sleep. Run the suspend callback to arm the IRQ +during the shutdown process, so the RTC alarm also works after shutdown. + +Signed-off-by: Samuel Holland +--- + drivers/rtc/rtc-sun6i.c | 12 +++++++++++- + 1 file changed, 11 insertions(+), 1 deletion(-) + +diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c +index e5e6013d080e..c8eca3b1b96d 100644 +--- a/drivers/rtc/rtc-sun6i.c ++++ b/drivers/rtc/rtc-sun6i.c +@@ -710,6 +710,7 @@ static struct nvmem_config sun6i_rtc_nvmem_cfg = { + }; + + #ifdef CONFIG_PM_SLEEP ++ + /* Enable IRQ wake on suspend, to wake up from RTC. */ + static int sun6i_rtc_suspend(struct device *dev) + { +@@ -722,7 +723,7 @@ static int sun6i_rtc_suspend(struct device *dev) + } + + /* Disable IRQ wake on resume. */ +-static int sun6i_rtc_resume(struct device *dev) ++static int __maybe_unused sun6i_rtc_resume(struct device *dev) + { + struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); + +@@ -731,6 +732,7 @@ static int sun6i_rtc_resume(struct device *dev) + + return 0; + } ++ + #endif + + static SIMPLE_DEV_PM_OPS(sun6i_rtc_pm_ops, +@@ -850,6 +852,13 @@ static int sun6i_rtc_probe(struct platform_device *pdev) + return 0; + } + ++static void sun6i_rtc_shutdown(struct platform_device *pdev) ++{ ++#ifdef CONFIG_PM_SLEEP ++ sun6i_rtc_suspend(&pdev->dev); ++#endif ++} ++ + /* + * As far as RTC functionality goes, all models are the same. The + * datasheets claim that different models have different number of +@@ -874,6 +883,7 @@ MODULE_DEVICE_TABLE(of, sun6i_rtc_dt_ids); + + static struct platform_driver sun6i_rtc_driver = { + .probe = sun6i_rtc_probe, ++ .shutdown = sun6i_rtc_shutdown, + .driver = { + .name = "sun6i-rtc", + .of_match_table = sun6i_rtc_dt_ids, +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/sound-soc-ac100-codec-Support-analog-part-of-X-Powers-AC100-cod.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/sound-soc-ac100-codec-Support-analog-part-of-X-Powers-AC100-cod.patch new file mode 100644 index 000000000000..e2cd38ce47c3 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/sound-soc-ac100-codec-Support-analog-part-of-X-Powers-AC100-cod.patch @@ -0,0 +1,1074 @@ +From d0f048c22adc859780fb354f0f229ef524306066 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sun, 12 Nov 2017 23:09:14 +0100 +Subject: sound: soc: ac100-codec: Support analog part of X-Powers AC100 codec + +Most of the controls are implemented. + +Signed-off-by: Ondrej Jirman +--- + drivers/mfd/ac100.c | 3 + + include/linux/mfd/ac100.h | 1 + + sound/soc/sunxi/Kconfig | 11 + + sound/soc/sunxi/Makefile | 1 + + sound/soc/sunxi/ac100-codec.c | 990 ++++++++++++++++++++++++++++++++++ + 5 files changed, 1006 insertions(+) + create mode 100644 sound/soc/sunxi/ac100-codec.c + +diff --git a/drivers/mfd/ac100.c b/drivers/mfd/ac100.c +index 8f47c392cbd1..6cc9b1035642 100644 +--- a/drivers/mfd/ac100.c ++++ b/drivers/mfd/ac100.c +@@ -79,6 +79,9 @@ static struct mfd_cell ac100_cells[] = { + { + .name = "ac100-codec", + .of_compatible = "x-powers,ac100-codec", ++ }, { ++ .name = "ac100-codec-analog", ++ .of_compatible = "x-powers,ac100-codec-analog", + }, { + .name = "ac100-rtc", + .of_compatible = "x-powers,ac100-rtc", +diff --git a/include/linux/mfd/ac100.h b/include/linux/mfd/ac100.h +index 88005c3a1b2d..621bd10b79b2 100644 +--- a/include/linux/mfd/ac100.h ++++ b/include/linux/mfd/ac100.h +@@ -80,6 +80,7 @@ struct ac100_dev { + #define AC100_ERPOUT_CTRL 0x57 + #define AC100_SPKOUT_CTRL 0x58 + #define AC100_LINEOUT_CTRL 0x59 ++#define AC100_ADDA_TUNE1 0x5a + + /* ADC digital audio processing (high pass filter & auto gain control */ + #define AC100_ADC_DAP_L_STA 0x80 +diff --git a/sound/soc/sunxi/Kconfig b/sound/soc/sunxi/Kconfig +index 1f18f016acbb..7b2b3bcb062e 100644 +--- a/sound/soc/sunxi/Kconfig ++++ b/sound/soc/sunxi/Kconfig +@@ -22,6 +22,17 @@ config SND_SUN8I_CODEC + + Say Y or M if you want to add sun8i digital audio codec support. + ++config SND_AC100_CODEC ++ tristate "Allwinner (X-Powers) AC100 audio codec (analog part)" ++ depends on OF ++ depends on MACH_SUN8I || COMPILE_TEST ++ select MFD_AC100 ++ help ++ This option enables the audio codec support for Allwinner (X-Powers) ++ AC100 chip. ++ ++ Say Y or M if you want to add AC100 audio codec support. ++ + config SND_SUN8I_CODEC_ANALOG + tristate "Allwinner sun8i Codec Analog Controls Support" + depends on MACH_SUN8I || (ARM64 && ARCH_SUNXI) || COMPILE_TEST +diff --git a/sound/soc/sunxi/Makefile b/sound/soc/sunxi/Makefile +index 4483fe9c94ef..6131aea97efa 100644 +--- a/sound/soc/sunxi/Makefile ++++ b/sound/soc/sunxi/Makefile +@@ -1,4 +1,5 @@ + # SPDX-License-Identifier: GPL-2.0 ++obj-$(CONFIG_SND_AC100_CODEC) += ac100-codec.o + obj-$(CONFIG_SND_SUN4I_CODEC) += sun4i-codec.o + obj-$(CONFIG_SND_SUN4I_I2S) += sun4i-i2s.o + obj-$(CONFIG_SND_SUN4I_SPDIF) += sun4i-spdif.o +diff --git a/sound/soc/sunxi/ac100-codec.c b/sound/soc/sunxi/ac100-codec.c +new file mode 100644 +index 000000000000..a36c168d4994 +--- /dev/null ++++ b/sound/soc/sunxi/ac100-codec.c +@@ -0,0 +1,990 @@ ++/* ++ * This driver supports the controls for X-Powers (Allwinner) ++ * AC100 audio codec. This codec is co-packaged with AXP81x PMICs. ++ * ++ * (C) Copyright 2020 Ondrej Jirman ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#define AC100_ADC_APC_CTRL_ADCR_EN_OFF 15 ++#define AC100_ADC_APC_CTRL_ADCR_EN_MASK BIT(15) ++#define AC100_ADC_APC_CTRL_ADCR_EN_DISABLED 0 ++#define AC100_ADC_APC_CTRL_ADCR_EN_ENABLED BIT(15) ++#define AC100_ADC_APC_CTRL_ADCR_GAIN_OFF 12 ++#define AC100_ADC_APC_CTRL_ADCR_GAIN(v) (((v) & 0x7) << 12) ++#define AC100_ADC_APC_CTRL_ADCL_EN_OFF 11 ++#define AC100_ADC_APC_CTRL_ADCL_EN_MASK BIT(11) ++#define AC100_ADC_APC_CTRL_ADCL_EN_DISABLED 0 ++#define AC100_ADC_APC_CTRL_ADCL_EN_ENABLED BIT(11) ++#define AC100_ADC_APC_CTRL_ADCL_GAIN_OFF 8 ++#define AC100_ADC_APC_CTRL_ADCL_GAIN(v) (((v) & 0x7) << 8) ++#define AC100_ADC_APC_CTRL_MBIAS_EN_OFF 7 ++#define AC100_ADC_APC_CTRL_MBIAS_EN_MASK BIT(7) ++#define AC100_ADC_APC_CTRL_MBIAS_EN_DISABLED 0 ++#define AC100_ADC_APC_CTRL_MBIAS_EN_ENABLED BIT(7) ++#define AC100_ADC_APC_CTRL_MMIC_BIAS_CHOPPER_EN_OFF 6 ++#define AC100_ADC_APC_CTRL_MMIC_BIAS_CHOPPER_EN_MASK BIT(6) ++#define AC100_ADC_APC_CTRL_MMIC_BIAS_CHOPPER_EN_DISABLED 0 ++#define AC100_ADC_APC_CTRL_MMIC_BIAS_CHOPPER_EN_ENABLED BIT(6) ++#define AC100_ADC_APC_CTRL_MMIC_BIAS_CHOPPER_CKS_OFF 4 ++#define AC100_ADC_APC_CTRL_MMIC_BIAS_CHOPPER_CKS_MASK GENMASK(5, 4) ++#define AC100_ADC_APC_CTRL_MMIC_BIAS_CHOPPER_CKS_250K (0x0 << 4) ++#define AC100_ADC_APC_CTRL_MMIC_BIAS_CHOPPER_CKS_500K (0x1 << 4) ++#define AC100_ADC_APC_CTRL_MMIC_BIAS_CHOPPER_CKS_1M (0x2 << 4) ++#define AC100_ADC_APC_CTRL_MMIC_BIAS_CHOPPER_CKS_2M (0x3 << 4) ++#define AC100_ADC_APC_CTRL_HBIAS_MODE_OFF 2 ++#define AC100_ADC_APC_CTRL_HBIAS_MODE_MASK BIT(2) ++#define AC100_ADC_APC_CTRL_HBIAS_MODE_LOAD 0 ++#define AC100_ADC_APC_CTRL_HBIAS_MODE_HBIAS_EN BIT(2) ++#define AC100_ADC_APC_CTRL_HBIAS_EN_OFF 1 ++#define AC100_ADC_APC_CTRL_HBIAS_EN_MASK BIT(1) ++#define AC100_ADC_APC_CTRL_HBIAS_EN_DISABLED 0 ++#define AC100_ADC_APC_CTRL_HBIAS_EN_ENABLED BIT(1) ++#define AC100_ADC_APC_CTRL_HBIAS_ADC_EN_OFF 0 ++#define AC100_ADC_APC_CTRL_HBIAS_ADC_EN_MASK BIT(0) ++#define AC100_ADC_APC_CTRL_HBIAS_ADC_EN_DISABLED 0 ++#define AC100_ADC_APC_CTRL_HBIAS_ADC_EN_ENABLED BIT(0) ++ ++#define AC100_ADC_SRC_ADCR_MIC1_BOOST_OFF 13 ++#define AC100_ADC_SRC_ADCR_MIC1_BOOST_MASK BIT(13) ++#define AC100_ADC_SRC_ADCR_MIC1_BOOST_DISABLED 0 ++#define AC100_ADC_SRC_ADCR_MIC1_BOOST_ENABLED BIT(13) ++#define AC100_ADC_SRC_ADCR_MIC2_BOOST_OFF 12 ++#define AC100_ADC_SRC_ADCR_MIC2_BOOST_MASK BIT(12) ++#define AC100_ADC_SRC_ADCR_MIC2_BOOST_DISABLED 0 ++#define AC100_ADC_SRC_ADCR_MIC2_BOOST_ENABLED BIT(12) ++#define AC100_ADC_SRC_ADCR_LINEINL_LINEINR_OFF 11 ++#define AC100_ADC_SRC_ADCR_LINEINL_LINEINR_MASK BIT(11) ++#define AC100_ADC_SRC_ADCR_LINEINL_LINEINR_DISABLED 0 ++#define AC100_ADC_SRC_ADCR_LINEINL_LINEINR_ENABLED BIT(11) ++#define AC100_ADC_SRC_ADCR_LINEINR_OFF 10 ++#define AC100_ADC_SRC_ADCR_LINEINR_MASK BIT(10) ++#define AC100_ADC_SRC_ADCR_LINEINR_DISABLED 0 ++#define AC100_ADC_SRC_ADCR_LINEINR_ENABLED BIT(10) ++#define AC100_ADC_SRC_ADCR_AUXINR_OFF 9 ++#define AC100_ADC_SRC_ADCR_AUXINR_MASK BIT(9) ++#define AC100_ADC_SRC_ADCR_AUXINR_DISABLED 0 ++#define AC100_ADC_SRC_ADCR_AUXINR_ENABLED BIT(9) ++#define AC100_ADC_SRC_ADCR_ROUTMIX_OFF 8 ++#define AC100_ADC_SRC_ADCR_ROUTMIX_MASK BIT(8) ++#define AC100_ADC_SRC_ADCR_ROUTMIX_DISABLED 0 ++#define AC100_ADC_SRC_ADCR_ROUTMIX_ENABLED BIT(8) ++#define AC100_ADC_SRC_ADCR_LOUTMIX_OFF 7 ++#define AC100_ADC_SRC_ADCR_LOUTMIX_MASK BIT(7) ++#define AC100_ADC_SRC_ADCR_LOUTMIX_DISABLED 0 ++#define AC100_ADC_SRC_ADCR_LOUTMIX_ENABLED BIT(7) ++#define AC100_ADC_SRC_ADCL_MIC1_BOOST_OFF 6 ++#define AC100_ADC_SRC_ADCL_MIC1_BOOST_MASK BIT(6) ++#define AC100_ADC_SRC_ADCL_MIC1_BOOST_DISABLED 0 ++#define AC100_ADC_SRC_ADCL_MIC1_BOOST_ENABLED BIT(6) ++#define AC100_ADC_SRC_ADCL_MIC2_BOOST_OFF 5 ++#define AC100_ADC_SRC_ADCL_MIC2_BOOST_MASK BIT(5) ++#define AC100_ADC_SRC_ADCL_MIC2_BOOST_DISABLED 0 ++#define AC100_ADC_SRC_ADCL_MIC2_BOOST_ENABLED BIT(5) ++#define AC100_ADC_SRC_ADCL_LINEINL_LINEINR_OFF 4 ++#define AC100_ADC_SRC_ADCL_LINEINL_LINEINR_MASK BIT(4) ++#define AC100_ADC_SRC_ADCL_LINEINL_LINEINR_DISABLED 0 ++#define AC100_ADC_SRC_ADCL_LINEINL_LINEINR_ENABLED BIT(4) ++#define AC100_ADC_SRC_ADCL_LINEINL_OFF 3 ++#define AC100_ADC_SRC_ADCL_LINEINL_MASK BIT(3) ++#define AC100_ADC_SRC_ADCL_LINEINL_DISABLED 0 ++#define AC100_ADC_SRC_ADCL_LINEINL_ENABLED BIT(3) ++#define AC100_ADC_SRC_ADCL_AUXINL_OFF 2 ++#define AC100_ADC_SRC_ADCL_AUXINL_MASK BIT(2) ++#define AC100_ADC_SRC_ADCL_AUXINL_DISABLED 0 ++#define AC100_ADC_SRC_ADCL_AUXINL_ENABLED BIT(2) ++#define AC100_ADC_SRC_ADCL_LOUTMIX_OFF 1 ++#define AC100_ADC_SRC_ADCL_LOUTMIX_MASK BIT(1) ++#define AC100_ADC_SRC_ADCL_LOUTMIX_DISABLED 0 ++#define AC100_ADC_SRC_ADCL_LOUTMIX_ENABLED BIT(1) ++#define AC100_ADC_SRC_ADCL_ROUTMIX_OFF 0 ++#define AC100_ADC_SRC_ADCL_ROUTMIX_MASK BIT(0) ++#define AC100_ADC_SRC_ADCL_ROUTMIX_DISABLED 0 ++#define AC100_ADC_SRC_ADCL_ROUTMIX_ENABLED BIT(0) ++ ++#define AC100_ADC_SRC_BST_CTRL_MIC1AMPEN_OFF 15 ++#define AC100_ADC_SRC_BST_CTRL_MIC1AMPEN_MASK BIT(15) ++#define AC100_ADC_SRC_BST_CTRL_MIC1AMPEN_DISABLED 0 ++#define AC100_ADC_SRC_BST_CTRL_MIC1AMPEN_ENABLED BIT(15) ++#define AC100_ADC_SRC_BST_CTRL_MIC1BOOST_OFF 12 ++#define AC100_ADC_SRC_BST_CTRL_MIC1BOOST(v) (((v) & 0x7) << 12) ++#define AC100_ADC_SRC_BST_CTRL_MIC2AMPEN_OFF 11 ++#define AC100_ADC_SRC_BST_CTRL_MIC2AMPEN_MASK BIT(11) ++#define AC100_ADC_SRC_BST_CTRL_MIC2AMPEN_DISABLED 0 ++#define AC100_ADC_SRC_BST_CTRL_MIC2AMPEN_ENABLED BIT(11) ++#define AC100_ADC_SRC_BST_CTRL_MIC2BOOST_OFF 8 ++#define AC100_ADC_SRC_BST_CTRL_MIC2BOOST(v) (((v) & 0x7) << 8) ++#define AC100_ADC_SRC_BST_CTRL_MIC2SLT_OFF 7 ++#define AC100_ADC_SRC_BST_CTRL_MIC2SLT_MASK BIT(7) ++#define AC100_ADC_SRC_BST_CTRL_MIC2SLT_MIC2 0 ++#define AC100_ADC_SRC_BST_CTRL_MIC2SLT_MIC3 BIT(7) ++#define AC100_ADC_SRC_BST_CTRL_LINEIN_DIFF_PREG_OFF 4 ++#define AC100_ADC_SRC_BST_CTRL_LINEIN_DIFF_PREG(v) (((v) & 0x7) << 4) ++#define AC100_ADC_SRC_BST_CTRL_AXI_PREG_OFF 0 ++#define AC100_ADC_SRC_BST_CTRL_AXI_PREG(v) ((v) & 0x7) ++ ++#define AC100_OUT_MXR_DAC_A_CTRL_DAC_AR_EN_OFF 15 ++#define AC100_OUT_MXR_DAC_A_CTRL_DAC_AR_EN_MASK BIT(15) ++#define AC100_OUT_MXR_DAC_A_CTRL_DAC_AR_EN_DISABLED 0 ++#define AC100_OUT_MXR_DAC_A_CTRL_DAC_AR_EN_ENABLED BIT(15) ++#define AC100_OUT_MXR_DAC_A_CTRL_DAC_AL_EN_OFF 14 ++#define AC100_OUT_MXR_DAC_A_CTRL_DAC_AL_EN_MASK BIT(14) ++#define AC100_OUT_MXR_DAC_A_CTRL_DAC_AL_EN_DISABLED 0 ++#define AC100_OUT_MXR_DAC_A_CTRL_DAC_AL_EN_ENABLED BIT(14) ++#define AC100_OUT_MXR_DAC_A_CTRL_AR_MIX_EN_OFF 13 ++#define AC100_OUT_MXR_DAC_A_CTRL_AR_MIX_EN_MASK BIT(13) ++#define AC100_OUT_MXR_DAC_A_CTRL_AR_MIX_EN_DISABLED 0 ++#define AC100_OUT_MXR_DAC_A_CTRL_AR_MIX_EN_ENABLED BIT(13) ++#define AC100_OUT_MXR_DAC_A_CTRL_AL_MIX_EN_OFF 12 ++#define AC100_OUT_MXR_DAC_A_CTRL_AL_MIX_EN_MASK BIT(12) ++#define AC100_OUT_MXR_DAC_A_CTRL_AL_MIX_EN_DISABLED 0 ++#define AC100_OUT_MXR_DAC_A_CTRL_AL_MIX_EN_ENABLED BIT(12) ++#define AC100_OUT_MXR_DAC_A_CTRL_HP_DCRM_EN_OFF 8 ++#define AC100_OUT_MXR_DAC_A_CTRL_HP_DCRM_EN(v) (((v) & 0xf) << 8) ++ ++#define AC100_OUT_MXR_SRC_RMIX_MIC1_BOOST_OFF 13 ++#define AC100_OUT_MXR_SRC_RMIX_MIC1_BOOST_MASK BIT(13) ++#define AC100_OUT_MXR_SRC_RMIX_MIC1_BOOST_DISABLED 0 ++#define AC100_OUT_MXR_SRC_RMIX_MIC1_BOOST_ENABLED BIT(13) ++#define AC100_OUT_MXR_SRC_RMIX_MIC2_BOOST_OFF 12 ++#define AC100_OUT_MXR_SRC_RMIX_MIC2_BOOST_MASK BIT(12) ++#define AC100_OUT_MXR_SRC_RMIX_MIC2_BOOST_DISABLED 0 ++#define AC100_OUT_MXR_SRC_RMIX_MIC2_BOOST_ENABLED BIT(12) ++#define AC100_OUT_MXR_SRC_RMIX_LINEINL_LINEINR_OFF 11 ++#define AC100_OUT_MXR_SRC_RMIX_LINEINL_LINEINR_MASK BIT(11) ++#define AC100_OUT_MXR_SRC_RMIX_LINEINL_LINEINR_DISABLED 0 ++#define AC100_OUT_MXR_SRC_RMIX_LINEINL_LINEINR_ENABLED BIT(11) ++#define AC100_OUT_MXR_SRC_RMIX_LINEINR_OFF 10 ++#define AC100_OUT_MXR_SRC_RMIX_LINEINR_MASK BIT(10) ++#define AC100_OUT_MXR_SRC_RMIX_LINEINR_DISABLED 0 ++#define AC100_OUT_MXR_SRC_RMIX_LINEINR_ENABLED BIT(10) ++#define AC100_OUT_MXR_SRC_RMIX_AUXINR_OFF 9 ++#define AC100_OUT_MXR_SRC_RMIX_AUXINR_MASK BIT(9) ++#define AC100_OUT_MXR_SRC_RMIX_AUXINR_DISABLED 0 ++#define AC100_OUT_MXR_SRC_RMIX_AUXINR_ENABLED BIT(9) ++#define AC100_OUT_MXR_SRC_RMIX_DACR_OFF 8 ++#define AC100_OUT_MXR_SRC_RMIX_DACR_MASK BIT(8) ++#define AC100_OUT_MXR_SRC_RMIX_DACR_DISABLED 0 ++#define AC100_OUT_MXR_SRC_RMIX_DACR_ENABLED BIT(8) ++#define AC100_OUT_MXR_SRC_RMIX_DACL_OFF 7 ++#define AC100_OUT_MXR_SRC_RMIX_DACL_MASK BIT(7) ++#define AC100_OUT_MXR_SRC_RMIX_DACL_DISABLED 0 ++#define AC100_OUT_MXR_SRC_RMIX_DACL_ENABLED BIT(7) ++#define AC100_OUT_MXR_SRC_LMIX_MIC1_BOOST_OFF 6 ++#define AC100_OUT_MXR_SRC_LMIX_MIC1_BOOST_MASK BIT(6) ++#define AC100_OUT_MXR_SRC_LMIX_MIC1_BOOST_DISABLED 0 ++#define AC100_OUT_MXR_SRC_LMIX_MIC1_BOOST_ENABLED BIT(6) ++#define AC100_OUT_MXR_SRC_LMIX_MIC2_BOOST_OFF 5 ++#define AC100_OUT_MXR_SRC_LMIX_MIC2_BOOST_MASK BIT(5) ++#define AC100_OUT_MXR_SRC_LMIX_MIC2_BOOST_DISABLED 0 ++#define AC100_OUT_MXR_SRC_LMIX_MIC2_BOOST_ENABLED BIT(5) ++#define AC100_OUT_MXR_SRC_LMIX_LINEINL_LINEINR_OFF 4 ++#define AC100_OUT_MXR_SRC_LMIX_LINEINL_LINEINR_MASK BIT(4) ++#define AC100_OUT_MXR_SRC_LMIX_LINEINL_LINEINR_DISABLED 0 ++#define AC100_OUT_MXR_SRC_LMIX_LINEINL_LINEINR_ENABLED BIT(4) ++#define AC100_OUT_MXR_SRC_LMIX_LINEINL_OFF 3 ++#define AC100_OUT_MXR_SRC_LMIX_LINEINL_MASK BIT(3) ++#define AC100_OUT_MXR_SRC_LMIX_LINEINL_DISABLED 0 ++#define AC100_OUT_MXR_SRC_LMIX_LINEINL_ENABLED BIT(3) ++#define AC100_OUT_MXR_SRC_LMIX_AUXINL_OFF 2 ++#define AC100_OUT_MXR_SRC_LMIX_AUXINL_MASK BIT(2) ++#define AC100_OUT_MXR_SRC_LMIX_AUXINL_DISABLED 0 ++#define AC100_OUT_MXR_SRC_LMIX_AUXINL_ENABLED BIT(2) ++#define AC100_OUT_MXR_SRC_LMIX_DACL_OFF 1 ++#define AC100_OUT_MXR_SRC_LMIX_DACL_MASK BIT(1) ++#define AC100_OUT_MXR_SRC_LMIX_DACL_DISABLED 0 ++#define AC100_OUT_MXR_SRC_LMIX_DACL_ENABLED BIT(1) ++#define AC100_OUT_MXR_SRC_LMIX_DACR_OFF 0 ++#define AC100_OUT_MXR_SRC_LMIX_DACR_MASK BIT(0) ++#define AC100_OUT_MXR_SRC_LMIX_DACR_DISABLED 0 ++#define AC100_OUT_MXR_SRC_LMIX_DACR_ENABLED BIT(0) ++ ++#define AC100_OUT_MXR_SRC_BST_HMICBIAS_VOLTAGE_OFF 14 ++#define AC100_OUT_MXR_SRC_BST_HMICBIAS_VOLTAGE_MASK GENMASK(15, 14) ++#define AC100_OUT_MXR_SRC_BST_HMICBIAS_VOLTAGE_1_88V (0x0 << 14) ++#define AC100_OUT_MXR_SRC_BST_HMICBIAS_VOLTAGE_2_09V (0x1 << 14) ++#define AC100_OUT_MXR_SRC_BST_HMICBIAS_VOLTAGE_2_33V (0x2 << 14) ++#define AC100_OUT_MXR_SRC_BST_HMICBIAS_VOLTAGE_2_50V (0x3 << 14) ++#define AC100_OUT_MXR_SRC_BST_MMICBIAS_VOLTAGE_OFF 12 ++#define AC100_OUT_MXR_SRC_BST_MMICBIAS_VOLTAGE_MASK GENMASK(13, 12) ++#define AC100_OUT_MXR_SRC_BST_MMICBIAS_VOLTAGE_1_88V (0x0 << 12) ++#define AC100_OUT_MXR_SRC_BST_MMICBIAS_VOLTAGE_2_09V (0x1 << 12) ++#define AC100_OUT_MXR_SRC_BST_MMICBIAS_VOLTAGE_2_33V (0x2 << 12) ++#define AC100_OUT_MXR_SRC_BST_MMICBIAS_VOLTAGE_2_50V (0x3 << 12) ++#define AC100_OUT_MXR_SRC_BST_AX_GAIN_OFF 9 ++#define AC100_OUT_MXR_SRC_BST_AX_GAIN(v) (((v) & 0x7) << 9) ++#define AC100_OUT_MXR_SRC_BST_MIC1_GAIN_OFF 6 ++#define AC100_OUT_MXR_SRC_BST_MIC1_GAIN(v) (((v) & 0x7) << 6) ++#define AC100_OUT_MXR_SRC_BST_MIC2_GAIN_OFF 3 ++#define AC100_OUT_MXR_SRC_BST_MIC2_GAIN(v) (((v) & 0x7) << 3) ++#define AC100_OUT_MXR_SRC_BST_LINEIN_GAIN_OFF 0 ++#define AC100_OUT_MXR_SRC_BST_LINEIN_GAIN(v) ((v) & 0x7) ++ ++#define AC100_HPOUT_CTRL_RIGHT_SRC_OFF 15 ++#define AC100_HPOUT_CTRL_RIGHT_SRC_MASK BIT(15) ++#define AC100_HPOUT_CTRL_RIGHT_SRC_DACR 0 ++#define AC100_HPOUT_CTRL_RIGHT_SRC_RAMIX BIT(15) ++#define AC100_HPOUT_CTRL_LEFT_SRC_OFF 14 ++#define AC100_HPOUT_CTRL_LEFT_SRC_MASK BIT(14) ++#define AC100_HPOUT_CTRL_LEFT_SRC_DACL 0 ++#define AC100_HPOUT_CTRL_LEFT_SRC_LAMIX BIT(14) ++#define AC100_HPOUT_CTRL_RIGHT_PA_MUTE_OFF 13 ++#define AC100_HPOUT_CTRL_RIGHT_PA_MUTE_MASK BIT(13) ++#define AC100_HPOUT_CTRL_RIGHT_PA_MUTE_MUTE 0 ++#define AC100_HPOUT_CTRL_RIGHT_PA_MUTE_NOT_MUTE BIT(13) ++#define AC100_HPOUT_CTRL_LEFT_PA_MUTE_OFF 12 ++#define AC100_HPOUT_CTRL_LEFT_PA_MUTE_MASK BIT(12) ++#define AC100_HPOUT_CTRL_LEFT_PA_MUTE_MUTE 0 ++#define AC100_HPOUT_CTRL_LEFT_PA_MUTE_NOT_MUTE BIT(12) ++#define AC100_HPOUT_CTRL_PA_EN_OFF 11 ++#define AC100_HPOUT_CTRL_PA_EN_MASK BIT(11) ++#define AC100_HPOUT_CTRL_PA_EN_DISABLED 0 ++#define AC100_HPOUT_CTRL_PA_EN_ENABLED BIT(11) ++#define AC100_HPOUT_CTRL_VOLUME_OFF 4 ++#define AC100_HPOUT_CTRL_VOLUME(v) (((v) & 0x3f) << 4) ++#define AC100_HPOUT_CTRL_STARTUP_DELAY_OFF 2 ++#define AC100_HPOUT_CTRL_STARTUP_DELAY_MASK GENMASK(3, 2) ++#define AC100_HPOUT_CTRL_STARTUP_DELAY_4ms (0x0 << 2) ++#define AC100_HPOUT_CTRL_STARTUP_DELAY_8ms (0x1 << 2) ++#define AC100_HPOUT_CTRL_STARTUP_DELAY_16ms (0x2 << 2) ++#define AC100_HPOUT_CTRL_STARTUP_DELAY_32ms (0x3 << 2) ++#define AC100_HPOUT_CTRL_OUTPUT_CURRENT_OFF 0 ++#define AC100_HPOUT_CTRL_OUTPUT_CURRENT(v) ((v) & 0x3) ++ ++#define AC100_ERPOUT_CTRL_RAMP_TIME_OFF 11 ++#define AC100_ERPOUT_CTRL_RAMP_TIME_MASK GENMASK(12, 11) ++#define AC100_ERPOUT_CTRL_RAMP_TIME_256ms (0x0 << 11) ++#define AC100_ERPOUT_CTRL_RAMP_TIME_512ms (0x1 << 11) ++#define AC100_ERPOUT_CTRL_RAMP_TIME_640ms (0x2 << 11) ++#define AC100_ERPOUT_CTRL_RAMP_TIME_768ms (0x3 << 11) ++#define AC100_ERPOUT_CTRL_OUT_CURRENT_OFF 9 ++#define AC100_ERPOUT_CTRL_OUT_CURRENT(v) (((v) & 0x3) << 9) ++#define AC100_ERPOUT_CTRL_INPUT_SOURCE_OFF 7 ++#define AC100_ERPOUT_CTRL_INPUT_SOURCE_MASK GENMASK(8, 7) ++#define AC100_ERPOUT_CTRL_INPUT_SOURCE_DACR (0x0 << 7) ++#define AC100_ERPOUT_CTRL_INPUT_SOURCE_DACL (0x1 << 7) ++#define AC100_ERPOUT_CTRL_INPUT_SOURCE_RAMIX (0x2 << 7) ++#define AC100_ERPOUT_CTRL_INPUT_SOURCE_LAMIX (0x3 << 7) ++#define AC100_ERPOUT_CTRL_MUTE_OFF 6 ++#define AC100_ERPOUT_CTRL_MUTE_MASK BIT(6) ++#define AC100_ERPOUT_CTRL_MUTE_MUTE 0 ++#define AC100_ERPOUT_CTRL_MUTE_NOT_MUTE BIT(6) ++#define AC100_ERPOUT_CTRL_PA_EN_OFF 5 ++#define AC100_ERPOUT_CTRL_PA_EN_MASK BIT(5) ++#define AC100_ERPOUT_CTRL_PA_EN_DISABLED 0 ++#define AC100_ERPOUT_CTRL_PA_EN_ENABLED BIT(5) ++#define AC100_ERPOUT_CTRL_VOLUME_OFF 0 ++#define AC100_ERPOUT_CTRL_VOLUME(v) ((v) & 0x1f) ++ ++#define AC100_SPKOUT_CTRL_RIGHT_SRC_OFF 12 ++#define AC100_SPKOUT_CTRL_RIGHT_SRC_MASK BIT(12) ++#define AC100_SPKOUT_CTRL_RIGHT_SRC_MIXR 0 ++#define AC100_SPKOUT_CTRL_RIGHT_SRC_MIXL_MIXR BIT(12) ++#define AC100_SPKOUT_CTRL_RIGHT_INV_EN_OFF 11 ++#define AC100_SPKOUT_CTRL_RIGHT_INV_EN_MASK BIT(11) ++#define AC100_SPKOUT_CTRL_RIGHT_INV_EN_DISABLED 0 ++#define AC100_SPKOUT_CTRL_RIGHT_INV_EN_ENABLED BIT(11) ++#define AC100_SPKOUT_CTRL_RIGHT_EN_OFF 9 ++#define AC100_SPKOUT_CTRL_RIGHT_EN_MASK BIT(9) ++#define AC100_SPKOUT_CTRL_RIGHT_EN_DISABLED 0 ++#define AC100_SPKOUT_CTRL_RIGHT_EN_ENABLED BIT(9) ++#define AC100_SPKOUT_CTRL_LEFT_SRC_OFF 8 ++#define AC100_SPKOUT_CTRL_LEFT_SRC_MASK BIT(8) ++#define AC100_SPKOUT_CTRL_LEFT_SRC_MIXL 0 ++#define AC100_SPKOUT_CTRL_LEFT_SRC_MIXL_MIXR BIT(8) ++#define AC100_SPKOUT_CTRL_LEFT_INV_EN_OFF 7 ++#define AC100_SPKOUT_CTRL_LEFT_INV_EN_MASK BIT(7) ++#define AC100_SPKOUT_CTRL_LEFT_INV_EN_DISABLED 0 ++#define AC100_SPKOUT_CTRL_LEFT_INV_EN_ENABLED BIT(7) ++#define AC100_SPKOUT_CTRL_LEFT_EN_OFF 5 ++#define AC100_SPKOUT_CTRL_LEFT_EN_MASK BIT(5) ++#define AC100_SPKOUT_CTRL_LEFT_EN_DISABLED 0 ++#define AC100_SPKOUT_CTRL_LEFT_EN_ENABLED BIT(5) ++#define AC100_SPKOUT_CTRL_VOLUME_OFF 0 ++#define AC100_SPKOUT_CTRL_VOLUME(v) ((v) & 0x1f) ++ ++#define AC100_LINEOUT_CTRL_LINEOUT_GAIN_OFF 5 ++#define AC100_LINEOUT_CTRL_LINEOUT_GAIN(v) (((v) & 0x7) << 5) ++#define AC100_LINEOUT_CTRL_LINEOUT_EN_OFF 4 ++#define AC100_LINEOUT_CTRL_LINEOUT_EN_MASK BIT(4) ++#define AC100_LINEOUT_CTRL_LINEOUT_EN_DISABLED 0 ++#define AC100_LINEOUT_CTRL_LINEOUT_EN_ENABLED BIT(4) ++#define AC100_LINEOUT_CTRL_LINEOUT_S0_OFF 3 ++#define AC100_LINEOUT_CTRL_LINEOUT_S0_MASK BIT(3) ++#define AC100_LINEOUT_CTRL_LINEOUT_S0_MUTE 0 ++#define AC100_LINEOUT_CTRL_LINEOUT_S0_ON BIT(3) ++#define AC100_LINEOUT_CTRL_LINEOUT_S1_OFF 2 ++#define AC100_LINEOUT_CTRL_LINEOUT_S1_MASK BIT(2) ++#define AC100_LINEOUT_CTRL_LINEOUT_S1_MUTE 0 ++#define AC100_LINEOUT_CTRL_LINEOUT_S1_ON BIT(2) ++#define AC100_LINEOUT_CTRL_LINEOUT_S2_OFF 1 ++#define AC100_LINEOUT_CTRL_LINEOUT_S2_MASK BIT(1) ++#define AC100_LINEOUT_CTRL_LINEOUT_S2_MUTE 0 ++#define AC100_LINEOUT_CTRL_LINEOUT_S2_ON BIT(1) ++#define AC100_LINEOUT_CTRL_LINEOUT_S3_OFF 0 ++#define AC100_LINEOUT_CTRL_LINEOUT_S3_MASK BIT(0) ++#define AC100_LINEOUT_CTRL_LINEOUT_S3_MUTE 0 ++#define AC100_LINEOUT_CTRL_LINEOUT_S3_ON BIT(0) ++ ++#define AC100_ADDA_TUNE1_ZERO_CROSSOVER_EN_OFF 8 ++#define AC100_ADDA_TUNE1_ZERO_CROSSOVER_EN_MASK BIT(8) ++#define AC100_ADDA_TUNE1_ZERO_CROSSOVER_EN_DIS 0 ++#define AC100_ADDA_TUNE1_ZERO_CROSSOVER_EN_EN BIT(8) ++#define AC100_ADDA_TUNE1_ZERO_CROSSOVER_TIME_OFF 7 ++#define AC100_ADDA_TUNE1_ZERO_CROSSOVER_TIME BIT(7) ++ ++struct ac100_codec { ++ struct device *dev; ++ struct snd_soc_component component; ++}; ++ ++/* ADC mixer controls */ ++static const struct snd_kcontrol_new ac100_codec_adc_mixer_controls[] = { ++ SOC_DAPM_DOUBLE("Mic1 Capture Switch", ++ AC100_ADC_SRC, ++ AC100_ADC_SRC_ADCL_MIC1_BOOST_OFF, ++ AC100_ADC_SRC_ADCR_MIC1_BOOST_OFF, 1, 0), ++ SOC_DAPM_DOUBLE("Mic2 Capture Switch", ++ AC100_ADC_SRC, ++ AC100_ADC_SRC_ADCL_MIC2_BOOST_OFF, ++ AC100_ADC_SRC_ADCR_MIC2_BOOST_OFF, 1, 0), ++ SOC_DAPM_DOUBLE("Line In Differential Capture Switch", ++ AC100_ADC_SRC, ++ AC100_ADC_SRC_ADCL_LINEINL_LINEINR_OFF, ++ AC100_ADC_SRC_ADCR_LINEINL_LINEINR_OFF, 1, 0), ++ SOC_DAPM_DOUBLE("Line In Capture Switch", ++ AC100_ADC_SRC, ++ AC100_ADC_SRC_ADCL_LINEINL_OFF, ++ AC100_ADC_SRC_ADCR_LINEINR_OFF, 1, 0), ++ SOC_DAPM_DOUBLE("Aux In Capture Switch", ++ AC100_ADC_SRC, ++ AC100_ADC_SRC_ADCL_AUXINL_OFF, ++ AC100_ADC_SRC_ADCR_AUXINR_OFF, 1, 0), ++ SOC_DAPM_DOUBLE("Mixer Capture Switch", ++ AC100_ADC_SRC, ++ AC100_ADC_SRC_ADCL_LOUTMIX_OFF, ++ AC100_ADC_SRC_ADCR_ROUTMIX_OFF, 1, 0), ++ SOC_DAPM_DOUBLE("Mixer Reversed Capture Switch", ++ AC100_ADC_SRC, ++ AC100_ADC_SRC_ADCL_ROUTMIX_OFF, ++ AC100_ADC_SRC_ADCR_LOUTMIX_OFF, 1, 0), ++}; ++ ++/* Output mixer controls */ ++static const struct snd_kcontrol_new ac100_codec_mixer_controls[] = { ++ SOC_DAPM_DOUBLE("Mic1 Playback Switch", ++ AC100_OUT_MXR_SRC, ++ AC100_OUT_MXR_SRC_LMIX_MIC1_BOOST_OFF, ++ AC100_OUT_MXR_SRC_RMIX_MIC1_BOOST_OFF, 1, 0), ++ SOC_DAPM_DOUBLE("Mic2 Playback Switch", ++ AC100_OUT_MXR_SRC, ++ AC100_OUT_MXR_SRC_LMIX_MIC2_BOOST_OFF, ++ AC100_OUT_MXR_SRC_RMIX_MIC2_BOOST_OFF, 1, 0), ++ SOC_DAPM_DOUBLE("Line In Differential Playback Switch", ++ AC100_OUT_MXR_SRC, ++ AC100_OUT_MXR_SRC_LMIX_LINEINL_LINEINR_OFF, ++ AC100_OUT_MXR_SRC_RMIX_LINEINL_LINEINR_OFF, 1, 0), ++ SOC_DAPM_DOUBLE("Line In Playback Switch", ++ AC100_OUT_MXR_SRC, ++ AC100_OUT_MXR_SRC_LMIX_LINEINL_OFF, ++ AC100_OUT_MXR_SRC_RMIX_LINEINR_OFF, 1, 0), ++ SOC_DAPM_DOUBLE("Aux In Playback Switch", ++ AC100_OUT_MXR_SRC, ++ AC100_OUT_MXR_SRC_LMIX_AUXINL_OFF, ++ AC100_OUT_MXR_SRC_RMIX_AUXINR_OFF, 1, 0), ++ SOC_DAPM_DOUBLE("DAC Playback Switch", ++ AC100_OUT_MXR_SRC, ++ AC100_OUT_MXR_SRC_LMIX_DACL_OFF, ++ AC100_OUT_MXR_SRC_RMIX_DACR_OFF, 1, 0), ++ SOC_DAPM_DOUBLE("DAC Reversed Playback Switch", ++ AC100_OUT_MXR_SRC, ++ AC100_OUT_MXR_SRC_LMIX_DACR_OFF, ++ AC100_OUT_MXR_SRC_RMIX_DACL_OFF, 1, 0), ++}; ++ ++static const DECLARE_TLV_DB_SCALE(ac100_codec_out_mixer_pregain_scale, ++ -450, 150, 0); ++ ++static const DECLARE_TLV_DB_RANGE(ac100_codec_mic_gain_scale, ++ 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), ++ 1, 7, TLV_DB_SCALE_ITEM(3000, 300, 0), ++); ++ ++static const DECLARE_TLV_DB_SCALE(ac100_codec_pre_gain_scale, ++ -1200, 300, 0); ++ ++static const DECLARE_TLV_DB_RANGE(ac100_codec_earpiece_vol_scale, ++ 0, 1, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), ++ 2, 31, TLV_DB_SCALE_ITEM(-4350, 150, 0), ++); ++ ++static const DECLARE_TLV_DB_SCALE(ac100_codec_lineout_vol_scale, -450, 150, 0); ++ ++static const DECLARE_TLV_DB_SCALE(ac100_codec_hp_vol_scale, -6300, 100, 1); ++ ++static const char *ac100_codec_hp_pa_delay_texts[] = { ++ "4ms", "8ms", "16ms", "32ms" ++}; ++ ++static SOC_ENUM_SINGLE_DECL(ac100_codec_hp_pa_delay_enum, ++ AC100_HPOUT_CTRL, ++ AC100_HPOUT_CTRL_STARTUP_DELAY_OFF, ++ ac100_codec_hp_pa_delay_texts); ++ ++static const char *ac100_codec_hp_pa_cur_texts[] = { ++ "low", "mid", "higher", "highest" ++}; ++ ++static SOC_ENUM_SINGLE_DECL(ac100_codec_hp_pa_cur_enum, ++ AC100_HPOUT_CTRL, ++ AC100_HPOUT_CTRL_OUTPUT_CURRENT_OFF, ++ ac100_codec_hp_pa_cur_texts); ++ ++static SOC_ENUM_SINGLE_DECL(ac100_codec_ep_pa_cur_enum, ++ AC100_ERPOUT_CTRL, ++ AC100_ERPOUT_CTRL_OUT_CURRENT_OFF, ++ ac100_codec_hp_pa_cur_texts); ++ ++static const char *ac100_codec_ep_pa_ramp_time_texts[] = { ++ "256ms", "512ms", "640ms", "768ms" ++}; ++ ++static SOC_ENUM_SINGLE_DECL(ac100_codec_ep_pa_ramp_time_enum, ++ AC100_ERPOUT_CTRL, ++ AC100_ERPOUT_CTRL_RAMP_TIME_OFF, ++ ac100_codec_ep_pa_ramp_time_texts); ++ ++static const char *ac100_codec_mic_bv_texts[] = { ++ "1.88V", "2.09V", "2.33V", "2.5V" ++}; ++ ++static SOC_ENUM_SINGLE_DECL(ac100_codec_mic1_bv_enum, ++ AC100_OUT_MXR_SRC_BST, ++ AC100_OUT_MXR_SRC_BST_MMICBIAS_VOLTAGE_OFF, ++ ac100_codec_mic_bv_texts); ++ ++static SOC_ENUM_SINGLE_DECL(ac100_codec_mic2_bv_enum, ++ AC100_OUT_MXR_SRC_BST, ++ AC100_OUT_MXR_SRC_BST_HMICBIAS_VOLTAGE_OFF, ++ ac100_codec_mic_bv_texts); ++ ++/* volume / mute controls */ ++static const struct snd_kcontrol_new ac100_codec_controls[] = { ++ /* Microphone Amp boost gain */ ++ SOC_SINGLE_TLV("Mic1 Boost Volume", AC100_ADC_SRC_BST_CTRL, ++ AC100_ADC_SRC_BST_CTRL_MIC1BOOST_OFF, 0x7, 0, ++ ac100_codec_mic_gain_scale), ++ ++ SOC_SINGLE_TLV("Mic2 Boost Volume", AC100_ADC_SRC_BST_CTRL, ++ AC100_ADC_SRC_BST_CTRL_MIC2BOOST_OFF, 0x7, 0, ++ ac100_codec_mic_gain_scale), ++ ++ SOC_SINGLE_TLV("Line In Pre-Gain Volume", AC100_ADC_SRC_BST_CTRL, ++ AC100_ADC_SRC_BST_CTRL_LINEIN_DIFF_PREG_OFF, 0x7, 0, ++ ac100_codec_pre_gain_scale), ++ ++ SOC_SINGLE_TLV("Aux In Pre-Gain Volume", AC100_ADC_SRC_BST_CTRL, ++ AC100_ADC_SRC_BST_CTRL_AXI_PREG_OFF, 0x7, 0, ++ ac100_codec_pre_gain_scale), ++ ++ /* ADC */ ++ SOC_DOUBLE_TLV("ADC Gain Capture Volume", AC100_ADC_APC_CTRL, ++ AC100_ADC_APC_CTRL_ADCL_GAIN_OFF, ++ AC100_ADC_APC_CTRL_ADCR_GAIN_OFF, 0x7, 0, ++ ac100_codec_out_mixer_pregain_scale), ++ ++ /* Mixer pre-gain */ ++ SOC_SINGLE_TLV("Mic1 Playback Volume", AC100_OUT_MXR_SRC_BST, ++ AC100_OUT_MXR_SRC_BST_MIC1_GAIN_OFF, ++ 0x7, 0, ac100_codec_out_mixer_pregain_scale), ++ ++ SOC_SINGLE_TLV("Mic2 Playback Volume", AC100_OUT_MXR_SRC_BST, ++ AC100_OUT_MXR_SRC_BST_MIC2_GAIN_OFF, ++ 0x7, 0, ac100_codec_out_mixer_pregain_scale), ++ ++ SOC_SINGLE_TLV("Line In Playback Volume", AC100_OUT_MXR_SRC_BST, ++ AC100_OUT_MXR_SRC_BST_LINEIN_GAIN_OFF, ++ 0x7, 0, ac100_codec_out_mixer_pregain_scale), ++ ++ SOC_SINGLE_TLV("Aux In Playback Volume", AC100_OUT_MXR_SRC_BST, ++ AC100_OUT_MXR_SRC_BST_AX_GAIN_OFF, ++ 0x7, 0, ac100_codec_out_mixer_pregain_scale), ++ ++ SOC_SINGLE_TLV("Headphone Playback Volume", ++ AC100_HPOUT_CTRL, ++ AC100_HPOUT_CTRL_VOLUME_OFF, 0x3f, 0, ++ ac100_codec_hp_vol_scale), ++ ++ SOC_SINGLE_TLV("Earpiece Playback Volume", ++ AC100_ERPOUT_CTRL, ++ AC100_ERPOUT_CTRL_VOLUME_OFF, 0x1f, 0, ++ ac100_codec_earpiece_vol_scale), ++ ++ SOC_SINGLE_TLV("Speaker Playback Volume", ++ AC100_SPKOUT_CTRL, ++ AC100_SPKOUT_CTRL_VOLUME_OFF, 0x1f, 0, ++ ac100_codec_earpiece_vol_scale), ++ ++ SOC_SINGLE_TLV("Line Out Playback Volume", ++ AC100_LINEOUT_CTRL, ++ AC100_LINEOUT_CTRL_LINEOUT_GAIN_OFF, 0x7, 0, ++ ac100_codec_lineout_vol_scale), ++ ++ SOC_ENUM("Headphone Amplifier Startup Delay", ++ ac100_codec_hp_pa_delay_enum), ++ SOC_ENUM("Headphone Amplifier Current", ac100_codec_hp_pa_cur_enum), ++ ++ SOC_ENUM("Earpiece Amplifier Ramp Time", ++ ac100_codec_ep_pa_ramp_time_enum), ++ SOC_ENUM("Earpiece Amplifier Current", ac100_codec_ep_pa_cur_enum), ++ ++ SOC_ENUM("Mic1 Bias Voltage", ac100_codec_mic1_bv_enum), ++ SOC_ENUM("Mic2 Bias Voltage", ac100_codec_mic2_bv_enum), ++}; ++ ++/* Headphone */ ++ ++static const char * const ac100_codec_hp_src_enum_text[] = { ++ "DAC", "Mixer", ++}; ++ ++static SOC_ENUM_DOUBLE_DECL(ac100_codec_hp_src_enum, ++ AC100_HPOUT_CTRL, ++ AC100_HPOUT_CTRL_LEFT_SRC_OFF, ++ AC100_HPOUT_CTRL_RIGHT_SRC_OFF, ++ ac100_codec_hp_src_enum_text); ++ ++static const struct snd_kcontrol_new ac100_codec_hp_src[] = { ++ SOC_DAPM_ENUM("Headphone Source Playback Route", ++ ac100_codec_hp_src_enum), ++}; ++ ++static const struct snd_kcontrol_new ac100_codec_hp_switch = ++ SOC_DAPM_DOUBLE("Headphone Playback Switch", ++ AC100_HPOUT_CTRL, ++ AC100_HPOUT_CTRL_LEFT_PA_MUTE_OFF, ++ AC100_HPOUT_CTRL_RIGHT_PA_MUTE_OFF, 1, 0); ++ ++/* Earpiece */ ++ ++static const struct snd_kcontrol_new ac100_codec_earpiece_switch = ++ SOC_DAPM_SINGLE("Playback Switch", ++ AC100_ERPOUT_CTRL, ++ AC100_ERPOUT_CTRL_MUTE_OFF, 1, 0); ++ ++static const char * const ac100_codec_earpiece_src_enum_text[] = { ++ "DACR", "DACL", "Right Mixer", "Left Mixer", ++}; ++ ++static SOC_ENUM_SINGLE_DECL(ac100_codec_earpiece_src_enum, ++ AC100_ERPOUT_CTRL, ++ AC100_ERPOUT_CTRL_INPUT_SOURCE_OFF, ++ ac100_codec_earpiece_src_enum_text); ++ ++static const struct snd_kcontrol_new ac100_codec_earpiece_src[] = { ++ SOC_DAPM_ENUM("Earpiece Source Playback Route", ++ ac100_codec_earpiece_src_enum), ++}; ++ ++/* Speaker */ ++ ++static const char * const ac100_codec_spk_src_enum_text[] = { ++ "Stereo", "Mono", ++}; ++ ++static SOC_ENUM_DOUBLE_DECL(ac100_codec_spk_src_enum, ++ AC100_SPKOUT_CTRL, ++ AC100_SPKOUT_CTRL_LEFT_SRC_OFF, ++ AC100_SPKOUT_CTRL_RIGHT_SRC_OFF, ++ ac100_codec_spk_src_enum_text); ++ ++static const struct snd_kcontrol_new ac100_codec_spk_src[] = { ++ SOC_DAPM_ENUM("Speaker Source Playback Route", ++ ac100_codec_spk_src_enum), ++}; ++ ++static const struct snd_kcontrol_new ac100_codec_spk_switch = ++ SOC_DAPM_DOUBLE("Speaker Playback Switch", ++ AC100_SPKOUT_CTRL, ++ AC100_SPKOUT_CTRL_LEFT_EN_OFF, ++ AC100_SPKOUT_CTRL_RIGHT_EN_OFF, 1, 0); ++ ++static const struct snd_kcontrol_new ac100_codec_spk_inv_switch = ++ SOC_DAPM_DOUBLE("Speaker Invert Switch", ++ AC100_SPKOUT_CTRL, ++ AC100_SPKOUT_CTRL_LEFT_INV_EN_OFF, ++ AC100_SPKOUT_CTRL_RIGHT_INV_EN_OFF, 1, 0); ++ ++/* Line Out */ ++ ++static const struct snd_kcontrol_new ac100_codec_lineout_mixer_controls[] = { ++ SOC_DAPM_SINGLE("Mic1 Playback Switch", ++ AC100_LINEOUT_CTRL, ++ AC100_LINEOUT_CTRL_LINEOUT_S0_OFF, 1, 0), ++ SOC_DAPM_SINGLE("Mic2 Playback Switch", ++ AC100_LINEOUT_CTRL, ++ AC100_LINEOUT_CTRL_LINEOUT_S1_OFF, 1, 0), ++ SOC_DAPM_SINGLE("Right Mixer Playback Switch", ++ AC100_LINEOUT_CTRL, ++ AC100_LINEOUT_CTRL_LINEOUT_S2_OFF, 1, 0), ++ SOC_DAPM_SINGLE("Left Mixer Playback Switch", ++ AC100_LINEOUT_CTRL, ++ AC100_LINEOUT_CTRL_LINEOUT_S3_OFF, 1, 0), ++}; ++ ++static const struct snd_kcontrol_new ac100_codec_lineout_switch = ++ SOC_DAPM_SINGLE("Playback Switch", ++ AC100_LINEOUT_CTRL, ++ AC100_LINEOUT_CTRL_LINEOUT_EN_OFF, 1, 0); ++ ++/* Mic2 Boost Source */ ++ ++static const char * const ac100_codec_mic2boost_src_enum_text[] = { ++ "Mic2", "Mic3", ++}; ++ ++static SOC_ENUM_SINGLE_DECL(ac100_codec_mic2boost_src_enum, ++ AC100_ADC_SRC_BST_CTRL, ++ AC100_ADC_SRC_BST_CTRL_MIC2SLT_OFF, ++ ac100_codec_mic2boost_src_enum_text); ++ ++static const struct snd_kcontrol_new ac100_codec_mic2boost_src[] = { ++ SOC_DAPM_ENUM("Mic2 Source Capture Route", ++ ac100_codec_mic2boost_src_enum), ++}; ++ ++/* This is done to remove the headphone buffer DC offset. */ ++static int ac100_codec_hp_power(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *k, int event) ++{ ++ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); ++ unsigned int val = SND_SOC_DAPM_EVENT_ON(event) ? 0xf : 0; ++ ++ // zero cross detection ++ if (SND_SOC_DAPM_EVENT_OFF(event)) { ++ snd_soc_component_update_bits(component, ++ AC100_ADDA_TUNE1, ++ AC100_ADDA_TUNE1_ZERO_CROSSOVER_EN_MASK, ++ AC100_ADDA_TUNE1_ZERO_CROSSOVER_EN_EN); ++ } else { ++ snd_soc_component_update_bits(component, ++ AC100_ADDA_TUNE1, ++ AC100_ADDA_TUNE1_ZERO_CROSSOVER_EN_MASK, ++ AC100_ADDA_TUNE1_ZERO_CROSSOVER_EN_DIS); ++ } ++ ++ snd_soc_component_update_bits(component, AC100_OUT_MXR_DAC_A_CTRL, ++ AC100_OUT_MXR_DAC_A_CTRL_HP_DCRM_EN(0xf), ++ AC100_OUT_MXR_DAC_A_CTRL_HP_DCRM_EN(val)); ++ return 0; ++} ++ ++static const struct snd_soc_dapm_widget ac100_codec_widgets[] = { ++ /* DAC */ ++ SND_SOC_DAPM_DAC("Left DAC", NULL, AC100_OUT_MXR_DAC_A_CTRL, ++ AC100_OUT_MXR_DAC_A_CTRL_DAC_AL_EN_OFF, 0), ++ SND_SOC_DAPM_DAC("Right DAC", NULL, AC100_OUT_MXR_DAC_A_CTRL, ++ AC100_OUT_MXR_DAC_A_CTRL_DAC_AR_EN_OFF, 0), ++ ++ /* ADC */ ++ SND_SOC_DAPM_ADC("Left ADC", NULL, AC100_ADC_APC_CTRL, ++ AC100_ADC_APC_CTRL_ADCL_EN_OFF, 0), ++ SND_SOC_DAPM_ADC("Right ADC", NULL, AC100_ADC_APC_CTRL, ++ AC100_ADC_APC_CTRL_ADCR_EN_OFF, 0), ++ ++ /* ++ * Due to this component and the codec belonging to separate DAPM ++ * contexts, we need to manually link the above widgets to their ++ * stream widgets at the card level. ++ */ ++ ++ /* Headphones */ ++ ++ SND_SOC_DAPM_REGULATOR_SUPPLY("cpvdd", 0, 0), ++ SND_SOC_DAPM_MUX("Left Headphone Source", ++ SND_SOC_NOPM, 0, 0, ac100_codec_hp_src), ++ SND_SOC_DAPM_MUX("Right Headphone Source", ++ SND_SOC_NOPM, 0, 0, ac100_codec_hp_src), ++ SND_SOC_DAPM_SWITCH("Left Headphone Switch", ++ SND_SOC_NOPM, 0, 0, &ac100_codec_hp_switch), ++ SND_SOC_DAPM_SWITCH("Right Headphone Switch", ++ SND_SOC_NOPM, 0, 0, &ac100_codec_hp_switch), ++ SND_SOC_DAPM_OUT_DRV("Left Headphone Amp", ++ SND_SOC_NOPM, 0, 0, NULL, 0), ++ SND_SOC_DAPM_OUT_DRV("Right Headphone Amp", ++ SND_SOC_NOPM, 0, 0, NULL, 0), ++ SND_SOC_DAPM_SUPPLY("Headphone Amp", AC100_HPOUT_CTRL, ++ AC100_HPOUT_CTRL_PA_EN_OFF, 0, ++ ac100_codec_hp_power, ++ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), ++ SND_SOC_DAPM_OUTPUT("HP"), ++ ++ /* Earpiece */ ++ ++ SND_SOC_DAPM_MUX("Earpiece Source Playback Route", ++ SND_SOC_NOPM, 0, 0, ac100_codec_earpiece_src), ++ SND_SOC_DAPM_SWITCH("Earpiece", ++ SND_SOC_NOPM, 0, 0, &ac100_codec_earpiece_switch), ++ SND_SOC_DAPM_OUT_DRV("Earpiece Amp", AC100_ERPOUT_CTRL, ++ AC100_ERPOUT_CTRL_PA_EN_OFF, 0, NULL, 0), ++ SND_SOC_DAPM_OUTPUT("EARPIECE"), ++ ++ /* Speaker */ ++ ++ SND_SOC_DAPM_MUX("Left Speaker Source", ++ SND_SOC_NOPM, 0, 0, ac100_codec_spk_src), ++ SND_SOC_DAPM_MUX("Right Speaker Source", ++ SND_SOC_NOPM, 0, 0, ac100_codec_spk_src), ++ SND_SOC_DAPM_SWITCH("Left Speaker Switch", ++ SND_SOC_NOPM, 0, 0, &ac100_codec_spk_switch), ++ SND_SOC_DAPM_SWITCH("Right Speaker Switch", ++ SND_SOC_NOPM, 0, 0, &ac100_codec_spk_switch), ++ SND_SOC_DAPM_SWITCH("Left Speaker Invert Switch", ++ SND_SOC_NOPM, 0, 0, &ac100_codec_spk_inv_switch), ++ SND_SOC_DAPM_SWITCH("Right Speaker Invert Switch", ++ SND_SOC_NOPM, 0, 0, &ac100_codec_spk_inv_switch), ++ SND_SOC_DAPM_OUTPUT("SPKOUTL"), ++ SND_SOC_DAPM_OUTPUT("SPKOUTR"), ++ ++ /* Line Out */ ++ ++ SND_SOC_DAPM_MIXER("Line Out Mixer", SND_SOC_NOPM, 0, 0, ++ ac100_codec_lineout_mixer_controls, ++ ARRAY_SIZE(ac100_codec_lineout_mixer_controls)), ++ SND_SOC_DAPM_SWITCH("Line Out", ++ SND_SOC_NOPM, 0, 0, &ac100_codec_lineout_switch), ++ SND_SOC_DAPM_OUTPUT("LINEOUT"), ++ ++ /* Microphone 1 */ ++ ++ SND_SOC_DAPM_INPUT("MIC1"), ++ SND_SOC_DAPM_SUPPLY("MBIAS", AC100_ADC_APC_CTRL, ++ AC100_ADC_APC_CTRL_MBIAS_EN_OFF, ++ 0, NULL, 0), ++ SND_SOC_DAPM_PGA("Mic1 Amplifier", AC100_ADC_SRC_BST_CTRL, ++ AC100_ADC_SRC_BST_CTRL_MIC1AMPEN_OFF, 0, NULL, 0), ++ ++ /* Microphone 2 and 3 */ ++ ++ SND_SOC_DAPM_INPUT("MIC2"), ++ SND_SOC_DAPM_INPUT("MIC3"), ++ SND_SOC_DAPM_MUX("Mic2 Amplifier Source", ++ SND_SOC_NOPM, 0, 0, ac100_codec_mic2boost_src), ++ SND_SOC_DAPM_SUPPLY("HBIAS", AC100_ADC_APC_CTRL, ++ AC100_ADC_APC_CTRL_HBIAS_EN_OFF, ++ 0, NULL, 0), ++ SND_SOC_DAPM_PGA("Mic2 Amplifier", AC100_ADC_SRC_BST_CTRL, ++ AC100_ADC_SRC_BST_CTRL_MIC2AMPEN_OFF, 0, NULL, 0), ++ ++ /* Line input */ ++ ++ SND_SOC_DAPM_INPUT("LINEIN"), ++ ++ /* Aux input */ ++ ++ SND_SOC_DAPM_INPUT("AUXIN"), ++ ++ /* Output mixers */ ++ SND_SOC_DAPM_MIXER("Left Mixer", AC100_OUT_MXR_DAC_A_CTRL, ++ AC100_OUT_MXR_DAC_A_CTRL_AL_MIX_EN_OFF, 0, ++ ac100_codec_mixer_controls, ++ ARRAY_SIZE(ac100_codec_mixer_controls)), ++ SND_SOC_DAPM_MIXER("Right Mixer", AC100_OUT_MXR_DAC_A_CTRL, ++ AC100_OUT_MXR_DAC_A_CTRL_AR_MIX_EN_OFF, 0, ++ ac100_codec_mixer_controls, ++ ARRAY_SIZE(ac100_codec_mixer_controls)), ++ ++ /* Input mixers */ ++ SND_SOC_DAPM_MIXER("Left ADC Mixer", SND_SOC_NOPM, 0, 0, ++ ac100_codec_adc_mixer_controls, ++ ARRAY_SIZE(ac100_codec_adc_mixer_controls)), ++ SND_SOC_DAPM_MIXER("Right ADC Mixer", SND_SOC_NOPM, 0, 0, ++ ac100_codec_adc_mixer_controls, ++ ARRAY_SIZE(ac100_codec_adc_mixer_controls)), ++}; ++ ++static const struct snd_soc_dapm_route ac100_codec_routes[] = { ++ /* Microphone Routes */ ++ { "Mic1 Amplifier", NULL, "MIC1"}, ++ { "Mic2 Amplifier", NULL, "Mic2 Amplifier Source"}, ++ { "Mic2 Amplifier Source", "Mic2", "MIC2" }, ++ { "Mic2 Amplifier Source", "Mic3", "MIC3" }, ++ ++ /* Mixer Routes */ ++ { "Left Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" }, ++ { "Left Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" }, ++ { "Left Mixer", "Line In Differential Playback Switch", "LINEIN" }, ++ { "Left Mixer", "Line In Playback Switch", "LINEIN" }, ++ { "Left Mixer", "Aux In Playback Switch", "AUXIN" }, ++ { "Left Mixer", "DAC Playback Switch", "Left DAC" }, ++ { "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" }, ++ ++ { "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" }, ++ { "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" }, ++ { "Right Mixer", "Line In Differential Playback Switch", "LINEIN" }, ++ { "Right Mixer", "Line In Playback Switch", "LINEIN" }, ++ { "Right Mixer", "Aux In Playback Switch", "AUXIN" }, ++ { "Right Mixer", "DAC Playback Switch", "Right DAC" }, ++ { "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" }, ++ ++ /* ADC Mixer Routes */ ++ { "Left ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" }, ++ { "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" }, ++ { "Left ADC Mixer", "Line In Differential Capture Switch", "LINEIN" }, ++ { "Left ADC Mixer", "Line In Capture Switch", "LINEIN" }, ++ { "Left ADC Mixer", "Aux In Capture Switch", "AUXIN" }, ++ { "Left ADC Mixer", "Mixer Capture Switch", "Left Mixer" }, ++ { "Left ADC Mixer", "Mixer Reversed Capture Switch", "Right Mixer" }, ++ ++ { "Right ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" }, ++ { "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" }, ++ { "Right ADC Mixer", "Line In Differential Capture Switch", "LINEIN" }, ++ { "Right ADC Mixer", "Line In Capture Switch", "LINEIN" }, ++ { "Right ADC Mixer", "Aux In Capture Switch", "AUXIN" }, ++ { "Right ADC Mixer", "Mixer Capture Switch", "Right Mixer" }, ++ { "Right ADC Mixer", "Mixer Reversed Capture Switch", "Left Mixer" }, ++ ++ /* ADC Routes */ ++ { "Left ADC", NULL, "Left ADC Mixer" }, ++ { "Right ADC", NULL, "Right ADC Mixer" }, ++ ++ /* Headphone Routes */ ++ { "Left Headphone Source", "DAC", "Left DAC" }, ++ { "Left Headphone Source", "Mixer", "Left Mixer" }, ++ { "Left Headphone Switch", "Headphone Playback Switch", "Left Headphone Source" }, ++ { "Left Headphone Amp", NULL, "Left Headphone Switch" }, ++ { "Left Headphone Amp", NULL, "Headphone Amp" }, ++ { "HP", NULL, "Left Headphone Amp" }, ++ ++ { "Right Headphone Source", "DAC", "Right DAC" }, ++ { "Right Headphone Source", "Mixer", "Right Mixer" }, ++ { "Right Headphone Switch", "Headphone Playback Switch", "Right Headphone Source" }, ++ { "Right Headphone Amp", NULL, "Right Headphone Switch" }, ++ { "Right Headphone Amp", NULL, "Headphone Amp" }, ++ { "HP", NULL, "Right Headphone Amp" }, ++ ++ { "Headphone Amp", NULL, "cpvdd" }, ++ ++ /* Speaker Routes */ ++ { "Left Speaker Source", "Stereo", "Left Mixer" }, ++ { "Left Speaker Source", "Mono", "Right Mixer" }, ++ { "Left Speaker Source", "Mono", "Left Mixer" }, ++ { "Left Speaker Switch", "Speaker Playback Switch", "Left Speaker Source" }, ++ { "SPKOUTL", NULL, "Left Speaker Switch" }, ++ ++ { "Right Speaker Source", "Stereo", "Right Mixer" }, ++ { "Right Speaker Source", "Mono", "Right Mixer" }, ++ { "Right Speaker Source", "Mono", "Left Mixer" }, ++ { "Right Speaker Switch", "Speaker Playback Switch", "Right Speaker Source" }, ++ { "SPKOUTR", NULL, "Right Speaker Switch" }, ++ ++ /* Earpiece Routes */ ++ { "Earpiece Source Playback Route", "DACR", "Right DAC" }, ++ { "Earpiece Source Playback Route", "DACL", "Left DAC" }, ++ { "Earpiece Source Playback Route", "Right Mixer", "Right Mixer" }, ++ { "Earpiece Source Playback Route", "Left Mixer", "Left Mixer" }, ++ { "Earpiece", "Playback Switch", "Earpiece Source Playback Route" }, ++ { "Earpiece Amp", NULL, "Earpiece" }, ++ { "EARPIECE", NULL, "Earpiece Amp" }, ++ ++ /* Line-out Routes */ ++ { "Line Out", "Playback Switch", "Line Out Mixer" }, ++ { "Line Out Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" }, ++ { "Line Out Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" }, ++ { "Line Out Mixer", "Right Mixer Playback Switch", "Right Mixer" }, ++ { "Line Out Mixer", "Left Mixer Playback Switch", "Left Mixer" }, ++ { "LINEOUT", NULL, "Line Out" }, ++}; ++ ++static const struct snd_soc_component_driver ac100_codec_analog_cmpnt_drv = { ++ .controls = ac100_codec_controls, ++ .num_controls = ARRAY_SIZE(ac100_codec_controls), ++ .dapm_widgets = ac100_codec_widgets, ++ .num_dapm_widgets = ARRAY_SIZE(ac100_codec_widgets), ++ .dapm_routes = ac100_codec_routes, ++ .num_dapm_routes = ARRAY_SIZE(ac100_codec_routes), ++}; ++ ++static int ac100_codec_probe(struct platform_device *pdev) ++{ ++ struct ac100_dev *ac100 = dev_get_drvdata(pdev->dev.parent); ++ struct ac100_codec *codec; ++ int ret = 0; ++ ++ codec = devm_kzalloc(&pdev->dev, sizeof(*codec), GFP_KERNEL); ++ if (!codec) ++ return -ENOMEM; ++ ++ codec->dev = &pdev->dev; ++ platform_set_drvdata(pdev, codec); ++ ++ snd_soc_component_init_regmap(&codec->component, ac100->regmap); ++ ++ ret = snd_soc_component_initialize(&codec->component, ++ &ac100_codec_analog_cmpnt_drv, ++ &pdev->dev); ++ if (ret < 0) ++ return ret; ++ ++ ret = snd_soc_add_component(&codec->component, NULL, 0); ++ if (ret) { ++ dev_err(&pdev->dev, ++ "Failed to register codec component (%d)\n", ret); ++ return ret; ++ } ++ ++ return ret; ++} ++ ++static void ac100_codec_remove(struct platform_device *pdev) ++{ ++ snd_soc_unregister_component(&pdev->dev); ++ ++ /* ++ * We do not call snd_soc_component_exit_regmap, because regmap ++ * is still owned by the mfd device. ++ */ ++} ++ ++static const struct of_device_id ac100_codec_of_match[] = { ++ { .compatible = "x-powers,ac100-codec-analog" }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, ac100_codec_of_match); ++ ++static struct platform_driver ac100_codec_driver = { ++ .driver = { ++ .name = "ac100-codec-analog", ++ .of_match_table = ac100_codec_of_match, ++ }, ++ .probe = ac100_codec_probe, ++ .remove = ac100_codec_remove, ++}; ++module_platform_driver(ac100_codec_driver); ++ ++MODULE_DESCRIPTION("X-Powers AC100 codec driver"); ++MODULE_AUTHOR("Ondrej Jirman "); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:ac100-codec"); +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/sound-soc-sun8i-codec-Add-support-for-digital-part-of-the-AC100.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/sound-soc-sun8i-codec-Add-support-for-digital-part-of-the-AC100.patch new file mode 100644 index 000000000000..e305ebd3f3c5 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/sound-soc-sun8i-codec-Add-support-for-digital-part-of-the-AC100.patch @@ -0,0 +1,371 @@ +From 098efa67e28d6ed17911b54030337059db3ff7a9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sun, 9 Feb 2020 17:58:59 +0100 +Subject: sound: soc: sun8i-codec: Add support for digital part of the AC100 + codec + +X-Power AC100 codec has almost the same digital part as the A64 codec. +The major difference is that registers are spaced differently. A64 +has codec register address stride of 4 bytes, while AC100 has addresses +packed together. + +We use a custom regmap/regmap_bus to translate register addresses from +the A64 regmap to the correct registers in the AC100's MFD regmap. + +We also need to power the codec, so AC100's regulators are also enabled +by the codec driver. + +Signed-off-by: Ondrej Jirman +--- + sound/soc/sunxi/Kconfig | 4 +- + sound/soc/sunxi/sun8i-codec.c | 216 ++++++++++++++++++++++++++++++++-- + 2 files changed, 212 insertions(+), 8 deletions(-) + +diff --git a/sound/soc/sunxi/Kconfig b/sound/soc/sunxi/Kconfig +index 7b2b3bcb062e..f87b061f59b2 100644 +--- a/sound/soc/sunxi/Kconfig ++++ b/sound/soc/sunxi/Kconfig +@@ -16,9 +16,11 @@ config SND_SUN8I_CODEC + depends on MACH_SUN8I || (ARM64 && ARCH_SUNXI) || COMPILE_TEST + depends on COMMON_CLK + select REGMAP_MMIO ++ select MFD_AC100 + help + This option enables the digital part of the internal audio codec for +- Allwinner sun8i SoC (and particularly A33). ++ Allwinner sun8i SoC (and particularly A33). It also supports digital ++ part of X-Powers AC100. + + Say Y or M if you want to add sun8i digital audio codec support. + +diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c +index b195ddc5d5cf..d6408c7af76c 100644 +--- a/sound/soc/sunxi/sun8i-codec.c ++++ b/sound/soc/sunxi/sun8i-codec.c +@@ -21,7 +21,9 @@ + #include + #include + #include ++#include + #include ++#include + + #include + #include +@@ -194,6 +196,50 @@ + SNDRV_PCM_RATE_192000 |\ + SNDRV_PCM_RATE_KNOT) + ++#define AC100_SYSCLK_CTRL_PLLCLK_ENA_OFF 15 ++#define AC100_SYSCLK_CTRL_PLLCLK_ENA_MASK BIT(15) ++#define AC100_SYSCLK_CTRL_PLLCLK_ENA_DISABLED 0 ++#define AC100_SYSCLK_CTRL_PLLCLK_ENA_ENABLED BIT(15) ++#define AC100_SYSCLK_CTRL_PLLCLK_SRC_OFF 12 ++#define AC100_SYSCLK_CTRL_PLLCLK_SRC_MASK GENMASK(13, 12) ++#define AC100_SYSCLK_CTRL_PLLCLK_SRC_MCLK1 (0x0 << 12) ++#define AC100_SYSCLK_CTRL_PLLCLK_SRC_MCLK2 (0x1 << 12) ++#define AC100_SYSCLK_CTRL_PLLCLK_SRC_BCLK1 (0x2 << 12) ++#define AC100_SYSCLK_CTRL_PLLCLK_SRC_BCLK2 (0x3 << 12) ++#define AC100_SYSCLK_CTRL_I2S1CLK_ENA_OFF 11 ++#define AC100_SYSCLK_CTRL_I2S1CLK_ENA_MASK BIT(11) ++#define AC100_SYSCLK_CTRL_I2S1CLK_ENA_DISABLED 0 ++#define AC100_SYSCLK_CTRL_I2S1CLK_ENA_ENABLED BIT(11) ++#define AC100_SYSCLK_CTRL_I2S1CLK_SRC_OFF 8 ++#define AC100_SYSCLK_CTRL_I2S1CLK_SRC_MASK GENMASK(9, 8) ++#define AC100_SYSCLK_CTRL_I2S1CLK_SRC_MCLK1 (0x0 << 8) ++#define AC100_SYSCLK_CTRL_I2S1CLK_SRC_MCLK2 (0x1 << 8) ++#define AC100_SYSCLK_CTRL_I2S1CLK_SRC_PLL (0x2 << 8) ++#define AC100_SYSCLK_CTRL_I2S2CLK_ENA_OFF 7 ++#define AC100_SYSCLK_CTRL_I2S2CLK_ENA_MASK BIT(7) ++#define AC100_SYSCLK_CTRL_I2S2CLK_ENA_DISABLED 0 ++#define AC100_SYSCLK_CTRL_I2S2CLK_ENA_ENABLED BIT(7) ++#define AC100_SYSCLK_CTRL_I2S2CLK_SRC_OFF 4 ++#define AC100_SYSCLK_CTRL_I2S2CLK_SRC_MASK GENMASK(5, 4) ++#define AC100_SYSCLK_CTRL_I2S2CLK_SRC_MCLK1 (0x0 << 4) ++#define AC100_SYSCLK_CTRL_I2S2CLK_SRC_MCLK2 (0x1 << 4) ++#define AC100_SYSCLK_CTRL_I2S2CLK_SRC_PLL (0x2 << 4) ++#define AC100_SYSCLK_CTRL_SYSCLK_ENA_OFF 3 ++#define AC100_SYSCLK_CTRL_SYSCLK_ENA_MASK BIT(3) ++#define AC100_SYSCLK_CTRL_SYSCLK_ENA_DISABLED 0 ++#define AC100_SYSCLK_CTRL_SYSCLK_ENA_ENABLED BIT(3) ++#define AC100_SYSCLK_CTRL_SYSCLK_SRC_OFF 0 ++#define AC100_SYSCLK_CTRL_SYSCLK_SRC_MASK BIT(0) ++#define AC100_SYSCLK_CTRL_SYSCLK_SRC_I2S1CLK 0 ++#define AC100_SYSCLK_CTRL_SYSCLK_SRC_I2S2CLK BIT(0) ++ ++static const char *const ac100_supply_names[] = { ++ "LDOIN", ++ "AVCC", ++ "VDDIO1", ++ "VDDIO2", ++}; ++ + enum { + SUN8I_CODEC_AIF1, + SUN8I_CODEC_AIF2, +@@ -242,6 +288,9 @@ struct sun8i_codec { + int last_hmic_irq; + unsigned int sysclk_rate; + int sysclk_refcnt; ++ ++ struct regmap *ac100_regmap; ++ struct regulator_bulk_data supplies[ARRAY_SIZE(ac100_supply_names)]; + }; + + static struct snd_soc_dai_driver sun8i_codec_dais[]; +@@ -645,6 +694,10 @@ static int sun8i_codec_hw_params(struct snd_pcm_substream *substream, + SUN8I_AIF_CLK_CTRL_BCLK_DIV_MASK, + bclk_div << SUN8I_AIF_CLK_CTRL_BCLK_DIV); + ++ /* TODO: Implement clk driver for AC100 codec system clock */ ++ if (scodec->ac100_regmap) ++ goto update_sample_rate; ++ + /* + * SYSCLK rate + * +@@ -667,6 +720,7 @@ static int sun8i_codec_hw_params(struct snd_pcm_substream *substream, + scodec->sysclk_refcnt++; + scodec->sysclk_rate = sysclk_rate; + ++update_sample_rate: + aif->lrck_div_order = lrck_div_order; + aif->sample_rate = sample_rate; + aif->open_streams |= BIT(substream->stream); +@@ -684,8 +738,11 @@ static int sun8i_codec_hw_free(struct snd_pcm_substream *substream, + if (aif->open_streams != BIT(substream->stream)) + goto done; + +- clk_rate_exclusive_put(scodec->clk_module); +- scodec->sysclk_refcnt--; ++ if (!scodec->ac100_regmap) { ++ clk_rate_exclusive_put(scodec->clk_module); ++ scodec->sysclk_refcnt--; ++ } ++ + aif->lrck_div_order = 0; + aif->sample_rate = 0; + +@@ -958,8 +1015,6 @@ static const struct snd_kcontrol_new sun8i_dac_mixer_controls[] = { + + static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = { + /* System Clocks */ +- SND_SOC_DAPM_CLOCK_SUPPLY("mod"), +- + SND_SOC_DAPM_SUPPLY("AIF1CLK", + SUN8I_SYSCLK_CTL, + SUN8I_SYSCLK_CTL_AIF1CLK_ENA, 0, NULL, 0), +@@ -1120,8 +1175,6 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = { + + static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = { + /* Clock Routes */ +- { "AIF1CLK", NULL, "mod" }, +- + { "SYSCLK", NULL, "AIF1CLK" }, + + { "CLK AIF1", NULL, "AIF1CLK" }, +@@ -1291,6 +1344,16 @@ static const struct snd_soc_dapm_route sun8i_codec_legacy_routes[] = { + { "AIF1 Slot 0 Right", NULL, "DACR" }, + }; + ++static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets_sun8i[] = { ++ SND_SOC_DAPM_CLOCK_SUPPLY("mod"), ++}; ++ ++static const struct snd_soc_dapm_route sun8i_codec_dapm_routes_sun8i[] = { ++ { "AIF1CLK", NULL, "mod" }, ++}; ++ ++static int ac100_codec_component_probe(struct snd_soc_component *component); ++ + static int sun8i_codec_component_probe(struct snd_soc_component *component) + { + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); +@@ -1299,6 +1362,21 @@ static int sun8i_codec_component_probe(struct snd_soc_component *component) + + scodec->component = component; + ++ if (scodec->ac100_regmap) ++ return ac100_codec_component_probe(component); ++ ++ ret = snd_soc_dapm_new_controls(dapm, ++ sun8i_codec_dapm_widgets_sun8i, ++ ARRAY_SIZE(sun8i_codec_dapm_widgets_sun8i)); ++ if (ret) ++ return ret; ++ ++ ret = snd_soc_dapm_add_routes(dapm, ++ sun8i_codec_dapm_routes_sun8i, ++ ARRAY_SIZE(sun8i_codec_dapm_routes_sun8i)); ++ if (ret) ++ return ret; ++ + /* Add widgets for backward compatibility with old device trees. */ + if (scodec->quirks->legacy_widgets) { + ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_legacy_widgets, +@@ -1640,7 +1718,7 @@ static bool sun8i_codec_volatile_reg(struct device *dev, unsigned int reg) + return reg == SUN8I_HMIC_STS; + } + +-static const struct regmap_config sun8i_codec_regmap_config = { ++static struct regmap_config sun8i_codec_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, +@@ -1650,12 +1728,124 @@ static const struct regmap_config sun8i_codec_regmap_config = { + .cache_type = REGCACHE_FLAT, + }; + ++/* AC100 Codec Support (digital parts) */ ++ ++static int sun8i_codec_ac100_regmap_read(void *context, ++ unsigned int reg, unsigned int *val) ++{ ++ struct sun8i_codec *scodec = context; ++ ++ return regmap_read(scodec->ac100_regmap, reg / 4, val); ++} ++ ++static int sun8i_codec_ac100_regmap_write(void *context, ++ unsigned int reg, unsigned int val) ++{ ++ struct sun8i_codec *scodec = context; ++ ++ return regmap_write(scodec->ac100_regmap, reg / 4, val); ++} ++ ++static struct regmap_bus sun8i_codec_ac100_regmap_bus = { ++ .reg_write = sun8i_codec_ac100_regmap_write, ++ .reg_read = sun8i_codec_ac100_regmap_read, ++}; ++ ++static int ac100_codec_component_probe(struct snd_soc_component *component) ++{ ++ struct sun8i_codec *scodec = snd_soc_component_get_drvdata(component); ++ ++ /* ++ * The system clock(SYSCLK) of AC100 must be 512*fs(fs=48KHz or 44.1KHz) ++ * Source clocks from the SoC. ++ */ ++ ++ regmap_update_bits(scodec->ac100_regmap, AC100_SYSCLK_CTRL, ++ AC100_SYSCLK_CTRL_I2S1CLK_SRC_MASK, ++ AC100_SYSCLK_CTRL_I2S1CLK_SRC_MCLK1); ++ regmap_update_bits(scodec->ac100_regmap, AC100_SYSCLK_CTRL, ++ AC100_SYSCLK_CTRL_I2S2CLK_SRC_MASK, ++ AC100_SYSCLK_CTRL_I2S2CLK_SRC_MCLK1); ++ regmap_update_bits(scodec->ac100_regmap, AC100_SYSCLK_CTRL, ++ AC100_SYSCLK_CTRL_SYSCLK_SRC_MASK, ++ AC100_SYSCLK_CTRL_SYSCLK_SRC_I2S1CLK); ++ ++ /* Program the default sample rate. */ ++ return sun8i_codec_update_sample_rate(scodec); ++} ++ ++static int sun8i_codec_probe_ac100(struct platform_device *pdev) ++{ ++ struct ac100_dev *ac100 = dev_get_drvdata(pdev->dev.parent); ++ struct device* dev = &pdev->dev; ++ struct sun8i_codec *scodec; ++ int ret, i; ++ ++ scodec = devm_kzalloc(dev, sizeof(*scodec), GFP_KERNEL); ++ if (!scodec) ++ return -ENOMEM; ++ ++ scodec->quirks = of_device_get_match_data(&pdev->dev); ++ scodec->ac100_regmap = ac100->regmap; ++ ++ platform_set_drvdata(pdev, scodec); ++ ++ /* ++ * Caching is done by the MFD regmap, so disable the mapped regmap ++ * cache. ++ */ ++ sun8i_codec_regmap_config.cache_type = REGCACHE_NONE; ++ ++ /* ++ * We need to create a custom regmap_bus that will map reads/writes to ++ * the MFD regmap ++ */ ++ scodec->regmap = __regmap_lockdep_wrapper(__devm_regmap_init, ++ "ac100-regmap-codec", dev, ++ &sun8i_codec_ac100_regmap_bus, scodec, ++ &sun8i_codec_regmap_config); ++ if (IS_ERR(scodec->regmap)) ++ return dev_err_probe(dev, PTR_ERR(scodec->regmap), ++ "Failed to create our regmap\n"); ++ ++ for (i = 0; i < ARRAY_SIZE(scodec->supplies); i++) ++ scodec->supplies[i].supply = ac100_supply_names[i]; ++ ++ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(scodec->supplies), ++ scodec->supplies); ++ if (ret) ++ return dev_err_probe(dev, ret, "Failed to request supplies\n"); ++ ++ ret = regulator_bulk_enable(ARRAY_SIZE(scodec->supplies), ++ scodec->supplies); ++ if (ret) ++ return dev_err_probe(dev, ret, "Failed to enable supplies\n"); ++ ++ ret = devm_snd_soc_register_component(dev, &sun8i_soc_component, ++ sun8i_codec_dais, ++ ARRAY_SIZE(sun8i_codec_dais)); ++ if (ret) { ++ dev_err_probe(dev, ret, "Failed to register codec\n"); ++ goto err_disable_reg; ++ } ++ ++ return 0; ++ ++err_disable_reg: ++ regulator_bulk_disable(ARRAY_SIZE(scodec->supplies), ++ scodec->supplies); ++ return ret; ++} ++ + static int sun8i_codec_probe(struct platform_device *pdev) + { + struct sun8i_codec *scodec; + void __iomem *base; + int ret; + ++ if (of_device_is_compatible(pdev->dev.of_node, "x-powers,ac100-codec")) ++ return sun8i_codec_probe_ac100(pdev); ++ + scodec = devm_kzalloc(&pdev->dev, sizeof(*scodec), GFP_KERNEL); + if (!scodec) + return -ENOMEM; +@@ -1728,6 +1918,14 @@ static int sun8i_codec_probe(struct platform_device *pdev) + + static void sun8i_codec_remove(struct platform_device *pdev) + { ++ struct sun8i_codec *scodec = dev_get_drvdata(&pdev->dev); ++ ++ if (scodec->ac100_regmap) { ++ regulator_bulk_disable(ARRAY_SIZE(scodec->supplies), ++ scodec->supplies); ++ return; ++ } ++ + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + sun8i_codec_runtime_suspend(&pdev->dev); +@@ -1744,9 +1942,13 @@ static const struct sun8i_codec_quirks sun50i_a64_quirks = { + .jack_detection = true, + }; + ++static const struct sun8i_codec_quirks ac100_quirks = { ++}; ++ + static const struct of_device_id sun8i_codec_of_match[] = { + { .compatible = "allwinner,sun8i-a33-codec", .data = &sun8i_a33_quirks }, + { .compatible = "allwinner,sun50i-a64-codec", .data = &sun50i_a64_quirks }, ++ { .compatible = "x-powers,ac100-codec", .data = &ac100_quirks }, + {} + }; + MODULE_DEVICE_TABLE(of, sun8i_codec_of_match); +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/sunxi-Use-dev_err_probe-to-handle-EPROBE_DEFER-errors.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/sunxi-Use-dev_err_probe-to-handle-EPROBE_DEFER-errors.patch new file mode 100644 index 000000000000..7ae868ce5db9 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/sunxi-Use-dev_err_probe-to-handle-EPROBE_DEFER-errors.patch @@ -0,0 +1,150 @@ +From d4a21d660b3fd92157aef4a08d0451c171c529e9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Mon, 2 Sep 2019 14:51:17 +0200 +Subject: sunxi: Use dev_err_probe to handle EPROBE_DEFER errors + +This makes it easier to debug EPROBE_DEFER issues via debugfs. + +Signed-off-by: Ondrej Jirman +--- + arch/arm/kernel/topology.c | 2 +- + drivers/gpu/drm/panel/panel-sitronix-st7703.c | 2 +- + drivers/i2c/busses/i2c-gpio.c | 11 +++++++++-- + drivers/phy/allwinner/phy-sun4i-usb.c | 16 ++++++++++------ + drivers/phy/allwinner/phy-sun6i-mipi-dphy.c | 6 ++++-- + 5 files changed, 25 insertions(+), 12 deletions(-) + +diff --git a/arch/arm/kernel/topology.c b/arch/arm/kernel/topology.c +index 2336ee2aa44a..20efde3cb1c2 100644 +--- a/arch/arm/kernel/topology.c ++++ b/arch/arm/kernel/topology.c +@@ -121,7 +121,7 @@ static void __init parse_dt_topology(void) + + rate = of_get_property(cn, "clock-frequency", &len); + if (!rate || len != 4) { +- pr_err("%pOF missing clock-frequency property\n", cn); ++ //pr_err("%pOF missing clock-frequency property\n", cn); + continue; + } + +diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7703.c b/drivers/gpu/drm/panel/panel-sitronix-st7703.c +index 8e359c9e0a09..ff6871e93730 100644 +--- a/drivers/gpu/drm/panel/panel-sitronix-st7703.c ++++ b/drivers/gpu/drm/panel/panel-sitronix-st7703.c +@@ -904,7 +904,7 @@ static int st7703_probe(struct mipi_dsi_device *dsi) + + ret = mipi_dsi_attach(dsi); + if (ret < 0) { +- dev_err(dev, "mipi_dsi_attach failed (%d). Is host ready?\n", ret); ++ dev_err_probe(dev, ret, "mipi_dsi_attach failed\n"); + drm_panel_remove(&ctx->panel); + return ret; + } +diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c +index f4355b17bfbf..3f158ca4b58f 100644 +--- a/drivers/i2c/busses/i2c-gpio.c ++++ b/drivers/i2c/busses/i2c-gpio.c +@@ -314,6 +314,13 @@ static struct gpio_desc *i2c_gpio_get_desc(struct device *dev, + return retdesc; + } + ++ /* return early so that EPROBE_DEFER is not ignored on named gpios */ ++ if (PTR_ERR(retdesc) == -EPROBE_DEFER) { ++ dev_err_probe(dev, PTR_ERR(retdesc), ++ "error trying to get descriptor\n"); ++ return retdesc; ++ } ++ + retdesc = devm_gpiod_get_index(dev, NULL, index, gflags); + if (!IS_ERR(retdesc)) { + dev_dbg(dev, "got GPIO from index %u\n", index); +@@ -330,8 +337,8 @@ static struct gpio_desc *i2c_gpio_get_desc(struct device *dev, + if (ret == -ENOENT) + retdesc = ERR_PTR(-EPROBE_DEFER); + +- if (PTR_ERR(retdesc) != -EPROBE_DEFER) +- dev_err(dev, "error trying to get descriptor: %d\n", ret); ++ dev_err_probe(dev, PTR_ERR(retdesc), ++ "error trying to get descriptor\n"); + + return retdesc; + } +diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c +index 3a42865f9a5b..3d8d94594bc5 100644 +--- a/drivers/phy/allwinner/phy-sun4i-usb.c ++++ b/drivers/phy/allwinner/phy-sun4i-usb.c +@@ -774,14 +774,16 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) + data->id_det_gpio = devm_gpiod_get_optional(dev, "usb0_id_det", + GPIOD_IN); + if (IS_ERR(data->id_det_gpio)) { +- dev_err(dev, "Couldn't request ID GPIO\n"); ++ dev_err_probe(dev, PTR_ERR(data->id_det_gpio), ++ "Couldn't request ID GPIO\n"); + return PTR_ERR(data->id_det_gpio); + } + + data->vbus_det_gpio = devm_gpiod_get_optional(dev, "usb0_vbus_det", + GPIOD_IN); + if (IS_ERR(data->vbus_det_gpio)) { +- dev_err(dev, "Couldn't request VBUS detect GPIO\n"); ++ dev_err_probe(dev, PTR_ERR(data->vbus_det_gpio), ++ "Couldn't request VBUS detect GPIO\n"); + return PTR_ERR(data->vbus_det_gpio); + } + +@@ -789,7 +791,8 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) + data->vbus_power_supply = devm_power_supply_get_by_phandle(dev, + "usb0_vbus_power-supply"); + if (IS_ERR(data->vbus_power_supply)) { +- dev_err(dev, "Couldn't get the VBUS power supply\n"); ++ dev_err_probe(dev, PTR_ERR(data->vbus_power_supply), ++ "Couldn't get the VBUS power supply\n"); + return PTR_ERR(data->vbus_power_supply); + } + +@@ -822,8 +825,8 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) + phy->vbus = devm_regulator_get_optional(dev, name); + if (IS_ERR(phy->vbus)) { + if (PTR_ERR(phy->vbus) == -EPROBE_DEFER) { +- dev_err(dev, +- "Couldn't get regulator %s... Deferring probe\n", ++ dev_err_probe(dev, PTR_ERR(phy->vbus), ++ "Couldn't get regulator %s\n", + name); + return -EPROBE_DEFER; + } +@@ -838,7 +841,8 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) + + phy->clk = devm_clk_get(dev, name); + if (IS_ERR(phy->clk)) { +- dev_err(dev, "failed to get clock %s\n", name); ++ dev_err_probe(dev, PTR_ERR(phy->clk), ++ "failed to get clock %s\n", name); + return PTR_ERR(phy->clk); + } + +diff --git a/drivers/phy/allwinner/phy-sun6i-mipi-dphy.c b/drivers/phy/allwinner/phy-sun6i-mipi-dphy.c +index a19a27cea860..0107514e7d2b 100644 +--- a/drivers/phy/allwinner/phy-sun6i-mipi-dphy.c ++++ b/drivers/phy/allwinner/phy-sun6i-mipi-dphy.c +@@ -584,13 +584,15 @@ static int sun6i_dphy_probe(struct platform_device *pdev) + dphy->regs = devm_regmap_init_mmio_clk(&pdev->dev, "bus", + regs, &sun6i_dphy_regmap_config); + if (IS_ERR(dphy->regs)) { +- dev_err(&pdev->dev, "Couldn't create the DPHY encoder regmap\n"); ++ dev_err_probe(&pdev->dev, PTR_ERR(dphy->regs), ++ "Couldn't create the DPHY encoder regmap\n"); + return PTR_ERR(dphy->regs); + } + + dphy->reset = devm_reset_control_get_shared(&pdev->dev, NULL); + if (IS_ERR(dphy->reset)) { +- dev_err(&pdev->dev, "Couldn't get our reset line\n"); ++ dev_err_probe(&pdev->dev, PTR_ERR(dphy->reset), ++ "Couldn't get our reset line\n"); + return PTR_ERR(dphy->reset); + } + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/thermal-sun8i-Be-loud-when-probe-fails.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/thermal-sun8i-Be-loud-when-probe-fails.patch new file mode 100644 index 000000000000..8826a4a6f8f5 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/thermal-sun8i-Be-loud-when-probe-fails.patch @@ -0,0 +1,88 @@ +From a1a6076e6a12342802c48ab522e841b8386ab0fc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Wed, 8 Jul 2020 12:21:14 +0200 +Subject: thermal: sun8i: Be loud when probe fails + +I noticed several mobile Linux distributions failing to enable the +thermal regulation correctly, because the kernel is silent +when thermal driver fails to probe. Add enough error reporting +to debug issues and warn users in case thermal sensor is failing +to probe. + +Signed-off-by: Ondrej Jirman +--- + drivers/thermal/sun8i_thermal.c | 22 +++++++++++++++++++--- + 1 file changed, 19 insertions(+), 3 deletions(-) + +diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c +index 22674790629a..1fc7fe3a1531 100644 +--- a/drivers/thermal/sun8i_thermal.c ++++ b/drivers/thermal/sun8i_thermal.c +@@ -309,8 +309,13 @@ static int sun8i_ths_calibrate(struct ths_device *tmdev) + + calcell = nvmem_cell_get(dev, "calibration"); + if (IS_ERR(calcell)) { ++ dev_err_probe(dev, PTR_ERR(calcell), ++ "Failed to get calibration nvmem cell (%pe)\n", ++ calcell); ++ + if (PTR_ERR(calcell) == -EPROBE_DEFER) + return -EPROBE_DEFER; ++ + /* + * Even if the external calibration data stored in sid is + * not accessible, the THS hardware can still work, although +@@ -330,6 +335,8 @@ static int sun8i_ths_calibrate(struct ths_device *tmdev) + caldata = nvmem_cell_read(calcell, &callen); + if (IS_ERR(caldata)) { + ret = PTR_ERR(caldata); ++ dev_err(dev, "Failed to read calibration data (%pe)\n", ++ caldata); + goto out; + } + +@@ -385,13 +392,17 @@ static int sun8i_ths_resource_init(struct ths_device *tmdev) + return PTR_ERR(base); + + tmdev->regmap = devm_regmap_init_mmio(dev, base, &config); +- if (IS_ERR(tmdev->regmap)) ++ if (IS_ERR(tmdev->regmap)) { ++ dev_err(dev, "Failed to init regmap (%pe)\n", tmdev->regmap); + return PTR_ERR(tmdev->regmap); ++ } + + if (tmdev->chip->has_bus_clk_reset) { + tmdev->reset = devm_reset_control_get(dev, NULL); +- if (IS_ERR(tmdev->reset)) ++ if (IS_ERR(tmdev->reset)) { ++ dev_err(dev, "Failed to get reset (%pe)\n", tmdev->reset); + return PTR_ERR(tmdev->reset); ++ } + + ret = reset_control_deassert(tmdev->reset); + if (ret) +@@ -542,6 +553,9 @@ static int sun8i_ths_register(struct ths_device *tmdev) + if (IS_ERR(tmdev->sensor[i].tzd)) { + if (PTR_ERR(tmdev->sensor[i].tzd) == -EPROBE_DEFER) + return PTR_ERR(tmdev->sensor[i].tzd); ++ dev_err(tmdev->dev, ++ "Failed to register sensor %d (%pe)\n", ++ i, tmdev->sensor[i].tzd); + continue; + } + +@@ -590,8 +604,10 @@ static int sun8i_ths_probe(struct platform_device *pdev) + ret = devm_request_threaded_irq(dev, irq, NULL, + sun8i_irq_thread, + IRQF_ONESHOT, "ths", tmdev); +- if (ret) ++ if (ret) { ++ dev_err(dev, "Failed to request irq (%d)\n", ret); + return ret; ++ } + + return 0; + } +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/usb-gadget-Fix-dangling-pointer-in-netdev-private-data.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-gadget-Fix-dangling-pointer-in-netdev-private-data.patch new file mode 100644 index 000000000000..ea0cf34ca45a --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-gadget-Fix-dangling-pointer-in-netdev-private-data.patch @@ -0,0 +1,307 @@ +From f2dc4bc534baf1d2cf7f43dbf040601e1542f9c5 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Thu, 7 Sep 2023 14:07:26 +0200 +Subject: usb: gadget: Fix dangling pointer in netdev private data + +Some USB drivers destroy and re-create the gadget regularly. This +leads to stale gadget pointer in gether_* using USB gadget functions +(ecm, eem, ncm, rndis, etc.). + +Don't parent the netdev to the gadget device, and set gadget to +NULL in function unbind callback, to avoid potential user-after-free +issues. + +Signed-off-by: Ondrej Jirman +--- + drivers/usb/gadget/function/f_ecm.c | 12 ++++++----- + drivers/usb/gadget/function/f_eem.c | 28 +++++++++++++------------- + drivers/usb/gadget/function/f_ncm.c | 9 ++++++--- + drivers/usb/gadget/function/f_rndis.c | 26 ++++++++++++------------ + drivers/usb/gadget/function/f_subset.c | 18 +++++++++++------ + drivers/usb/gadget/function/u_ether.c | 10 ++++----- + 6 files changed, 56 insertions(+), 47 deletions(-) + +diff --git a/drivers/usb/gadget/function/f_ecm.c b/drivers/usb/gadget/function/f_ecm.c +index 80841de845b0..4758994b5f26 100644 +--- a/drivers/usb/gadget/function/f_ecm.c ++++ b/drivers/usb/gadget/function/f_ecm.c +@@ -685,14 +685,12 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) + ecm_opts = container_of(f->fi, struct f_ecm_opts, func_inst); + + mutex_lock(&ecm_opts->lock); +- + gether_set_gadget(ecm_opts->net, cdev->gadget); +- + if (!ecm_opts->bound) { + status = gether_register_netdev(ecm_opts->net); +- ecm_opts->bound = true; ++ if (!status) ++ ecm_opts->bound = true; + } +- + mutex_unlock(&ecm_opts->lock); + if (status) + return status; +@@ -907,7 +905,9 @@ static void ecm_free(struct usb_function *f) + + static void ecm_unbind(struct usb_configuration *c, struct usb_function *f) + { +- struct f_ecm *ecm = func_to_ecm(f); ++ struct f_ecm *ecm = func_to_ecm(f); ++ struct f_ecm_opts *opts = container_of(f->fi, struct f_ecm_opts, ++ func_inst); + + DBG(c->cdev, "ecm unbind\n"); + +@@ -920,6 +920,8 @@ static void ecm_unbind(struct usb_configuration *c, struct usb_function *f) + + kfree(ecm->notify_req->buf); + usb_ep_free_request(ecm->notify, ecm->notify_req); ++ ++ gether_set_gadget(opts->net, NULL); + } + + static struct usb_function *ecm_alloc(struct usb_function_instance *fi) +diff --git a/drivers/usb/gadget/function/f_eem.c b/drivers/usb/gadget/function/f_eem.c +index 6de81ea17274..b1e1a26808dd 100644 +--- a/drivers/usb/gadget/function/f_eem.c ++++ b/drivers/usb/gadget/function/f_eem.c +@@ -247,28 +247,23 @@ static int eem_bind(struct usb_configuration *c, struct usb_function *f) + struct usb_composite_dev *cdev = c->cdev; + struct f_eem *eem = func_to_eem(f); + struct usb_string *us; +- int status; ++ int status = 0; + struct usb_ep *ep; + + struct f_eem_opts *eem_opts; + + eem_opts = container_of(f->fi, struct f_eem_opts, func_inst); +- /* +- * in drivers/usb/gadget/configfs.c:configfs_composite_bind() +- * configurations are bound in sequence with list_for_each_entry, +- * in each configuration its functions are bound in sequence +- * with list_for_each_entry, so we assume no race condition +- * with regard to eem_opts->bound access +- */ ++ ++ mutex_lock(&eem_opts->lock); ++ gether_set_gadget(eem_opts->net, cdev->gadget); + if (!eem_opts->bound) { +- mutex_lock(&eem_opts->lock); +- gether_set_gadget(eem_opts->net, cdev->gadget); + status = gether_register_netdev(eem_opts->net); +- mutex_unlock(&eem_opts->lock); +- if (status) +- return status; +- eem_opts->bound = true; ++ if (!status) ++ eem_opts->bound = true; + } ++ mutex_unlock(&eem_opts->lock); ++ if (status) ++ return status; + + us = usb_gstrings_attach(cdev, eem_strings, + ARRAY_SIZE(eem_string_defs)); +@@ -635,9 +630,14 @@ static void eem_free(struct usb_function *f) + + static void eem_unbind(struct usb_configuration *c, struct usb_function *f) + { ++ struct f_eem_opts *opts = container_of(f->fi, struct f_eem_opts, ++ func_inst); ++ + DBG(c->cdev, "eem unbind\n"); + + usb_free_all_descriptors(f); ++ ++ gether_set_gadget(opts->net, NULL); + } + + static struct usb_function *eem_alloc(struct usb_function_instance *fi) +diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c +index f60576a65ca6..454a451491b1 100644 +--- a/drivers/usb/gadget/function/f_ncm.c ++++ b/drivers/usb/gadget/function/f_ncm.c +@@ -1455,14 +1455,13 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) + if (!ncm_opts->bound) { + ncm_opts->net->mtu = (ncm_opts->max_segment_size - ETH_HLEN); + status = gether_register_netdev(ncm_opts->net); ++ if (!status) ++ ncm_opts->bound = true; + } + mutex_unlock(&ncm_opts->lock); +- + if (status) + goto fail; + +- ncm_opts->bound = true; +- + us = usb_gstrings_attach(cdev, ncm_strings, + ARRAY_SIZE(ncm_string_defs)); + if (IS_ERR(us)) { +@@ -1729,6 +1728,8 @@ static void ncm_free(struct usb_function *f) + static void ncm_unbind(struct usb_configuration *c, struct usb_function *f) + { + struct f_ncm *ncm = func_to_ncm(f); ++ struct f_ncm_opts *opts = container_of(f->fi, struct f_ncm_opts, ++ func_inst); + + DBG(c->cdev, "ncm unbind\n"); + +@@ -1747,6 +1748,8 @@ static void ncm_unbind(struct usb_configuration *c, struct usb_function *f) + + kfree(ncm->notify_req->buf); + usb_ep_free_request(ncm->notify, ncm->notify_req); ++ ++ gether_set_gadget(opts->net, NULL); + } + + static struct usb_function *ncm_alloc(struct usb_function_instance *fi) +diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c +index 7cec19d65fb5..c2138f17a1c4 100644 +--- a/drivers/usb/gadget/function/f_rndis.c ++++ b/drivers/usb/gadget/function/f_rndis.c +@@ -658,7 +658,7 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) + struct usb_composite_dev *cdev = c->cdev; + struct f_rndis *rndis = func_to_rndis(f); + struct usb_string *us; +- int status; ++ int status = 0; + struct usb_ep *ep; + + struct f_rndis_opts *rndis_opts; +@@ -681,20 +681,16 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) + rndis_iad_descriptor.bFunctionSubClass = rndis_opts->subclass; + rndis_iad_descriptor.bFunctionProtocol = rndis_opts->protocol; + +- /* +- * in drivers/usb/gadget/configfs.c:configfs_composite_bind() +- * configurations are bound in sequence with list_for_each_entry, +- * in each configuration its functions are bound in sequence +- * with list_for_each_entry, so we assume no race condition +- * with regard to rndis_opts->bound access +- */ ++ mutex_lock(&rndis_opts->lock); ++ gether_set_gadget(rndis_opts->net, cdev->gadget); + if (!rndis_opts->bound) { +- gether_set_gadget(rndis_opts->net, cdev->gadget); + status = gether_register_netdev(rndis_opts->net); +- if (status) +- goto fail; +- rndis_opts->bound = true; ++ if (!status) ++ rndis_opts->bound = true; + } ++ mutex_unlock(&rndis_opts->lock); ++ if (status) ++ goto fail; + + us = usb_gstrings_attach(cdev, rndis_strings, + ARRAY_SIZE(rndis_string_defs)); +@@ -954,7 +950,9 @@ static void rndis_free(struct usb_function *f) + + static void rndis_unbind(struct usb_configuration *c, struct usb_function *f) + { +- struct f_rndis *rndis = func_to_rndis(f); ++ struct f_rndis *rndis = func_to_rndis(f); ++ struct f_rndis_opts *opts = container_of(f->fi, struct f_rndis_opts, ++ func_inst); + + kfree(f->os_desc_table); + f->os_desc_n = 0; +@@ -962,6 +960,8 @@ static void rndis_unbind(struct usb_configuration *c, struct usb_function *f) + + kfree(rndis->notify_req->buf); + usb_ep_free_request(rndis->notify, rndis->notify_req); ++ ++ gether_set_gadget(opts->net, NULL); + } + + static struct usb_function *rndis_alloc(struct usb_function_instance *fi) +diff --git a/drivers/usb/gadget/function/f_subset.c b/drivers/usb/gadget/function/f_subset.c +index ea3fdd842462..b595beb5f474 100644 +--- a/drivers/usb/gadget/function/f_subset.c ++++ b/drivers/usb/gadget/function/f_subset.c +@@ -308,15 +308,16 @@ geth_bind(struct usb_configuration *c, struct usb_function *f) + * with list_for_each_entry, so we assume no race condition + * with regard to gether_opts->bound access + */ ++ mutex_lock(&gether_opts->lock); ++ gether_set_gadget(gether_opts->net, cdev->gadget); + if (!gether_opts->bound) { +- mutex_lock(&gether_opts->lock); +- gether_set_gadget(gether_opts->net, cdev->gadget); + status = gether_register_netdev(gether_opts->net); +- mutex_unlock(&gether_opts->lock); +- if (status) +- return status; +- gether_opts->bound = true; ++ if (!status) ++ gether_opts->bound = true; + } ++ mutex_unlock(&gether_opts->lock); ++ if (status) ++ return status; + + us = usb_gstrings_attach(cdev, geth_strings, + ARRAY_SIZE(geth_string_defs)); +@@ -456,8 +457,13 @@ static void geth_free(struct usb_function *f) + + static void geth_unbind(struct usb_configuration *c, struct usb_function *f) + { ++ struct f_gether_opts *opts = container_of(f->fi, struct f_gether_opts, ++ func_inst); ++ + geth_string_defs[0].id = 0; + usb_free_all_descriptors(f); ++ ++ gether_set_gadget(opts->net, NULL); + } + + static struct usb_function *geth_alloc(struct usb_function_instance *fi) +diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c +index f58590bf5e02..a522e9d43a3a 100644 +--- a/drivers/usb/gadget/function/u_ether.c ++++ b/drivers/usb/gadget/function/u_ether.c +@@ -112,8 +112,10 @@ static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p) + + strscpy(p->driver, "g_ether", sizeof(p->driver)); + strscpy(p->version, UETH__VERSION, sizeof(p->version)); +- strscpy(p->fw_version, dev->gadget->name, sizeof(p->fw_version)); +- strscpy(p->bus_info, dev_name(&dev->gadget->dev), sizeof(p->bus_info)); ++ if (dev->gadget) { ++ strscpy(p->fw_version, dev->gadget->name, sizeof(p->fw_version)); ++ strscpy(p->bus_info, dev_name(&dev->gadget->dev), sizeof(p->bus_info)); ++ } + } + + /* REVISIT can also support: +@@ -787,7 +789,6 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g, + net->max_mtu = GETHER_MAX_MTU_SIZE; + + dev->gadget = g; +- SET_NETDEV_DEV(net, &g->dev); + SET_NETDEV_DEVTYPE(net, &gadget_type); + + status = register_netdev(net); +@@ -860,8 +861,6 @@ int gether_register_netdev(struct net_device *net) + struct usb_gadget *g; + int status; + +- if (!net->dev.parent) +- return -EINVAL; + dev = netdev_priv(net); + g = dev->gadget; + +@@ -892,7 +891,6 @@ void gether_set_gadget(struct net_device *net, struct usb_gadget *g) + + dev = netdev_priv(net); + dev->gadget = g; +- SET_NETDEV_DEV(net, &g->dev); + } + EXPORT_SYMBOL_GPL(gether_set_gadget); + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/usb-musb-sunxi-Avoid-enabling-host-side-code-on-SoCs-where-it-s.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-musb-sunxi-Avoid-enabling-host-side-code-on-SoCs-where-it-s.patch new file mode 100644 index 000000000000..501c5fb6a4aa --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-musb-sunxi-Avoid-enabling-host-side-code-on-SoCs-where-it-s.patch @@ -0,0 +1,107 @@ +From 0766c518fb42f94f9ae7c24dcdaf79423eaa3b43 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Mon, 6 Mar 2023 01:19:52 +0100 +Subject: usb: musb: sunxi: Avoid enabling host side code on SoCs where it's + not used + +For some sunxi SoCs host side code is active but unused, because +phy re-routes USB handling to a regular EHCI/OHCI driver. + +When host side code of musb is left initialized it sometimes +interferes with suspend to RAM. Disabling the host side code +on SoCs that re-route host mode to a regular *HCI hardware +block fixes the issue. + +Issue: https://gitlab.com/postmarketOS/pmaports/-/issues/1478 + +Signed-off-by: Ondrej Jirman +--- + drivers/usb/musb/musb_core.c | 10 ++++++++++ + drivers/usb/musb/sunxi.c | 30 +++++++++++++++++++++++------- + 2 files changed, 33 insertions(+), 7 deletions(-) + +diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c +index 7f349f5e781d..521074d68c3b 100644 +--- a/drivers/usb/musb/musb_core.c ++++ b/drivers/usb/musb/musb_core.c +@@ -976,6 +976,16 @@ static void musb_handle_intr_disconnect(struct musb *musb, u8 devctl) + case OTG_STATE_B_IDLE: + musb_g_disconnect(musb); + break; ++ case OTG_STATE_A_WAIT_VRISE: ++ /* ++ * For sunxi use case, where host side of the musb driver ++ * is not used for host mode, we want to ignore ++ * OTG_STATE_A_WAIT_VRISE state set in sunxi glue code ++ * when transitioning to host mode on disconnect, in ++ * order to not confuse the dmesg reader about possible ++ * issues. ++ */ ++ break; + default: + WARNING("unhandled DISCONNECT transition (%s)\n", + musb_otg_state_string(musb)); +diff --git a/drivers/usb/musb/sunxi.c b/drivers/usb/musb/sunxi.c +index eac1cde86be3..e7b9c9bd56aa 100644 +--- a/drivers/usb/musb/sunxi.c ++++ b/drivers/usb/musb/sunxi.c +@@ -350,12 +350,6 @@ static int sunxi_musb_set_mode(struct musb *musb, u8 mode) + if (glue->phy_mode == new_mode) + return 0; + +- if (musb->port_mode != MUSB_OTG) { +- dev_err(musb->controller->parent, +- "Error changing modes is only supported in dual role mode\n"); +- return -EINVAL; +- } +- + if (musb->port1_status & USB_PORT_STAT_ENABLE) + musb_root_disconnect(musb); + +@@ -674,13 +668,22 @@ static const struct musb_hdrc_config sunxi_musb_hdrc_config_4eps = { + .ram_bits = SUNXI_MUSB_RAM_BITS, + }; + ++static const char * const sunxi_musb_host_rerouted_phys[] = { ++ "allwinner,sun8i-h3-usb-phy", ++ "allwinner,sun8i-r40-usb-phy", ++ "allwinner,sun8i-v3s-usb-phy", ++ "allwinner,sun50i-a64-usb-phy", ++ "allwinner,sun50i-h6-usb-phy", ++ NULL, ++}; ++ + static int sunxi_musb_probe(struct platform_device *pdev) + { + struct musb_hdrc_platform_data pdata; + struct platform_device_info pinfo; + struct sunxi_glue *glue; +- struct device_node *np = pdev->dev.of_node; + const struct sunxi_musb_cfg *cfg; ++ struct device_node *np = pdev->dev.of_node, *phy_np; + int ret; + + if (!np) { +@@ -761,6 +764,19 @@ static int sunxi_musb_probe(struct platform_device *pdev) + return dev_err_probe(&pdev->dev, PTR_ERR(glue->phy), + "Error getting phy\n"); + ++ /* ++ * Host mode is handled outside of the musb driver on some allwinner ++ * SoCs. We don't need musb host side code to be enabled at all. ++ * In fact it causes occasional issues with suspend to ram, when ++ * the host side code is enabled and unused (due to phy being re-routed ++ * to a different *HCI controller). ++ */ ++ phy_np = glue->phy->dev.of_node; ++ if (of_device_compatible_match(phy_np, sunxi_musb_host_rerouted_phys)) { ++ dev_info(&pdev->dev, "Disabling musb host side code due to re-routed phy\n"); ++ pdata.mode = MUSB_PERIPHERAL; ++ } ++ + glue->usb_phy = usb_phy_generic_register(); + if (IS_ERR(glue->usb_phy)) { + dev_err(&pdev->dev, "Error registering usb-phy %ld\n", +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/usb-serial-option-add-reset_resume-callback-for-WWAN-devices.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-serial-option-add-reset_resume-callback-for-WWAN-devices.patch new file mode 100644 index 000000000000..9001f922ab63 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-serial-option-add-reset_resume-callback-for-WWAN-devices.patch @@ -0,0 +1,33 @@ +From 0e8b72e264443819a1e8674ac9318ec382c84050 Mon Sep 17 00:00:00 2001 +From: Thomas Thorne +Date: Tue, 20 Sep 2022 20:34:57 -0400 +Subject: usb: serial: option: add 'reset_resume' callback for WWAN devices + +This avoids the issue where the PPP modem does not reset correctly on resume, +causing issues with incoming calls on suspend. + +Noted here: https://github.com/the-modem-distro/pinephone_modem_sdk/issues/97 + +This is part of a larger patch: +https://github.com/Quectel-Community/meta-quectel-community/commit/24adef2128f79b4d31490634438a917b3df462e2#diff-45dc5960ca2f1ef57139fe751587b3b043de9cca5864e2801210c00e75bd0780 + +However the rest of the patch is not needed/already upstreamed. +--- + drivers/usb/serial/option.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c +index 5cd26dac2069..f65c1d78a2cc 100644 +--- a/drivers/usb/serial/option.c ++++ b/drivers/usb/serial/option.c +@@ -2471,6 +2471,7 @@ static struct usb_serial_driver option_1port_device = { + #ifdef CONFIG_PM + .suspend = usb_wwan_suspend, + .resume = usb_wwan_resume, ++ .reset_resume = usb_wwan_resume, + #endif + }; + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-altmodes-displayport-Respect-DP_CAP_RECEPTACLE-bit.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-altmodes-displayport-Respect-DP_CAP_RECEPTACLE-bit.patch new file mode 100644 index 000000000000..753bdb81747b --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-altmodes-displayport-Respect-DP_CAP_RECEPTACLE-bit.patch @@ -0,0 +1,99 @@ +From d7895c7ef2ffbb7c8ad7e949a4427e82c77150e6 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Mon, 5 Sep 2022 00:56:07 +0200 +Subject: usb: typec: altmodes: displayport: Respect DP_CAP_RECEPTACLE bit + +DP_CAP_RECEPTACLE swaps the meaning of pin assignments. So eg. +pin assignments for DFP_D plug are stored in DP_CAP_UFP_D_PIN_ASSIGN +bits. Yes, it's slightly confusing. :) + +Make the kernel report lack of match in supported pin configurations +between connected ports, so that the user is not confused why their +USB-C dock doesn't have working Alt-DP mode, in case the dock returns +wrong VDO. + +Signed-off-by: Ondrej Jirman +--- + drivers/usb/typec/altmodes/displayport.c | 50 +++++++++++++++++++++--- + 1 file changed, 45 insertions(+), 5 deletions(-) + +diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c +index ac84a6d64c2f..c9dfccaeeff3 100644 +--- a/drivers/usb/typec/altmodes/displayport.c ++++ b/drivers/usb/typec/altmodes/displayport.c +@@ -116,11 +116,29 @@ static int dp_altmode_configure(struct dp_altmode *dp, u8 con) + /* Account for active cable capabilities */ + if (dp->plug_prime) + pin_assign &= DP_CAP_UFP_D_PIN_ASSIGN(dp->plug_prime->vdo); ++ ++ /* ++ * The Display Port Alt mode standard is not publicly available, ++ * so this is based on guesswork and real VDOs received from ++ * receptacle based and plug based Type-C alt mode supporting ++ * docks to make configuration work in practice: ++ * ++ * Plug (captive cable) based dock: port=c46 alt=c05 ++ * Recpetacle based dock: port=c46 alt=c0045 ++ * ++ pin_assign = DP_CAP_DFP_D_PIN_ASSIGN(dp->port->vdo); ++ pin_assign &= dp->alt->vdo & DP_CAP_RECEPTACLE ? ++ DP_CAP_UFP_D_PIN_ASSIGN(dp->alt->vdo) : ++ DP_CAP_DFP_D_PIN_ASSIGN(dp->alt->vdo); ++ */ + break; + default: + break; + } + ++ dev_info(&dp->alt->dev, "con=%d pin_assign=%x (port=%x alt=%x)\n", ++ (int)con, (unsigned)pin_assign, dp->port->vdo, dp->alt->vdo); ++ + /* Determining the initial pin assignment. */ + if (!DP_CONF_GET_PIN_ASSIGN(dp->data.conf)) { + /* Is USB together with DP preferred */ +@@ -726,15 +744,37 @@ int dp_altmode_probe(struct typec_altmode *alt) + struct typec_altmode *plug = typec_altmode_get_plug(alt, TYPEC_PLUG_SOP_P); + struct fwnode_handle *fwnode; + struct dp_altmode *dp; ++ u32 port_pins, alt_pins; + + /* FIXME: Port can only be DFP_U. */ + +- /* Make sure we have compatible pin configurations */ +- if (!(DP_CAP_PIN_ASSIGN_DFP_D(port->vdo) & +- DP_CAP_PIN_ASSIGN_UFP_D(alt->vdo)) && +- !(DP_CAP_PIN_ASSIGN_UFP_D(port->vdo) & +- DP_CAP_PIN_ASSIGN_DFP_D(alt->vdo))) ++ /* ++ * When port is a receptacle DP_CAP_xFP_D_PIN_ASSIGN macros have the ++ * regular meaning. When the port is a plug, the meaning is swapped. ++ * ++ * Check if we have any matching DFP_D<->UFP_D or UFP_D<->DFP_D pin assignment. ++ */ ++ port_pins = port->vdo & DP_CAP_RECEPTACLE ? ++ DP_CAP_DFP_D_PIN_ASSIGN(port->vdo) | DP_CAP_UFP_D_PIN_ASSIGN(port->vdo) << 8 : ++ DP_CAP_UFP_D_PIN_ASSIGN(port->vdo) | DP_CAP_DFP_D_PIN_ASSIGN(port->vdo) << 8; ++ ++ alt_pins = alt->vdo & DP_CAP_RECEPTACLE ? ++ DP_CAP_UFP_D_PIN_ASSIGN(alt->vdo) | DP_CAP_DFP_D_PIN_ASSIGN(alt->vdo) << 8 : ++ DP_CAP_DFP_D_PIN_ASSIGN(alt->vdo) | DP_CAP_UFP_D_PIN_ASSIGN(alt->vdo) << 8; ++ ++ /* Can't plug plug into a plug */ ++ if (!(port->vdo & DP_CAP_RECEPTACLE) && !(alt->vdo & DP_CAP_RECEPTACLE)) { ++ dev_warn(&alt->dev, "Our Alt-DP VDO 0x%06x and peer port VDO 0x%06x are not compatible (both are reported as plugs!)\n", ++ port->vdo, alt->vdo); ++ return -ENODEV; ++ } ++ ++ /* Make sure we have compatiple pin configurations */ ++ if (!(port_pins & alt_pins)) { ++ dev_warn(&alt->dev, "Our Alt-DP VDO 0x%06x and peer port VDO 0x%06x are not compatible (no shared pinconf %04x<->%04x (UUDD))\n", ++ port->vdo, alt->vdo, port_pins, alt_pins); + return -ENODEV; ++ } + + dp = devm_kzalloc(&alt->dev, sizeof(*dp), GFP_KERNEL); + if (!dp) +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-anx7688-Add-driver-for-ANX7688-USB-C-HDMI-bridge.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-anx7688-Add-driver-for-ANX7688-USB-C-HDMI-bridge.patch new file mode 100644 index 000000000000..f9f8fa152ee9 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-anx7688-Add-driver-for-ANX7688-USB-C-HDMI-bridge.patch @@ -0,0 +1,2333 @@ +From 08c64a2d7b390c78dccd932d6c8f53127bbc18e3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Thu, 13 Feb 2020 15:37:25 +0100 +Subject: usb: typec: anx7688: Add driver for ANX7688 USB-C HDMI bridge + +The driver is now ready for some testing with more USB-C devices. +It supports data and power role switching properly on all PinePhone +variants, that have the HW mod: https://xnux.eu/devices/pp-usbc-fix.jpg. + +- fix possibly unbalanced connect/disconnect +- better cabledet interrupt handling + - handle connect/disconnect in a workqueue + - debounce cabledet changes +- send PDO to the right i2c address +- proper error handling where it matters the most +- proper locking +- handle power supplies +- handle inital connection/cable status after driver probe + not just on cabledet change +- handle software interrupts from OCM +- allow to update OCM firmware via sysfs +- allow to dump OCM firmware via debugfs +- allow to dump registers via debugfs +- hook into allwinner usb phy driver to handle change in + data role (via usb_role_switch) +- handle vbus/vconn enable/disable based on firmware + requests +- proper cleanup of resources on driver unload + +Todo: +- tell PMIC what USB input current limit to set based on + PD messages from firmware +- add support for manual data/power role setting for devices + without the HW mod + +Signed-off-by: Martijn Braam +Signed-off-by: Samuel Holland +Signed-off-by: Ondrej Jirman +--- + drivers/usb/typec/Kconfig | 12 + + drivers/usb/typec/Makefile | 1 + + drivers/usb/typec/anx7688.c | 2237 +++++++++++++++++++++++++++++++++++ + 3 files changed, 2250 insertions(+) + create mode 100644 drivers/usb/typec/anx7688.c + +diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig +index 2f80c2792dbd..982b7c444a1f 100644 +--- a/drivers/usb/typec/Kconfig ++++ b/drivers/usb/typec/Kconfig +@@ -52,6 +52,7 @@ source "drivers/usb/typec/ucsi/Kconfig" + + source "drivers/usb/typec/tipd/Kconfig" + ++ + config TYPEC_ANX7411 + tristate "Analogix ANX7411 Type-C DRP Port controller driver" + depends on I2C +@@ -64,6 +65,17 @@ config TYPEC_ANX7411 + If you choose to build this driver as a dynamically linked module, the + module will be called anx7411.ko. + ++config TYPEC_ANX7688 ++ tristate "Analogix ANX7688 Type-C DRP Port controller and mux driver" ++ depends on I2C ++ depends on USB_ROLE_SWITCH ++ help ++ Say Y or M here if your system has Analogix ANX7688 Type-C Bridge ++ controller driver. ++ ++ If you choose to build this driver as a dynamically linked module, the ++ module will be called anx7688.ko. ++ + config TYPEC_RT1719 + tristate "Richtek RT1719 Sink Only Type-C controller driver" + depends on USB_ROLE_SWITCH || !USB_ROLE_SWITCH +diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile +index 7a368fea61bc..3f8ff94ad294 100644 +--- a/drivers/usb/typec/Makefile ++++ b/drivers/usb/typec/Makefile +@@ -7,6 +7,7 @@ obj-$(CONFIG_TYPEC_TCPM) += tcpm/ + obj-$(CONFIG_TYPEC_UCSI) += ucsi/ + obj-$(CONFIG_TYPEC_TPS6598X) += tipd/ + obj-$(CONFIG_TYPEC_ANX7411) += anx7411.o ++obj-$(CONFIG_TYPEC_ANX7688) += anx7688.o + obj-$(CONFIG_TYPEC_HD3SS3220) += hd3ss3220.o + obj-$(CONFIG_TYPEC_STUSB160X) += stusb160x.o + obj-$(CONFIG_TYPEC_RT1719) += rt1719.o +diff --git a/drivers/usb/typec/anx7688.c b/drivers/usb/typec/anx7688.c +new file mode 100644 +index 000000000000..fa1d3496af73 +--- /dev/null ++++ b/drivers/usb/typec/anx7688.c +@@ -0,0 +1,2237 @@ ++/* ++ * ANX7688 USB-C HDMI bridge/PD driver ++ * ++ * Warning, this driver is somewhat PinePhone specific. ++ * ++ * How this works: ++ * - this driver allows to program firmware into ANX7688 EEPROM, and ++ * initialize it ++ * - it then communicates with the firmware running on the OCM (on-chip ++ * microcontroller) ++ * - it detects whether there is cable plugged in or not and powers ++ * up or down the ANX7688 based on that ++ * - when the cable is connected the firmware on the OCM will handle ++ * the detection of the nature of the device on the other end ++ * of the USB-C cable ++ * - this driver then communicates with the USB phy to let it swap ++ * data roles accordingly ++ * - it also enables VBUS and VCONN regulators as appropriate ++ * - USB phy driver (Allwinner) needs to know whether to switch to ++ * device or host mode, or whether to turn off ++ * - when the firmware detects SRC.1.5A or SRC.3.0A via CC pins ++ * or something else via PD, it notifies this driver via software ++ * interrupt and this driver will determine how to update the TypeC ++ * port status and what input current limit is appropriate ++ * - input current limit determination happens 500ms after cable ++ * insertion or hard reset (delay is necessary to determine whether ++ * the remote end is PD capable or not) ++ * - this driver tells to the PMIC driver that the input current limit ++ * needs to be changed ++ * - this driver also monitors PMIC status and re-sets the input current ++ * limit if it changes for some reason (due to PMIC internal decision ++ * making) (this is disabled for now) ++ * ++ * ANX7688 FW behavior as observed: ++ * ++ * - DO NOT SET MORE THAN 1 SINK CAPABILITY! Firmware will ignore what ++ * you set and send hardcoded PDO_BATT 5-21V 30W message! ++ */ ++ ++#define DEBUG ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define DISABLE_OCM 0 ++ ++/* firmware regs */ ++ ++#define ANX7688_REG_VBUS_OFF_DELAY_TIME 0x22 ++#define ANX7688_REG_FEATURE_CTRL 0x27 ++#define ANX7688_REG_EEPROM_LOAD_STATUS1 0x11 ++#define ANX7688_REG_EEPROM_LOAD_STATUS0 0x12 ++#define ANX7688_REG_FW_VERSION1 0x15 ++#define ANX7688_REG_FW_VERSION0 0x16 ++ ++#define ANX7688_EEPROM_FW_LOADED 0x01 ++ ++#define ANX7688_REG_STATUS_INT_MASK 0x17 ++#define ANX7688_REG_STATUS_INT 0x28 ++#define ANX7688_IRQS_RECEIVED_MSG BIT(0) ++#define ANX7688_IRQS_RECEIVED_ACK BIT(1) ++#define ANX7688_IRQS_VCONN_CHANGE BIT(2) ++#define ANX7688_IRQS_VBUS_CHANGE BIT(3) ++#define ANX7688_IRQS_CC_STATUS_CHANGE BIT(4) ++#define ANX7688_IRQS_DATA_ROLE_CHANGE BIT(5) ++ ++#define ANX7688_REG_STATUS 0x29 ++#define ANX7688_VCONN_STATUS BIT(2) /* 0 = off 1 = on */ ++#define ANX7688_VBUS_STATUS BIT(3) /* 0 = off 1 = on */ ++#define ANX7688_DATA_ROLE_STATUS BIT(5) /* 0 = device 1 = host */ ++ ++#define ANX7688_REG_CC_STATUS 0x2a ++#define ANX7688_REG_TRY_UFP_TIMER 0x23 ++#define ANX7688_REG_TIME_CTRL 0x24 ++ ++#define ANX7688_REG_MAX_VOLTAGE 0x1b ++#define ANX7688_REG_MAX_POWER 0x1c ++#define ANX7688_REG_MIN_POWER 0x1d ++#define ANX7688_REG_MAX_VOLTAGE_STATUS 0x1e ++#define ANX7688_REG_MAX_POWER_STATUS 0x1f ++ ++#define ANX7688_SOFT_INT_MASK 0x7f ++ ++/* tcpc regs */ ++ ++#define ANX7688_TCPC_REG_VENDOR_ID0 0x00 ++#define ANX7688_TCPC_REG_VENDOR_ID1 0x01 ++#define ANX7688_TCPC_REG_ALERT0 0x10 ++#define ANX7688_TCPC_REG_ALERT1 0x11 ++#define ANX7688_TCPC_REG_ALERT_MASK0 0x12 ++#define ANX7688_TCPC_REG_ALERT_MASK1 0x13 ++#define ANX7688_TCPC_REG_INTERFACE_SEND 0x30 ++#define ANX7688_TCPC_REG_INTERFACE_RECV 0x51 ++ ++/* hw regs */ ++ ++#define ANX7688_REG_IRQ_EXT_SOURCE0 0x3e ++#define ANX7688_REG_IRQ_EXT_SOURCE1 0x4e ++#define ANX7688_REG_IRQ_EXT_SOURCE2 0x4f ++#define ANX7688_REG_IRQ_EXT_MASK0 0x3b ++#define ANX7688_REG_IRQ_EXT_MASK1 0x3c ++#define ANX7688_REG_IRQ_EXT_MASK2 0x3d ++#define ANX7688_REG_IRQ_SOURCE0 0x54 ++#define ANX7688_REG_IRQ_SOURCE1 0x55 ++#define ANX7688_REG_IRQ_SOURCE2 0x56 ++#define ANX7688_REG_IRQ_MASK0 0x57 ++#define ANX7688_REG_IRQ_MASK1 0x58 ++#define ANX7688_REG_IRQ_MASK2 0x59 ++ ++#define ANX7688_IRQ2_SOFT_INT BIT(2) ++ ++#define ANX7688_REG_USBC_RESET_CTRL 0x05 ++#define ANX7688_USBC_RESET_CTRL_OCM_RESET BIT(4) ++ ++//#define ANX7688_IRQ2_USB_PLUGIN BIT(4) ++//#define ANX7688_REG_IRQ_STATUS 0x53 ++ ++/* ocm messages */ ++ ++#define ANX7688_OCM_MSG_PWR_SRC_CAP 0x00 ++#define ANX7688_OCM_MSG_PWR_SNK_CAP 0x01 ++#define ANX7688_OCM_MSG_DP_SNK_IDENTITY 0x02 ++#define ANX7688_OCM_MSG_SVID 0x03 ++#define ANX7688_OCM_MSG_GET_DP_SNK_CAP 0x04 ++#define ANX7688_OCM_MSG_ACCEPT 0x05 ++#define ANX7688_OCM_MSG_REJECT 0x06 ++#define ANX7688_OCM_MSG_PSWAP_REQ 0x10 ++#define ANX7688_OCM_MSG_DSWAP_REQ 0x11 ++#define ANX7688_OCM_MSG_GOTO_MIN_REQ 0x12 ++#define ANX7688_OCM_MSG_VCONN_SWAP_REQ 0x13 ++#define ANX7688_OCM_MSG_VDM 0x14 ++#define ANX7688_OCM_MSG_DP_SNK_CFG 0x15 ++#define ANX7688_OCM_MSG_PWR_OBJ_REQ 0x16 ++#define ANX7688_OCM_MSG_PD_STATUS_REQ 0x17 ++#define ANX7688_OCM_MSG_DP_ALT_ENTER 0x19 ++#define ANX7688_OCM_MSG_DP_ALT_EXIT 0x1a ++#define ANX7688_OCM_MSG_GET_SNK_CAP 0x1b ++#define ANX7688_OCM_MSG_RESPONSE_TO_REQ 0xf0 ++#define ANX7688_OCM_MSG_SOFT_RST 0xf1 ++#define ANX7688_OCM_MSG_HARD_RST 0xf2 ++#define ANX7688_OCM_MSG_RESTART 0xf3 ++ ++static const char * const anx7688_supply_names[] = { ++ "avdd33", ++ "avdd18", ++ "dvdd18", ++ "avdd10", ++ "dvdd10", ++ "i2c", ++ "hdmi_vt", ++ ++ "vconn", // power for VCONN1/VCONN2 switches ++ "vbus", // vbus power ++}; ++ ++#define ANX7688_NUM_SUPPLIES ARRAY_SIZE(anx7688_supply_names) ++#define ANX7688_NUM_ALWAYS_ON_SUPPLIES (ANX7688_NUM_SUPPLIES - 1) ++ ++#define ANX7688_I2C_INDEX (ANX7688_NUM_SUPPLIES - 4) ++#define ANX7688_VCONN_INDEX (ANX7688_NUM_SUPPLIES - 2) ++#define ANX7688_VBUS_INDEX (ANX7688_NUM_SUPPLIES - 1) ++ ++enum { ++ ANX7688_F_POWERED, ++ ANX7688_F_CONNECTED, ++ ANX7688_F_FW_FAILED, ++ ANX7688_F_PWRSUPPLY_CHANGE, ++ ANX7688_F_CURRENT_UPDATE, ++}; ++ ++struct anx7688 { ++ struct device *dev; ++ struct i2c_client *client; ++ struct i2c_client *client_tcpc; ++ struct regulator_bulk_data supplies[ANX7688_NUM_SUPPLIES]; ++ struct power_supply *vbus_in_supply; ++ struct notifier_block vbus_in_nb; ++ int input_current_limit; // mA ++ struct gpio_desc *gpio_enable; ++ struct gpio_desc *gpio_reset; ++ struct gpio_desc *gpio_cabledet; ++ ++ uint32_t src_caps[8]; ++ unsigned n_src_caps; ++ ++ uint32_t snk_caps[8]; ++ unsigned n_snk_caps; ++ ++ unsigned long flags[1]; ++ ++ struct delayed_work work; ++ struct timer_list work_timer; ++ ++ struct mutex lock; ++ bool vbus_on, vconn_on; ++ bool pd_capable; ++ int pd_current_limit; // mA ++ ktime_t current_update_deadline; ++ ++ struct typec_port *port; ++ struct typec_partner *partner; ++ struct usb_pd_identity partner_identity; ++ struct usb_role_switch *role_sw; ++ int pwr_role, data_role; ++ ++ struct dentry *debug_root; ++ ++ /* for debug */ ++ int last_status; ++ int last_cc_status; ++ int last_dp_state; ++ int last_bc_result; ++ ++ /* for HDMI HPD */ ++ struct extcon_dev *extcon; ++ int last_extcon_state; ++}; ++ ++static const unsigned int anx7688_extcon_cable[] = { ++ EXTCON_DISP_HDMI, ++ EXTCON_NONE, ++}; ++ ++static int anx7688_reg_read(struct anx7688 *anx7688, u8 reg_addr) ++{ ++ int ret; ++ ++ ret = i2c_smbus_read_byte_data(anx7688->client, reg_addr); ++ if (ret < 0) ++ dev_err(anx7688->dev, "i2c read failed at 0x%x (%d)\n", ++ reg_addr, ret); ++ ++ return ret; ++} ++ ++static int anx7688_reg_write(struct anx7688 *anx7688, u8 reg_addr, u8 value) ++{ ++ int ret; ++ ++ ret = i2c_smbus_write_byte_data(anx7688->client, reg_addr, value); ++ if (ret < 0) ++ dev_err(anx7688->dev, "i2c write failed at 0x%x (%d)\n", ++ reg_addr, ret); ++ ++ return ret; ++} ++ ++static int anx7688_reg_update_bits(struct anx7688 *anx7688, u8 reg_addr, ++ u8 mask, u8 value) ++{ ++ int ret; ++ ++ ret = anx7688_reg_read(anx7688, reg_addr); ++ if (ret < 0) ++ return ret; ++ ++ ret &= ~mask; ++ ret |= value; ++ ++ return anx7688_reg_write(anx7688, reg_addr, ret); ++} ++ ++static int anx7688_tcpc_reg_read(struct anx7688 *anx7688, u8 reg_addr) ++{ ++ int ret; ++ ++ ret = i2c_smbus_read_byte_data(anx7688->client_tcpc, reg_addr); ++ if (ret < 0) ++ dev_err(anx7688->dev, "tcpc i2c read failed at 0x%x (%d)\n", ++ reg_addr, ret); ++ ++ return ret; ++} ++ ++static int anx7688_tcpc_reg_write(struct anx7688 *anx7688, u8 reg_addr, u8 value) ++{ ++ int ret; ++ ++ ret = i2c_smbus_write_byte_data(anx7688->client_tcpc, reg_addr, value); ++ if (ret < 0) ++ dev_err(anx7688->dev, "tcpc i2c write failed at 0x%x (%d)\n", ++ reg_addr, ret); ++ ++ return ret; ++} ++ ++static void anx7688_power_enable(struct anx7688 *anx7688) ++{ ++ gpiod_set_value(anx7688->gpio_reset, 1); ++ gpiod_set_value(anx7688->gpio_enable, 1); ++ ++ /* wait for power to stabilize and release reset */ ++ msleep(10); ++ gpiod_set_value(anx7688->gpio_reset, 0); ++ udelay(2); ++ ++ dev_dbg(anx7688->dev, "power enabled\n"); ++ ++ set_bit(ANX7688_F_POWERED, anx7688->flags); ++} ++ ++static void anx7688_power_disable(struct anx7688 *anx7688) ++{ ++ gpiod_set_value(anx7688->gpio_reset, 1); ++ msleep(5); ++ gpiod_set_value(anx7688->gpio_enable, 0); ++ ++ dev_dbg(anx7688->dev, "power disabled\n"); ++ ++ clear_bit(ANX7688_F_POWERED, anx7688->flags); ++} ++ ++static int anx7688_send_ocm_message(struct anx7688 *anx7688, int cmd, ++ const u8 *data, int data_len) ++{ ++ int ret = 0, i; ++ u8 pkt[32]; ++ ++ if (data_len > sizeof(pkt) - 3) { ++ dev_dbg(anx7688->dev, ++ "invalid ocm message length cmd=0x%02x len=%d\n", ++ cmd, data_len); ++ return -EINVAL; ++ } ++ ++ // prepare pd packet ++ pkt[0] = data_len + 1; ++ pkt[1] = cmd; ++ if (data_len > 0) ++ memcpy(pkt + 2, data, data_len); ++ pkt[2 + data_len] = 0; ++ for (i = 0; i < data_len + 2; i++) ++ pkt[data_len + 2] -= pkt[i]; ++ ++ dev_dbg(anx7688->dev, "send pd packet cmd=0x%02x %*ph\n", ++ cmd, data_len + 3, pkt); ++ ++ ret = anx7688_tcpc_reg_read(anx7688, ANX7688_TCPC_REG_INTERFACE_SEND); ++ if (ret) { ++ dev_err(anx7688->dev, ++ "failed to send pd packet (tx buffer full)\n"); ++ return -EBUSY; ++ } ++ ++ ret = i2c_smbus_write_i2c_block_data(anx7688->client_tcpc, ++ ANX7688_TCPC_REG_INTERFACE_SEND, ++ data_len + 3, pkt); ++ if (ret < 0) ++ dev_err(anx7688->dev, ++ "failed to send pd packet (err=%d)\n", ret); ++ ++ // wait until the message is processed (30ms max) ++ for (i = 0; i < 300; i++) { ++ ret = anx7688_tcpc_reg_read(anx7688, ANX7688_TCPC_REG_INTERFACE_SEND); ++ if (ret <= 0) ++ return ret; ++ ++ udelay(100); ++ } ++ ++ dev_err(anx7688->dev, "timeout waiting for the message queue flush\n"); ++ return -ETIMEDOUT; ++} ++ ++static int anx7688_connect(struct anx7688 *anx7688) ++{ ++#if DISABLE_OCM ++ int ret; ++ ++ dev_dbg(anx7688->dev, "cable inserted\n"); ++ ++ msleep(10); ++ anx7688_power_enable(anx7688); ++ ++ /* reset the OCM right away */ ++ ret = anx7688_reg_update_bits(anx7688, ANX7688_REG_USBC_RESET_CTRL, ++ ANX7688_USBC_RESET_CTRL_OCM_RESET, ++ ANX7688_USBC_RESET_CTRL_OCM_RESET); ++ if (ret) ++ goto err_poweroff; ++ ++ /* enable interrupts for VBUS, etc. */ ++ ret = anx7688_reg_write(anx7688, ANX7688_REG_IRQ_EXT_SOURCE2, 0xff); ++ if (ret) ++ goto err_poweroff; ++ ++ ret = anx7688_reg_write(anx7688, ANX7688_REG_IRQ_EXT_MASK2, ~(BIT(6) | BIT(5) | BIT(4))); ++ if (ret) ++ goto err_poweroff; ++ ++ set_bit(ANX7688_F_CONNECTED, anx7688->flags); ++ return 0; ++ ++err_poweroff: ++ anx7688_power_disable(anx7688); ++ return ret; ++#else ++ struct typec_partner_desc desc = {}; ++ int ret, i; ++ u8 fw[2]; ++ const u8 dp_snk_identity[16] = { ++ 0x00, 0x00, 0x00, 0xec, /* id header */ ++ 0x00, 0x00, 0x00, 0x00, /* cert stat */ ++ 0x00, 0x00, 0x00, 0x00, /* product type */ ++ 0x39, 0x00, 0x00, 0x51 /* alt mode adapter */ ++ }; ++ const u8 svid[4] = { ++ 0x00, 0x00, 0x01, 0xff, ++ }; ++ u32 caps[8]; ++ ++ dev_dbg(anx7688->dev, "cable inserted\n"); ++ ++ anx7688->last_status = -1; ++ anx7688->last_cc_status = -1; ++ anx7688->last_dp_state = -1; ++ ++ msleep(10); ++ anx7688_power_enable(anx7688); ++ ++ ret = regulator_enable(anx7688->supplies[ANX7688_VCONN_INDEX].consumer); ++ if (ret) { ++ dev_err(anx7688->dev, "failed to enable vconn\n"); ++ goto err_poweroff; ++ } ++ anx7688->vconn_on = true; ++ ++ /* wait till the firmware is loaded (typically ~30ms) */ ++ for (i = 0; i < 100; i++) { ++ ret = anx7688_reg_read(anx7688, ANX7688_REG_EEPROM_LOAD_STATUS0); ++ ++ if (ret >= 0 && (ret & ANX7688_EEPROM_FW_LOADED) == ANX7688_EEPROM_FW_LOADED) { ++ dev_dbg(anx7688->dev, "eeprom0 = 0x%02x\n", ret); ++ dev_info(anx7688->dev, "fw loaded after %d ms\n", i * 10); ++ goto fw_loaded; ++ } ++ ++ msleep(5); ++ } ++ ++ set_bit(ANX7688_F_FW_FAILED, anx7688->flags); ++ dev_err(anx7688->dev, "boot firmware load failed (you may need to flash FW to anx7688 first)\n"); ++ ret = -ETIMEDOUT; ++ goto err_vconoff; ++ ++fw_loaded: ++ ret = i2c_smbus_read_i2c_block_data(anx7688->client, ++ ANX7688_REG_FW_VERSION1, 2, fw); ++ if (ret < 0) { ++ dev_err(anx7688->dev, "failed to read firmware version\n"); ++ goto err_vconoff; ++ } ++ ++ dev_info(anx7688->dev, "OCM firmware loaded (version 0x%04x)\n", ++ fw[1] | fw[0] << 8); ++ ++ /* Unmask interrupts */ ++ ret = anx7688_reg_write(anx7688, ANX7688_REG_STATUS_INT, 0); ++ if (ret) ++ goto err_vconoff; ++ ++ ret = anx7688_reg_write(anx7688, ANX7688_REG_STATUS_INT_MASK, ~ANX7688_SOFT_INT_MASK); ++ if (ret) ++ goto err_vconoff; ++ ++ ret = anx7688_reg_write(anx7688, ANX7688_REG_IRQ_EXT_SOURCE2, 0xff); ++ if (ret) ++ goto err_vconoff; ++ ++ ret = anx7688_reg_write(anx7688, ANX7688_REG_IRQ_EXT_MASK2, (u8)~ANX7688_IRQ2_SOFT_INT); ++ if (ret) ++ goto err_vconoff; ++ ++ /* time to turn off vbus after cc disconnect (unit is 4 ms) */ ++ ret = anx7688_reg_write(anx7688, ANX7688_REG_VBUS_OFF_DELAY_TIME, 100 / 4); ++ if (ret) ++ goto err_vconoff; ++ ++ //anx7688_reg_write(anx7688, ANX7688_REG_TIME_CTRL, 0x00); ++ ++ /* 300ms (unit is 2 ms) */ ++ ret = anx7688_reg_write(anx7688, ANX7688_REG_TRY_UFP_TIMER, 300 / 2); ++ if (ret) ++ goto err_vconoff; ++ ++ /* maximum voltage in 100 mV units */ ++ ret = anx7688_reg_write(anx7688, ANX7688_REG_MAX_VOLTAGE, 50); /* 5 V */ ++ if (ret) ++ goto err_vconoff; ++ ++ /* min/max power in 500 mW units */ ++ ret = anx7688_reg_write(anx7688, ANX7688_REG_MAX_POWER, 15 * 2); /* 15 W */ ++ if (ret) ++ goto err_vconoff; ++ ++ ret = anx7688_reg_write(anx7688, ANX7688_REG_MIN_POWER, 1); /* 0.5 W */ ++ if (ret) ++ goto err_vconoff; ++ ++ /* auto_pd, try.src, try.sink, goto safe 5V */ ++ ret = anx7688_reg_write(anx7688, ANX7688_REG_FEATURE_CTRL, 0x1e & ~BIT(2)); // disable try_src ++ if (ret) ++ goto err_vconoff; ++ ++ for (i = 0; i < anx7688->n_src_caps; i++) ++ caps[i] = cpu_to_le32(anx7688->src_caps[i]); ++ ret = anx7688_send_ocm_message(anx7688, ANX7688_OCM_MSG_PWR_SRC_CAP, ++ (u8*)&caps, 4 * anx7688->n_src_caps); ++ if (ret) ++ goto err_vconoff; ++ ++ for (i = 0; i < anx7688->n_snk_caps; i++) ++ caps[i] = cpu_to_le32(anx7688->snk_caps[i]); ++ ret = anx7688_send_ocm_message(anx7688, ANX7688_OCM_MSG_PWR_SNK_CAP, ++ (u8*)&caps, 4 * anx7688->n_snk_caps); ++ if (ret) ++ goto err_vconoff; ++ ++ /* Send DP SNK identity */ ++ ret = anx7688_send_ocm_message(anx7688, ANX7688_OCM_MSG_DP_SNK_IDENTITY, ++ dp_snk_identity, sizeof dp_snk_identity); ++ if (ret) ++ goto err_vconoff; ++ ++ ret = anx7688_send_ocm_message(anx7688, ANX7688_OCM_MSG_SVID, ++ svid, sizeof svid); ++ if (ret) ++ goto err_vconoff; ++ ++ dev_dbg(anx7688->dev, "OCM configuration completed\n"); ++ ++ desc.accessory = TYPEC_ACCESSORY_NONE; ++ ++ typec_unregister_partner(anx7688->partner); ++ ++ anx7688->partner = typec_register_partner(anx7688->port, &desc); ++ if (IS_ERR(anx7688->partner)) { ++ ret = PTR_ERR(anx7688->partner); ++ goto err_vconoff; ++ } ++ ++ // after this deadline passes we'll check if device is pd_capable and ++ // set up the current limit accordingly ++ anx7688->current_update_deadline = ktime_add_ms(ktime_get(), 3000); ++ ++ set_bit(ANX7688_F_CONNECTED, anx7688->flags); ++ return 0; ++ ++err_vconoff: ++ regulator_disable(anx7688->supplies[ANX7688_VCONN_INDEX].consumer); ++ anx7688->vconn_on = false; ++err_poweroff: ++ anx7688_power_disable(anx7688); ++ dev_err(anx7688->dev, "OCM configuration failed\n"); ++ return ret; ++#endif ++} ++ ++static void anx7688_set_hdmi_hpd(struct anx7688 *anx7688, int state) ++{ ++ if (!anx7688->extcon) ++ return; ++ ++ if (anx7688->last_extcon_state != state) { ++ extcon_set_state_sync(anx7688->extcon, EXTCON_DISP_HDMI, state); ++ anx7688->last_extcon_state = state; ++ } ++} ++ ++static void anx7688_disconnect(struct anx7688 *anx7688) ++{ ++ union power_supply_propval val = {0,}; ++ struct device *dev = anx7688->dev; ++ int ret; ++ ++ dev_dbg(dev, "cable removed\n"); ++ ++ anx7688->current_update_deadline = 0; ++ ++ anx7688_set_hdmi_hpd(anx7688, 0); ++ ++ if (anx7688->vconn_on) { ++ regulator_disable(anx7688->supplies[ANX7688_VCONN_INDEX].consumer); ++ anx7688->vconn_on = false; ++ } ++ ++ if (anx7688->vbus_on) { ++ regulator_disable(anx7688->supplies[ANX7688_VBUS_INDEX].consumer); ++ anx7688->vbus_on = false; ++ } ++ ++ anx7688_power_disable(anx7688); ++ ++ anx7688->pd_capable = false; ++ ++ typec_unregister_partner(anx7688->partner); ++ anx7688->partner = NULL; ++ ++ anx7688->pwr_role = TYPEC_SINK; ++ anx7688->data_role = TYPEC_DEVICE; ++ typec_set_pwr_role(anx7688->port, anx7688->pwr_role); ++ typec_set_data_role(anx7688->port, anx7688->data_role); ++ typec_set_pwr_opmode(anx7688->port, TYPEC_PWR_MODE_USB); ++ typec_set_vconn_role(anx7688->port, TYPEC_SINK); ++ ++ usb_role_switch_set_role(anx7688->role_sw, USB_ROLE_NONE); ++ ++ val.intval = 500 * 1000; ++ dev_dbg(dev, "setting vbus_in current limit to %d mA\n", val.intval); ++ ret = power_supply_set_property(anx7688->vbus_in_supply, ++ POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, ++ &val); ++ if (ret) ++ dev_err(dev, "failed to set vbus_in current to %d mA\n", ++ val.intval / 1000); ++ ++ val.intval = 0; ++ dev_dbg(dev, "disabling vbus_in power path\n"); ++ ret = power_supply_set_property(anx7688->vbus_in_supply, ++ POWER_SUPPLY_PROP_ONLINE, ++ &val); ++ if (ret) ++ dev_err(dev, "failed to offline vbus_in\n"); ++ ++ val.intval = 1; ++ dev_dbg(dev, "enabling USB BC 1.2 detection\n"); ++ ret = power_supply_set_property(anx7688->vbus_in_supply, ++ POWER_SUPPLY_PROP_USB_BC_ENABLED, ++ &val); ++ if (ret) ++ dev_err(dev, "failed to enabled USB BC1.2 detection\n"); ++ ++ clear_bit(ANX7688_F_CONNECTED, anx7688->flags); ++} ++ ++static void anx7688_handle_cable_change(struct anx7688* anx7688) ++{ ++ int cabledet; ++ bool connected; ++ ++ connected = test_bit(ANX7688_F_CONNECTED, anx7688->flags); ++ cabledet = gpiod_get_value(anx7688->gpio_cabledet); ++ ++ if (cabledet && !connected) ++ anx7688_connect(anx7688); ++ else if (!cabledet && connected) ++ anx7688_disconnect(anx7688); ++} ++ ++static irqreturn_t anx7688_irq_plug_handler(int irq, void *data) ++{ ++ struct anx7688 *anx7688 = data; ++ ++ dev_dbg(anx7688->dev, "plug irq (cd=%d)\n", ++ gpiod_get_value(anx7688->gpio_cabledet)); ++ ++ /* ++ * After each cabledet change the scheduled work timer is reset ++ * to fire in ~10ms. So the work is done only after the cabledet ++ * is stable for ~10ms. ++ */ ++ schedule_delayed_work(&anx7688->work, msecs_to_jiffies(10)); ++ ++ return IRQ_HANDLED; ++} ++ ++enum { ++ CMD_SUCCESS, ++ CMD_REJECT, ++ CMD_FAIL, ++ CMD_BUSY, ++}; ++ ++static const char* anx7688_cmd_statuses[] = { ++ "SUCCESS", ++ "REJECT", ++ "FAIL", ++ "BUSY", ++}; ++ ++static int anx7688_handle_pd_message_response(struct anx7688* anx7688, ++ u8 to_cmd, u8 resp) ++{ ++ const char* status = resp <= CMD_BUSY ? anx7688_cmd_statuses[resp] : "UNKNOWN"; ++ ++ switch (to_cmd) { ++ case ANX7688_OCM_MSG_PSWAP_REQ: ++ dev_info(anx7688->dev, "received response to PSWAP_REQ (%s)\n", status); ++ break; ++ ++ case ANX7688_OCM_MSG_DSWAP_REQ: ++ dev_info(anx7688->dev, "received response to DSWAP_REQ (%s)\n", status); ++ break; ++ ++ case ANX7688_OCM_MSG_VCONN_SWAP_REQ: ++ dev_info(anx7688->dev, "received response to VCONN_SWAP_REQ (%s)\n", status); ++ break; ++ ++ case ANX7688_OCM_MSG_PWR_OBJ_REQ: ++ dev_info(anx7688->dev, "received response to PWR_OBJ_REQ (%s)\n", status); ++ break; ++ ++ case ANX7688_OCM_MSG_VDM: ++ dev_info(anx7688->dev, "received response to VDM (%s)\n", status); ++ break; ++ ++ case ANX7688_OCM_MSG_GOTO_MIN_REQ: ++ dev_info(anx7688->dev, "received response to GOTO_MIN_REQ (%s)\n", status); ++ break; ++ ++ case ANX7688_OCM_MSG_GET_SNK_CAP: ++ dev_info(anx7688->dev, "received response to GET_SNK_CAP (%s)\n", status); ++ break; ++ ++ default: ++ dev_info(anx7688->dev, "received response to unknown request (%s)\n", status); ++ break; ++ } ++ ++ return 0; ++} ++ ++static int anx7688_handle_pd_message(struct anx7688* anx7688, ++ u8 cmd, u8* msg, unsigned len) ++{ ++ struct device *dev = anx7688->dev; ++ union power_supply_propval psy_val = {0,}; ++ uint32_t* pdos = (uint32_t*)msg; ++ int ret, i, rdo_max_v, rdo_max_p; ++ uint32_t pdo, rdo; ++ ++ switch (cmd) { ++ case ANX7688_OCM_MSG_PWR_SRC_CAP: ++ dev_info(anx7688->dev, "received SRC_CAP\n"); ++ ++ if (len % 4 != 0) { ++ dev_warn(anx7688->dev, "received invalid sized PDO array\n"); ++ break; ++ } ++ ++ /* the partner is PD capable */ ++ anx7688->pd_capable = true; ++ ++ for (i = 0; i < len / 4; i++) { ++ pdo = le32_to_cpu(pdos[i]); ++ ++ if (pdo_type(pdo) == PDO_TYPE_FIXED) { ++ unsigned voltage = pdo_fixed_voltage(pdo); ++ unsigned max_curr = pdo_max_current(pdo); ++ ++ dev_info(anx7688->dev, "SRC_CAP PDO_FIXED (%umV %umA)\n", voltage, max_curr); ++ } else if (pdo_type(pdo) == PDO_TYPE_BATT) { ++ unsigned min_volt = pdo_min_voltage(pdo); ++ unsigned max_volt = pdo_max_voltage(pdo); ++ unsigned max_pow = pdo_max_power(pdo); ++ ++ dev_info(anx7688->dev, "SRC_CAP PDO_BATT (%umV-%umV %umW)\n", min_volt, max_volt, max_pow); ++ } else if (pdo_type(pdo) == PDO_TYPE_VAR) { ++ unsigned min_volt = pdo_min_voltage(pdo); ++ unsigned max_volt = pdo_max_voltage(pdo); ++ unsigned max_curr = pdo_max_current(pdo); ++ ++ dev_info(anx7688->dev, "SRC_CAP PDO_VAR (%umV-%umV %umA)\n", min_volt, max_volt, max_curr); ++ } else { ++ dev_info(anx7688->dev, "SRC_CAP PDO_APDO (0x%08X)\n", pdo); ++ } ++ } ++ ++ /* when auto_pd mode is enabled, the FW has already set ++ * RDO_MAX_VOLTAGE and RDO_MAX_POWER for the RDO it sent to the ++ * partner based on the received SOURCE_CAPs. This does not ++ * mean, the request was acked, but we can't do better here than ++ * calculate the current_limit to set later and hope for the best. ++ */ ++ rdo_max_v = anx7688_reg_read(anx7688, ANX7688_REG_MAX_VOLTAGE_STATUS); ++ if (rdo_max_v < 0) ++ return rdo_max_v; ++ if (rdo_max_v == 0) ++ return -EINVAL; ++ ++ rdo_max_p = anx7688_reg_read(anx7688, ANX7688_REG_MAX_POWER_STATUS); ++ if (rdo_max_p < 0) ++ return rdo_max_p; ++ ++ anx7688->pd_current_limit = rdo_max_p * 5000 / rdo_max_v; ++ ++ dev_dbg(anx7688->dev, "RDO max voltage = %dmV, max power = %dmW, PD current limit = %dmA\n", ++ rdo_max_v * 100, rdo_max_p * 500, anx7688->pd_current_limit); ++ ++ // update current limit sooner, now that we have PD negotiation result ++ anx7688->current_update_deadline = ktime_add_ms(ktime_get(), 500); ++ ++ //TODO: we should go through PDOs and decide which one ++ //to request, build a RDO with a proper index of the ++ //selected PDO and send it via PWR_OBJ_REQ message to FW ++ //but we're using auto_pd, so FW does this for us ++ // ++ //rdo = cpu_to_le32(RDO_FIXED(0, 1500, 1500, RDO_USB_COMM)); ++ //ret = anx7688_send_ocm_message(anx7688, ANX7688_OCM_MSG_PWR_OBJ_REQ, &rdo, 4); ++ ++ break; ++ ++ case ANX7688_OCM_MSG_PWR_SNK_CAP: ++ dev_info(anx7688->dev, "received SNK_CAP\n"); ++ ++ if (len % 4 != 0) { ++ dev_warn(anx7688->dev, "received invalid sized PDO array\n"); ++ break; ++ } ++ ++ for (i = 0; i < len / 4; i++) { ++ pdo = le32_to_cpu(pdos[i]); ++ ++ if (pdo_type(pdo) == PDO_TYPE_FIXED) { ++ unsigned voltage = pdo_fixed_voltage(pdo); ++ unsigned max_curr = pdo_max_current(pdo); ++ ++ dev_info(anx7688->dev, "SNK_CAP PDO_FIXED (%umV %umA)\n", voltage, max_curr); ++ } else if (pdo_type(pdo) == PDO_TYPE_BATT) { ++ unsigned min_volt = pdo_min_voltage(pdo); ++ unsigned max_volt = pdo_max_voltage(pdo); ++ unsigned max_pow = pdo_max_power(pdo); ++ ++ dev_info(anx7688->dev, "SNK_CAP PDO_BATT (%umV-%umV %umW)\n", min_volt, max_volt, max_pow); ++ } else if (pdo_type(pdo) == PDO_TYPE_VAR) { ++ unsigned min_volt = pdo_min_voltage(pdo); ++ unsigned max_volt = pdo_max_voltage(pdo); ++ unsigned max_curr = pdo_max_current(pdo); ++ ++ dev_info(anx7688->dev, "SNK_CAP PDO_VAR (%umV-%umV %umA)\n", min_volt, max_volt, max_curr); ++ } else { ++ dev_info(anx7688->dev, "SNK_CAP PDO_APDO (0x%08X)\n", pdo); ++ } ++ } ++ ++ break; ++ ++ case ANX7688_OCM_MSG_PWR_OBJ_REQ: ++ dev_info(anx7688->dev, "received PWR_OBJ_REQ\n"); ++ ++ anx7688->pd_capable = true; ++ ++ if (len != 4) { ++ dev_warn(anx7688->dev, "received invalid sized RDO\n"); ++ break; ++ } ++ ++ rdo = le32_to_cpu(pdos[0]); ++ ++ if (rdo_index(rdo) >= 1 && rdo_index(rdo) <= anx7688->n_src_caps) { ++ unsigned rdo_op_curr = rdo_op_current(rdo); ++ unsigned rdo_max_curr = rdo_max_current(rdo); ++ unsigned rdo_idx = rdo_index(rdo) - 1; ++ unsigned pdo_volt, pdo_max_curr; ++ ++ pdo = anx7688->src_caps[rdo_idx]; ++ pdo_volt = pdo_fixed_voltage(pdo); ++ pdo_max_curr = pdo_max_current(pdo); ++ ++ dev_info(anx7688->dev, "RDO (idx=%d op=%umA max=%umA)\n", ++ rdo_idx, rdo_op_curr, rdo_max_curr); ++ ++ dev_info(anx7688->dev, "PDO_FIXED (%umV %umA)\n", ++ pdo_volt, pdo_max_curr); ++ ++ //TODO: we should check the req and respond with accept/reject ++ // but we're using auto_pd feature, so the FW will do ++ // this for us ++ if (rdo_op_curr > pdo_max_curr || rdo_max_curr > pdo_max_curr) { ++ //ret = anx7688_send_ocm_message(anx7688, ANX7688_OCM_MSG_REJECT, 0, 0); ++ } else { ++ //ret = anx7688_send_ocm_message(anx7688, ANX7688_OCM_MSG_ACCEPT, 0, 0); ++ } ++ } else { ++ dev_info(anx7688->dev, "PWR_OBJ RDO index out of range (RDO = 0x%08X)\n", rdo); ++ } ++ ++ break; ++ ++ case ANX7688_OCM_MSG_ACCEPT: ++ dev_info(anx7688->dev, "received ACCEPT\n"); ++ break; ++ ++ case ANX7688_OCM_MSG_REJECT: ++ dev_info(anx7688->dev, "received REJECT\n"); ++ break; ++ ++ case ANX7688_OCM_MSG_RESPONSE_TO_REQ: ++ if (len < 2) { ++ dev_warn(anx7688->dev, "received short RESPONSE_TO_REQ\n"); ++ break; ++ } ++ ++ anx7688_handle_pd_message_response(anx7688, msg[0], msg[1]); ++ break; ++ ++ case ANX7688_OCM_MSG_SOFT_RST: ++ dev_info(anx7688->dev, "received SOFT_RST\n"); ++ break; ++ ++ case ANX7688_OCM_MSG_HARD_RST: ++ if (anx7688->pd_capable) { ++ dev_info(anx7688->dev, "received HARD_RST\n"); ++ ++ // stop drawing power from VBUS ++ psy_val.intval = 0; ++ dev_dbg(dev, "disabling vbus_in power path\n"); ++ ret = power_supply_set_property(anx7688->vbus_in_supply, ++ POWER_SUPPLY_PROP_ONLINE, ++ &psy_val); ++ if (ret) ++ dev_err(anx7688->dev, "failed to offline vbus_in\n"); ++ ++ // wait till the dust settles ++ anx7688->current_update_deadline = ktime_add_ms(ktime_get(), 3000); ++ } else { ++ dev_dbg(anx7688->dev, "received HARD_RST, idiot firmware is bored\n"); ++ } ++ ++ break; ++ ++ case ANX7688_OCM_MSG_RESTART: ++ dev_info(anx7688->dev, "received RESTART\n"); ++ break; ++ ++ case ANX7688_OCM_MSG_PSWAP_REQ: ++ dev_info(anx7688->dev, "received PSWAP_REQ\n"); ++ break; ++ ++ case ANX7688_OCM_MSG_DSWAP_REQ: ++ dev_info(anx7688->dev, "received DSWAP_REQ\n"); ++ break; ++ ++ case ANX7688_OCM_MSG_VCONN_SWAP_REQ: ++ dev_info(anx7688->dev, "received VCONN_SWAP_REQ\n"); ++ break; ++ ++ case ANX7688_OCM_MSG_DP_ALT_ENTER: ++ dev_info(anx7688->dev, "received DP_ALT_ENTER\n"); ++ break; ++ ++ case ANX7688_OCM_MSG_DP_ALT_EXIT: ++ dev_info(anx7688->dev, "received DP_ALT_EXIT\n"); ++ break; ++ ++ case ANX7688_OCM_MSG_DP_SNK_IDENTITY: ++ dev_info(anx7688->dev, "received DP_SNK_IDENTITY\n"); ++ break; ++ ++ case ANX7688_OCM_MSG_SVID: ++ dev_info(anx7688->dev, "received SVID\n"); ++ break; ++ ++ case ANX7688_OCM_MSG_VDM: ++ dev_info(anx7688->dev, "received VDM\n"); ++ break; ++ ++ case ANX7688_OCM_MSG_GOTO_MIN_REQ: ++ dev_info(anx7688->dev, "received GOTO_MIN_REQ\n"); ++ break; ++ ++ case ANX7688_OCM_MSG_PD_STATUS_REQ: ++ dev_info(anx7688->dev, "received PD_STATUS_REQ\n"); ++ break; ++ ++ case ANX7688_OCM_MSG_GET_DP_SNK_CAP: ++ dev_info(anx7688->dev, "received GET_DP_SNK_CAP\n"); ++ break; ++ ++ case ANX7688_OCM_MSG_DP_SNK_CFG: ++ dev_info(anx7688->dev, "received DP_SNK_CFG\n"); ++ break; ++ ++ default: ++ dev_info(anx7688->dev, "received unknown message 0x%02x\n", cmd); ++ break; ++ } ++ ++ return 0; ++} ++ ++static int anx7688_receive_msg(struct anx7688* anx7688) ++{ ++ u8 pkt[32], checksum = 0; ++ int i, ret; ++ ++ ret = i2c_smbus_read_i2c_block_data(anx7688->client_tcpc, ++ ANX7688_TCPC_REG_INTERFACE_RECV, ++ 32, pkt); ++ if (ret < 0) { ++ dev_err(anx7688->dev, "failed to read pd msg\n"); ++ return ret; ++ } ++ ++ ret = anx7688_tcpc_reg_write(anx7688, ANX7688_TCPC_REG_INTERFACE_RECV, 0); ++ if (ret) { ++ dev_warn(anx7688->dev, "failed to clear recv fifo\n"); ++ } ++ ++ if (pkt[0] == 0 || pkt[0] > sizeof(pkt) - 2) { ++ dev_err(anx7688->dev, "received invalid pd message: %*ph\n", ++ (int)sizeof(pkt), pkt); ++ return -EINVAL; ++ } ++ ++ dev_dbg(anx7688->dev, "recv ocm message cmd=0x%02x %*ph\n", ++ pkt[1], pkt[0] + 2, pkt); ++ ++ for (i = 0; i < pkt[0] + 2; i++) ++ checksum += pkt[i]; ++ ++ if (checksum != 0) { ++ dev_err(anx7688->dev, "bad checksum on received message\n"); ++ return -EINVAL; ++ } ++ ++ anx7688_handle_pd_message(anx7688, pkt[1], pkt + 2, pkt[0] - 1); ++ return 0; ++} ++ ++static const char* anx7688_cc_status_string(unsigned v) ++{ ++ switch (v) { ++ case 0: return "SRC.Open"; ++ case 1: return "SRC.Rd"; ++ case 2: return "SRC.Ra"; ++ case 4: return "SNK.Default"; ++ case 8: return "SNK.Power1.5"; ++ case 12: return "SNK.Power3.0"; ++ default: return "UNK"; ++ } ++} ++ ++static int anx7688_update_status(struct anx7688 *anx7688) ++{ ++ struct device *dev = anx7688->dev; ++ bool vbus_on, vconn_on, dr_dfp; ++ int status, cc_status, dp_state, dp_substate, ret; ++ ++ status = anx7688_reg_read(anx7688, ANX7688_REG_STATUS); ++ if (status < 0) ++ return status; ++ ++ cc_status = anx7688_reg_read(anx7688, ANX7688_REG_CC_STATUS); ++ if (cc_status < 0) ++ return cc_status; ++ ++ dp_state = anx7688_tcpc_reg_read(anx7688, 0x87); ++ if (dp_state < 0) ++ return dp_state; ++ ++ dp_substate = anx7688_tcpc_reg_read(anx7688, 0x88); ++ if (dp_substate < 0) ++ return dp_substate; ++ ++ anx7688_set_hdmi_hpd(anx7688, dp_state >= 3); ++ ++ dp_state = (dp_state << 8) | dp_substate; ++ ++ if (anx7688->last_status == -1 || anx7688->last_status != status) { ++ anx7688->last_status = status; ++ dev_dbg(dev, "status changed to 0x%02x\n", status); ++ } ++ ++ if (anx7688->last_cc_status == -1 || anx7688->last_cc_status != cc_status) { ++ anx7688->last_cc_status = cc_status; ++ dev_dbg(dev, "cc_status changed to CC1 = %s CC2 = %s\n", ++ anx7688_cc_status_string(cc_status & 0xf), ++ anx7688_cc_status_string((cc_status >> 4) & 0xf)); ++ } ++ ++ if (anx7688->last_dp_state == -1 || anx7688->last_dp_state != dp_state) { ++ anx7688->last_dp_state = dp_state; ++ dev_dbg(dev, "DP state changed to 0x%04x\n", dp_state); ++ } ++ ++ vbus_on = !!(status & ANX7688_VBUS_STATUS); ++ vconn_on = !!(status & ANX7688_VCONN_STATUS); ++ dr_dfp = !!(status & ANX7688_DATA_ROLE_STATUS); ++ ++ if (anx7688->vbus_on != vbus_on) { ++ dev_dbg(anx7688->dev, "POWER role change to %s\n", ++ vbus_on ? "SOURCE" : "SINK"); ++ ++ if (vbus_on) { ++ ret = regulator_enable(anx7688->supplies[ANX7688_VBUS_INDEX].consumer); ++ if (ret) { ++ dev_err(anx7688->dev, "failed to enable vbus\n"); ++ return ret; ++ } ++ } else { ++ ret = regulator_disable(anx7688->supplies[ANX7688_VBUS_INDEX].consumer); ++ if (ret) { ++ dev_err(anx7688->dev, "failed to disable vbus\n"); ++ return ret; ++ } ++ } ++ ++ anx7688->pwr_role = vbus_on ? TYPEC_SOURCE : TYPEC_SINK; ++ typec_set_pwr_role(anx7688->port, anx7688->pwr_role); ++ anx7688->vbus_on = vbus_on; ++ } ++ ++ if (anx7688->vconn_on != vconn_on) { ++ dev_dbg(anx7688->dev, "VCONN role change to %s\n", ++ vconn_on ? "SOURCE" : "SINK"); ++ ++ if (vconn_on) { ++ ret = regulator_enable(anx7688->supplies[ANX7688_VCONN_INDEX].consumer); ++ if (ret) { ++ dev_err(anx7688->dev, "failed to enable vconn\n"); ++ return ret; ++ } ++ } else { ++ ret = regulator_disable(anx7688->supplies[ANX7688_VCONN_INDEX].consumer); ++ if (ret) { ++ dev_err(anx7688->dev, "failed to disable vconn\n"); ++ return ret; ++ } ++ } ++ ++ typec_set_vconn_role(anx7688->port, vconn_on ? TYPEC_SOURCE : TYPEC_SINK); ++ anx7688->vconn_on = vconn_on; ++ } ++ ++ anx7688->data_role = dr_dfp ? TYPEC_HOST : TYPEC_DEVICE; ++ typec_set_data_role(anx7688->port, anx7688->data_role); ++ ++ if (usb_role_switch_get_role(anx7688->role_sw) != ++ (dr_dfp ? USB_ROLE_HOST : USB_ROLE_DEVICE)) { ++ dev_dbg(anx7688->dev, "DATA role change requested to %s\n", ++ dr_dfp ? "DFP" : "UFP"); ++ ++ ret = usb_role_switch_set_role(anx7688->role_sw, ++ dr_dfp ? USB_ROLE_HOST : USB_ROLE_DEVICE); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static irqreturn_t anx7688_irq_status_handler(int irq, void *data) ++{ ++#if DISABLE_OCM ++ struct anx7688 *anx7688 = data; ++ struct device *dev = anx7688->dev; ++ int ext2_status; ++ ++ mutex_lock(&anx7688->lock); ++ ++ if (!test_bit(ANX7688_F_CONNECTED, anx7688->flags)) { ++ /* ++ * The chip should be disabled and powered off, nothing ++ * more to do. ++ */ ++ dev_dbg(dev, "spurious status irq\n"); ++ goto out_unlock; ++ } ++ ++ //dev_dbg(dev, "status irq\n"); ++ ++ ext2_status = anx7688_reg_read(anx7688, ANX7688_REG_IRQ_EXT_SOURCE2); ++ dev_dbg(dev, " ext2 = 0x%02x\n", ext2_status); ++ anx7688_reg_write(anx7688, ANX7688_REG_IRQ_EXT_SOURCE2, ext2_status); ++ ++out_unlock: ++ mutex_unlock(&anx7688->lock); ++ ++ return IRQ_HANDLED; ++#else ++ struct anx7688 *anx7688 = data; ++ struct device *dev = anx7688->dev; ++ int tcpc_status, ext2_status, soft_status; ++ ++ mutex_lock(&anx7688->lock); ++ ++ if (!test_bit(ANX7688_F_CONNECTED, anx7688->flags)) { ++ dev_dbg(dev, "spurious status irq\n"); ++ /* anx chip should be disabled and power off, nothing ++ * more to do */ ++ goto out_unlock; ++ } ++ ++ //dev_dbg(dev, "status irq\n"); ++ ++ // clear tcpc interrupt ++ tcpc_status = anx7688_tcpc_reg_read(anx7688, ANX7688_TCPC_REG_ALERT0); ++ if (tcpc_status > 0) { ++ //dev_dbg(dev, " tcpc = 0x%02x\n", tcpc_status); ++ anx7688_tcpc_reg_write(anx7688, ANX7688_TCPC_REG_ALERT0, tcpc_status); ++ } ++ ++ ext2_status = anx7688_reg_read(anx7688, ANX7688_REG_IRQ_EXT_SOURCE2); ++ if (ext2_status & ANX7688_IRQ2_SOFT_INT) { ++ soft_status = anx7688_reg_read(anx7688, ANX7688_REG_STATUS_INT); ++ anx7688_reg_write(anx7688, ANX7688_REG_STATUS_INT, 0); ++ ++ //dev_dbg(dev, " soft = 0x%02x\n", soft_status); ++ ++ if (soft_status > 0) { ++ soft_status &= ANX7688_SOFT_INT_MASK; ++ ++ if (soft_status & ANX7688_IRQS_RECEIVED_MSG) ++ anx7688_receive_msg(anx7688); ++ ++ if (soft_status & (ANX7688_IRQS_CC_STATUS_CHANGE | ++ ANX7688_IRQS_VBUS_CHANGE | ++ ANX7688_IRQS_VCONN_CHANGE | ++ ANX7688_IRQS_DATA_ROLE_CHANGE)) { ++ anx7688_update_status(anx7688); ++ } ++ } ++ ++ anx7688_reg_write(anx7688, ANX7688_REG_IRQ_EXT_SOURCE2, ANX7688_IRQ2_SOFT_INT); ++ } ++ ++out_unlock: ++ mutex_unlock(&anx7688->lock); ++ ++ return IRQ_HANDLED; ++#endif ++} ++ ++static int anx7688_dr_set(struct typec_port *port, enum typec_data_role role) ++{ ++ struct anx7688 *anx7688 = typec_get_drvdata(port); ++ int ret = 0; ++ ++ dev_info(anx7688->dev, "data role set %d\n", role); ++ ++ if (anx7688->data_role != role) { ++ mutex_lock(&anx7688->lock); ++ ret = anx7688_send_ocm_message(anx7688, ANX7688_OCM_MSG_DSWAP_REQ, 0, 0); ++ mutex_unlock(&anx7688->lock); ++ } ++ ++ return ret; ++} ++ ++static int anx7688_pr_set(struct typec_port *port, enum typec_role role) ++{ ++ struct anx7688 *anx7688 = typec_get_drvdata(port); ++ int ret = 0; ++ ++ dev_info(anx7688->dev, "power role set %d\n", role); ++ ++ if (anx7688->pwr_role != role) { ++ mutex_lock(&anx7688->lock); ++ ret = anx7688_send_ocm_message(anx7688, ANX7688_OCM_MSG_PSWAP_REQ, 0, 0); ++ mutex_unlock(&anx7688->lock); ++ } ++ ++ return ret; ++} ++ ++/* ++ * Calls to the eerpom functions need to be taken under the anx7688 lock. ++ */ ++ ++static int anx7688_eeprom_set_address(struct anx7688 *anx7688, u16 addr) ++{ ++ int ret; ++ ++ ret = anx7688_reg_write(anx7688, 0xe0, (addr >> 8) & 0xff); ++ if (ret < 0) ++ return ret; ++ ++ return anx7688_reg_write(anx7688, 0xe1, addr & 0xff); ++} ++ ++static int anx7688_eeprom_wait_done(struct anx7688 *anx7688) ++{ ++ ktime_t timeout; ++ int ret; ++ ++ // wait for read to be done ++ timeout = ktime_add_us(ktime_get(), 50000); ++ while (true) { ++ ret = anx7688_reg_read(anx7688, 0xe2); ++ if (ret < 0) ++ return ret; ++ ++ if (ret & BIT(3)) ++ return 0; ++ ++ if (ktime_after(ktime_get(), timeout)) { ++ dev_err(anx7688->dev, "timeout waiting for eeprom\n"); ++ return -ETIMEDOUT; ++ } ++ } ++} ++ ++/* wait for internal FSM of EEPROM to be in a state ready for ++ * programming/reading ++ */ ++static int anx7688_eeprom_wait_ready(struct anx7688 *anx7688) ++{ ++ ktime_t timeout; ++ int ret; ++ ++ // wait until eeprom is ready ++ timeout = ktime_add_us(ktime_get(), 1000000); ++ while (true) { ++ ret = anx7688_reg_read(anx7688, 0x7f); ++ if (ret < 0) ++ return ret; ++ ++ if ((ret & 0x0f) == 7) ++ return 0; ++ ++ if (ktime_after(ktime_get(), timeout)) { ++ dev_err(anx7688->dev, "timeout waiting for eeprom to initialize\n"); ++ return -ETIMEDOUT; ++ } ++ ++ msleep(5); ++ } ++} ++ ++static int anx7688_eeprom_read(struct anx7688 *anx7688, unsigned addr, u8 buf[16]) ++{ ++ int ret; ++ ++ ret = anx7688_eeprom_set_address(anx7688, addr); ++ if (ret) ++ return ret; ++ ++ // initiate read ++ ret = anx7688_reg_write(anx7688, 0xe2, 0x06); ++ if (ret < 0) ++ return ret; ++ ++ ret = anx7688_eeprom_wait_done(anx7688); ++ if (ret) ++ return ret; ++ ++ ret = i2c_smbus_read_i2c_block_data(anx7688->client, 0xd0, 16, buf); ++ if (ret < 0) { ++ dev_err(anx7688->dev, ++ "failed to read eeprom data (err=%d)\n", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int anx7688_eeprom_write(struct anx7688 *anx7688, unsigned addr, ++ const u8 buf[16]) ++{ ++ int ret; ++ ++ ret = anx7688_eeprom_set_address(anx7688, addr); ++ if (ret) ++ return ret; ++ ++ ret = i2c_smbus_write_i2c_block_data(anx7688->client, 0xd0, 16, buf); ++ if (ret < 0) { ++ dev_err(anx7688->dev, ++ "failed to write eeprom data (err=%d)\n", ret); ++ return ret; ++ } ++ ++ dev_info(anx7688->dev, "eeprom wr %x %*ph\n", addr, 16, buf); ++ ++ // initiate write ++ ret = anx7688_reg_write(anx7688, 0xe2, 0x01); ++ if (ret < 0) ++ return ret; ++ ++ ret = anx7688_eeprom_wait_done(anx7688); ++ if (ret) ++ return ret; ++ ++ udelay(1500); ++ udelay(1000); ++ ++ return 0; ++} ++ ++/* ++ * The firmware is flashed as is from address 0x10 up to 0xffff. ++ */ ++static int anx7688_flash_firmware(struct anx7688 *anx7688) ++{ ++ const char* fw_name = "anx7688-fw.bin"; ++ const struct firmware *fw; ++ unsigned addr; ++ u8 buf[16]; ++ int ret; ++ ++ ret = request_firmware(&fw, fw_name, anx7688->dev); ++ if (ret < 0) ++ return ret; ++ ++ if (fw->size > 0x10000 - 16) { ++ dev_err(anx7688->dev, "Firmware is too big %s\n", fw_name); ++ ret = -E2BIG; ++ goto err_release; ++ } ++ ++ mutex_lock(&anx7688->lock); ++ ++ /* ++ * Cabledet changes will not be processed, since we're holding the ++ * lock, so the firmware flashing is safe from interruptions. ++ */ ++ ++ if (test_bit(ANX7688_F_CONNECTED, anx7688->flags)) ++ anx7688_disconnect(anx7688); ++ ++ msleep(20); ++ ++ anx7688_power_enable(anx7688); ++ ++ // reset OCM ++ ret = anx7688_reg_update_bits(anx7688, ANX7688_REG_USBC_RESET_CTRL, ++ ANX7688_USBC_RESET_CTRL_OCM_RESET, ++ ANX7688_USBC_RESET_CTRL_OCM_RESET); ++ if (ret < 0) ++ goto err_unlock; ++ ++ ret = anx7688_eeprom_wait_ready(anx7688); ++ if (ret) ++ goto err_unlock; ++ ++ msleep(10); ++ ++ /* ++ * Write to some magic registers to unlock flashing the EEPROM. ++ */ ++ ret = anx7688_reg_update_bits(anx7688, 0x3f, BIT(5), BIT(5)); ++ if (ret < 0) ++ goto err_unlock; ++ ++ ret = anx7688_reg_update_bits(anx7688, 0x44, ++ BIT(0) | BIT(7), ++ BIT(0) | BIT(7)); ++ if (ret < 0) ++ goto err_unlock; ++ ++ ret = anx7688_reg_update_bits(anx7688, 0x66, BIT(3), BIT(3)); ++ if (ret < 0) ++ goto err_unlock; ++ ++ msleep(50); ++ ++ ret = anx7688_eeprom_wait_done(anx7688); ++ if (ret) ++ goto err_unlock; ++ ++ for (addr = 0; addr < fw->size; addr += sizeof buf) { ++ memset(buf, 0, sizeof buf); ++ memcpy(buf, fw->data + addr, min_t(unsigned, fw->size - addr, sizeof buf)); ++ ++ ret = anx7688_eeprom_write(anx7688, addr + 0x10, buf); ++ if (ret < 0) ++ goto err_unlock; ++ } ++ ++ clear_bit(ANX7688_F_FW_FAILED, anx7688->flags); ++ ++err_unlock: ++ anx7688_power_disable(anx7688); ++ schedule_delayed_work(&anx7688->work, msecs_to_jiffies(20)); ++ mutex_unlock(&anx7688->lock); ++ ++err_release: ++ release_firmware(fw); ++ return ret; ++} ++ ++static const struct typec_operations anx7688_typec_ops = { ++ .dr_set = anx7688_dr_set, ++ .pr_set = anx7688_pr_set, ++}; ++ ++static ssize_t flash_eeprom_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ return scnprintf(buf, PAGE_SIZE, "Write 1 to this file to initiate firmware flashing. Consult dmesg for results.\n"); ++} ++ ++static ssize_t flash_eeprom_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t len) ++{ ++ struct anx7688 *anx7688 = i2c_get_clientdata(to_i2c_client(dev)); ++ unsigned val; ++ int ret; ++ ++ ret = kstrtouint(buf, 0, &val); ++ if (ret) ++ return ret; ++ ++ if (val == 1) { ++ ret = anx7688_flash_firmware(anx7688); ++ if (ret) ++ return ret; ++ } ++ ++ return len; ++} ++static DEVICE_ATTR_RW(flash_eeprom); ++ ++static ssize_t reg_update_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t len) ++{ ++ struct anx7688 *anx7688 = i2c_get_clientdata(to_i2c_client(dev)); ++ unsigned val, addr, devaddr, data; ++ int ret; ++ ++ ret = kstrtouint(buf, 16, &val); ++ if (ret) ++ return ret; ++ ++ data = val & 0xff; ++ addr = (val >> 8) & 0xff; ++ devaddr = (val >> 16) & 0xff; ++ ++ mutex_lock(&anx7688->lock); ++ ++ if (!test_bit(ANX7688_F_POWERED, anx7688->flags)) { ++ ret = -ENODEV; ++ goto out_unlock; ++ } ++ ++ if (devaddr == 0x50) ++ ret = anx7688_reg_write(anx7688, addr, val); ++ else if (devaddr == 0x58) ++ ret = anx7688_tcpc_reg_write(anx7688, addr, val); ++ else ++ ret = -EINVAL; ++ ++out_unlock: ++ mutex_unlock(&anx7688->lock); ++ ++ return ret ? ret : len; ++} ++static DEVICE_ATTR_WO(reg_update); ++ ++static ssize_t hwreset_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t len) ++{ ++ struct anx7688 *anx7688 = i2c_get_clientdata(to_i2c_client(dev)); ++ ++ mutex_lock(&anx7688->lock); ++ ++ if (test_bit(ANX7688_F_CONNECTED, anx7688->flags)) ++ anx7688_disconnect(anx7688); ++ ++ schedule_delayed_work(&anx7688->work, msecs_to_jiffies(20)); ++ mutex_unlock(&anx7688->lock); ++ ++ return len; ++} ++static DEVICE_ATTR_WO(hwreset); ++ ++static struct attribute *anx7688_attrs[] = { ++ &dev_attr_flash_eeprom.attr, ++ &dev_attr_reg_update.attr, ++ &dev_attr_hwreset.attr, ++ NULL, ++}; ++ ++ATTRIBUTE_GROUPS(anx7688); ++ ++/* ++ * This function has to work when the ANX7688 is active, and when ++ * it is powered down. It power cycles the chip and asserts the OCM ++ * reset, to prevent OCM FW interfering with EEPROM reading. ++ * ++ * After reading EEPROM, the reconnection is scheduled. ++ */ ++static int anx7688_firmware_show(struct seq_file *s, void *data) ++{ ++ struct anx7688 *anx7688 = s->private; ++ unsigned addr; ++ u8 buf[16]; ++ int ret; ++ ++ mutex_lock(&anx7688->lock); ++ ++ if (test_bit(ANX7688_F_CONNECTED, anx7688->flags)) ++ anx7688_disconnect(anx7688); ++ ++ msleep(20); ++ ++ anx7688_power_enable(anx7688); ++ ++ ret = anx7688_reg_update_bits(anx7688, ANX7688_REG_USBC_RESET_CTRL, ++ ANX7688_USBC_RESET_CTRL_OCM_RESET, ++ ANX7688_USBC_RESET_CTRL_OCM_RESET); ++ if (ret < 0) ++ goto out_powerdown; ++ ++ ret = anx7688_eeprom_wait_ready(anx7688); ++ if (ret) ++ goto out_powerdown; ++ ++ msleep(10); ++ ++ for (addr = 0x10; addr < 0x10000; addr += 16) { ++ // set address ++ ret = anx7688_eeprom_read(anx7688, addr, buf); ++ if (ret < 0) ++ goto out_powerdown; ++ ++ seq_write(s, buf, sizeof buf); ++ } ++ ++out_powerdown: ++ anx7688_power_disable(anx7688); ++ schedule_delayed_work(&anx7688->work, 0); ++ mutex_unlock(&anx7688->lock); ++ ++ return ret; ++} ++DEFINE_SHOW_ATTRIBUTE(anx7688_firmware); ++ ++static int anx7688_regs_show(struct seq_file *s, void *data) ++{ ++ struct anx7688 *anx7688 = s->private; ++ u8 buf[16]; ++ unsigned i, addr; ++ int ret = -ENODEV; ++ ++ mutex_lock(&anx7688->lock); ++ ++ if (!test_bit(ANX7688_F_POWERED, anx7688->flags)) ++ goto out_unlock; ++ ++ for (addr = 0; addr < 256; addr += 16) { ++ ret = i2c_smbus_read_i2c_block_data(anx7688->client, addr, ++ sizeof buf, buf); ++ if (ret < 0) { ++ dev_err(anx7688->dev, ++ "failed to read registers (err=%d)\n", ret); ++ goto out_unlock; ++ } ++ ++ for (i = 0; i < 16; i++) ++ seq_printf(s, "50%02x: %02x\n", addr + i, buf[i]); ++ } ++ ++ for (addr = 0; addr < 256; addr += 16) { ++ ret = i2c_smbus_read_i2c_block_data(anx7688->client_tcpc, addr, ++ sizeof buf, buf); ++ if (ret < 0) { ++ dev_err(anx7688->dev, ++ "failed to read registers (err=%d)\n", ret); ++ goto out_unlock; ++ } ++ ++ for (i = 0; i < 16; i++) ++ seq_printf(s, "58%02x: %02x\n", addr + i, buf[i]); ++ } ++ ++out_unlock: ++ mutex_unlock(&anx7688->lock); ++ ++ return ret; ++} ++DEFINE_SHOW_ATTRIBUTE(anx7688_regs); ++ ++static int anx7688_status_show(struct seq_file *s, void *data) ++{ ++ struct anx7688 *anx7688 = s->private; ++ ++ mutex_lock(&anx7688->lock); ++ ++ seq_printf(s, "not much\n"); ++ ++ mutex_unlock(&anx7688->lock); ++ ++ return 0; ++} ++DEFINE_SHOW_ATTRIBUTE(anx7688_status); ++ ++/* ++ * This is just a 1s watchdog checking the state if cabledet pin. ++ */ ++static void anx7688_cabledet_timer_fn(struct timer_list *t) ++{ ++ struct anx7688 *anx7688 = from_timer(anx7688, t, work_timer); ++ ++ schedule_delayed_work(&anx7688->work, 0); ++ mod_timer(t, jiffies + msecs_to_jiffies(1000)); ++} ++ ++static void anx7688_handle_vbus_in_notify(struct anx7688* anx7688) ++{ ++ union power_supply_propval psy_val = {0,}; ++ struct device *dev = anx7688->dev; ++ int ret; ++ ++ /* PD charger doesn't like this for some reason, so it's disabled for now */ ++ if (anx7688->input_current_limit && false) { ++ ret = power_supply_get_property(anx7688->vbus_in_supply, ++ POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, ++ &psy_val); ++ if (ret) { ++ dev_err(dev, "failed to get vbus_in current\n"); ++ return; ++ } ++ ++ if (psy_val.intval == anx7688->input_current_limit) ++ goto check_type; ++ ++ psy_val.intval = anx7688->input_current_limit * 1000; ++ dev_dbg(dev, "setting vbus_in current limit to %d mA\n", ++ psy_val.intval / 1000); ++ ret = power_supply_set_property(anx7688->vbus_in_supply, ++ POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, ++ &psy_val); ++ if (ret) ++ dev_err(dev, "failed to set vbus_in current to %d mA\n", ++ psy_val.intval / 1000); ++ } ++ ++check_type: ++ ret = power_supply_get_property(anx7688->vbus_in_supply, ++ POWER_SUPPLY_PROP_USB_TYPE, ++ &psy_val); ++ if (ret) { ++ dev_err(dev, "failed to get USB BC1.2 result\n"); ++ return; ++ } ++ ++ if (anx7688->last_bc_result == psy_val.intval) ++ return; ++ ++ anx7688->last_bc_result = psy_val.intval; ++ ++ switch (psy_val.intval) { ++ case POWER_SUPPLY_USB_TYPE_DCP: ++ case POWER_SUPPLY_USB_TYPE_CDP: ++ dev_dbg(dev, "BC 1.2 result: DCP or CDP\n"); ++ break; ++ case POWER_SUPPLY_USB_TYPE_SDP: ++ default: ++ dev_dbg(dev, "BC 1.2 result: SDP\n"); ++ break; ++ } ++} ++ ++static int anx7688_cc_status(unsigned v) ++{ ++ switch (v) { ++ case 0: return -1; ++ case 1: return -1; ++ case 2: return -1; ++ case 4: return TYPEC_PWR_MODE_USB; ++ case 8: return TYPEC_PWR_MODE_1_5A; ++ case 12: return TYPEC_PWR_MODE_3_0A; ++ default: return -1; ++ } ++} ++ ++static const char* anx7688_get_power_mode_name(enum typec_pwr_opmode mode) ++{ ++ switch (mode) { ++ case TYPEC_PWR_MODE_USB: return "USB"; ++ case TYPEC_PWR_MODE_1_5A: return "1.5A"; ++ case TYPEC_PWR_MODE_3_0A: return "3.0A"; ++ case TYPEC_PWR_MODE_PD: return "PD"; ++ default: return "Unknown"; ++ } ++} ++ ++/* ++ * This is called after 500ms after connection when the PD contract should have ++ * been negotiated. We should inspect CC pins or PD status here and decide what ++ * input current limit to set. ++ */ ++static void anx7688_handle_current_update(struct anx7688* anx7688) ++{ ++ unsigned cc_status = anx7688->last_cc_status; ++ union power_supply_propval val = {0,}; ++ struct device *dev = anx7688->dev; ++ int pwr_mode, ret, current_limit = 0; ++ ++ if (anx7688->pd_capable) { ++ pwr_mode = TYPEC_PWR_MODE_PD; ++ } else if (cc_status < 0) { ++ pwr_mode = TYPEC_PWR_MODE_USB; ++ } else { ++ pwr_mode = anx7688_cc_status(cc_status & 0xf); ++ if (pwr_mode < 0) ++ pwr_mode = anx7688_cc_status((cc_status >> 4) & 0xf); ++ if (pwr_mode < 0) ++ pwr_mode = TYPEC_PWR_MODE_USB; ++ } ++ ++ if (pwr_mode == TYPEC_PWR_MODE_1_5A) ++ current_limit = 1500; ++ else if (pwr_mode == TYPEC_PWR_MODE_3_0A) ++ current_limit = 3000; ++ else if (pwr_mode == TYPEC_PWR_MODE_PD) ++ current_limit = anx7688->pd_current_limit; ++ ++ anx7688->input_current_limit = current_limit; ++ ++ dev_info(anx7688->dev, "updating power mode to %s, current limit %dmA (0 => BC1.2)\n", ++ anx7688_get_power_mode_name(pwr_mode), current_limit); ++ ++ if (current_limit) { ++ /* ++ * Disable BC1.2 detection, because we'll be setting ++ * a current limit determined by USB-PD ++ */ ++ val.intval = 0; ++ dev_dbg(dev, "disabling USB BC 1.2 detection\n"); ++ ret = power_supply_set_property(anx7688->vbus_in_supply, ++ POWER_SUPPLY_PROP_USB_BC_ENABLED, ++ &val); ++ if (ret) ++ dev_err(dev, "failed to disable USB BC1.2 detection\n"); ++ ++ val.intval = current_limit * 1000; ++ dev_dbg(dev, "setting vbus_in current limit to %d mA\n", current_limit); ++ ret = power_supply_set_property(anx7688->vbus_in_supply, ++ POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, ++ &val); ++ if (ret) ++ dev_err(dev, "failed to set vbus_in current to %d mA\n", ++ current_limit); ++ } else { ++ /* ++ * Use the result of BC1.2 detection performed by PMIC. ++ */ ++ ret = power_supply_get_property(anx7688->vbus_in_supply, ++ POWER_SUPPLY_PROP_USB_BC_ENABLED, ++ &val); ++ if (ret) ++ dev_err(dev, "failed to get USB BC1.2 detection status\n"); ++ ++ if (ret != 0 || val.intval == 0) { ++ /* ++ * If BC is disabled or we can't get its status, ++ * set conservative 500mA limit. Otherwise leave ++ * the limit to BC1.2. ++ */ ++ val.intval = 500 * 1000; ++ dev_dbg(dev, "setting vbus_in current limit to %d mA\n", ++ val.intval / 1000); ++ ret = power_supply_set_property(anx7688->vbus_in_supply, ++ POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, ++ &val); ++ if (ret) ++ dev_err(dev, "failed to set vbus_in current to %d mA\n", ++ val.intval / 1000); ++ } ++ } ++ ++ /* Turn on VBUS power path inside PMIC. */ ++ val.intval = 1; ++ dev_dbg(dev, "enabling vbus_in power path\n"); ++ ret = power_supply_set_property(anx7688->vbus_in_supply, ++ POWER_SUPPLY_PROP_ONLINE, ++ &val); ++ if (ret) ++ dev_err(anx7688->dev, "failed to enable vbus_in\n"); ++ ++ typec_set_pwr_opmode(anx7688->port, pwr_mode); ++} ++ ++static int anx7688_vbus_in_notify(struct notifier_block *nb, ++ unsigned long val, void *v) ++{ ++ struct anx7688 *anx7688 = container_of(nb, struct anx7688, vbus_in_nb); ++ struct power_supply *psy = v; ++ ++ /* atomic context */ ++ if (val == PSY_EVENT_PROP_CHANGED && psy == anx7688->vbus_in_supply) { ++ set_bit(ANX7688_F_PWRSUPPLY_CHANGE, anx7688->flags); ++ schedule_delayed_work(&anx7688->work, 0); ++ } ++ ++ return NOTIFY_OK; ++} ++ ++static void anx7688_work(struct work_struct *work) ++{ ++ struct anx7688 *anx7688 = container_of(work, struct anx7688, work.work); ++ ++ if (test_bit(ANX7688_F_FW_FAILED, anx7688->flags)) ++ return; ++ ++ mutex_lock(&anx7688->lock); ++ ++ if (test_and_clear_bit(ANX7688_F_PWRSUPPLY_CHANGE, anx7688->flags)) ++ anx7688_handle_vbus_in_notify(anx7688); ++ ++ anx7688_handle_cable_change(anx7688); ++ ++ if (test_bit(ANX7688_F_CONNECTED, anx7688->flags)) { ++ /* ++ * We check status periodically outside of interrupt, just to ++ * be sure we didn't miss any status interrupts ++ */ ++ anx7688_update_status(anx7688); ++ ++ if (anx7688->current_update_deadline && ++ ktime_after(ktime_get(), anx7688->current_update_deadline)) { ++ anx7688->current_update_deadline = 0; ++ anx7688_handle_current_update(anx7688); ++ } ++ } ++ ++ mutex_unlock(&anx7688->lock); ++} ++ ++static int anx7688_i2c_probe(struct i2c_client *client) ++{ ++ struct anx7688 *anx7688; ++ struct device *dev = &client->dev; ++ struct typec_capability typec_cap = { }; ++ union power_supply_propval psy_val; ++ int i, vid_h, vid_l; ++ int irq_cabledet; ++ int ret = 0; ++ ++ anx7688 = devm_kzalloc(dev, sizeof(*anx7688), GFP_KERNEL); ++ if (!anx7688) ++ return -ENOMEM; ++ ++ i2c_set_clientdata(client, anx7688); ++ anx7688->client = client; ++ anx7688->dev = &client->dev; ++ mutex_init(&anx7688->lock); ++ INIT_DELAYED_WORK(&anx7688->work, anx7688_work); ++ anx7688->last_extcon_state = -1; ++ ++ ret = of_property_read_variable_u32_array(dev->of_node, "source-caps", ++ anx7688->src_caps, ++ 1, ARRAY_SIZE(anx7688->src_caps)); ++ if (ret < 0) { ++ dev_err(dev, "failed to get source-caps from DT\n"); ++ return ret; ++ } ++ anx7688->n_src_caps = ret; ++ ++ ret = of_property_read_variable_u32_array(dev->of_node, "sink-caps", ++ anx7688->snk_caps, ++ 1, ARRAY_SIZE(anx7688->snk_caps)); ++ if (ret < 0) { ++ dev_err(dev, "failed to get sink-caps from DT\n"); ++ return ret; ++ } ++ anx7688->n_snk_caps = ret; ++ ++ for (i = 0; i < ANX7688_NUM_SUPPLIES; i++) ++ anx7688->supplies[i].supply = anx7688_supply_names[i]; ++ ret = devm_regulator_bulk_get(dev, ANX7688_NUM_SUPPLIES, ++ anx7688->supplies); ++ if (ret) ++ return ret; ++ ++ anx7688->vbus_in_supply = ++ devm_power_supply_get_by_phandle(dev, "vbus_in-supply"); ++ if (IS_ERR(anx7688->vbus_in_supply)) { ++ dev_err(dev, "Couldn't get the VBUS power supply\n"); ++ return PTR_ERR(anx7688->vbus_in_supply); ++ } ++ ++ if (!anx7688->vbus_in_supply) ++ return -EPROBE_DEFER; ++ ++ anx7688->gpio_enable = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); ++ if (IS_ERR(anx7688->gpio_enable)) { ++ dev_err(dev, "Could not get enable gpio\n"); ++ return PTR_ERR(anx7688->gpio_enable); ++ } ++ ++ anx7688->gpio_reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); ++ if (IS_ERR(anx7688->gpio_reset)) { ++ dev_err(dev, "Could not get reset gpio\n"); ++ return PTR_ERR(anx7688->gpio_reset); ++ } ++ ++ anx7688->gpio_cabledet = devm_gpiod_get(dev, "cabledet", GPIOD_IN); ++ if (IS_ERR(anx7688->gpio_cabledet)) { ++ dev_err(dev, "Could not get cabledet gpio\n"); ++ return PTR_ERR(anx7688->gpio_cabledet); ++ } ++ ++ irq_cabledet = gpiod_to_irq(anx7688->gpio_cabledet); ++ if (irq_cabledet < 0) { ++ dev_err(dev, "Could not get cabledet irq\n"); ++ return irq_cabledet; ++ } ++ ++ ret = devm_device_add_groups(&client->dev, anx7688_groups); ++ if (ret) ++ return ret; ++ ++ // Initialize extcon device ++ anx7688->extcon = devm_extcon_dev_allocate(dev, anx7688_extcon_cable); ++ if (IS_ERR(anx7688->extcon)) ++ return -ENOMEM; ++ ++ ret = devm_extcon_dev_register(dev, anx7688->extcon); ++ if (ret) { ++ dev_err(dev, "failed to register extcon device\n"); ++ return ret; ++ } ++ ++ // Register the TCPC i2c interface as second interface (0x58) ++ anx7688->client_tcpc = i2c_new_dummy_device(client->adapter, 0x2c); ++ if (IS_ERR(anx7688->client_tcpc)) { ++ dev_err(dev, "Could not register tcpc i2c client\n"); ++ return PTR_ERR(anx7688->client_tcpc); ++ } ++ i2c_set_clientdata(anx7688->client_tcpc, anx7688); ++ ++ // powerup and probe the ANX chip ++ ++ ret = regulator_bulk_enable(ANX7688_NUM_ALWAYS_ON_SUPPLIES, ++ anx7688->supplies); ++ if (ret) { ++ dev_err(dev, "Could not enable regulators\n"); ++ goto err_dummy_dev; ++ } ++ ++ msleep(10); ++ ++ anx7688_power_enable(anx7688); ++ ++ vid_l = anx7688_tcpc_reg_read(anx7688, ANX7688_TCPC_REG_VENDOR_ID0); ++ vid_h = anx7688_tcpc_reg_read(anx7688, ANX7688_TCPC_REG_VENDOR_ID1); ++ if (vid_l < 0 || vid_h < 0) { ++ ret = vid_l < 0 ? vid_l : vid_h; ++ anx7688_power_disable(anx7688); ++ goto err_disable_reg; ++ } ++ ++ dev_info(dev, "Vendor id 0x%04x\n", vid_l | vid_h << 8); ++ ++ anx7688_power_disable(anx7688); ++ ++ anx7688->role_sw = usb_role_switch_get(dev); ++ if (IS_ERR(anx7688->role_sw)) { ++ dev_err(dev, "Could not get role switch\n"); ++ ret = PTR_ERR(anx7688->role_sw); ++ goto err_disable_reg; ++ } ++ ++ // setup a typec port device ++ typec_cap.revision = USB_TYPEC_REV_1_2; ++ typec_cap.pd_revision = 0x200; ++ typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE; ++ typec_cap.type = TYPEC_PORT_DRP; ++ typec_cap.data = TYPEC_PORT_DRD; ++ typec_cap.driver_data = anx7688; ++ typec_cap.ops = &anx7688_typec_ops; ++ ++ anx7688->port = typec_register_port(dev, &typec_cap); ++ if (IS_ERR(anx7688->port)) { ++ dev_err(dev, "Could not register type-c port\n"); ++ ret = PTR_ERR(anx7688->port); ++ goto err_role_sw; ++ } ++ ++ anx7688->pwr_role = TYPEC_SINK; ++ anx7688->data_role = TYPEC_DEVICE; ++ typec_set_pwr_role(anx7688->port, anx7688->pwr_role); ++ typec_set_data_role(anx7688->port, anx7688->data_role); ++ typec_set_pwr_opmode(anx7688->port, TYPEC_PWR_MODE_USB); ++ typec_set_vconn_role(anx7688->port, TYPEC_SINK); ++ ++ // make sure BC1.2 detection in PMIC is enabled ++ anx7688->last_bc_result = -1; ++ psy_val.intval = 1; ++ dev_dbg(dev, "enabling USB BC 1.2 detection\n"); ++ ret = power_supply_set_property(anx7688->vbus_in_supply, ++ POWER_SUPPLY_PROP_USB_BC_ENABLED, ++ &psy_val); ++ if (ret) { ++ dev_err(anx7688->dev, "failed to enable BC1.2 detection\n"); ++ goto err_cport; ++ } ++ ++ ret = devm_request_irq(dev, irq_cabledet, anx7688_irq_plug_handler, ++ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, ++ "anx7688-cabledet", anx7688); ++ if (ret < 0) { ++ dev_err(dev, "Could not request cabledet irq (%d)\n", ret); ++ goto err_cport; ++ } ++ ++ ret = devm_request_threaded_irq(dev, client->irq, ++ NULL, anx7688_irq_status_handler, ++ IRQF_ONESHOT, NULL, anx7688); ++ if (ret < 0) { ++ dev_err(dev, "Could not request irq (%d)\n", ret); ++ goto err_cport; ++ } ++ ++ anx7688->vbus_in_nb.notifier_call = anx7688_vbus_in_notify; ++ anx7688->vbus_in_nb.priority = 0; ++ ret = power_supply_reg_notifier(&anx7688->vbus_in_nb); ++ if (ret) ++ goto err_cport; ++ ++ anx7688->debug_root = debugfs_create_dir("anx7688", NULL); ++ debugfs_create_file("firmware", 0444, anx7688->debug_root, anx7688, ++ &anx7688_firmware_fops); ++ debugfs_create_file("regs", 0444, anx7688->debug_root, anx7688, ++ &anx7688_regs_fops); ++ debugfs_create_file("status", 0444, anx7688->debug_root, anx7688, ++ &anx7688_status_fops); ++ ++ schedule_delayed_work(&anx7688->work, msecs_to_jiffies(10)); ++ ++ timer_setup(&anx7688->work_timer, anx7688_cabledet_timer_fn, 0); ++ mod_timer(&anx7688->work_timer, jiffies + msecs_to_jiffies(1000)); ++ ++ return 0; ++ ++err_cport: ++ typec_unregister_port(anx7688->port); ++err_role_sw: ++ usb_role_switch_put(anx7688->role_sw); ++err_disable_reg: ++ regulator_bulk_disable(ANX7688_NUM_ALWAYS_ON_SUPPLIES, anx7688->supplies); ++err_dummy_dev: ++ i2c_unregister_device(anx7688->client_tcpc); ++ return ret; ++} ++ ++static void anx7688_i2c_remove(struct i2c_client *client) ++{ ++ struct anx7688 *anx7688 = i2c_get_clientdata(client); ++ ++ mutex_lock(&anx7688->lock); ++ ++ power_supply_unreg_notifier(&anx7688->vbus_in_nb); ++ ++ del_timer_sync(&anx7688->work_timer); ++ ++ cancel_delayed_work_sync(&anx7688->work); ++ ++ if (test_bit(ANX7688_F_CONNECTED, anx7688->flags)) ++ anx7688_disconnect(anx7688); ++ ++ typec_unregister_partner(anx7688->partner); ++ typec_unregister_port(anx7688->port); ++ usb_role_switch_put(anx7688->role_sw); ++ ++ regulator_bulk_disable(ANX7688_NUM_ALWAYS_ON_SUPPLIES, anx7688->supplies); ++ i2c_unregister_device(anx7688->client_tcpc); ++ ++ debugfs_remove(anx7688->debug_root); ++ ++ mutex_unlock(&anx7688->lock); ++} ++ ++static int __maybe_unused anx7688_suspend(struct device *dev) ++{ ++ struct anx7688 *anx7688 = i2c_get_clientdata(to_i2c_client(dev)); ++ ++ del_timer_sync(&anx7688->work_timer); ++ cancel_delayed_work_sync(&anx7688->work); ++ ++ regulator_disable(anx7688->supplies[ANX7688_I2C_INDEX].consumer); ++ ++ return 0; ++} ++ ++static int __maybe_unused anx7688_resume(struct device *dev) ++{ ++ struct anx7688 *anx7688 = i2c_get_clientdata(to_i2c_client(dev)); ++ int ret; ++ ++ ret = regulator_enable(anx7688->supplies[ANX7688_I2C_INDEX].consumer); ++ if (ret) ++ dev_warn(anx7688->dev, ++ "failed to enable I2C regulator (%d)\n", ret); ++ ++ // check status right after resume, since it could have changed during ++ // sleep ++ schedule_delayed_work(&anx7688->work, msecs_to_jiffies(50)); ++ mod_timer(&anx7688->work_timer, jiffies + msecs_to_jiffies(1000)); ++ ++ return 0; ++} ++ ++static const struct dev_pm_ops anx7688_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(anx7688_suspend, anx7688_resume) ++}; ++ ++static const struct i2c_device_id anx7688_ids[] = { ++ { "anx7688", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, anx7688_ids); ++ ++static struct of_device_id anx7688_of_match_table[] = { ++ { .compatible = "analogix,anx7688" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, anx7688_of_match_table); ++ ++static struct i2c_driver anx7688_driver = { ++ .driver = { ++ .name = "anx7688", ++ .of_match_table = anx7688_of_match_table, ++ .pm = &anx7688_pm_ops, ++ }, ++ .probe = anx7688_i2c_probe, ++ .remove = anx7688_i2c_remove, ++ .id_table = anx7688_ids, ++}; ++ ++module_i2c_driver(anx7688_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Martijn Braam "); ++MODULE_AUTHOR("Ondrej Jirman "); ++MODULE_DESCRIPTION("Analogix ANX7688 USB-C DisplayPort bridge"); +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-anx7688-Port-to-Linux-6.10.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-anx7688-Port-to-Linux-6.10.patch new file mode 100644 index 000000000000..ee6d63ef4996 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-anx7688-Port-to-Linux-6.10.patch @@ -0,0 +1,28 @@ +From 2e0399dce5fedc6750458a4284bfb78db5d3f403 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Fri, 5 Jul 2024 23:25:25 +0200 +Subject: usb: typec: anx7688: Port to Linux 6.10 + +devm_* variant of device_add_groups was dropped. + +Signed-off-by: Ondrej Jirman +--- + drivers/usb/typec/anx7688.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/usb/typec/anx7688.c b/drivers/usb/typec/anx7688.c +index 77c154565340..448a6f48ab36 100644 +--- a/drivers/usb/typec/anx7688.c ++++ b/drivers/usb/typec/anx7688.c +@@ -1975,7 +1975,7 @@ static int anx7688_i2c_probe(struct i2c_client *client) + return irq_cabledet; + } + +- ret = devm_device_add_groups(&client->dev, anx7688_groups); ++ ret = device_add_groups(&client->dev, anx7688_groups); + if (ret) + return ret; + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-anx7688-Port-to-Linux-6.9.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-anx7688-Port-to-Linux-6.9.patch new file mode 100644 index 000000000000..7785d8c8a696 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-anx7688-Port-to-Linux-6.9.patch @@ -0,0 +1,110 @@ +From 4e782d0f529ea1f1ae6a5c01f21cacc3903395c6 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Fri, 15 Mar 2024 23:05:10 +0100 +Subject: usb: typec: anx7688: Port to Linux 6.9 + +POWER_SUPPLY_PROP_USB_BC_ENABLED is no longer needed, nor supported +by axp20x driver. + +Signed-off-by: Ondrej Jirman +--- + drivers/usb/typec/anx7688.c | 51 ++++--------------------------------- + 1 file changed, 5 insertions(+), 46 deletions(-) + +diff --git a/drivers/usb/typec/anx7688.c b/drivers/usb/typec/anx7688.c +index fa1d3496af73..77c154565340 100644 +--- a/drivers/usb/typec/anx7688.c ++++ b/drivers/usb/typec/anx7688.c +@@ -636,14 +636,6 @@ static void anx7688_disconnect(struct anx7688 *anx7688) + if (ret) + dev_err(dev, "failed to offline vbus_in\n"); + +- val.intval = 1; +- dev_dbg(dev, "enabling USB BC 1.2 detection\n"); +- ret = power_supply_set_property(anx7688->vbus_in_supply, +- POWER_SUPPLY_PROP_USB_BC_ENABLED, +- &val); +- if (ret) +- dev_err(dev, "failed to enabled USB BC1.2 detection\n"); +- + clear_bit(ANX7688_F_CONNECTED, anx7688->flags); + } + +@@ -1822,17 +1814,8 @@ static void anx7688_handle_current_update(struct anx7688* anx7688) + + if (current_limit) { + /* +- * Disable BC1.2 detection, because we'll be setting +- * a current limit determined by USB-PD ++ * Set a current limit determined by USB-PD + */ +- val.intval = 0; +- dev_dbg(dev, "disabling USB BC 1.2 detection\n"); +- ret = power_supply_set_property(anx7688->vbus_in_supply, +- POWER_SUPPLY_PROP_USB_BC_ENABLED, +- &val); +- if (ret) +- dev_err(dev, "failed to disable USB BC1.2 detection\n"); +- + val.intval = current_limit * 1000; + dev_dbg(dev, "setting vbus_in current limit to %d mA\n", current_limit); + ret = power_supply_set_property(anx7688->vbus_in_supply, +@@ -1846,27 +1829,13 @@ static void anx7688_handle_current_update(struct anx7688* anx7688) + * Use the result of BC1.2 detection performed by PMIC. + */ + ret = power_supply_get_property(anx7688->vbus_in_supply, +- POWER_SUPPLY_PROP_USB_BC_ENABLED, ++ POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, + &val); + if (ret) +- dev_err(dev, "failed to get USB BC1.2 detection status\n"); +- +- if (ret != 0 || val.intval == 0) { +- /* +- * If BC is disabled or we can't get its status, +- * set conservative 500mA limit. Otherwise leave +- * the limit to BC1.2. +- */ +- val.intval = 500 * 1000; +- dev_dbg(dev, "setting vbus_in current limit to %d mA\n", ++ dev_err(dev, "failed to get vbus_in current limit\n"); ++ if (ret == 0) ++ dev_dbg(dev, "vbus_in current limit is %d mA\n", + val.intval / 1000); +- ret = power_supply_set_property(anx7688->vbus_in_supply, +- POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, +- &val); +- if (ret) +- dev_err(dev, "failed to set vbus_in current to %d mA\n", +- val.intval / 1000); +- } + } + + /* Turn on VBUS power path inside PMIC. */ +@@ -1932,7 +1901,6 @@ static int anx7688_i2c_probe(struct i2c_client *client) + struct anx7688 *anx7688; + struct device *dev = &client->dev; + struct typec_capability typec_cap = { }; +- union power_supply_propval psy_val; + int i, vid_h, vid_l; + int irq_cabledet; + int ret = 0; +@@ -2087,15 +2055,6 @@ static int anx7688_i2c_probe(struct i2c_client *client) + + // make sure BC1.2 detection in PMIC is enabled + anx7688->last_bc_result = -1; +- psy_val.intval = 1; +- dev_dbg(dev, "enabling USB BC 1.2 detection\n"); +- ret = power_supply_set_property(anx7688->vbus_in_supply, +- POWER_SUPPLY_PROP_USB_BC_ENABLED, +- &psy_val); +- if (ret) { +- dev_err(anx7688->dev, "failed to enable BC1.2 detection\n"); +- goto err_cport; +- } + + ret = devm_request_irq(dev, irq_cabledet, anx7688_irq_plug_handler, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-fusb302-Add-OF-extcon-support.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-fusb302-Add-OF-extcon-support.patch new file mode 100644 index 000000000000..0e3fd9f9f0ae --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-fusb302-Add-OF-extcon-support.patch @@ -0,0 +1,38 @@ +From 709e2922a1a3df25257575a268c61d76821ca1bc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sun, 14 Nov 2021 01:14:25 +0100 +Subject: usb: typec: fusb302: Add OF extcon support + +It's possible to create a dependency cycle between fusb302 and +other drivers via extcon device, so we retrieve the device on +demand after probe and not during probe. + +Signed-off-by: Ondrej Jirman +--- + drivers/usb/typec/tcpm/fusb302.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c +index 16c3f8d94a9d..eb87fbad87fb 100644 +--- a/drivers/usb/typec/tcpm/fusb302.c ++++ b/drivers/usb/typec/tcpm/fusb302.c +@@ -519,6 +519,16 @@ static int tcpm_get_current_limit(struct tcpc_dev *dev) + int current_limit = 0; + unsigned long timeout; + ++ /* ++ * To avoid cycles in OF dependencies, we get extcon when necessary ++ * outside of probe function. ++ */ ++ if (of_property_read_bool(chip->dev->of_node, "extcon") && !chip->extcon) { ++ chip->extcon = extcon_get_edev_by_phandle(chip->dev, 0); ++ if (IS_ERR(chip->extcon)) ++ chip->extcon = NULL; ++ } ++ + if (!chip->extcon) + return 0; + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-fusb302-Clear-interrupts-before-we-start-toggling.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-fusb302-Clear-interrupts-before-we-start-toggling.patch new file mode 100644 index 000000000000..2595cd232e01 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-fusb302-Clear-interrupts-before-we-start-toggling.patch @@ -0,0 +1,40 @@ +From 4f7d79c40e49fd119dbd6ee104c1c881528ce595 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sat, 20 Nov 2021 14:35:10 +0100 +Subject: usb: typec: fusb302: Clear interrupts before we start toggling + +This is recommended by the datasheet. + +Signed-off-by: Ondrej Jirman +--- + drivers/usb/typec/tcpm/fusb302.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c +index eb87fbad87fb..734e87f7cb76 100644 +--- a/drivers/usb/typec/tcpm/fusb302.c ++++ b/drivers/usb/typec/tcpm/fusb302.c +@@ -587,6 +587,7 @@ static int fusb302_set_toggling(struct fusb302_chip *chip, + enum toggling_mode mode) + { + int ret = 0; ++ u8 reg; + + /* first disable toggling */ + ret = fusb302_i2c_clear_bits(chip, FUSB_REG_CONTROL2, +@@ -645,6 +646,12 @@ static int fusb302_set_toggling(struct fusb302_chip *chip, + } else { + /* Datasheet says vconn MUST be off when toggling */ + WARN(chip->vconn_on, "Vconn is on during toggle start"); ++ ++ /* clear interrupts */ ++ ret = fusb302_i2c_read(chip, FUSB_REG_INTERRUPT, ®); ++ if (ret < 0) ++ return ret; ++ + /* unmask TOGDONE interrupt */ + ret = fusb302_i2c_clear_bits(chip, FUSB_REG_MASKA, + FUSB_REG_MASKA_TOGDONE); +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-fusb302-Extend-debugging-interface-with-driver-state-.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-fusb302-Extend-debugging-interface-with-driver-state-.patch new file mode 100644 index 000000000000..8ba6789d2696 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-fusb302-Extend-debugging-interface-with-driver-state-.patch @@ -0,0 +1,112 @@ +From e1b00df124f5b8feb982559b0cd5c31d18ebebae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sun, 7 Nov 2021 19:29:06 +0100 +Subject: usb: typec: fusb302: Extend debugging interface with driver state + dumps + +This is useful for debugging. + +Signed-off-by: Ondrej Jirman +--- + drivers/usb/typec/tcpm/fusb302.c | 78 ++++++++++++++++++++++++++++++++ + 1 file changed, 78 insertions(+) + +diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c +index 53fa863dfa96..2ee7222e531a 100644 +--- a/drivers/usb/typec/tcpm/fusb302.c ++++ b/drivers/usb/typec/tcpm/fusb302.c +@@ -208,6 +208,81 @@ static int fusb302_debug_show(struct seq_file *s, void *v) + } + DEFINE_SHOW_ATTRIBUTE(fusb302_debug); + ++static const char * const typec_cc_status_name[]; ++static const char * const cc_polarity_name[]; ++static const char * const toggling_mode_name[] = { ++ [TOGGLING_MODE_OFF] = "Off", ++ [TOGGLING_MODE_DRP] = "DRP", ++ [TOGGLING_MODE_SNK] = "SNK", ++ [TOGGLING_MODE_SRC] = "SRC", ++}; ++static const char * const src_current_status_name[] = { ++ [SRC_CURRENT_DEFAULT] = "Default", ++ [SRC_CURRENT_MEDIUM] = "Medium", ++ [SRC_CURRENT_HIGH] = "High", ++}; ++ ++#define FUSB_REG(n) { n, #n }, ++struct fusb_reg { ++ u8 addr; ++ const char* name; ++} fusb_regs[] = { ++ FUSB_REG(FUSB_REG_DEVICE_ID) ++ FUSB_REG(FUSB_REG_SWITCHES0) ++ FUSB_REG(FUSB_REG_SWITCHES1) ++ FUSB_REG(FUSB_REG_MEASURE) ++ FUSB_REG(FUSB_REG_CONTROL0) ++ FUSB_REG(FUSB_REG_CONTROL1) ++ FUSB_REG(FUSB_REG_CONTROL2) ++ FUSB_REG(FUSB_REG_CONTROL3) ++ FUSB_REG(FUSB_REG_MASK) ++ FUSB_REG(FUSB_REG_POWER) ++ FUSB_REG(FUSB_REG_RESET) ++ FUSB_REG(FUSB_REG_MASKA) ++ FUSB_REG(FUSB_REG_MASKB) ++ FUSB_REG(FUSB_REG_STATUS0A) ++ FUSB_REG(FUSB_REG_STATUS1A) ++ FUSB_REG(FUSB_REG_INTERRUPTA) ++ FUSB_REG(FUSB_REG_INTERRUPTB) ++ FUSB_REG(FUSB_REG_STATUS0) ++ FUSB_REG(FUSB_REG_STATUS1) ++ FUSB_REG(FUSB_REG_INTERRUPT) ++}; ++ ++static int fusb302_i2c_read(struct fusb302_chip *chip, ++ u8 address, u8 *data); ++ ++static int fusb302_debug_regs_show(struct seq_file *s, void *v) ++{ ++ struct fusb302_chip *chip = (struct fusb302_chip *)s->private; ++ int i, ret; ++ ++ seq_printf(s, "chip->intr_togdone = %d\n", chip->intr_togdone); ++ seq_printf(s, "chip->intr_bc_lvl = %d\n", chip->intr_bc_lvl); ++ seq_printf(s, "chip->intr_comp_chng = %d\n", chip->intr_comp_chng); ++ seq_printf(s, "chip->vconn_on = %d\n", chip->vconn_on); ++ seq_printf(s, "chip->vbus_on = %d\n", chip->vbus_on); ++ seq_printf(s, "chip->charge_on = %d\n", chip->charge_on); ++ seq_printf(s, "chip->vbus_present = %d\n", chip->vbus_present); ++ seq_printf(s, "chip->cc_polarity = %s\n", cc_polarity_name[chip->cc_polarity]); ++ seq_printf(s, "chip->cc1 = %s\n", typec_cc_status_name[chip->cc1]); ++ seq_printf(s, "chip->cc2 = %s\n", typec_cc_status_name[chip->cc2]); ++ seq_printf(s, "chip->toggling_mode = %s\n", toggling_mode_name[chip->toggling_mode]); ++ seq_printf(s, "chip->src_current_status = %s\n", src_current_status_name[chip->src_current_status]); ++ ++ seq_printf(s, "\nRegisters:\n"); ++ for (i = 0; i < ARRAY_SIZE(fusb_regs); i++) { ++ u8 val = 0; ++ ++ ret = fusb302_i2c_read(chip, fusb_regs[i].addr, &val); ++ if (ret >= 0) ++ seq_printf(s, "%s = %02hhx\n", fusb_regs[i].name, val); ++ } ++ ++ return 0; ++} ++DEFINE_SHOW_ATTRIBUTE(fusb302_debug_regs); ++ + static void fusb302_debugfs_init(struct fusb302_chip *chip) + { + char name[NAME_MAX]; +@@ -217,6 +292,9 @@ static void fusb302_debugfs_init(struct fusb302_chip *chip) + chip->dentry = debugfs_create_dir(name, usb_debug_root); + debugfs_create_file("log", S_IFREG | 0444, chip->dentry, chip, + &fusb302_debug_fops); ++ ++ debugfs_create_file("regs", S_IFREG | 0444, chip->dentry, chip, ++ &fusb302_debug_regs_fops); + } + + static void fusb302_debugfs_exit(struct fusb302_chip *chip) +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-fusb302-Fix-register-definitions.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-fusb302-Fix-register-definitions.patch new file mode 100644 index 000000000000..8330ea2057ed --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-fusb302-Fix-register-definitions.patch @@ -0,0 +1,49 @@ +From 147bad79f8711565de8231fb6763cadeac0e7eed Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sat, 20 Nov 2021 14:33:58 +0100 +Subject: usb: typec: fusb302: Fix register definitions + +MEASURE_VBUS bit is at position 6. MDAC bits are also wrong. + +Signed-off-by: Ondrej Jirman +--- + drivers/usb/typec/tcpm/fusb302_reg.h | 16 +++++++--------- + 1 file changed, 7 insertions(+), 9 deletions(-) + +diff --git a/drivers/usb/typec/tcpm/fusb302_reg.h b/drivers/usb/typec/tcpm/fusb302_reg.h +index edc0e4b0f1e6..f37d226c5027 100644 +--- a/drivers/usb/typec/tcpm/fusb302_reg.h ++++ b/drivers/usb/typec/tcpm/fusb302_reg.h +@@ -27,14 +27,13 @@ + #define FUSB_REG_SWITCHES1_TXCC2_EN BIT(1) + #define FUSB_REG_SWITCHES1_TXCC1_EN BIT(0) + #define FUSB_REG_MEASURE 0x04 +-#define FUSB_REG_MEASURE_MDAC5 BIT(7) +-#define FUSB_REG_MEASURE_MDAC4 BIT(6) +-#define FUSB_REG_MEASURE_MDAC3 BIT(5) +-#define FUSB_REG_MEASURE_MDAC2 BIT(4) +-#define FUSB_REG_MEASURE_MDAC1 BIT(3) +-#define FUSB_REG_MEASURE_MDAC0 BIT(2) +-#define FUSB_REG_MEASURE_VBUS BIT(1) +-#define FUSB_REG_MEASURE_XXXX5 BIT(0) ++#define FUSB_REG_MEASURE_VBUS BIT(6) ++#define FUSB_REG_MEASURE_MDAC5 BIT(5) ++#define FUSB_REG_MEASURE_MDAC4 BIT(4) ++#define FUSB_REG_MEASURE_MDAC3 BIT(3) ++#define FUSB_REG_MEASURE_MDAC2 BIT(2) ++#define FUSB_REG_MEASURE_MDAC1 BIT(1) ++#define FUSB_REG_MEASURE_MDAC0 BIT(0) + #define FUSB_REG_CONTROL0 0x06 + #define FUSB_REG_CONTROL0_TX_FLUSH BIT(6) + #define FUSB_REG_CONTROL0_INT_MASK BIT(5) +@@ -105,7 +104,6 @@ + #define FUSB_REG_STATUS0A_RX_SOFT_RESET BIT(1) + #define FUSB_REG_STATUS0A_RX_HARD_RESET BIT(0) + #define FUSB_REG_STATUS1A 0x3D +-#define FUSB_REG_STATUS1A_TOGSS BIT(3) + #define FUSB_REG_STATUS1A_TOGSS_RUNNING 0x0 + #define FUSB_REG_STATUS1A_TOGSS_SRC1 0x1 + #define FUSB_REG_STATUS1A_TOGSS_SRC2 0x2 +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-fusb302-More-useful-of-logging-status-on-interrupt.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-fusb302-More-useful-of-logging-status-on-interrupt.patch new file mode 100644 index 000000000000..375cbc7ec38b --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-fusb302-More-useful-of-logging-status-on-interrupt.patch @@ -0,0 +1,189 @@ +From e678c93a59c3442d081762fcaaad8e5af760a5e5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Tue, 23 Nov 2021 17:55:34 +0100 +Subject: usb: typec: fusb302: More useful of logging status on interrupt + +This is just for debugging. It prints more info that's useful to +see how hardware state changes in time. + +Signed-off-by: Ondrej Jirman +--- + drivers/usb/typec/tcpm/fusb302.c | 121 ++++++++++++++++++++++++++----- + 1 file changed, 104 insertions(+), 17 deletions(-) + +diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c +index 808bcf4cb24d..c30f93ec78fa 100644 +--- a/drivers/usb/typec/tcpm/fusb302.c ++++ b/drivers/usb/typec/tcpm/fusb302.c +@@ -69,7 +69,7 @@ static const u8 rd_mda_value[] = { + }; + + #define LOG_BUFFER_ENTRIES 1024 +-#define LOG_BUFFER_ENTRY_SIZE 128 ++#define LOG_BUFFER_ENTRY_SIZE 256 + + struct fusb302_chip { + struct device *dev; +@@ -1598,6 +1598,84 @@ static irqreturn_t fusb302_irq_intn(int irq, void *dev_id) + return IRQ_HANDLED; + } + ++static void fusb302_print_state(struct fusb302_chip *chip) ++{ ++ u8 ctl0, ctl2, measure, status0, status1a, sw0, mask; ++ int ret; ++ ++ ret = fusb302_i2c_read(chip, FUSB_REG_CONTROL0, &ctl0); ++ if (ret < 0) ++ return; ++ ret = fusb302_i2c_read(chip, FUSB_REG_CONTROL2, &ctl2); ++ if (ret < 0) ++ return; ++ ret = fusb302_i2c_read(chip, FUSB_REG_MEASURE, &measure); ++ if (ret < 0) ++ return; ++ ret = fusb302_i2c_read(chip, FUSB_REG_STATUS0, &status0); ++ if (ret < 0) ++ return; ++ ret = fusb302_i2c_read(chip, FUSB_REG_STATUS1A, &status1a); ++ if (ret < 0) ++ return; ++ ret = fusb302_i2c_read(chip, FUSB_REG_SWITCHES0, &sw0); ++ if (ret < 0) ++ return; ++ ret = fusb302_i2c_read(chip, FUSB_REG_MASK, &mask); ++ if (ret < 0) ++ return; ++ ++ //FUSB_REG(FUSB_REG_POWER) // power control ++ ++ const char* host_cur = "?"; ++ switch ((ctl0 >> 2) & 3) { ++ case 0: host_cur = "none"; break; ++ case 1: host_cur = "80uA"; break; ++ case 2: host_cur = "160uA"; break; ++ case 3: host_cur = "330uA"; break; ++ } ++ ++ const char* bc_lvl = "?"; ++ switch (status0 & 3) { ++ case 0: bc_lvl = "0-200mV"; break; ++ case 1: bc_lvl = "200-660mV"; break; ++ case 2: bc_lvl = "660-1230mV"; break; ++ case 3: bc_lvl = ">1230mV"; break; ++ } ++ ++ // status0 ++ unsigned vbusok = !!(status0 & BIT(7)); ++ unsigned activity = !!(status0 & BIT(6)); ++ unsigned comp = !!(status0 & BIT(5)); ++ unsigned wake = !!(status0 & BIT(2)); ++ ++ // measure ++ unsigned mdac = ((measure & 0x3f) + 1) * 42 * (measure & BIT(6) ? 10 : 1); ++ ++ // status1a ++ unsigned togss = (status1a >> 3) & 7; ++ const char* togss_s = "?"; ++ switch (togss) { ++ case 0: togss_s = "running"; break; ++ case 1: togss_s = "src1"; break; ++ case 2: togss_s = "src2"; break; ++ case 5: togss_s = "snk1"; break; ++ case 6: togss_s = "snk2"; break; ++ case 7: togss_s = "audio"; break; ++ } ++ ++ // ctl2 print as is ++ ++#define SW(n) (!!(sw0 & BIT(n))) ++ ++ fusb302_log(chip, "state: cc(puen=%u%u vconn=%u%u meas=%u%u pdwn=%u%u) " ++ "host_cur=%s mdac=%umV comp=%u bc_lvl=%s vbusok=%u act=%u " ++ "wake=%u togss=%s ctl2=0x%02x mask=0x%02x", ++ SW(6), SW(7), SW(4), SW(5), SW(2), SW(3), SW(0), SW(1), ++ host_cur, mdac, comp, bc_lvl, vbusok, activity, ++ wake, togss_s, ctl2, mask); ++} ++ + static void fusb302_irq_work(struct work_struct *work) + { + struct fusb302_chip *chip = container_of(work, struct fusb302_chip, +@@ -1607,6 +1685,7 @@ static void fusb302_irq_work(struct work_struct *work) + u8 interrupta; + u8 interruptb; + u8 status0; ++ u8 mda; + bool vbus_present; + bool comp_result; + bool intr_togdone; +@@ -1632,9 +1711,10 @@ static void fusb302_irq_work(struct work_struct *work) + ret = fusb302_i2c_read(chip, FUSB_REG_STATUS0, &status0); + if (ret < 0) + goto done; +- fusb302_log(chip, +- "IRQ: 0x%02x, a: 0x%02x, b: 0x%02x, status0: 0x%02x", +- interrupt, interrupta, interruptb, status0); ++ fusb302_log(chip, "IRQ: 0x%02x, a: 0x%02x, b: 0x%02x", ++ interrupt, interrupta, interruptb); ++ ++ fusb302_print_state(chip); + + if (interrupt & FUSB_REG_INTERRUPT_VBUSOK) { + vbus_present = !!(status0 & FUSB_REG_STATUS0_VBUSOK); +@@ -1646,32 +1726,39 @@ static void fusb302_irq_work(struct work_struct *work) + } + } + +- if ((interrupta & FUSB_REG_INTERRUPTA_TOGDONE) && intr_togdone) { ++ if (interrupta & FUSB_REG_INTERRUPTA_TOGDONE) { + fusb302_log(chip, "IRQ: TOGDONE"); +- ret = fusb302_handle_togdone(chip); +- if (ret < 0) { +- fusb302_log(chip, +- "handle togdone error, ret=%d", ret); +- goto done; ++ if (intr_togdone) { ++ ret = fusb302_handle_togdone(chip); ++ if (ret < 0) { ++ fusb302_log(chip, ++ "handle togdone error, ret=%d", ret); ++ goto done; ++ } + } + } + +- if ((interrupt & FUSB_REG_INTERRUPT_BC_LVL) && intr_bc_lvl) { ++ if (interrupt & FUSB_REG_INTERRUPT_BC_LVL) { + fusb302_log(chip, "IRQ: BC_LVL, handler pending"); + /* + * as BC_LVL interrupt can be affected by PD activity, + * apply delay to for the handler to wait for the PD + * signaling to finish. + */ +- mod_delayed_work(chip->wq, &chip->bc_lvl_handler, +- msecs_to_jiffies(T_BC_LVL_DEBOUNCE_DELAY_MS)); ++ if (intr_bc_lvl) ++ mod_delayed_work(chip->wq, &chip->bc_lvl_handler, ++ msecs_to_jiffies(T_BC_LVL_DEBOUNCE_DELAY_MS)); + } + +- if ((interrupt & FUSB_REG_INTERRUPT_COMP_CHNG) && intr_comp_chng) { ++ if (interrupt & FUSB_REG_INTERRUPT_COMP_CHNG) { ++ ret = fusb302_i2c_read(chip, FUSB_REG_MEASURE, &mda); ++ if (ret < 0) ++ goto done; ++ + comp_result = !!(status0 & FUSB_REG_STATUS0_COMP); +- fusb302_log(chip, "IRQ: COMP_CHNG, comp=%s", +- str_true_false(comp_result)); +- if (comp_result) { ++ fusb302_log(chip, "IRQ: COMP_CHNG, cc* %s mdac (%u mV)", ++ comp_result ? ">" : "<", ((mda & 0x3f) + 1) * 42 * (mda & BIT(6) ? 10 : 1)); ++ if (comp_result && intr_comp_chng) { + /* cc level > Rd_threshold, detach */ + chip->cc1 = TYPEC_CC_OPEN; + chip->cc2 = TYPEC_CC_OPEN; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-fusb302-Retry-reading-of-CC-pins-status-if-activity-i.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-fusb302-Retry-reading-of-CC-pins-status-if-activity-i.patch new file mode 100644 index 000000000000..0fcc83ba5051 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-fusb302-Retry-reading-of-CC-pins-status-if-activity-i.patch @@ -0,0 +1,76 @@ +From b4c45356612f9188c1cdd64419c60f262921410b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Tue, 23 Nov 2021 17:53:27 +0100 +Subject: usb: typec: fusb302: Retry reading of CC pins status if activity is + detected + +This is just for testing, to see if this ever happens. It should +also help when this happens. + +Signed-off-by: Ondrej Jirman +--- + drivers/usb/typec/tcpm/fusb302.c | 34 ++++++++++++++++++++++++++++++-- + 1 file changed, 32 insertions(+), 2 deletions(-) + +diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c +index 2ee7222e531a..808bcf4cb24d 100644 +--- a/drivers/usb/typec/tcpm/fusb302.c ++++ b/drivers/usb/typec/tcpm/fusb302.c +@@ -1317,6 +1317,36 @@ static int fusb302_handle_togdone_snk(struct fusb302_chip *chip, + return ret; + } + ++static int fusb302_get_status0_stable(struct fusb302_chip *chip, u8 *status0) ++{ ++ int ret, tries = 0; ++ u8 reg; ++ ++try_again: ++ ret = fusb302_i2c_read(chip, FUSB_REG_STATUS0, ®); ++ if (ret < 0) ++ return ret; ++ ++ if (reg & FUSB_REG_STATUS0_ACTIVITY) { ++ fusb302_log(chip, "activity reading CC status"); ++ if (++tries == 5) { ++ fusb302_log(chip, "failed to read stable status0 value"); ++ ++ /* ++ * The best we can do is to return at least something. ++ */ ++ *status0 = reg; ++ return 0; ++ } ++ ++ usleep_range(50, 100); ++ goto try_again; ++ } ++ ++ *status0 = reg; ++ return 0; ++} ++ + /* On error returns < 0, otherwise a typec_cc_status value */ + static int fusb302_get_src_cc_status(struct fusb302_chip *chip, + enum typec_cc_polarity cc_polarity, +@@ -1344,7 +1374,7 @@ static int fusb302_get_src_cc_status(struct fusb302_chip *chip, + return ret; + + usleep_range(50, 100); +- ret = fusb302_i2c_read(chip, FUSB_REG_STATUS0, &status0); ++ ret = fusb302_get_status0_stable(chip, &status0); + if (ret < 0) + return ret; + +@@ -1360,7 +1390,7 @@ static int fusb302_get_src_cc_status(struct fusb302_chip *chip, + return ret; + + usleep_range(50, 100); +- ret = fusb302_i2c_read(chip, FUSB_REG_STATUS0, &status0); ++ ret = fusb302_get_status0_stable(chip, &status0); + if (ret < 0) + return ret; + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-fusb302-Set-the-current-before-enabling-pullups.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-fusb302-Set-the-current-before-enabling-pullups.patch new file mode 100644 index 000000000000..7fcb8731cc89 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-fusb302-Set-the-current-before-enabling-pullups.patch @@ -0,0 +1,50 @@ +From 9e2282a1cf5c500f3805b1cbfbedc550ea74f921 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sun, 7 Nov 2021 19:28:27 +0100 +Subject: usb: typec: fusb302: Set the current before enabling pullups + +This seems more reasonable and should avoid short period of incorrect +current setting being applied to CC pin. + +Signed-off-by: Ondrej Jirman +--- + drivers/usb/typec/tcpm/fusb302.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c +index 5d5e1a1ffefc..53fa863dfa96 100644 +--- a/drivers/usb/typec/tcpm/fusb302.c ++++ b/drivers/usb/typec/tcpm/fusb302.c +@@ -636,6 +636,14 @@ static int tcpm_set_cc(struct tcpc_dev *dev, enum typec_cc_status cc) + goto done; + } + ++ /* adjust current for SRC */ ++ ret = fusb302_set_src_current(chip, cc_src_current[cc]); ++ if (ret < 0) { ++ fusb302_log(chip, "cannot set src current %s, ret=%d", ++ typec_cc_status_name[cc], ret); ++ goto done; ++ } ++ + ret = fusb302_i2c_mask_write(chip, FUSB_REG_SWITCHES0, + switches0_mask, switches0_data); + if (ret < 0) { +@@ -646,14 +654,6 @@ static int tcpm_set_cc(struct tcpc_dev *dev, enum typec_cc_status cc) + chip->cc1 = TYPEC_CC_OPEN; + chip->cc2 = TYPEC_CC_OPEN; + +- /* adjust current for SRC */ +- ret = fusb302_set_src_current(chip, cc_src_current[cc]); +- if (ret < 0) { +- fusb302_log(chip, "cannot set src current %s, ret=%d", +- typec_cc_status_name[cc], ret); +- goto done; +- } +- + /* enable/disable interrupts, BC_LVL for SNK and COMP_CHNG for SRC */ + switch (cc) { + case TYPEC_CC_RP_DEF: +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-fusb302-Slightly-increase-wait-time-for-BC1.2-result.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-fusb302-Slightly-increase-wait-time-for-BC1.2-result.patch new file mode 100644 index 000000000000..9d28fb2866c8 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-fusb302-Slightly-increase-wait-time-for-BC1.2-result.patch @@ -0,0 +1,33 @@ +From edb5ad67b7842d4fc9758e2cdb010a7835089d50 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sun, 30 Jan 2022 23:46:37 +0100 +Subject: usb: typec: fusb302: Slightly increase wait time for BC1.2 result + +This is not strictly necessary, but it should not hurt. Looking at +kernel log timing, the BC1.2 detection completion time and this +timeout are quite tight. This adds some headspace just in case. + +Signed-off-by: Ondrej Jirman +--- + drivers/usb/typec/tcpm/fusb302.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c +index f15c63d3a8f4..5d5e1a1ffefc 100644 +--- a/drivers/usb/typec/tcpm/fusb302.c ++++ b/drivers/usb/typec/tcpm/fusb302.c +@@ -446,9 +446,9 @@ static int tcpm_get_current_limit(struct tcpc_dev *dev) + + /* + * USB2 Charger detection may still be in progress when we get here, +- * this can take upto 600ms, wait 800ms max. ++ * this can take upto 600ms, wait 1000ms max. + */ +- timeout = jiffies + msecs_to_jiffies(800); ++ timeout = jiffies + msecs_to_jiffies(1000); + do { + if (extcon_get_state(chip->extcon, EXTCON_CHG_USB_SDP) == 1) + current_limit = 500; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-fusb302-Update-VBUS-state-even-if-VBUS-interrupt-is-n.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-fusb302-Update-VBUS-state-even-if-VBUS-interrupt-is-n.patch new file mode 100644 index 000000000000..b040b79cafb3 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-fusb302-Update-VBUS-state-even-if-VBUS-interrupt-is-n.patch @@ -0,0 +1,43 @@ +From 190f76e5c4c74c2cff9dc8cec5158712638f68eb Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Tue, 23 Nov 2021 17:57:06 +0100 +Subject: usb: typec: fusb302: Update VBUS state even if VBUS interrupt is not + triggered + +This seems to improve robustness. + +Signed-off-by: Ondrej Jirman +--- + drivers/usb/typec/tcpm/fusb302.c | 14 ++++++++------ + 1 file changed, 8 insertions(+), 6 deletions(-) + +diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c +index c30f93ec78fa..16c3f8d94a9d 100644 +--- a/drivers/usb/typec/tcpm/fusb302.c ++++ b/drivers/usb/typec/tcpm/fusb302.c +@@ -1716,14 +1716,16 @@ static void fusb302_irq_work(struct work_struct *work) + + fusb302_print_state(chip); + +- if (interrupt & FUSB_REG_INTERRUPT_VBUSOK) { +- vbus_present = !!(status0 & FUSB_REG_STATUS0_VBUSOK); ++ vbus_present = !!(status0 & FUSB_REG_STATUS0_VBUSOK); ++ if (interrupt & FUSB_REG_INTERRUPT_VBUSOK) + fusb302_log(chip, "IRQ: VBUS_OK, vbus=%s", + str_on_off(vbus_present)); +- if (vbus_present != chip->vbus_present) { +- chip->vbus_present = vbus_present; +- tcpm_vbus_change(chip->tcpm_port); +- } ++ if (vbus_present != chip->vbus_present) { ++ chip->vbus_present = vbus_present; ++ if (!(interrupt & FUSB_REG_INTERRUPT_VBUSOK)) ++ fusb302_log(chip, "IRQ: VBUS changed without interrupt, vbus=%s", ++ str_on_off(vbus_present)); ++ tcpm_vbus_change(chip->tcpm_port); + } + + if (interrupta & FUSB_REG_INTERRUPTA_TOGDONE) { +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-tcpm-Fix-PD-devices-capabilities-registration.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-tcpm-Fix-PD-devices-capabilities-registration.patch new file mode 100644 index 000000000000..958cd0d36768 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-tcpm-Fix-PD-devices-capabilities-registration.patch @@ -0,0 +1,95 @@ +From 6330c7bba3992991c37f3204828293a3cda064a9 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sat, 18 Feb 2023 00:38:44 +0100 +Subject: usb: typec: tcpm: Fix PD devices/capabilities registration + +Unregister caps before registering them. Store NULL to the struct +if registration fails, so that next attempt can succeed. + +Fixes "sysfs: cannot create duplicate filename +'/devices/virtual/usb_power_delivery/pd1/source-capabilities'" error. + +Signed-off-by: Ondrej Jirman +--- + drivers/usb/typec/tcpm/tcpm.c | 37 +++++++++++++++++++++++++++-------- + 1 file changed, 29 insertions(+), 8 deletions(-) + +diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c +index 01e1a2c066c7..9f2e376aafd6 100644 +--- a/drivers/usb/typec/tcpm/tcpm.c ++++ b/drivers/usb/typec/tcpm/tcpm.c +@@ -3057,15 +3057,22 @@ static int tcpm_register_source_caps(struct tcpm_port *port) + struct usb_power_delivery_desc desc = { port->negotiated_rev }; + struct usb_power_delivery_capabilities_desc caps = { }; + struct usb_power_delivery_capabilities *cap; ++ struct usb_power_delivery *partner_pd; ++ ++ if (!port->partner_pd) { ++ partner_pd = usb_power_delivery_register(NULL, &desc); ++ if (IS_ERR(partner_pd)) ++ return PTR_ERR(partner_pd); + +- if (!port->partner_pd) +- port->partner_pd = usb_power_delivery_register(NULL, &desc); +- if (IS_ERR(port->partner_pd)) +- return PTR_ERR(port->partner_pd); ++ port->partner_pd = partner_pd; ++ } + + memcpy(caps.pdo, port->source_caps, sizeof(u32) * port->nr_source_caps); + caps.role = TYPEC_SOURCE; + ++ usb_power_delivery_unregister_capabilities(port->partner_source_caps); ++ port->partner_source_caps = NULL; ++ + cap = usb_power_delivery_register_capabilities(port->partner_pd, &caps); + if (IS_ERR(cap)) + return PTR_ERR(cap); +@@ -3080,15 +3087,22 @@ static int tcpm_register_sink_caps(struct tcpm_port *port) + struct usb_power_delivery_desc desc = { port->negotiated_rev }; + struct usb_power_delivery_capabilities_desc caps = { }; + struct usb_power_delivery_capabilities *cap; ++ struct usb_power_delivery *partner_pd; ++ ++ if (!port->partner_pd) { ++ partner_pd = usb_power_delivery_register(NULL, &desc); ++ if (IS_ERR(partner_pd)) ++ return PTR_ERR(partner_pd); + +- if (!port->partner_pd) +- port->partner_pd = usb_power_delivery_register(NULL, &desc); +- if (IS_ERR(port->partner_pd)) +- return PTR_ERR(port->partner_pd); ++ port->partner_pd = partner_pd; ++ } + + memcpy(caps.pdo, port->sink_caps, sizeof(u32) * port->nr_sink_caps); + caps.role = TYPEC_SINK; + ++ usb_power_delivery_unregister_capabilities(port->partner_sink_caps); ++ port->partner_sink_caps = NULL; ++ + cap = usb_power_delivery_register_capabilities(port->partner_pd, &caps); + if (IS_ERR(cap)) + return PTR_ERR(cap); +@@ -7098,10 +7112,17 @@ static int tcpm_port_register_pd(struct tcpm_port *port) + port->pds[i] = usb_power_delivery_register(port->dev, &desc); + if (IS_ERR(port->pds[i])) { + ret = PTR_ERR(port->pds[i]); ++ port->pds[i] = NULL; + goto err_unregister; + } + port->pd_list[i]->pd = port->pds[i]; + ++ usb_power_delivery_unregister_capabilities(port->pd_list[i]->source_cap); ++ port->pd_list[i]->source_cap = NULL; ++ ++ usb_power_delivery_unregister_capabilities(port->pd_list[i]->sink_cap); ++ port->pd_list[i]->sink_cap = NULL; ++ + if (port->pd_list[i]->source_desc.pdo[0]) { + cap = usb_power_delivery_register_capabilities(port->pds[i], + &port->pd_list[i]->source_desc); +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-tcpm-Improve-logs.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-tcpm-Improve-logs.patch new file mode 100644 index 000000000000..d85ac5da37c7 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-tcpm-Improve-logs.patch @@ -0,0 +1,64 @@ +From aa7b2907c1dd687c07d1a8245d7c01d1b05f4aa2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Tue, 23 Nov 2021 17:58:05 +0100 +Subject: usb: typec: tcpm: Improve logs + +This adds clarity to debugging. + +Signed-off-by: Ondrej Jirman +--- + drivers/usb/typec/tcpm/tcpm.c | 15 +++++++++++---- + 1 file changed, 11 insertions(+), 4 deletions(-) + +diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c +index 9f2e376aafd6..d44e12fb2a53 100644 +--- a/drivers/usb/typec/tcpm/tcpm.c ++++ b/drivers/usb/typec/tcpm/tcpm.c +@@ -986,10 +986,13 @@ static int tcpm_pd_transmit(struct tcpm_port *port, + break; + } + +- if (msg) ++ if (msg) { + tcpm_log(port, "PD TX, header: %#x", le16_to_cpu(msg->header)); +- else ++ for (ret = 0; ret < pd_header_cnt_le(msg->header); ret++) ++ tcpm_log(port, " tx payload[%d]: %#x", ret, le32_to_cpu(msg->payload[ret])); ++ } else { + tcpm_log(port, "PD TX, type: %#x", tx_sop_type); ++ } + + reinit_completion(&port->tx_complete); + ret = port->tcpc->pd_transmit(port->tcpc, tx_sop_type, msg, negotiated_rev); +@@ -3740,6 +3743,10 @@ void tcpm_pd_receive(struct tcpm_port *port, const struct pd_message *msg, + enum tcpm_transmit_type rx_sop_type) + { + struct pd_rx_event *event; ++ int ret; ++ ++ for (ret = 0; ret < pd_header_cnt_le(msg->header); ret++) ++ tcpm_log(port, " rx payload[%d]: %#x", ret, le32_to_cpu(msg->payload[ret])); + + event = kzalloc(sizeof(*event), GFP_ATOMIC); + if (!event) +@@ -6043,7 +6050,7 @@ static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1, + + static void _tcpm_pd_vbus_on(struct tcpm_port *port) + { +- tcpm_log_force(port, "VBUS on"); ++ tcpm_log_force(port, "VBUS event received: on"); + port->vbus_present = true; + /* + * When vbus_present is true i.e. Voltage at VBUS is greater than VSAFE5V implicitly +@@ -6133,7 +6140,7 @@ static void _tcpm_pd_vbus_on(struct tcpm_port *port) + + static void _tcpm_pd_vbus_off(struct tcpm_port *port) + { +- tcpm_log_force(port, "VBUS off"); ++ tcpm_log_force(port, "VBUS event received: off"); + port->vbus_present = false; + port->vbus_never_low = false; + switch (port->state) { +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-tcpm-Unregister-altmodes-before-registering-new-ones.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-tcpm-Unregister-altmodes-before-registering-new-ones.patch new file mode 100644 index 000000000000..c7ace7157842 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-tcpm-Unregister-altmodes-before-registering-new-ones.patch @@ -0,0 +1,52 @@ +From c02b2e7f1fc2126867ea3500684704c3a7006fd1 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sun, 14 Aug 2022 16:23:28 +0200 +Subject: usb: typec: tcpm: Unregister altmodes before registering new ones + +Just in case there are already altmodes registered. Otherwise the +kernel will fail with: + +[50554.467172] sysfs: cannot create duplicate filename '/bus/typec/devices/port0-partner.0' +[50554.467198] CPU: 2 PID: 897 Comm: 4-0022 Tainted: G C 5.18.15-1-MANJARO-ARM #1 +[50554.467208] Hardware name: Pine64 PinePhonePro (DT) +[50554.467214] Call trace: +[50554.467218] dump_backtrace.part.0+0xcc/0xe0 +[50554.467241] show_stack+0x18/0x6c +[50554.467249] dump_stack_lvl+0x64/0x80 +[50554.467262] dump_stack+0x18/0x34 +[50554.467270] sysfs_warn_dup+0x60/0x7c +[50554.467279] sysfs_do_create_link_sd+0xf0/0x100 +[50554.467286] sysfs_create_link+0x24/0x44 +[50554.467292] bus_add_device+0x64/0x120 +[50554.467304] device_add+0x320/0x890 +[50554.467312] device_register+0x20/0x30 +[50554.467318] typec_register_altmode+0x1e4/0x35c +[50554.467330] typec_partner_register_altmode+0x10/0x20 +[50554.467339] tcpm_pd_rx_handler+0x1804/0x18e0 +[50554.467346] kthread_worker_fn+0xa0/0x180 +[50554.467357] kthread+0x108/0x10c +[50554.467364] ret_from_fork+0x10/0x20 +[50554.467528] typec port0-partner: failed to register alternate mode (-17) + +Signed-off-by: Ondrej Jirman +--- + drivers/usb/typec/tcpm/tcpm.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c +index 2c92996954b2..01e1a2c066c7 100644 +--- a/drivers/usb/typec/tcpm/tcpm.c ++++ b/drivers/usb/typec/tcpm/tcpm.c +@@ -1798,6 +1798,9 @@ static void tcpm_register_partner_altmodes(struct tcpm_port *port) + return; + + for (i = 0; i < modep->altmodes; i++) { ++ typec_unregister_altmode(port->partner_altmode[i]); ++ port->partner_altmode[i] = NULL; ++ + altmode = typec_partner_register_altmode(port->partner, + &modep->altmode_desc[i]); + if (IS_ERR(altmode)) { +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-typec-extcon-Add-typec-extcon-bridge-driver.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-typec-extcon-Add-typec-extcon-bridge-driver.patch new file mode 100644 index 000000000000..9a3d19ff2c75 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-typec-extcon-Add-typec-extcon-bridge-driver.patch @@ -0,0 +1,384 @@ +From c51b4cd25c8224ea7667def456cb55f18d8e5d5a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sun, 7 Nov 2021 19:24:40 +0100 +Subject: usb: typec: typec-extcon: Add typec -> extcon bridge driver + +This bridge connects standard Type C port interfaces for controling +muxes, switches and usb roles to muxes, switches and usb role +drivers controlled via extcon interface. + +Signed-off-by: Ondrej Jirman +--- + drivers/usb/typec/Kconfig | 7 + + drivers/usb/typec/Makefile | 1 + + drivers/usb/typec/typec-extcon.c | 330 +++++++++++++++++++++++++++++++ + 3 files changed, 338 insertions(+) + create mode 100644 drivers/usb/typec/typec-extcon.c + +diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig +index 982b7c444a1f..9913435a0fd9 100644 +--- a/drivers/usb/typec/Kconfig ++++ b/drivers/usb/typec/Kconfig +@@ -122,6 +122,13 @@ config TYPEC_WUSB3801 + If you choose to build this driver as a dynamically linked module, the + module will be called wusb3801.ko. + ++config TYPEC_EXTCON ++ tristate "Type-C switch/mux -> extcon interface bridge driver" ++ depends on USB_ROLE_SWITCH ++ help ++ Say Y or M here if your system needs bridging between typec class ++ and extcon interfaces. ++ + source "drivers/usb/typec/mux/Kconfig" + + source "drivers/usb/typec/altmodes/Kconfig" +diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile +index 3f8ff94ad294..0b2d1d251c53 100644 +--- a/drivers/usb/typec/Makefile ++++ b/drivers/usb/typec/Makefile +@@ -12,4 +12,5 @@ obj-$(CONFIG_TYPEC_HD3SS3220) += hd3ss3220.o + obj-$(CONFIG_TYPEC_STUSB160X) += stusb160x.o + obj-$(CONFIG_TYPEC_RT1719) += rt1719.o + obj-$(CONFIG_TYPEC_WUSB3801) += wusb3801.o ++obj-$(CONFIG_TYPEC_EXTCON) += typec-extcon.o + obj-$(CONFIG_TYPEC) += mux/ +diff --git a/drivers/usb/typec/typec-extcon.c b/drivers/usb/typec/typec-extcon.c +new file mode 100644 +index 000000000000..e09cd7a28c24 +--- /dev/null ++++ b/drivers/usb/typec/typec-extcon.c +@@ -0,0 +1,330 @@ ++/* ++ * typec -> extcon bridge ++ * Copyright (c) 2021 OndÅ™ej Jirman ++ * ++ * This driver bridges standard type-c interfaces to drivers that ++ * expect extcon interface. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct typec_extcon { ++ struct device *dev; ++ ++ /* consumers */ ++ struct usb_role_switch *role_sw; ++ struct typec_switch_dev *sw; ++ struct typec_mux_dev *mux; ++ ++ /* providers */ ++ struct extcon_dev *extcon; ++ struct notifier_block extcon_nb; ++ ++ /* cached state from typec controller */ ++ enum usb_role role; ++ enum typec_orientation orientation; ++ struct typec_altmode alt; ++ unsigned long mode; ++ bool has_alt; ++ struct mutex lock; ++}; ++ ++static const unsigned int typec_extcon_cable[] = { ++ EXTCON_DISP_DP, ++ ++ EXTCON_USB, ++ EXTCON_USB_HOST, ++ ++ EXTCON_CHG_USB_SDP, ++ EXTCON_CHG_USB_CDP, ++ EXTCON_CHG_USB_DCP, ++ EXTCON_CHG_USB_ACA, ++ ++ EXTCON_NONE, ++}; ++ ++static void typec_extcon_set_cable(struct typec_extcon *tce, int id, bool on, ++ union extcon_property_value prop_ss, ++ union extcon_property_value prop_or) ++{ ++ union extcon_property_value cur_ss, cur_or; ++ bool prop_diff = false; ++ int ret; ++ ++ ret = extcon_get_property(tce->extcon, id, ++ EXTCON_PROP_USB_SS, &cur_ss); ++ if (ret || cur_ss.intval != prop_ss.intval) ++ prop_diff = true; ++ ++ ret = extcon_get_property(tce->extcon, id, ++ EXTCON_PROP_USB_TYPEC_POLARITY, &cur_or); ++ if (ret || cur_or.intval != prop_or.intval) ++ prop_diff = true; ++ ++ if (!on && extcon_get_state(tce->extcon, id)) { ++ extcon_set_state_sync(tce->extcon, id, false); ++ } else if (on && (!extcon_get_state(tce->extcon, id) || prop_diff)) { ++ extcon_set_state(tce->extcon, id, true); ++ extcon_set_property(tce->extcon, id, ++ EXTCON_PROP_USB_SS, prop_ss); ++ extcon_set_property(tce->extcon, id, ++ EXTCON_PROP_USB_TYPEC_POLARITY, prop_or); ++ extcon_sync(tce->extcon, id); ++ } ++} ++ ++static int typec_extcon_sync_extcon(struct typec_extcon *tce) ++{ ++ union extcon_property_value prop_ss, prop_or; ++ bool has_dp = false; ++ ++ mutex_lock(&tce->lock); ++ ++ /* connector is disconnected */ ++ if (tce->orientation == TYPEC_ORIENTATION_NONE) { ++ typec_extcon_set_cable(tce, EXTCON_USB, false, prop_ss, prop_or); ++ typec_extcon_set_cable(tce, EXTCON_USB_HOST, false, prop_ss, prop_or); ++ typec_extcon_set_cable(tce, EXTCON_DISP_DP, false, prop_ss, prop_or); ++ ++ goto out_unlock; ++ } ++ ++ prop_or.intval = tce->orientation == TYPEC_ORIENTATION_NORMAL ? 0 : 1; ++ prop_ss.intval = 0; ++ ++ if (tce->has_alt && tce->alt.svid == USB_TYPEC_DP_SID) { ++ switch (tce->mode) { ++ case TYPEC_STATE_SAFE: ++ break; ++ case TYPEC_DP_STATE_C: ++ case TYPEC_DP_STATE_E: ++ has_dp = true; ++ break; ++ case TYPEC_DP_STATE_D: ++ has_dp = true; ++ fallthrough; ++ case TYPEC_STATE_USB: ++ prop_ss.intval = 1; ++ break; ++ default: ++ dev_err(tce->dev, "unhandled mux mode=%lu\n", tce->mode); ++ break; ++ } ++ } ++ ++ typec_extcon_set_cable(tce, EXTCON_USB, ++ tce->role == USB_ROLE_DEVICE, prop_ss, prop_or); ++ typec_extcon_set_cable(tce, EXTCON_USB_HOST, ++ tce->role == USB_ROLE_HOST, prop_ss, prop_or); ++ ++ typec_extcon_set_cable(tce, EXTCON_DISP_DP, has_dp, prop_ss, prop_or); ++ ++out_unlock: ++ mutex_unlock(&tce->lock); ++ return 0; ++} ++ ++static int typec_extcon_sw_set(struct typec_switch_dev *sw, ++ enum typec_orientation orientation) ++{ ++ struct typec_extcon *tce = typec_switch_get_drvdata(sw); ++ ++ dev_dbg(tce->dev, "SW SET: orientation=%d\n", orientation); ++ ++ mutex_lock(&tce->lock); ++ tce->orientation = orientation; ++ mutex_unlock(&tce->lock); ++ ++ typec_extcon_sync_extcon(tce); ++ ++ return 0; ++} ++ ++static int typec_extcon_mux_set(struct typec_mux_dev *mux, ++ struct typec_mux_state *state) ++{ ++ struct typec_extcon *tce = typec_mux_get_drvdata(mux); ++ struct typec_altmode *alt = state->alt; ++ ++ dev_dbg(tce->dev, "MUX SET: state->mode=%lu\n", state->mode); ++ if (alt) ++ dev_dbg(tce->dev, " ...alt: svid=%04hx mode=%d vdo=%08x active=%u\n", ++ alt->svid, alt->mode, alt->vdo, alt->active); ++ ++ mutex_lock(&tce->lock); ++ tce->mode = state->mode; ++ tce->has_alt = alt != NULL; ++ if (alt) ++ tce->alt = *alt; ++ mutex_unlock(&tce->lock); ++ ++ typec_extcon_sync_extcon(tce); ++ ++ return 0; ++} ++ ++static int typec_extcon_usb_set_role(struct usb_role_switch *sw, ++ enum usb_role role) ++{ ++ struct typec_extcon *tce = usb_role_switch_get_drvdata(sw); ++ ++ dev_dbg(tce->dev, "ROLE SET: role=%d\n", role); ++ ++ mutex_lock(&tce->lock); ++ tce->role = role; ++ mutex_unlock(&tce->lock); ++ ++ typec_extcon_sync_extcon(tce); ++ ++ return 0; ++} ++ ++static int typec_extcon_notifier(struct notifier_block *nb, ++ unsigned long action, void *data) ++{ ++ struct typec_extcon *tce = container_of(nb, struct typec_extcon, extcon_nb); ++ ++ bool sdp = extcon_get_state(tce->extcon, EXTCON_CHG_USB_SDP); ++ bool cdp = extcon_get_state(tce->extcon, EXTCON_CHG_USB_CDP); ++ bool dcp = extcon_get_state(tce->extcon, EXTCON_CHG_USB_DCP); ++ bool usb = extcon_get_state(tce->extcon, EXTCON_USB); ++ bool usb_host = extcon_get_state(tce->extcon, EXTCON_USB_HOST); ++ bool dp = extcon_get_state(tce->extcon, EXTCON_DISP_DP); ++ ++ dev_info(tce->dev, "extcon changed sdp=%d cdp=%d dcp=%d usb=%d usb_host=%d dp=%d\n", ++ sdp, cdp, dcp, usb, usb_host, dp); ++ ++ return NOTIFY_OK; ++} ++ ++static int typec_extcon_probe(struct platform_device *pdev) ++{ ++ struct typec_switch_desc sw_desc = { }; ++ struct typec_mux_desc mux_desc = { }; ++ struct usb_role_switch_desc role_desc = { }; ++ struct device *dev = &pdev->dev; ++ struct typec_extcon *tce; ++ int ret = 0; ++ ++ tce = devm_kzalloc(dev, sizeof(*tce), GFP_KERNEL); ++ if (!tce) ++ return -ENOMEM; ++ ++ tce->dev = &pdev->dev; ++ mutex_init(&tce->lock); ++ tce->mode = TYPEC_STATE_SAFE; ++ ++ sw_desc.drvdata = tce; ++ sw_desc.fwnode = dev->fwnode; ++ sw_desc.set = typec_extcon_sw_set; ++ ++ tce->sw = typec_switch_register(dev, &sw_desc); ++ if (IS_ERR(tce->sw)) ++ return dev_err_probe(dev, PTR_ERR(tce->sw), ++ "Error registering typec switch\n"); ++ ++ mux_desc.drvdata = tce; ++ mux_desc.fwnode = dev->fwnode; ++ mux_desc.set = typec_extcon_mux_set; ++ ++ tce->mux = typec_mux_register(dev, &mux_desc); ++ if (IS_ERR(tce->mux)) { ++ ret = dev_err_probe(dev, PTR_ERR(tce->mux), ++ "Error registering typec mux\n"); ++ goto err_sw; ++ } ++ ++ role_desc.driver_data = tce; ++ role_desc.fwnode = dev->fwnode; ++ role_desc.name = fwnode_get_name(dev->fwnode); ++ role_desc.set = typec_extcon_usb_set_role; ++ ++ tce->role_sw = usb_role_switch_register(dev, &role_desc); ++ if (IS_ERR(tce->role_sw)) { ++ ret = dev_err_probe(dev, PTR_ERR(tce->role_sw), ++ "Error registering USB role switch\n"); ++ goto err_mux; ++ } ++ ++ tce->extcon = devm_extcon_dev_allocate(dev, typec_extcon_cable); ++ if (IS_ERR(tce->extcon)) { ++ ret = PTR_ERR(tce->extcon); ++ goto err_role; ++ } ++ ++ ret = devm_extcon_dev_register(dev, tce->extcon); ++ if (ret) { ++ ret = dev_err_probe(dev, ret, "failed to register extcon device\n"); ++ goto err_role; ++ } ++ ++ extcon_set_property_capability(tce->extcon, EXTCON_USB, ++ EXTCON_PROP_USB_SS); ++ extcon_set_property_capability(tce->extcon, EXTCON_USB, ++ EXTCON_PROP_USB_TYPEC_POLARITY); ++ extcon_set_property_capability(tce->extcon, EXTCON_USB_HOST, ++ EXTCON_PROP_USB_SS); ++ extcon_set_property_capability(tce->extcon, EXTCON_USB_HOST, ++ EXTCON_PROP_USB_TYPEC_POLARITY); ++ extcon_set_property_capability(tce->extcon, EXTCON_DISP_DP, ++ EXTCON_PROP_USB_SS); ++ extcon_set_property_capability(tce->extcon, EXTCON_DISP_DP, ++ EXTCON_PROP_USB_TYPEC_POLARITY); ++ ++ tce->extcon_nb.notifier_call = typec_extcon_notifier; ++ ret = devm_extcon_register_notifier_all(dev, tce->extcon, &tce->extcon_nb); ++ if (ret) { ++ dev_err_probe(dev, ret, "Failed to register extcon notifier\n"); ++ goto err_role; ++ } ++ ++ return 0; ++ ++err_role: ++ usb_role_switch_unregister(tce->role_sw); ++err_mux: ++ typec_mux_unregister(tce->mux); ++err_sw: ++ typec_switch_unregister(tce->sw); ++ return ret; ++} ++ ++static void typec_extcon_remove(struct platform_device *pdev) ++{ ++ struct typec_extcon *tce = platform_get_drvdata(pdev); ++ ++ usb_role_switch_unregister(tce->role_sw); ++ typec_mux_unregister(tce->mux); ++ typec_switch_unregister(tce->sw); ++} ++ ++static struct of_device_id typec_extcon_of_match_table[] = { ++ { .compatible = "linux,typec-extcon-bridge" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, typec_extcon_of_match_table); ++ ++static struct platform_driver typec_extcon_driver = { ++ .driver = { ++ .name = "typec-extcon", ++ .of_match_table = typec_extcon_of_match_table, ++ }, ++ .probe = typec_extcon_probe, ++ .remove = typec_extcon_remove, ++}; ++ ++module_platform_driver(typec_extcon_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Ondrej Jirman "); ++MODULE_DESCRIPTION("typec -> extcon bridge driver"); +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-typec-extcon-Allow-to-force-reset-on-each-mux-change.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-typec-extcon-Allow-to-force-reset-on-each-mux-change.patch new file mode 100644 index 000000000000..d6fb880d0a1b --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-typec-extcon-Allow-to-force-reset-on-each-mux-change.patch @@ -0,0 +1,48 @@ +From 0ef3b1cee239d5a9ae7bc7e8ad075ac1fef5ed84 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Thu, 9 Feb 2023 20:33:50 +0100 +Subject: usb: typec: typec-extcon: Allow to force reset on each mux change + +This may help with some Alt-DP USB adapters. + +Signed-off-by: Ondrej Jirman +--- + drivers/usb/typec/typec-extcon.c | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/drivers/usb/typec/typec-extcon.c b/drivers/usb/typec/typec-extcon.c +index f617e90b71a0..81fc4114bc59 100644 +--- a/drivers/usb/typec/typec-extcon.c ++++ b/drivers/usb/typec/typec-extcon.c +@@ -19,6 +19,10 @@ + #include + #include + ++static bool reset_on_mux; ++module_param(reset_on_mux, bool, S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(reset_on_mux, "Set DP=0 on each type-c mux change"); ++ + struct typec_extcon { + struct device *dev; + +@@ -162,6 +166,17 @@ static int typec_extcon_mux_set(struct typec_mux_dev *mux, + dev_dbg(tce->dev, " ...alt: svid=%04hx mode=%d vdo=%08x active=%u\n", + alt->svid, alt->mode, alt->vdo, alt->active); + ++ mutex_lock(&tce->lock); ++ if (reset_on_mux && alt != NULL && tce->has_alt) { ++ tce->mode = state->mode; ++ tce->has_alt = false; ++ mutex_unlock(&tce->lock); ++ ++ typec_extcon_sync_extcon(tce); ++ } else { ++ mutex_unlock(&tce->lock); ++ } ++ + mutex_lock(&tce->lock); + tce->mode = state->mode; + tce->has_alt = alt != NULL; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-typec-extcon-Enable-debugging-for-now.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-typec-extcon-Enable-debugging-for-now.patch new file mode 100644 index 000000000000..2faaec70634e --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/usb-typec-typec-extcon-Enable-debugging-for-now.patch @@ -0,0 +1,25 @@ +From 169c2c30b74c8ecb334823420b30da6030e48247 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Wed, 23 Mar 2022 14:21:34 +0100 +Subject: usb: typec: typec-extcon: Enable debugging for now + +Signed-off-by: Ondrej Jirman +--- + drivers/usb/typec/typec-extcon.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/usb/typec/typec-extcon.c b/drivers/usb/typec/typec-extcon.c +index e09cd7a28c24..f617e90b71a0 100644 +--- a/drivers/usb/typec/typec-extcon.c ++++ b/drivers/usb/typec/typec-extcon.c +@@ -5,6 +5,7 @@ + * This driver bridges standard type-c interfaces to drivers that + * expect extcon interface. + */ ++#define DEBUG + + #include + #include +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/video-fbdev-eInk-display-driver-for-A13-based-PocketBooks.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/video-fbdev-eInk-display-driver-for-A13-based-PocketBooks.patch new file mode 100644 index 000000000000..7f9406459e29 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/video-fbdev-eInk-display-driver-for-A13-based-PocketBooks.patch @@ -0,0 +1,1274 @@ +From 1a8de735c0b99b2d9aa538603431e63bf121bf0a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Tue, 24 Sep 2019 17:53:02 +0200 +Subject: video: fbdev: eInk display driver for A13 based PocketBooks + +This driver uses DEBE and TCON0 to bitbang the ED060XD4 eInk panel +protocol on LCD data pins. There's no other sane way to do it, +as PocketBook e-book reader was designed this way. + +Signed-off-by: Ondrej Jirman +--- + drivers/video/fbdev/Kconfig | 12 + + drivers/video/fbdev/Makefile | 4 + + drivers/video/fbdev/sun5i-eink-neon.c | 49 ++ + drivers/video/fbdev/sun5i-eink.c | 1156 +++++++++++++++++++++++++ + 4 files changed, 1221 insertions(+) + create mode 100644 drivers/video/fbdev/sun5i-eink-neon.c + create mode 100644 drivers/video/fbdev/sun5i-eink.c + +diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig +index 55c6686f091e..5685c65da340 100644 +--- a/drivers/video/fbdev/Kconfig ++++ b/drivers/video/fbdev/Kconfig +@@ -658,6 +658,18 @@ config FB_ATMEL + help + This enables support for the AT91 LCD Controller. + ++config FB_SUN5I_EINK ++ tristate "eInk display Framebuffer Support (A13 based eBook readers)" ++ depends on FB ++ select FB_MODE_HELPERS ++ select FB_CFB_FILLRECT ++ select FB_CFB_COPYAREA ++ select FB_CFB_IMAGEBLIT ++ select BITREVERSE ++ help ++ This driver supports PocketBook Touch Lux 3 e-paper display. ++ ++ + config FB_NVIDIA + tristate "nVidia Framebuffer Support" + depends on FB && PCI +diff --git a/drivers/video/fbdev/Makefile b/drivers/video/fbdev/Makefile +index b3d12f977c06..36026f5adf6e 100644 +--- a/drivers/video/fbdev/Makefile ++++ b/drivers/video/fbdev/Makefile +@@ -126,3 +126,7 @@ obj-$(CONFIG_FB_SIMPLE) += simplefb.o + + # the test framebuffer is last + obj-$(CONFIG_FB_VIRTUAL) += vfb.o ++ ++obj-$(CONFIG_FB_SUN5I_EINK) += sun5ieink.o ++sun5ieink-objs = sun5i-eink.o sun5i-eink-neon.o ++CFLAGS_sun5i-eink-neon.o += -march=armv7-a -mfloat-abi=softfp -mfpu=neon -ffreestanding -isystem $(shell $(CC) -print-file-name=include) +diff --git a/drivers/video/fbdev/sun5i-eink-neon.c b/drivers/video/fbdev/sun5i-eink-neon.c +new file mode 100644 +index 000000000000..9e2386fd91ea +--- /dev/null ++++ b/drivers/video/fbdev/sun5i-eink-neon.c +@@ -0,0 +1,49 @@ ++#include ++#include ++#include ++#include ++ ++void eink_ctlstream_fill_data_neon(u32* cmd, u8* fb, int stride, ++ int sources, int gates, u32 masks[4]) ++{ ++ uint32x4_t mask; ++ int g, s; ++ int cutoff = sources / 4 / 8 / 3 * 2; ++ ++ mask = vdupq_n_u32(0); ++ ++ for (g = 0; g < gates; g++) { ++ if (unlikely(g == 0)) ++ mask = vdupq_n_u32(masks[0]); ++ else ++ mask = vdupq_n_u32(masks[2]); ++ ++ for (s = 0; s < sources / 4 / 8; s++) { ++ uint8x8_t src; ++ uint16x8_t dst; ++ uint32x4_t out; ++ ++ if (unlikely(s == cutoff)) ++ mask = vdupq_n_u32(g == 0 ? masks[1] : masks[3]); ++ ++ src = vld1_u8(fb); ++ fb += 8; ++ ++ dst = vshll_n_u8(src, 3); ++ dst = vsliq_n_u16(dst, vmovl_u8(vshr_n_u8(src, 3)), 8); ++ ++ out = vmovl_u16(vget_low_u16(dst)); ++ out = vorrq_u32(out, mask); ++ vst1q_u32(cmd, out); ++ cmd += 4; ++ ++ out = vmovl_u16(vget_high_u16(dst)); ++ out = vorrq_u32(out, mask); ++ vst1q_u32(cmd, out); ++ cmd += 4; ++ } ++ ++ cmd += stride - sources / 4; ++ } ++} ++EXPORT_SYMBOL_GPL(eink_ctlstream_fill_data_neon); +diff --git a/drivers/video/fbdev/sun5i-eink.c b/drivers/video/fbdev/sun5i-eink.c +new file mode 100644 +index 000000000000..fbc2cbaedb22 +--- /dev/null ++++ b/drivers/video/fbdev/sun5i-eink.c +@@ -0,0 +1,1156 @@ ++/* ++ * Copyright Ondrej Jirman ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++// {{{ Registry defines ++ ++#define SUN4I_TCON_GCTL_REG 0x0 ++#define SUN4I_TCON_GCTL_TCON_ENABLE BIT(31) ++#define SUN4I_TCON_GCTL_IOMAP_MASK BIT(0) ++#define SUN4I_TCON_GCTL_IOMAP_TCON1 (1 << 0) ++#define SUN4I_TCON_GCTL_IOMAP_TCON0 (0 << 0) ++ ++#define SUN4I_TCON_GINT0_REG 0x4 ++#define SUN4I_TCON_GINT0_VBLANK_ENABLE(pipe) BIT(31 - (pipe)) ++#define SUN4I_TCON_GINT0_VBLANK_INT(pipe) BIT(15 - (pipe)) ++#define SUN4I_TCON_GINT0_LINE_ENABLE(pipe) BIT(29 - (pipe)) ++#define SUN4I_TCON_GINT0_LINE_INT(pipe) BIT(13 - (pipe)) ++ ++#define SUN4I_TCON_GINT1_REG 0x8 ++ ++#define SUN4I_TCON0_CTL_REG 0x40 ++#define SUN4I_TCON0_CTL_TCON_ENABLE BIT(31) ++#define SUN4I_TCON0_CTL_IF_MASK GENMASK(25, 24) ++#define SUN4I_TCON0_CTL_IF_8080 (1 << 24) ++#define SUN4I_TCON0_CTL_CLK_DELAY_MASK GENMASK(8, 4) ++#define SUN4I_TCON0_CTL_CLK_DELAY(delay) ((delay << 4) & SUN4I_TCON0_CTL_CLK_DELAY_MASK) ++#define SUN4I_TCON0_CTL_SRC_SEL_MASK GENMASK(2, 0) ++ ++#define SUN4I_TCON0_DCLK_REG 0x44 ++#define SUN4I_TCON0_DCLK_GATE_BIT (31) ++#define SUN4I_TCON0_DCLK_DIV_SHIFT (0) ++#define SUN4I_TCON0_DCLK_DIV_WIDTH (7) ++ ++#define SUN4I_TCON0_BASIC0_REG 0x48 ++#define SUN4I_TCON0_BASIC0_X(width) ((((width) - 1) & 0xfff) << 16) ++#define SUN4I_TCON0_BASIC0_Y(height) (((height) - 1) & 0xfff) ++ ++#define SUN4I_TCON0_BASIC1_REG 0x4c ++#define SUN4I_TCON0_BASIC1_H_TOTAL(total) ((((total) - 1) & 0x1fff) << 16) ++#define SUN4I_TCON0_BASIC1_H_BACKPORCH(bp) (((bp) - 1) & 0xfff) ++ ++#define SUN4I_TCON0_BASIC2_REG 0x50 ++#define SUN4I_TCON0_BASIC2_V_TOTAL(total) (((total) & 0x1fff) << 16) ++#define SUN4I_TCON0_BASIC2_V_BACKPORCH(bp) (((bp) - 1) & 0xfff) ++ ++#define SUN4I_TCON0_BASIC3_REG 0x54 ++#define SUN4I_TCON0_BASIC3_H_SYNC(width) ((((width) - 1) & 0x7ff) << 16) ++#define SUN4I_TCON0_BASIC3_V_SYNC(height) (((height) - 1) & 0x7ff) ++ ++#define SUN4I_TCON0_HV_IF_REG 0x58 ++ ++#define SUN4I_TCON0_IO_POL_REG 0x88 ++#define SUN4I_TCON0_IO_POL_DCLK_PHASE(phase) ((phase & 3) << 28) ++#define SUN4I_TCON0_IO_POL_DE_NEGATIVE BIT(27) ++#define SUN4I_TCON0_IO_POL_DCLK_NEGATIVE BIT(26) ++#define SUN4I_TCON0_IO_POL_HSYNC_POSITIVE BIT(25) ++#define SUN4I_TCON0_IO_POL_VSYNC_POSITIVE BIT(24) ++ ++#define SUN4I_TCON0_IO_TRI_REG 0x8c ++#define SUN4I_TCON0_IO_TRI_HSYNC_DISABLE BIT(25) ++#define SUN4I_TCON0_IO_TRI_VSYNC_DISABLE BIT(24) ++#define SUN4I_TCON0_IO_TRI_DATA_PINS_DISABLE(pins) GENMASK(pins, 0) ++ ++#define SUN4I_BACKEND_MODCTL_REG 0x800 ++#define SUN4I_BACKEND_MODCTL_LINE_SEL BIT(29) ++#define SUN4I_BACKEND_MODCTL_ITLMOD_EN BIT(28) ++#define SUN4I_BACKEND_MODCTL_OUT_SEL GENMASK(22, 20) ++#define SUN4I_BACKEND_MODCTL_OUT_LCD0 (0 << 20) ++#define SUN4I_BACKEND_MODCTL_OUT_LCD1 (1 << 20) ++#define SUN4I_BACKEND_MODCTL_OUT_FE0 (6 << 20) ++#define SUN4I_BACKEND_MODCTL_OUT_FE1 (7 << 20) ++#define SUN4I_BACKEND_MODCTL_HWC_EN BIT(16) ++#define SUN4I_BACKEND_MODCTL_LAY_EN(l) BIT(8 + l) ++#define SUN4I_BACKEND_MODCTL_OCSC_EN BIT(5) ++#define SUN4I_BACKEND_MODCTL_DFLK_EN BIT(4) ++#define SUN4I_BACKEND_MODCTL_DLP_START_CTL BIT(2) ++#define SUN4I_BACKEND_MODCTL_START_CTL BIT(1) ++#define SUN4I_BACKEND_MODCTL_DEBE_EN BIT(0) ++ ++#define SUN4I_BACKEND_BACKCOLOR_REG 0x804 ++#define SUN4I_BACKEND_BACKCOLOR(r, g, b) (((r) << 16) | ((g) << 8) | (b)) ++ ++#define SUN4I_BACKEND_DISSIZE_REG 0x808 ++#define SUN4I_BACKEND_DISSIZE(w, h) (((((h) - 1) & 0xffff) << 16) | \ ++ (((w) - 1) & 0xffff)) ++ ++#define SUN4I_BACKEND_LAYSIZE_REG(l) (0x810 + (0x4 * (l))) ++#define SUN4I_BACKEND_LAYSIZE(w, h) (((((h) - 1) & 0x1fff) << 16) | \ ++ (((w) - 1) & 0x1fff)) ++ ++#define SUN4I_BACKEND_LAYCOOR_REG(l) (0x820 + (0x4 * (l))) ++#define SUN4I_BACKEND_LAYCOOR(x, y) ((((u32)(y) & 0xffff) << 16) | \ ++ ((u32)(x) & 0xffff)) ++ ++#define SUN4I_BACKEND_LAYLINEWIDTH_REG(l) (0x840 + (0x4 * (l))) ++ ++#define SUN4I_BACKEND_LAYFB_L32ADD_REG(l) (0x850 + (0x4 * (l))) ++ ++#define SUN4I_BACKEND_LAYFB_H4ADD_REG 0x860 ++#define SUN4I_BACKEND_LAYFB_H4ADD_MSK(l) GENMASK(3 + ((l) * 8), (l) * 8) ++#define SUN4I_BACKEND_LAYFB_H4ADD(l, val) ((val) << ((l) * 8)) ++ ++#define SUN4I_BACKEND_REGBUFFCTL_REG 0x870 ++#define SUN4I_BACKEND_REGBUFFCTL_AUTOLOAD_DIS BIT(1) ++#define SUN4I_BACKEND_REGBUFFCTL_LOADCTL BIT(0) ++ ++#define SUN4I_BACKEND_CKMAX_REG 0x880 ++#define SUN4I_BACKEND_CKMIN_REG 0x884 ++#define SUN4I_BACKEND_CKCFG_REG 0x888 ++#define SUN4I_BACKEND_ATTCTL_REG0(l) (0x890 + (0x4 * (l))) ++#define SUN4I_BACKEND_ATTCTL_REG0_LAY_GLBALPHA_MASK GENMASK(31, 24) ++#define SUN4I_BACKEND_ATTCTL_REG0_LAY_GLBALPHA(x) ((x) << 24) ++#define SUN4I_BACKEND_ATTCTL_REG0_LAY_PIPESEL_MASK BIT(15) ++#define SUN4I_BACKEND_ATTCTL_REG0_LAY_PIPESEL(x) ((x) << 15) ++#define SUN4I_BACKEND_ATTCTL_REG0_LAY_PRISEL_MASK GENMASK(11, 10) ++#define SUN4I_BACKEND_ATTCTL_REG0_LAY_PRISEL(x) ((x) << 10) ++#define SUN4I_BACKEND_ATTCTL_REG0_LAY_WORKMOD_MASK GENMASK(23, 22) ++#define SUN4I_BACKEND_ATTCTL_REG0_LAY_WORKMOD(x) ((x) << 22) ++#define SUN4I_BACKEND_ATTCTL_REG0_LAY_YUVEN BIT(2) ++#define SUN4I_BACKEND_ATTCTL_REG0_LAY_VDOEN BIT(1) ++#define SUN4I_BACKEND_ATTCTL_REG0_LAY_GLBALPHA_EN BIT(0) ++ ++#define SUN4I_BACKEND_ATTCTL_REG1(l) (0x8a0 + (0x4 * (l))) ++#define SUN4I_BACKEND_ATTCTL_REG1_LAY_HSCAFCT GENMASK(15, 14) ++#define SUN4I_BACKEND_ATTCTL_REG1_LAY_WSCAFCT GENMASK(13, 12) ++#define SUN4I_BACKEND_ATTCTL_REG1_LAY_FBFMT GENMASK(11, 8) ++#define SUN4I_BACKEND_LAY_FBFMT_1BPP (0 << 8) ++#define SUN4I_BACKEND_LAY_FBFMT_2BPP (1 << 8) ++#define SUN4I_BACKEND_LAY_FBFMT_4BPP (2 << 8) ++#define SUN4I_BACKEND_LAY_FBFMT_8BPP (3 << 8) ++#define SUN4I_BACKEND_LAY_FBFMT_RGB655 (4 << 8) ++#define SUN4I_BACKEND_LAY_FBFMT_RGB565 (5 << 8) ++#define SUN4I_BACKEND_LAY_FBFMT_RGB556 (6 << 8) ++#define SUN4I_BACKEND_LAY_FBFMT_ARGB1555 (7 << 8) ++#define SUN4I_BACKEND_LAY_FBFMT_RGBA5551 (8 << 8) ++#define SUN4I_BACKEND_LAY_FBFMT_XRGB8888 (9 << 8) ++#define SUN4I_BACKEND_LAY_FBFMT_ARGB8888 (10 << 8) ++#define SUN4I_BACKEND_LAY_FBFMT_RGB888 (11 << 8) ++#define SUN4I_BACKEND_LAY_FBFMT_ARGB4444 (12 << 8) ++#define SUN4I_BACKEND_LAY_FBFMT_RGBA4444 (13 << 8) ++ ++// }}} ++ ++#define EINK_IOCTL_UPDATE _IO('A', 0) ++//#define EINK_IOCTL_STATUS _IOR('A', 2, int) ++ ++enum { ++ EINK_REQ_NONE = 0, ++ EINK_REQ_UPDATE, ++ EINK_REQ_POWERDOWN, ++}; ++ ++enum { ++ EINK_WORKING, ++ EINK_OPEN, ++ EINK_WAITING_INT, ++ EINK_BE_SETUP, ++}; ++ ++struct eink_dev { ++ struct device *dev; ++ ++ // io ++ struct pinctrl* pinctrl; ++ struct pinctrl_state *pinctrl_active; ++ struct pinctrl_state *pinctrl_idle; ++ struct gpio_descs* gpios; ++ ++ // power ++ struct regulator *panel_supply; ++ struct timer_list powerdown_timer; ++ bool powered; ++ ++ // tcon0 ++ struct clk_bulk_data *clks; ++ int num_clks; ++ ++ struct reset_control *rstc; ++ struct regmap *tcon_regs; ++ struct regmap *be_regs; ++ int irq; ++ ++ // framebuffers ++ u8* framebuffers[2]; ++ u32* ctlstream; ++ dma_addr_t ctlstream_paddr; ++ size_t ctlstream_size; // byte size ++ ++ // control device ++ struct cdev cdev; ++ dev_t major; ++ bool is_open; // device can be used by only one user ++ ++ // rendering work ++ spinlock_t lock; ++ wait_queue_head_t waitqueue; ++ struct workqueue_struct *wq; ++ struct work_struct work; ++ unsigned long flags[1]; ++ int last_request; ++ ++ const struct eink_panel_config* panel; ++}; ++ ++struct eink_panel_config { ++ int sources; ++ int gates; ++ int h_lead; ++ int h_trail; ++ int v_extra; ++}; ++ ++static const struct eink_panel_config eink_ed060xd4_panel = { ++ .sources = 1024, ++ .gates = 758, ++ .h_lead = 40, // 1000ns (should be multiple of 8 for 256-bit alignment) ++ .h_trail = 8, // should be multiple of 8 for 256-bit alignment ++ .v_extra = 9, ++}; ++ ++// buf format is 1 byte per pixel, len needs to be multiple of 4 bytes ++ ++/* PocketBook 626(2) (Touch Lux 3) port D <-> Panel pin map */ ++ ++#define D0 BIT(3) ++#define D1 BIT(4) ++#define D2 BIT(5) ++#define D3 BIT(6) ++#define D4 BIT(7) ++ ++#define D5 BIT(10) ++#define D6 BIT(11) ++#define D7 BIT(12) ++#define S_LE BIT(13) ++#define G_MODE1 BIT(15) ++ ++#define S_OE BIT(20) ++#define S_SP BIT(21) ++#define G_CLK BIT(22) ++#define G_SP BIT(23) ++ ++/* ++ * we need to encode stream of control signals for various operations ++ * into separate rows: ++ * ++ * - sequence for starting a frame ++ * - three leading G_CLK pulses ++ * - sequence for all data rows ++ * - wait time with OE for previous row ++ * - wait time for the last row without shifting in more data ++ * - trailing row with a sequence for ending a frame ++ * ++ * So we have to have: ++ * - 6 special rows with no data + all the data rows ++ * - all of these have to have the same width ++ * - we want to use TCON to manage timing of the OE via HT/HBP settings ++ * ++ * Timings: ++ * - we use 20MHz clock (50ns period) ++ * - we have to spend 256 clocks per row, pushing data ++ * - we want to have 85Hz frame refresh rate ++ * - 11765us budget per frame ++ * - minimum G_CLK low time is 500ns, minimum G_CLK cycle time is 5us ++ * - G_SP 100ns setup (to G_CLK H->L)/hold time(after G_CLK H->L) ++ * - LE pulse width >150ns ++ * - LE delay before more data 200ns ++ * - 12us time the output reacts to LE (this should be hsync time/hbp) ++ * - at 20MHz, time to load data is 13us (about 100 FPS) ++ */ ++static void eink_ctlstream_prepare(struct eink_dev* eink) ++{ ++ const struct eink_panel_config* pc = eink->panel; ++ u32* p = eink->ctlstream; ++ int row_width = pc->sources / 4 + pc->h_lead + pc->h_trail; // ~262 ++ int i, j, t, r; ++ u32 base; ++ ++#define SET_PINS(t, v) { \ ++ for (i = 0; i < t; i++) \ ++ *p++ = v; \ ++ r -= t; \ ++ } ++ ++ // prepare a startup row ++ t = row_width / 3; ++ ++ r = row_width; ++ SET_PINS(20, G_MODE1 | G_SP | S_SP ); ++ SET_PINS(2*t,G_MODE1 | G_SP | S_SP | G_CLK ); ++ SET_PINS(r, G_MODE1 | G_SP | S_SP ); ++ ++ r = row_width; ++ SET_PINS(t, G_MODE1 | G_SP | S_SP | G_CLK ); ++ SET_PINS(t, G_MODE1 | S_SP | G_CLK ); ++ SET_PINS(r, G_MODE1 | S_SP ); ++ ++ r = row_width; ++ SET_PINS(t, G_MODE1 | S_SP | G_CLK ); ++ SET_PINS(t, G_MODE1 | G_SP | S_SP | G_CLK ); ++ SET_PINS(r, G_MODE1 | G_SP | S_SP ); ++ ++ // three empty rows ++ for (j = 0; j < 3; j++) { ++ r = row_width; ++ ++ SET_PINS(2*t, G_MODE1 | G_SP | S_SP | G_CLK ); ++ SET_PINS(r, G_MODE1 | G_SP | S_SP ); ++ } ++ ++ // extra row is there to keep S_OE high for the previous last data ++ // row that was shifted in ++ for (j = 0; j < pc->gates + 1; j++) { ++ base = G_MODE1 | G_SP; ++ ++ // We have 40 periods here to work with here (h_lead). ++ r = pc->h_lead; ++ ++ // S_SP goes high after the last data and there's 2us ++ // delay (it may even go high during the last data period?) ++ SET_PINS(20, base | S_SP); // 1000ns for tLEdly ++ base &= ~S_OE; ++ SET_PINS(2, base | S_SP); ++ SET_PINS(6, base | S_SP | (j > 0 ? S_LE : 0)); // 300ns for tLEw ++ if (j > 0) ++ base |= S_OE; ++ SET_PINS(2, base | S_SP | (j > 0 ? S_LE : 0)); // S_OE goes high ++ SET_PINS(r-2,base | S_SP); // 400ns for tLEoff ++ SET_PINS(r, base | S_SP | G_CLK); // 400ns for tLEoff ++ ++ if (j >= pc->gates) ++ base |= S_SP; ++ ++ // start loading data immediately at S_SP going low ++ r = pc->sources / 4; ++ t = r / 3; ++ SET_PINS(t, base | G_CLK); ++ SET_PINS(r, base); ++ ++ r = pc->h_trail; ++ SET_PINS(r, base | S_SP); ++ } ++ ++ r = row_width; ++ SET_PINS(r, G_SP | S_SP ); ++ ++ // this last row will be stopped by an interrupt before finishing ++ // completely, so it's a dummy one ++ r = row_width; ++ SET_PINS(r, G_SP | S_SP ); ++} ++ ++void eink_ctlstream_fill_data_neon(u32* cmd, u8* fb, int stride, ++ int sources, int gates, u32 masks[4]); ++ ++static void eink_ctlstream_fill_data(struct eink_dev* eink, u8* fb) ++{ ++ const struct eink_panel_config* pc = eink->panel; ++ int row_width = pc->sources / 4 + pc->h_lead + pc->h_trail; ++ u32 masks[4] = { ++ G_MODE1 | G_SP | G_CLK, ++ G_MODE1 | G_SP, ++ G_MODE1 | G_SP | S_OE | G_CLK, ++ G_MODE1 | G_SP | S_OE, ++ }; ++ ++ kernel_neon_begin(); ++ eink_ctlstream_fill_data_neon(eink->ctlstream + 6 * row_width + pc->h_lead, ++ fb, row_width, pc->sources, ++ pc->gates, masks); ++ kernel_neon_end(); ++} ++ ++static int eink_set_power(struct eink_dev* eink, bool en) ++{ ++ int ret; ++ ++ if (!en != !eink->powered) { ++ dev_warn(eink->dev, "%sable power supply", en ? "en" : "dis"); ++ ++ if (en) ++ ret = regulator_enable(eink->panel_supply); ++ else ++ ret = regulator_disable(eink->panel_supply); ++ if (ret) { ++ dev_err(eink->dev, "can't %sable power supply (%d)", ++ en ? "en" : "dis", ret); ++ return ret; ++ } ++ } ++ ++ if (en) ++ mod_timer(&eink->powerdown_timer, ++ jiffies + msecs_to_jiffies(5000)); ++ else ++ del_timer_sync(&eink->powerdown_timer); ++ ++ eink->powered = en; ++ return 0; ++} ++ ++__maybe_unused ++static void print_time(struct device* dev, u64 ts, char *buf) ++{ ++ do_div(ts, 1000); ++ dev_info(dev, "[%s] %6lu us", buf, (unsigned long)ts); ++} ++ ++// DCLK = 20MHz ++static int eink_update(struct eink_dev* eink) ++{ ++ struct regmap *tcon = eink->tcon_regs; ++ struct regmap *be = eink->be_regs; ++ unsigned long sclk_rate; ++ u32 ddiv = 6, w, h; ++ u32 lo_paddr, hi_paddr; ++ u32 vbp = 1, hbp = 40; ++ int ret; ++ ++ ret = eink_set_power(eink, 1); ++ if (ret) ++ return ret; ++ ++ sclk_rate = clk_get_rate(eink->clks[1].clk); ++ if (sclk_rate == 0) ++ return -EINVAL; ++ ++ spin_lock(&eink->lock); ++ eink_ctlstream_fill_data(eink, eink->framebuffers[0]); ++ spin_unlock(&eink->lock); ++ ++ w = eink->panel->sources / 4 + eink->panel->h_lead + eink->panel->h_trail; ++ h = eink->panel->gates + eink->panel->v_extra; ++ ++ // Setup DEBE ++ ++ if (!test_bit(EINK_BE_SETUP, eink->flags)) { ++ regmap_write(be, SUN4I_BACKEND_DISSIZE_REG, ++ SUN4I_BACKEND_DISSIZE(w, h)); ++ regmap_write(be, SUN4I_BACKEND_LAYSIZE_REG(0), ++ SUN4I_BACKEND_LAYSIZE(w, h)); ++ regmap_write(be, SUN4I_BACKEND_LAYCOOR_REG(0), ++ SUN4I_BACKEND_LAYCOOR(0, 0)); ++ regmap_write(be, SUN4I_BACKEND_LAYLINEWIDTH_REG(0), ++ w * 4 * 8); /* input format has 4 bytes per pixel */ ++ ++ lo_paddr = eink->ctlstream_paddr << 3; ++ hi_paddr = eink->ctlstream_paddr >> 29; ++ regmap_write(be, SUN4I_BACKEND_LAYFB_L32ADD_REG(0), lo_paddr); ++ regmap_update_bits(be, SUN4I_BACKEND_LAYFB_H4ADD_REG, ++ SUN4I_BACKEND_LAYFB_H4ADD_MSK(0), ++ SUN4I_BACKEND_LAYFB_H4ADD(0, hi_paddr)); ++ ++ regmap_write(be, SUN4I_BACKEND_ATTCTL_REG0(0), ++ SUN4I_BACKEND_ATTCTL_REG0_LAY_WORKMOD(0) | ++ SUN4I_BACKEND_ATTCTL_REG0_LAY_PIPESEL(0) | ++ SUN4I_BACKEND_ATTCTL_REG0_LAY_PRISEL(3)); ++ ++ regmap_write(be, SUN4I_BACKEND_ATTCTL_REG1(0), ++ SUN4I_BACKEND_LAY_FBFMT_XRGB8888); ++ ++ /* load BE buffers */ ++ regmap_write(be, SUN4I_BACKEND_REGBUFFCTL_REG, ++ SUN4I_BACKEND_REGBUFFCTL_AUTOLOAD_DIS | ++ SUN4I_BACKEND_REGBUFFCTL_LOADCTL); ++ set_bit(EINK_BE_SETUP, eink->flags); ++ } ++ ++ // Setup TCON/TCON0 ++ // ++ // TCON0 timings, allwinner style: ++ // ++ // ht - horizontal total time ++ // - total number of clock cycles in a row ++ // hbp - horizontal back porch ++ // - number of dclk cycles between start of hsync and data ++ // vt - vertical total time ++ // - number of rows in two frames ++ // vbp - vertical back porch ++ // - number of rows between vsync start and a valid data line ++ // hspw ++ // - width of hsync signal (in # of dclk cycles) ++ // vspw ++ // - width of vsync signal (in # of rows) ++ // ++ // Notes: ++ // hbp(aw)=hbp(panel)+hspw(panel) (same for vbp) ++ ++ /* Enable tcon module */ ++ regmap_write(tcon, SUN4I_TCON_GCTL_REG, ++ SUN4I_TCON_GCTL_TCON_ENABLE | ++ SUN4I_TCON_GCTL_IOMAP_TCON0); ++ ++ /* Set the resolution */ ++ regmap_write(tcon, SUN4I_TCON0_BASIC0_REG, ++ SUN4I_TCON0_BASIC0_X(w) | ++ SUN4I_TCON0_BASIC0_Y(h)); ++ ++ /* Set horizontal display timings */ ++ regmap_write(tcon, SUN4I_TCON0_BASIC1_REG, ++ SUN4I_TCON0_BASIC1_H_TOTAL(w + hbp + 2) | ++ SUN4I_TCON0_BASIC1_H_BACKPORCH(hbp)); ++ ++ /* Set vertical display timings */ ++ regmap_write(tcon, SUN4I_TCON0_BASIC2_REG, ++ SUN4I_TCON0_BASIC2_V_TOTAL((h + vbp + 2) * 2) | ++ SUN4I_TCON0_BASIC2_V_BACKPORCH(vbp)); ++ ++ /* Set Hsync and Vsync length */ ++ regmap_write(tcon, SUN4I_TCON0_BASIC3_REG, ++ SUN4I_TCON0_BASIC3_V_SYNC(1) | ++ SUN4I_TCON0_BASIC3_H_SYNC(1)); ++ ++ /* Setup output mode/pins */ ++ regmap_write(tcon, SUN4I_TCON0_HV_IF_REG, 0); // 24bit parallel mode ++ regmap_write(tcon, SUN4I_TCON0_IO_POL_REG, SUN4I_TCON0_IO_POL_DCLK_NEGATIVE); ++ regmap_write(tcon, SUN4I_TCON0_IO_TRI_REG, 0); ++ ++ ret = pinctrl_select_state(eink->pinctrl, eink->pinctrl_active); ++ if (ret) { ++ dev_err(eink->dev, "can't switch to active pinctrl state (%d)", ++ ret); ++ } ++ ++ set_bit(EINK_WAITING_INT, eink->flags); ++ ++ /* Interrupt after h lines */ ++ regmap_write(tcon, SUN4I_TCON_GINT0_REG, 0); ++ regmap_write(tcon, SUN4I_TCON_GINT1_REG, (h + vbp + 2) << 16); ++ //regmap_write(tcon, SUN4I_TCON_GINT0_REG, ++ //SUN4I_TCON_GINT0_VBLANK_ENABLE(0)); ++ regmap_write(tcon, SUN4I_TCON_GINT0_REG, ++ SUN4I_TCON_GINT0_LINE_ENABLE(0)); ++ ++ /* Configure and enable the dot clock */ ++ regmap_write(tcon, SUN4I_TCON0_DCLK_REG, ++ BIT(SUN4I_TCON0_DCLK_GATE_BIT) | ++ (ddiv << SUN4I_TCON0_DCLK_DIV_SHIFT)); ++ ++ /* Enable tcon 0, set clk delay */ ++ regmap_write(tcon, SUN4I_TCON0_CTL_REG, ++ SUN4I_TCON0_CTL_CLK_DELAY(3)); // up to 30 ++ ++ regmap_update_bits(tcon, SUN4I_TCON0_CTL_REG, ++ SUN4I_TCON0_CTL_TCON_ENABLE, ++ SUN4I_TCON0_CTL_TCON_ENABLE); ++ ++ ret = wait_event_timeout(eink->waitqueue, ++ !test_bit(EINK_WAITING_INT, eink->flags), ++ msecs_to_jiffies(100)); ++ if (ret == 0) ++ dev_warn(eink->dev, "Timeout waiting for TCON transfer\n"); ++ ++ // stop everything, cleanup ++ //regmap_write(tcon, SUN4I_TCON0_DCLK_REG, 0); ++ regmap_update_bits(tcon, SUN4I_TCON0_CTL_REG, ++ SUN4I_TCON0_CTL_TCON_ENABLE, ++ 0); ++ //regmap_write(tcon, SUN4I_TCON_GCTL_REG, 0); ++ regmap_write(tcon, SUN4I_TCON_GINT0_REG, 0); ++ regmap_write(tcon, SUN4I_TCON_GINT1_REG, 0); ++ ++ ret = pinctrl_select_state(eink->pinctrl, eink->pinctrl_idle); ++ if (ret) { ++ dev_err(eink->dev, "can't switch to idle pinctrl state (%d)", ++ ret); ++ } ++ ++ regmap_write(tcon, SUN4I_TCON0_IO_TRI_REG, ~0); ++ ++ return ret; ++} ++ ++static void eink_supply_powerdown_timer(struct timer_list *t) ++{ ++ struct eink_dev *eink = from_timer(eink, t, powerdown_timer); ++ ++ //dev_info(eink->dev, "powering down"); ++ ++ spin_lock(&eink->lock); ++ eink->last_request = EINK_REQ_POWERDOWN; ++ spin_unlock(&eink->lock); ++ ++ queue_work(eink->wq, &eink->work); ++} ++ ++static void eink_work_handler(struct work_struct *work) ++{ ++ struct eink_dev *eink = container_of(work, struct eink_dev, work); ++ int last_request; ++ ++ spin_lock(&eink->lock); ++ last_request = eink->last_request; ++ eink->last_request = 0; ++ spin_unlock(&eink->lock); ++ ++ if (last_request == EINK_REQ_UPDATE) { ++ eink_update(eink); ++ } else if (last_request == EINK_REQ_POWERDOWN) { ++ eink_set_power(eink, 0); ++ } ++ ++ clear_bit(EINK_WORKING, eink->flags); ++ wake_up(&eink->waitqueue); ++} ++ ++static ssize_t eink_read(struct file *fp, char __user *buf, size_t len, ++ loff_t *off) ++{ ++ struct eink_dev* eink = fp->private_data; ++ int ret, fb_size; ++ ++ fb_size = eink->panel->sources / 4 * eink->panel->gates; ++ if (len != fb_size) ++ return -EINVAL; ++ ++ spin_lock(&eink->lock); ++ if (copy_to_user(buf, eink->framebuffers[0], fb_size)) ++ ret = -EFAULT; ++ else ++ ret = fb_size; ++ spin_unlock(&eink->lock); ++ ++ return ret; ++} ++ ++static ssize_t eink_write(struct file *fp, const char __user *buf, ++ size_t len, loff_t *off) ++{ ++ struct eink_dev* eink = fp->private_data; ++ int non_blocking = fp->f_flags & O_NONBLOCK; ++ int ret, fb_size; ++ ++ fb_size = eink->panel->sources / 4 * eink->panel->gates; ++ if (len != fb_size) ++ return -EINVAL; ++ ++ if (test_and_set_bit(EINK_WORKING, eink->flags)) ++ return -EBUSY; ++ ++ spin_lock(&eink->lock); ++ ret = copy_from_user(eink->framebuffers[0], buf, len); ++ if (ret) { ++ clear_bit(EINK_WORKING, eink->flags); ++ spin_unlock(&eink->lock); ++ return -EFAULT; ++ } ++ ++ eink->last_request = EINK_REQ_UPDATE; ++ spin_unlock(&eink->lock); ++ ++ queue_work(eink->wq, &eink->work); ++ ++ // first handle non-blocking path ++ if (non_blocking && test_bit(EINK_WORKING, eink->flags)) ++ return -EWOULDBLOCK; ++ ++ // wait for availability of wakeup ++ wait_event(eink->waitqueue, ++ !test_bit(EINK_WORKING, eink->flags)); ++ ++ //u64 ts = ktime_get_boottime_ns(); ++ //print_time(eink->dev, ktime_get_boottime() - ts, "one frame"); ++ ++ return len; ++} ++ ++static unsigned int eink_poll(struct file *fp, poll_table *wait) ++{ ++ struct eink_dev* eink = fp->private_data; ++ int ret = 0; ++ ++ poll_wait(fp, &eink->waitqueue, wait); ++ ++ if (!test_bit(EINK_WORKING, eink->flags)) ++ ret |= POLLIN | POLLRDNORM; ++ ++ return ret; ++} ++ ++static long eink_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) ++{ ++ struct eink_dev* eink = fp->private_data; ++ unsigned long flags; ++ int ret = -ENOSYS; ++ //void __user *parg = (void __user *)arg; ++ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EACCES; ++ ++ if (test_and_set_bit(EINK_WORKING, eink->flags)) ++ return -EBUSY; ++ ++ spin_lock_irqsave(&eink->lock, flags); ++ ++ switch (cmd) { ++ case EINK_IOCTL_UPDATE: ++ eink->last_request = EINK_REQ_UPDATE; ++ ret = 0; ++ break; ++ /* ++ case EINK_IOCTL_BLANK: ++ eink->last_request = EINK_REQ_BLANK; ++ ret = 0; ++ break; ++ case EINK_IOCTL_STATUS: ++ if (copy_to_user(parg, &powered, sizeof powered)) ++ return -EFAULT; ++ ++ return 0; ++ */ ++ } ++ ++ spin_unlock_irqrestore(&eink->lock, flags); ++ ++ if (ret == 0) ++ queue_work(eink->wq, &eink->work); ++ else ++ clear_bit(EINK_WORKING, eink->flags); ++ ++ return ret; ++} ++ ++static int eink_release(struct inode *ip, struct file *fp) ++{ ++ struct eink_dev* eink = fp->private_data; ++ ++ clear_bit(EINK_OPEN, eink->flags); ++ return 0; ++} ++ ++static int eink_open(struct inode *ip, struct file *fp) ++{ ++ struct eink_dev* eink = container_of(ip->i_cdev, struct eink_dev, cdev); ++ ++ if (test_and_set_bit(EINK_OPEN, eink->flags)) ++ return -EBUSY; ++ ++ fp->private_data = eink; ++ ++ nonseekable_open(ip, fp); ++ return 0; ++} ++ ++static const struct file_operations eink_fops = { ++ .owner = THIS_MODULE, ++ .read = eink_read, ++ .write = eink_write, ++ .poll = eink_poll, ++ .unlocked_ioctl = eink_ioctl, ++ .open = eink_open, ++ .release = eink_release, ++ .llseek = noop_llseek, ++}; ++ ++static struct regmap_config eink_tcon_regmap_config = { ++ .name = "tcon", ++ .reg_bits = 32, ++ .val_bits = 32, ++ .reg_stride = 4, ++ .max_register = 0x7ff, ++ .cache_type = REGCACHE_NONE, ++}; ++ ++static struct regmap_config eink_be_regmap_config = { ++ .name = "be", ++ .reg_bits = 32, ++ .val_bits = 32, ++ .reg_stride = 4, ++ .max_register = 0x57ff, ++ .cache_type = REGCACHE_NONE, ++}; ++ ++static struct class* eink_class; ++ ++static irqreturn_t eink_tcon0_irq_handler(int irq, void *private) ++{ ++ struct eink_dev *eink = private; ++ struct regmap *tcon = eink->tcon_regs; ++ unsigned int status; ++ ++ regmap_read(eink->tcon_regs, SUN4I_TCON_GINT0_REG, &status); ++ ++ if (!(status & (SUN4I_TCON_GINT0_VBLANK_INT(0) | ++ SUN4I_TCON_GINT0_LINE_INT(0)))) ++ return IRQ_NONE; ++ ++ // stop everything ++ //regmap_write(tcon, SUN4I_TCON_GCTL_REG, 0); ++ ++ regmap_update_bits(tcon, SUN4I_TCON0_CTL_REG, ++ SUN4I_TCON0_CTL_TCON_ENABLE, ++ 0); ++ ++ regmap_write(tcon, SUN4I_TCON_GINT0_REG, 0); ++ regmap_write(tcon, SUN4I_TCON_GINT1_REG, 0); ++ //regmap_write(tcon, SUN4I_TCON0_DCLK_REG, 0); ++ //regmap_write(tcon, SUN4I_TCON0_IO_TRI_REG, ~0); ++ ++ clear_bit(EINK_WAITING_INT, eink->flags); ++ wake_up(&eink->waitqueue); ++ ++ /* ++ regmap_update_bits(eink->tcon_regs, SUN4I_TCON_GINT0_REG, ++ SUN4I_TCON_GINT0_VBLANK_INT(0) | ++ SUN4I_TCON_GINT0_LINE_INT(0), ++ 0); ++ */ ++ ++ return IRQ_HANDLED; ++} ++ ++static const struct clk_bulk_data eink_clocks[] = { ++ { .id = "tcon_bus" }, ++ { .id = "tcon_mod" }, ++ { .id = "be_bus" }, ++ { .id = "be_mod" }, ++ { .id = "be_ram" }, ++}; ++ ++static int eink_probe(struct platform_device *pdev) ++{ ++ struct eink_dev *eink; ++ struct device *dev = &pdev->dev; ++ struct device_node *np = dev->of_node; ++ struct device *sdev; ++ const char* cdev_name = NULL; ++ void __iomem *tcon_regs, *be_regs; ++ int ret, i, fb_size, cs_size; ++ ++ eink = devm_kzalloc(dev, sizeof(*eink), GFP_KERNEL); ++ if (!eink) ++ return -ENOMEM; ++ ++ eink->panel = &eink_ed060xd4_panel; ++ fb_size = eink->panel->sources / 4 * eink->panel->gates; ++ ++ // we shift four 2-bit source values per clock cycle ++ cs_size = ((eink->panel->sources / 4 + eink->panel->h_lead + eink->panel->h_trail) * ++ (eink->panel->gates + eink->panel->v_extra)) * 4; ++ ++ eink->framebuffers[0] = devm_kzalloc(dev, fb_size, GFP_KERNEL); ++ if (!eink->framebuffers[0]) ++ return -ENOMEM; ++ ++ eink->framebuffers[1] = devm_kzalloc(dev, fb_size, GFP_KERNEL); ++ if (!eink->framebuffers[1]) ++ return -ENOMEM; ++ ++ eink->dev = dev; ++ platform_set_drvdata(pdev, eink); ++ init_waitqueue_head(&eink->waitqueue); ++ spin_lock_init(&eink->lock); ++ INIT_WORK(&eink->work, &eink_work_handler); ++ timer_setup(&eink->powerdown_timer, eink_supply_powerdown_timer, 0); ++ ++ ret = of_dma_configure(dev, dev->of_node, true); ++ if (ret) { ++ dev_err(dev, "failed to configure dma (%d)\n", ret); ++ return ret; ++ } ++ ++ tcon_regs = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(tcon_regs)) ++ return PTR_ERR(tcon_regs); ++ ++ be_regs = devm_platform_ioremap_resource(pdev, 1); ++ if (IS_ERR(be_regs)) ++ return PTR_ERR(be_regs); ++ ++ eink->pinctrl = devm_pinctrl_get(dev); ++ if (IS_ERR(eink->pinctrl)) { ++ ret = PTR_ERR(eink->pinctrl); ++ dev_err(dev, "can't get pinctrl (%d)\n", ret); ++ return ret; ++ } ++ ++ eink->pinctrl_active = pinctrl_lookup_state(eink->pinctrl, "active"); ++ if (!eink->pinctrl_active) { ++ dev_err(dev, "missing active pinctrl state"); ++ return -EINVAL; ++ } ++ ++ eink->pinctrl_idle = pinctrl_lookup_state(eink->pinctrl, "idle"); ++ if (!eink->pinctrl_idle) { ++ dev_err(dev, "missing idle pinctrl state"); ++ return -EINVAL; ++ } ++ ++ ret = pinctrl_select_state(eink->pinctrl, eink->pinctrl_idle); ++ if (ret) { ++ dev_err(eink->dev, "can't switch to idle pinctrl state (%d)", ++ ret); ++ return ret; ++ } ++ ++ eink->gpios = devm_gpiod_get_array(dev, "all", GPIOD_OUT_LOW); ++ if (IS_ERR(eink->gpios)) { ++ ret = PTR_ERR(eink->gpios); ++ dev_err(dev, "can't get all gpios (%d)\n", ret); ++ return ret; ++ } ++ ++ eink->irq = platform_get_irq(pdev, 0); ++ if (eink->irq < 0) { ++ dev_err(dev, "Couldn't retrieve the TCON interrupt\n"); ++ return eink->irq; ++ } ++ ++ ret = of_property_read_string(np, "control-device-name", &cdev_name); ++ if (ret) { ++ dev_err(dev, "char-device-name is not configured (%d)\n", ret); ++ return ret; ++ } ++ ++ eink->panel_supply = devm_regulator_get(dev, "panel"); ++ if (IS_ERR(eink->panel_supply)) { ++ ret = PTR_ERR(eink->panel_supply); ++ dev_err(dev, "can't get panel supply (%d)\n", ret); ++ return ret; ++ } ++ ++ eink->clks = devm_kmemdup(dev, eink_clocks, sizeof(eink_clocks), ++ GFP_KERNEL); ++ if (!eink->clks) ++ return -ENOMEM; ++ ++ eink->num_clks = ARRAY_SIZE(eink_clocks); ++ ret = devm_clk_bulk_get(dev, eink->num_clks, eink->clks); ++ if (ret) ++ return ret; ++ ++ eink->rstc = devm_reset_control_array_get(dev, 0); ++ if (IS_ERR(eink->rstc)) { ++ ret = PTR_ERR(eink->rstc); ++ dev_err(dev, "Couldn't get our reset line (%d)\n", ret); ++ return ret; ++ } ++ ++ eink->tcon_regs = devm_regmap_init_mmio(dev, tcon_regs, &eink_tcon_regmap_config); ++ if (IS_ERR(eink->tcon_regs)) { ++ ret = PTR_ERR(eink->tcon_regs); ++ dev_err(dev, "Couldn't create the TCON regmap (%d)\n", ret); ++ return ret; ++ } ++ ++ eink->be_regs = devm_regmap_init_mmio(dev, be_regs, &eink_be_regmap_config); ++ if (IS_ERR(eink->be_regs)) { ++ ret = PTR_ERR(eink->be_regs); ++ dev_err(dev, "Couldn't create the BE regmap (%d)\n", ret); ++ return ret; ++ } ++ ++ // init the actual hardware ++ ret = reset_control_deassert(eink->rstc); ++ if (ret) { ++ dev_err(dev, "Couldn't deassert our reset line (%d)\n", ret); ++ return ret; ++ } ++ ++ // possible to set 27-381MHz in 3MHz steps ++ ret = clk_set_rate(eink->clks[1].clk, 120000000); ++ if (ret) { ++ dev_err(dev, "Couldn't set tcon0 rate to (%d)\n", ret); ++ goto err_reset; ++ } ++ ++ ret = clk_bulk_prepare_enable(eink->num_clks, eink->clks); ++ if (ret) { ++ dev_err(dev, "Couldn't enable clocks (%d)\n", ret); ++ goto err_reset; ++ } ++ ++ /* Clear DEBE registers */ ++ for (i = 0x800; i < 0x1000; i += 4) ++ regmap_write(eink->be_regs, i, 0); ++ ++ /* Disable registers autoloading */ ++ regmap_write(eink->be_regs, ++ SUN4I_BACKEND_REGBUFFCTL_REG, ++ SUN4I_BACKEND_REGBUFFCTL_AUTOLOAD_DIS); ++ ++ regmap_write(eink->be_regs, ++ SUN4I_BACKEND_MODCTL_REG, ++ SUN4I_BACKEND_MODCTL_START_CTL); ++ ++ /* Enable the backend */ ++ regmap_write(eink->be_regs, ++ SUN4I_BACKEND_MODCTL_REG, ++ SUN4I_BACKEND_MODCTL_DEBE_EN | ++ SUN4I_BACKEND_MODCTL_START_CTL | ++ SUN4I_BACKEND_MODCTL_OUT_LCD0 | ++ SUN4I_BACKEND_MODCTL_LAY_EN(0)); ++ ++ /* Make sure the TCON is disabled and all IRQs are off */ ++ regmap_write(eink->tcon_regs, SUN4I_TCON_GCTL_REG, 0); ++ regmap_write(eink->tcon_regs, SUN4I_TCON_GINT0_REG, 0); ++ regmap_write(eink->tcon_regs, SUN4I_TCON_GINT1_REG, 0); ++ ++ /* Disable IO lines and set them to tristate */ ++ regmap_write(eink->tcon_regs, SUN4I_TCON0_IO_TRI_REG, ~0); ++ ++ ret = devm_request_irq(dev, eink->irq, eink_tcon0_irq_handler, 0, ++ dev_name(dev), eink); ++ if (ret) { ++ dev_err(dev, "Couldn't request the IRQ\n"); ++ goto err_disable_hw; ++ } ++ ++ // create char device ++ ret = alloc_chrdev_region(&eink->major, 0, 1, "eink"); ++ if (ret) { ++ dev_err(dev, "can't allocate chrdev region"); ++ goto err_disable_hw; ++ } ++ ++ cdev_init(&eink->cdev, &eink_fops); ++ eink->cdev.owner = THIS_MODULE; ++ ret = cdev_add(&eink->cdev, eink->major, 1); ++ if (ret) { ++ dev_err(dev, "can't add cdev"); ++ goto err_unreg_chrev_region; ++ } ++ ++ sdev = device_create(eink_class, dev, eink->major, eink, cdev_name); ++ if (IS_ERR(sdev)) { ++ ret = PTR_ERR(sdev); ++ goto err_del_cdev; ++ } ++ ++ eink->ctlstream_size = round_up(cs_size, PAGE_SIZE); ++ eink->ctlstream = dma_alloc_wc(dev, eink->ctlstream_size, ++ &eink->ctlstream_paddr, ++ GFP_KERNEL | __GFP_NOWARN); ++ if (!eink->ctlstream) { ++ ret = -ENOMEM; ++ dev_err(dev, "failed to allocate buffer with size %zu\n", ++ eink->ctlstream_size); ++ goto err_destroy_device; ++ } ++ ++ eink_ctlstream_prepare(eink); ++ ++ eink->wq = alloc_workqueue("eink", WQ_SYSFS | WQ_HIGHPRI, 0); ++ if (!eink->wq) { ++ ret = -ENOMEM; ++ dev_err(dev, "failed to allocate workqueue\n"); ++ goto err_free_dma; ++ } ++ ++ dev_info(dev, "eink-panel driver ready!\n"); ++ return 0; ++ ++err_free_dma: ++ dma_free_wc(eink->dev, eink->ctlstream_size, eink->ctlstream, ++ eink->ctlstream_paddr); ++err_destroy_device: ++ device_destroy(eink_class, eink->major); ++err_del_cdev: ++ cdev_del(&eink->cdev); ++err_unreg_chrev_region: ++ unregister_chrdev(eink->major, "eink"); ++err_disable_hw: ++ clk_bulk_disable_unprepare(eink->num_clks, eink->clks); ++err_reset: ++ reset_control_assert(eink->rstc); ++ cancel_work_sync(&eink->work); ++ ++ return ret; ++} ++ ++static void eink_remove(struct platform_device *pdev) ++{ ++ struct eink_dev *eink = platform_get_drvdata(pdev); ++ ++ eink_set_power(eink, 0); ++ ++ del_timer_sync(&eink->powerdown_timer); ++ cancel_work_sync(&eink->work); ++ destroy_workqueue(eink->wq); ++ ++ dma_free_wc(eink->dev, eink->ctlstream_size, eink->ctlstream, ++ eink->ctlstream_paddr); ++ ++ clk_bulk_disable_unprepare(eink->num_clks, eink->clks); ++ reset_control_assert(eink->rstc); ++ ++ device_destroy(eink_class, eink->major); ++ cdev_del(&eink->cdev); ++ unregister_chrdev(eink->major, "eink"); ++} ++ ++static const struct of_device_id eink_of_match[] = { ++ { .compatible = "custom,pocketbook-touch-lux-3-tcon0-ed060xd4-display" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, eink_of_match); ++ ++static struct platform_driver eink_platform_driver = { ++ .probe = eink_probe, ++ .remove = eink_remove, ++ .driver = { ++ .name = "eink_tcon0", ++ .of_match_table = eink_of_match, ++ }, ++}; ++ ++static int __init eink_driver_init(void) ++{ ++ int ret; ++ ++ eink_class = class_create("eink-panel"); ++ ++ ret = platform_driver_register(&eink_platform_driver); ++ if (ret) ++ class_destroy(eink_class); ++ ++ return ret; ++} ++ ++static void __exit eink_driver_exit(void) ++{ ++ platform_driver_unregister(&eink_platform_driver); ++ class_destroy(eink_class); ++} ++ ++module_init(eink_driver_init); ++module_exit(eink_driver_exit); ++ ++MODULE_VERSION("1.0.0"); ++MODULE_DESCRIPTION("eInk display Allwinner TCON0 based bit-banging driver"); ++MODULE_AUTHOR("Ondrej Jirman "); ++MODULE_LICENSE("GPL v2"); +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/video-pwm_bl-Allow-to-change-lth_brightness-via-sysfs.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/video-pwm_bl-Allow-to-change-lth_brightness-via-sysfs.patch new file mode 100644 index 000000000000..c49c442432bf --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/video-pwm_bl-Allow-to-change-lth_brightness-via-sysfs.patch @@ -0,0 +1,126 @@ +From 9f67fcbe0e78e9b63df2e687b0eea50633f9e79e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Mon, 7 Jun 2021 20:27:07 +0200 +Subject: video: pwm_bl: Allow to change lth_brightness via sysfs + +Read minimum duty cycle in % from DT, and allow to change it via +sysfs. + +Minimum brightness calibration instructions: + + echo 1 > /sys/class/backlight/backlight/brightness + + echo 1 > /sys/class/backlight/backlight/device/lth_brightness + echo 2 > /sys/class/backlight/backlight/device/lth_brightness + echo 3 > /sys/class/backlight/backlight/device/lth_brightness + + .... increase values until you like the minimum brightness + +Then make sure to restore this value after each boot by writing it to +/sys/class/backlight/backlight/device/lth_brightness + +Signed-off-by: Ondrej Jirman +--- + drivers/video/backlight/pwm_bl.c | 72 +++++++++++++++++++++++++++++++- + 1 file changed, 70 insertions(+), 2 deletions(-) + +diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c +index f7d9d8813d92..8a7a158dc1f6 100644 +--- a/drivers/video/backlight/pwm_bl.c ++++ b/drivers/video/backlight/pwm_bl.c +@@ -437,6 +437,61 @@ static int pwm_backlight_initial_power_state(const struct pwm_bl_data *pb) + return active ? BACKLIGHT_POWER_ON : BACKLIGHT_POWER_OFF; + } + ++static ssize_t lth_brightness_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct backlight_device *bl = platform_get_drvdata(to_platform_device(dev)); ++ struct pwm_bl_data *pb = bl ? bl_get_data(bl) : NULL; ++ struct pwm_state state; ++ unsigned val; ++ ++ if (!pb) ++ return -EBUSY; ++ ++ pwm_get_state(pb->pwm, &state); ++ val = div_u64(100 * pb->lth_brightness, state.period); ++ ++ return scnprintf(buf, PAGE_SIZE, "%u\n", val); ++} ++ ++static ssize_t lth_brightness_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t len) ++{ ++ struct backlight_device *bl = platform_get_drvdata(to_platform_device(dev)); ++ struct pwm_bl_data *pb = bl ? bl_get_data(bl) : NULL; ++ struct pwm_state state; ++ unsigned val; ++ int ret; ++ ++ if (!pb) ++ return -EBUSY; ++ ++ ret = kstrtouint(buf, 10, &val); ++ if (ret) ++ return ret; ++ ++ if (val > 100) ++ val = 100; ++ ++ pwm_get_state(pb->pwm, &state); ++ pb->lth_brightness = div_u64(val * state.period, 100); ++ pwm_backlight_update_status(bl); ++ ++ return len; ++} ++ ++static DEVICE_ATTR_RW(lth_brightness); ++ ++static struct attribute *pwm_bl_attrs[] = { ++ &dev_attr_lth_brightness.attr, ++ NULL, ++}; ++ ++static const struct attribute_group pwm_bl_group = { ++ .attrs = pwm_bl_attrs, ++}; ++ + static int pwm_backlight_probe(struct platform_device *pdev) + { + struct platform_pwm_backlight_data *data = dev_get_platdata(&pdev->dev); +@@ -445,6 +500,7 @@ static int pwm_backlight_probe(struct platform_device *pdev) + struct backlight_device *bl; + struct pwm_bl_data *pb; + struct pwm_state state, state_real; ++ u32 lth_brightness; + unsigned int i; + int ret; + +@@ -584,8 +640,20 @@ static int pwm_backlight_probe(struct platform_device *pdev) + pb->scale = data->max_brightness; + } + +- pb->lth_brightness = data->lth_brightness * (div_u64(state.period, +- pb->scale)); ++ pb->lth_brightness = div_u64(data->lth_brightness * state.period, pb->scale); ++ ++ ret = of_property_read_u32(pdev->dev.of_node, "lth-brightness", ++ <h_brightness); ++ if (ret == 0) { ++ if (lth_brightness > 100) ++ lth_brightness = 100; ++ ++ pb->lth_brightness = div_u64(lth_brightness * state.period, 100); ++ } ++ ++ ret = devm_device_add_group(&pdev->dev, &pwm_bl_group); ++ if (ret) ++ goto err_alloc; + + props.type = BACKLIGHT_RAW; + props.max_brightness = data->max_brightness; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/series.conf b/patch/kernel/archive/sunxi-6.14/series.conf new file mode 100644 index 000000000000..9b2f89584aa6 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/series.conf @@ -0,0 +1,249 @@ +# +# This file is made manually by simply copying text +# from the target series.* files. +# Add (-) at the beginning of the line if the patch should not be applied. +# At the same time, the patch does not need to be deleted. +# + patches.megous/media-ov5640-Experiment-Try-to-disable-denoising-sharpening.patch + patches.megous/media-ov5640-Sleep-after-poweroff-to-ensure-next-poweron-is-not.patch + patches.megous/media-ov5640-Don-t-powerup-the-sensor-during-driver-probe.patch + patches.megous/media-ov5640-set-default-ae-target-lower.patch + patches.megous/media-ov5640-Improve-error-reporting.patch + patches.megous/media-ov5640-Implement-autofocus.patch + patches.megous/media-ov5640-Improve-firmware-load-time.patch + patches.megous/media-ov5640-Fix-focus-commands-blocking-until-complete.patch + patches.megous/media-ov5640-Add-read-only-property-for-vblank.patch + patches.megous/media-ov5640-use-pm_runtime_force_suspend-resume-for-system-sus.patch + patches.megous/media-sun6i-csi-capture-Use-subdev-operation-to-access-bridge-f.patch + patches.megous/media-sun6i-csi-subdev-Use-subdev-active-state-to-store-active-.patch + patches.megous/media-sun6i-csi-merge-sun6i_csi_formats-and-sun6i_csi_formats_m.patch + patches.megous/media-sun6i-csi-add-V4L2_CAP_IO_MC-capability.patch + patches.megous/media-sun6i-csi-implement-vidioc_enum_framesizes.patch + patches.megous/media-sun6i-csi-Add-multicamera-support-for-parallel-bus.patch + patches.megous/nfc-pn544-Add-support-for-VBAT-PVDD-regulators.patch + patches.megous/bluetooth-bcm-Restore-drive_rts_on_open-true-behavior-on-bcm207.patch + patches.megous/mmc-add-delay-after-power-class-selection.patch + patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-PN544-NFC-support.patch + patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-powerup-down-support-for-the-3G.patch + patches.megous/ARM-dts-sun8i-a83t-Add-cedrus-video-codec-support-to-A83T-untes.patch + patches.megous/ARM-dts-suni-a83t-Add-i2s0-pins.patch + patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-sound-support-via-AC100-codec.patch + patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-regulators-to-the-accelerometer.patch + patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-camera-sensors-HM5065-GC2145.patch + patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-flash-led-support.patch + patches.megous/dt-bindings-input-gpio-vibrator-Don-t-require-enable-gpios.patch + patches.megous/input-gpio-vibra-Allow-to-use-vcc-supply-alone-to-control-the-v.patch + patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-support-for-the-vibrator-motor.patch + patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Increase-voltage-on-the-vibrator.patch + patches.megous/dt-bindings-leds-Add-a-binding-for-AXP813-charger-led.patch + patches.megous/leds-axp20x-Support-charger-LED-on-AXP20x-like-PMICs.patch + patches.megous/ARM-dts-axp813-Add-charger-LED.patch + patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Enable-charging-LED.patch + patches.megous/MAINTAINERS-Add-entry-for-Himax-HM5065.patch + patches.megous/dt-bindings-media-Add-bindings-for-Himax-HM5065-camera-sensor.patch + patches.megous/hm5065-yaml-bindings-wip.patch + patches.megous/media-hm5065-Add-subdev-driver-for-Himax-HM5065-camera-sensor.patch + patches.megous/media-i2c-gc2145-Move-upstream-driver-out-of-the-way.patch + patches.megous/media-gc2145-Galaxycore-camera-module-driver.patch + patches.megous/media-gc2145-Added-BGGR-bayer-mode.patch + patches.megous/media-gc2145-Disable-debug-output.patch + patches.megous/media-gc2145-Add-PIXEL_RATE-HBLANK-and-VBLANK-controls.patch + patches.megous/media-gc2145-implement-system-suspend.patch + patches.megous/media-gc2145-fix-white-balance-colors.patch + patches.megous/media-i2c-gc2145-Parse-and-register-properties.patch + patches.megous/mailbox-Allow-to-run-mailbox-while-timekeeping-is-suspended.patch + patches.megous/ARM-sunxi-Add-experimental-suspend-to-memory-implementation-for.patch + patches.megous/ARM-sunxi-sunxi_cpu0_hotplug_support_set-is-not-supported-on-A8.patch + patches.megous/firmware-scpi-Add-support-for-sending-a-SCPI_CMD_SET_SYS_PWR_ST.patch + patches.megous/ARM-sunxi-Use-SCPI-to-send-suspend-message-to-SCP-on-A83T.patch + patches.megous/gnss-ubx-Send-soft-powerdown-message-on-suspend.patch + patches.megous/clk-sunxi-ng-Export-CLK_DRAM-for-devfreq.patch + patches.megous/ARM-dts-sun8i-a83t-Add-MBUS-node.patch + patches.megous/clk-sunxi-ng-Set-maximum-P-and-M-factors-to-1-for-H3-pll-cpux-c.patch + patches.megous/clk-sunxi-ng-Don-t-use-CPU-PLL-gating-and-CPUX-reparenting-to-H.patch + patches.megous/ARM-dts-sun8i-h3-Use-my-own-more-aggressive-OPPs-on-H3.patch + patches.megous/arm64-dts-sun50i-h5-Use-my-own-more-aggressive-OPPs-on-H5.patch + patches.megous/ARM-dts-sun8i-h3-orange-pi-pc-Increase-max-CPUX-voltage-to-1.4V.patch + patches.megous/ARM-dts-sun8i-a83t-Improve-CPU-OPP-tables-go-up-to-1.8GHz.patch + patches.megous/cpufreq-sun50i-Show-detected-CPU-bin-for-easier-debugging.patch + patches.megous/net-stmmac-sun8i-Use-devm_regulator_get-for-PHY-regulator.patch + patches.megous/net-stmmac-sun8i-Rename-PHY-regulator-variable-to-regulator_phy.patch + patches.megous/net-stmmac-sun8i-Add-support-for-enabling-a-regulator-for-PHY-I.patch + patches.megous/arm64-dts-allwinner-orange-pi-3-Enable-ethernet.patch + patches.megous/Revert-Input-cyttsp4-remove-driver.patch + patches.megous/input-cyttsp4-De-obfuscate-platform-data-for-keys.patch + patches.megous/input-cyttsp4-Remove-useless-indirection-with-driver-platform-d.patch + patches.megous/input-cyttsp4-Remove-unused-enable_vkeys.patch + patches.megous/input-cyttsp4-De-obfuscate-MT-signals-setup-platform-data.patch + patches.megous/input-cyttsp4-Clear-the-ids-buffer-in-a-saner-way.patch + patches.megous/input-cyttsp4-ENOSYS-error-is-ok-when-powering-up.patch + patches.megous/input-cyttsp4-Faster-recovery-from-failed-wakeup-HACK.patch + patches.megous/input-cyttsp4-Use-i2c-spi-names-directly-in-the-driver.patch + patches.megous/input-cyttsp4-Port-the-driver-to-use-device-properties.patch + patches.megous/input-cyttsp4-Restart-on-wakeup-wakeup-by-I2C-read-doesn-t-work.patch + patches.megous/input-cyttsp4-Fix-warnings.patch + patches.megous/input-cyttsp4-Make-the-driver-not-hog-the-system-s-workqueue.patch + patches.megous/input-cyttsp4-Fix-probe-oops.patch + patches.megous/input-cyttsp4-Fix-compile-issue.patch + patches.megous/video-fbdev-eInk-display-driver-for-A13-based-PocketBooks.patch + patches.megous/regulator-Add-simple-driver-for-enabling-a-regulator-from-users.patch + patches.megous/regulator-tp65185x-Add-tp65185x-eInk-panel-regulator-driver.patch + patches.megous/regulator-tp65185-Add-hwmon-device-for-reading-temperature.patch + patches.megous/iio-adc-sun4i-gpadc-iio-Allow-to-use-sun5i-a13-gpadc-iio-from-D.patch + patches.megous/mtd-spi-nor-Add-vdd-regulator-support.patch + patches.megous/ARM-dts-sun5i-Add-soc-handle.patch + patches.megous/ARM-dts-sun5i-Add-PocketBook-Touch-Lux-3-display-ctp-support.patch + patches.megous/ARM-dts-sun5i-a13-pocketbook-touch-lux-3-Add-RTC-clock-cells.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Add-front-back-cameras.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Add-Type-C-support-for-all-PP-va.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Add-modem-power-manager.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Fix-BH-modem-manager-behavior.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Add-detailed-OCV-to-capactiy-con.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Shorten-post-power-on-delay-on-m.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Add-support-for-Bluetooth-audio.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Enable-internal-HMIC-bias.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Add-support-for-modem-audio.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Bump-I2C-frequency-to-400kHz.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Add-interrupt-pin-for-WiFi.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Power-off-the-touch-controller-i.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Don-t-make-lradc-keys-a-wakeup-s.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Set-minimum-backlight-duty-cycle.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Add-supply-for-i2c-bus-to-anx768.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Workaround-broken-HDMI-HPD-signa.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Add-support-for-Pinephone-keyboa.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Enable-Pinephone-Keyboard-power-.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Add-support-for-Pinephone-1.2-be.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Add-power-supply-to-stk3311.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Add-reboot-mode-driver.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Use-newer-jack-detection-impleme.patch + patches.megous/arm64-dts-sun50i-Define-orientation-and-rotation-for-PinePhone-.patch + patches.megous/2-arm64-dts-sun50i-Define-orientation-and-rotation-for-PinePhone-.patch + patches.megous/arm64-dts-sun50-a64-pinephone-Define-jack-pins-in-DT.patch + patches.megous/dt-bindings-sound-Add-jack-type-property-to-sun8i-a33-codec.patch + patches.megous/ASoC-sun8i-codec-Allow-the-jack-type-to-be-set-via-device-tree.patch + patches.megous/ASoC-sun8i-codec-define-button-keycodes.patch + patches.megous/ASoC-sun8i-codec-Add-debug-output-for-jack-detection.patch + patches.megous/ASoC-sun8i-codec-Set-jack_type-from-DT-in-probe.patch + patches.megous/ASoC-simple-card-Allow-to-define-pins-for-aux-jack-devices.patch + patches.megous/clk-sunxi-ng-a64-Increase-PLL_AUDIO-base-frequency.patch + patches.megous/dt-bindings-mfd-Add-codec-related-properties-to-AC100-PMIC.patch + patches.megous/sound-soc-ac100-codec-Support-analog-part-of-X-Powers-AC100-cod.patch + patches.megous/sound-soc-sun8i-codec-Add-support-for-digital-part-of-the-AC100.patch + patches.megous/ASoC-ec25-New-codec-driver-for-the-EC25-modem.patch + patches.megous/ASOC-sun9i-hdmi-audio-Initial-implementation.patch + patches.megous/ARM-dts-sunxi-h3-h5-Add-hdmi-sound-card.patch + patches.megous/ARM-dts-sun8i-h3-Enable-hdmi-sound-card-on-boards-with-hdmi.patch + patches.megous/ARM-dts-sun8i-h2-plus-bananapi-m2-zero-Enable-HDMI-audio.patch + patches.megous/ARM-dts-sun8i-a83t-Add-hdmi-sound-card.patch + patches.megous/ARM-dts-sun8i-a83t-Enable-hdmi-sound-card-on-boards-with-hdmi.patch + patches.megous/ARM-dts-sun8i-r40-Add-hdmi-sound-card.patch + patches.megous/ARM-dts-sun8i-r40-bananapi-m2-ultra-Enable-HDMI-audio.patch + patches.megous/ARM-dts-sun8i-v40-bananapi-m2-berry-Enable-HDMI-audio.patch + patches.megous/arm64-dts-allwinner-h6-Add-hdmi-sound-card.patch + patches.megous/arm64-dts-allwinner-h6-Enable-hdmi-sound-card-on-boards-with-hd.patch + patches.megous/arm64-dts-allwinner-a64-Add-hdmi-sound-card.patch + patches.megous/arm64-dts-allwinner-a64-Enable-hdmi-sound-card-on-boards-with-h.patch + patches.megous/arm64-dts-allwinner-h5-Enable-hdmi-sound-card-on-boards-with-hd.patch + patches.megous/Move-a-node-to-avoid-merge-conflict.patch + patches.megous/arm64-dts-sun50i-a64-Set-fifo-size-for-uarts.patch + patches.megous/ARM-dts-sun8i-a83t-Set-fifo-size-for-uarts.patch + patches.megous/Mark-some-slow-drivers-for-async-probe-with-PROBE_PREFER_ASYNCH.patch + patches.megous/arm64-xor-Select-32regs-without-benchmark-to-speed-up-boot.patch + patches.megous/clk-sunxi-ng-Mark-TWD-clocks-as-critical.patch + patches.megous/firmware-arm_scpi-Support-unidirectional-mailbox-channels.patch + patches.megous/ARM-dts-sunxi-a83t-Add-SCPI-protocol.patch + patches.megous/ARM-dts-sunxi-h3-h5-Add-SCPI-protocol.patch + patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Give-Linux-more-privileges-over-SCP.patch + patches.megous/rtc-sun6i-Allow-RTC-wakeup-after-shutdown.patch + patches.megous/misc-modem-power-Power-manager-for-modems.patch + patches.megous/ARM-dts-sun8i-a83t-Add-missing-GPU-trip-point.patch + patches.megous/arm64-dts-sun50i-h5-Add-missing-GPU-trip-point.patch + patches.megous/arm64-dts-allwinner-a64-Fix-LRADC-compatible.patch + patches.megous/media-cedrus-Fix-failure-to-clean-up-hardware-on-probe-failure.patch + patches.megous/ASoC-rockchip-Fix-doubling-of-playback-speed-after-system-sleep.patch + patches.megous/usb-musb-sunxi-Avoid-enabling-host-side-code-on-SoCs-where-it-s.patch + patches.megous/arm64-dts-allwinner-Enforce-consistent-MMC-numbering.patch + patches.megous/ARM-dts-sunxi-Add-aliases-for-MMC.patch + patches.megous/drm-rockchip-Fix-panic-on-reboot-when-DRM-device-fails-to-bind.patch + patches.megous/usb-gadget-Fix-dangling-pointer-in-netdev-private-data.patch + patches.megous/mmc-dw-mmc-rockchip-fix-sdmmc-after-soft-reboot.patch + patches.megous/Revert-drm-sun4i-lvds-Invert-the-LVDS-polarity.patch + patches.megous/of-property-fw_devlink-Support-allwinner-sram-links.patch + patches.megous/Fix-broken-allwinner-sram-dependency-on-h616-h618.patch + patches.megous/arm64-dts-rockchip-rk356x-Fix-PCIe-register-map-and-ranges.patch + patches.megous/Fix-intptr_t-typedef.patch + patches.megous/mmc-sunxi-mmc-Remove-runtime-PM.patch + patches.megous/pci-Workaround-ITS-timeouts-on-poweroff-reboot-on-Orange-Pi-5-P.patch + patches.megous/usb-serial-option-add-reset_resume-callback-for-WWAN-devices.patch + patches.megous/media-ov5648-Fix-call-to-pm_runtime_set_suspended.patch + patches.megous/drm-rockchip-dw-mipi-dsi-rockchip-Fix-ISP1-PHY-initialization.patch + patches.megous/arm64-dts-rk3399-Add-dmc_opp_table.patch + patches.megous/arm64-dts-rockchip-rk3399-s-Add-DMC-table.patch + patches.megous/bluetooth-h5-Don-t-re-initialize-rtl8723cs-on-resume.patch + patches.megous/drm-sun4i-Mark-one-of-the-UI-planes-as-a-cursor-one.patch + patches.megous/drm-sun4i-Implement-gamma-correction.patch + patches.megous/drm-panel-st7703-Fix-xbd599-timings-to-make-refresh-rate-exactl.patch + patches.megous/drm-sun4i-Support-taking-over-display-pipeline-state-from-p-boo.patch + patches.megous/video-pwm_bl-Allow-to-change-lth_brightness-via-sysfs.patch + patches.megous/clk-sunxi-ng-sun50i-a64-Switch-parent-of-MIPI-DSI-to-periph0-1x.patch + patches.megous/drm-sun4i-tcon-Support-keeping-dclk-rate-upon-ancestor-clock-ch.patch + patches.megous/phy-allwinner-sun4i-usb-Add-support-for-usb_role_switch.patch + patches.megous/regulator-axp20x-Add-support-for-vin-supply-for-drivevbus.patch + patches.megous/regulator-axp20x-Turn-N_VBUSEN-to-input-on-x-powers-sense-vbus-.patch + patches.megous/drm-bridge-dw-hdmi-Allow-to-accept-HPD-status-from-other-driver.patch + patches.megous/drm-bridge-dw-hdmi-Report-HDMI-hotplug-events.patch + patches.megous/usb-typec-anx7688-Add-driver-for-ANX7688-USB-C-HDMI-bridge.patch + patches.megous/usb-typec-anx7688-Port-to-Linux-6.9.patch + patches.megous/usb-typec-anx7688-Port-to-Linux-6.10.patch + patches.megous/dt-bindings-axp20x-adc-allow-to-use-TS-pin-as-GPADC.patch + patches.megous/iio-adc-axp20x_adc-allow-to-set-TS-pin-to-GPADC-mode.patch + patches.megous/power-axp20x_battery-Allow-to-set-target-voltage-to-4.35V.patch + patches.megous/power-supply-axp20x_battery-Add-support-for-reporting-OCV.patch + patches.megous/regulator-axp20x-Enable-over-temperature-protection-and-16s-res.patch + patches.megous/power-supply-axp20x_battery-Setup-thermal-regulation-experiment.patch + patches.megous/power-supply-axp20x_battery-Fix-charging-done-detection.patch + patches.megous/mfd-axp20x-Add-battery-IRQ-resources.patch + patches.megous/power-supply-axp20x_battery-Send-uevents-for-status-changes.patch + patches.megous/power-supply-axp20x_battery-Monitor-battery-health.patch + patches.megous/power-supply-axp20x-usb-power-Change-Vbus-hold-voltage-to-4.5V.patch + patches.megous/power-axp803-Add-interrupts-for-low-battery-power-condition.patch + patches.megous/power-supply-axp20x-battery-Support-POWER_SUPPLY_PROP_CHARGE_BE.patch + patches.megous/power-supply-axp20x-battery-Enable-poweron-by-RTC-alarm.patch + patches.megous/power-supply-axp20x-battery-Add-support-for-POWER_SUPPLY_PROP_E.patch + patches.megous/power-supply-Add-support-for-USB_BC_ENABLED-and-USB_DCP_INPUT_C.patch + patches.megous/power-supply-axp20x-usb-power-Add-missing-interrupts.patch + patches.megous/power-supply-axp20x-battery-Improve-probe-error-reporting.patch + patches.megous/sunxi-Use-dev_err_probe-to-handle-EPROBE_DEFER-errors.patch + patches.megous/thermal-sun8i-Be-loud-when-probe-fails.patch + patches.megous/i2c-mv64xxx-Don-t-make-a-fuss-when-pinctrl-recovery-state-is-no.patch + patches.megous/iio-st_sensors-Don-t-report-error-when-the-device-is-not-presen.patch + patches.megous/opp-core-Avoid-confusing-error-when-no-regulator-is-defined-in-.patch + patches.megous/rtc-Print-which-error-caused-RTC-read-failure.patch + patches.megous/arm64-dts-allwinner-a64-pinetab-add-front-camera.patch + patches.megous/arm64-allwinner-dts-a64-enable-K101-IM2BYL02-panel-for-PineTab.patch + patches.megous/arm64-dts-sun50i-a64-pinetab-Name-sound-card-PineTab.patch + patches.megous/arm64-dts-sun50i-a64-pinetab-Add-accelerometer.patch + patches.megous/arm64-dts-sun50i-a64-pinetab-enable-RTL8723CS-bluetooth.patch + patches.megous/usb-typec-fusb302-Slightly-increase-wait-time-for-BC1.2-result.patch + patches.megous/usb-typec-fusb302-Set-the-current-before-enabling-pullups.patch + patches.megous/usb-typec-fusb302-Extend-debugging-interface-with-driver-state-.patch + patches.megous/usb-typec-fusb302-Retry-reading-of-CC-pins-status-if-activity-i.patch + patches.megous/usb-typec-fusb302-More-useful-of-logging-status-on-interrupt.patch + patches.megous/usb-typec-fusb302-Update-VBUS-state-even-if-VBUS-interrupt-is-n.patch + patches.megous/usb-typec-fusb302-Add-OF-extcon-support.patch + patches.megous/usb-typec-fusb302-Fix-register-definitions.patch + patches.megous/usb-typec-fusb302-Clear-interrupts-before-we-start-toggling.patch + patches.megous/usb-typec-typec-extcon-Add-typec-extcon-bridge-driver.patch + patches.megous/usb-typec-typec-extcon-Enable-debugging-for-now.patch + patches.megous/usb-typec-typec-extcon-Allow-to-force-reset-on-each-mux-change.patch + patches.megous/Revert-usb-typec-tcpm-unregister-existing-source-caps-before-re.patch + patches.megous/usb-typec-altmodes-displayport-Respect-DP_CAP_RECEPTACLE-bit.patch + patches.megous/usb-typec-tcpm-Unregister-altmodes-before-registering-new-ones.patch + patches.megous/usb-typec-tcpm-Fix-PD-devices-capabilities-registration.patch + patches.megous/usb-typec-tcpm-Improve-logs.patch + patches.megous/Make-microbuttons-on-Orange-Pi-PC-and-PC-2-work-as-power-off-bu.patch + patches.megous/Add-support-for-my-private-Sapomat-device.patch + patches.megous/ARM-dts-sun8i-h3-orange-pi-one-Enable-all-gpio-header-UARTs.patch + patches.megous/mtd-spi-nor-Add-Alliance-memory-support.patch + patches.megous/Add-README.md-with-information-and-u-boot-patches.patch + patches.megous/Defconfigs-for-all-my-devices.patch diff --git a/patch/kernel/archive/sunxi-6.14/series.megous b/patch/kernel/archive/sunxi-6.14/series.megous new file mode 100644 index 000000000000..1773cf5e27b9 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/series.megous @@ -0,0 +1,248 @@ +# +# Automatically generated by the script mk_format_patch +# +# git@github.com:The-going/linux-sf.git +# + patches.megous/media-ov5640-Experiment-Try-to-disable-denoising-sharpening.patch + patches.megous/media-ov5640-Sleep-after-poweroff-to-ensure-next-poweron-is-not.patch + patches.megous/media-ov5640-Don-t-powerup-the-sensor-during-driver-probe.patch + patches.megous/media-ov5640-set-default-ae-target-lower.patch + patches.megous/media-ov5640-Improve-error-reporting.patch + patches.megous/media-ov5640-Implement-autofocus.patch + patches.megous/media-ov5640-Improve-firmware-load-time.patch + patches.megous/media-ov5640-Fix-focus-commands-blocking-until-complete.patch + patches.megous/media-ov5640-Add-read-only-property-for-vblank.patch + patches.megous/media-ov5640-use-pm_runtime_force_suspend-resume-for-system-sus.patch + patches.megous/media-sun6i-csi-capture-Use-subdev-operation-to-access-bridge-f.patch + patches.megous/media-sun6i-csi-subdev-Use-subdev-active-state-to-store-active-.patch + patches.megous/media-sun6i-csi-merge-sun6i_csi_formats-and-sun6i_csi_formats_m.patch + patches.megous/media-sun6i-csi-add-V4L2_CAP_IO_MC-capability.patch + patches.megous/media-sun6i-csi-implement-vidioc_enum_framesizes.patch + patches.megous/media-sun6i-csi-Add-multicamera-support-for-parallel-bus.patch + patches.megous/nfc-pn544-Add-support-for-VBAT-PVDD-regulators.patch + patches.megous/bluetooth-bcm-Restore-drive_rts_on_open-true-behavior-on-bcm207.patch + patches.megous/mmc-add-delay-after-power-class-selection.patch + patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-PN544-NFC-support.patch + patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-powerup-down-support-for-the-3G.patch + patches.megous/ARM-dts-sun8i-a83t-Add-cedrus-video-codec-support-to-A83T-untes.patch + patches.megous/ARM-dts-suni-a83t-Add-i2s0-pins.patch + patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-sound-support-via-AC100-codec.patch + patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-regulators-to-the-accelerometer.patch + patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-camera-sensors-HM5065-GC2145.patch + patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-flash-led-support.patch + patches.megous/dt-bindings-input-gpio-vibrator-Don-t-require-enable-gpios.patch + patches.megous/input-gpio-vibra-Allow-to-use-vcc-supply-alone-to-control-the-v.patch + patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Add-support-for-the-vibrator-motor.patch + patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Increase-voltage-on-the-vibrator.patch + patches.megous/dt-bindings-leds-Add-a-binding-for-AXP813-charger-led.patch + patches.megous/leds-axp20x-Support-charger-LED-on-AXP20x-like-PMICs.patch + patches.megous/ARM-dts-axp813-Add-charger-LED.patch + patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Enable-charging-LED.patch + patches.megous/MAINTAINERS-Add-entry-for-Himax-HM5065.patch + patches.megous/dt-bindings-media-Add-bindings-for-Himax-HM5065-camera-sensor.patch + patches.megous/hm5065-yaml-bindings-wip.patch + patches.megous/media-hm5065-Add-subdev-driver-for-Himax-HM5065-camera-sensor.patch + patches.megous/media-i2c-gc2145-Move-upstream-driver-out-of-the-way.patch + patches.megous/media-gc2145-Galaxycore-camera-module-driver.patch + patches.megous/media-gc2145-Added-BGGR-bayer-mode.patch + patches.megous/media-gc2145-Disable-debug-output.patch + patches.megous/media-gc2145-Add-PIXEL_RATE-HBLANK-and-VBLANK-controls.patch + patches.megous/media-gc2145-implement-system-suspend.patch + patches.megous/media-gc2145-fix-white-balance-colors.patch + patches.megous/media-i2c-gc2145-Parse-and-register-properties.patch + patches.megous/mailbox-Allow-to-run-mailbox-while-timekeeping-is-suspended.patch + patches.megous/ARM-sunxi-Add-experimental-suspend-to-memory-implementation-for.patch + patches.megous/ARM-sunxi-sunxi_cpu0_hotplug_support_set-is-not-supported-on-A8.patch + patches.megous/firmware-scpi-Add-support-for-sending-a-SCPI_CMD_SET_SYS_PWR_ST.patch + patches.megous/ARM-sunxi-Use-SCPI-to-send-suspend-message-to-SCP-on-A83T.patch + patches.megous/gnss-ubx-Send-soft-powerdown-message-on-suspend.patch + patches.megous/clk-sunxi-ng-Export-CLK_DRAM-for-devfreq.patch + patches.megous/ARM-dts-sun8i-a83t-Add-MBUS-node.patch + patches.megous/clk-sunxi-ng-Set-maximum-P-and-M-factors-to-1-for-H3-pll-cpux-c.patch + patches.megous/clk-sunxi-ng-Don-t-use-CPU-PLL-gating-and-CPUX-reparenting-to-H.patch + patches.megous/ARM-dts-sun8i-h3-Use-my-own-more-aggressive-OPPs-on-H3.patch + patches.megous/arm64-dts-sun50i-h5-Use-my-own-more-aggressive-OPPs-on-H5.patch + patches.megous/ARM-dts-sun8i-h3-orange-pi-pc-Increase-max-CPUX-voltage-to-1.4V.patch + patches.megous/ARM-dts-sun8i-a83t-Improve-CPU-OPP-tables-go-up-to-1.8GHz.patch + patches.megous/cpufreq-sun50i-Show-detected-CPU-bin-for-easier-debugging.patch + patches.megous/net-stmmac-sun8i-Use-devm_regulator_get-for-PHY-regulator.patch + patches.megous/net-stmmac-sun8i-Rename-PHY-regulator-variable-to-regulator_phy.patch + patches.megous/net-stmmac-sun8i-Add-support-for-enabling-a-regulator-for-PHY-I.patch + patches.megous/arm64-dts-allwinner-orange-pi-3-Enable-ethernet.patch + patches.megous/Revert-Input-cyttsp4-remove-driver.patch + patches.megous/input-cyttsp4-De-obfuscate-platform-data-for-keys.patch + patches.megous/input-cyttsp4-Remove-useless-indirection-with-driver-platform-d.patch + patches.megous/input-cyttsp4-Remove-unused-enable_vkeys.patch + patches.megous/input-cyttsp4-De-obfuscate-MT-signals-setup-platform-data.patch + patches.megous/input-cyttsp4-Clear-the-ids-buffer-in-a-saner-way.patch + patches.megous/input-cyttsp4-ENOSYS-error-is-ok-when-powering-up.patch + patches.megous/input-cyttsp4-Faster-recovery-from-failed-wakeup-HACK.patch + patches.megous/input-cyttsp4-Use-i2c-spi-names-directly-in-the-driver.patch + patches.megous/input-cyttsp4-Port-the-driver-to-use-device-properties.patch + patches.megous/input-cyttsp4-Restart-on-wakeup-wakeup-by-I2C-read-doesn-t-work.patch + patches.megous/input-cyttsp4-Fix-warnings.patch + patches.megous/input-cyttsp4-Make-the-driver-not-hog-the-system-s-workqueue.patch + patches.megous/input-cyttsp4-Fix-probe-oops.patch + patches.megous/input-cyttsp4-Fix-compile-issue.patch + patches.megous/video-fbdev-eInk-display-driver-for-A13-based-PocketBooks.patch + patches.megous/regulator-Add-simple-driver-for-enabling-a-regulator-from-users.patch + patches.megous/regulator-tp65185x-Add-tp65185x-eInk-panel-regulator-driver.patch + patches.megous/regulator-tp65185-Add-hwmon-device-for-reading-temperature.patch + patches.megous/iio-adc-sun4i-gpadc-iio-Allow-to-use-sun5i-a13-gpadc-iio-from-D.patch + patches.megous/mtd-spi-nor-Add-vdd-regulator-support.patch + patches.megous/ARM-dts-sun5i-Add-soc-handle.patch + patches.megous/ARM-dts-sun5i-Add-PocketBook-Touch-Lux-3-display-ctp-support.patch + patches.megous/ARM-dts-sun5i-a13-pocketbook-touch-lux-3-Add-RTC-clock-cells.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Add-front-back-cameras.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Add-Type-C-support-for-all-PP-va.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Add-modem-power-manager.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Fix-BH-modem-manager-behavior.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Add-detailed-OCV-to-capactiy-con.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Shorten-post-power-on-delay-on-m.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Add-support-for-Bluetooth-audio.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Enable-internal-HMIC-bias.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Add-support-for-modem-audio.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Bump-I2C-frequency-to-400kHz.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Add-interrupt-pin-for-WiFi.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Power-off-the-touch-controller-i.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Don-t-make-lradc-keys-a-wakeup-s.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Set-minimum-backlight-duty-cycle.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Add-supply-for-i2c-bus-to-anx768.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Workaround-broken-HDMI-HPD-signa.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Add-support-for-Pinephone-keyboa.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Enable-Pinephone-Keyboard-power-.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Add-support-for-Pinephone-1.2-be.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Add-power-supply-to-stk3311.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Add-reboot-mode-driver.patch + patches.megous/arm64-dts-sun50i-a64-pinephone-Use-newer-jack-detection-impleme.patch + patches.megous/arm64-dts-sun50i-Define-orientation-and-rotation-for-PinePhone-.patch + patches.megous/2-arm64-dts-sun50i-Define-orientation-and-rotation-for-PinePhone-.patch + patches.megous/arm64-dts-sun50-a64-pinephone-Define-jack-pins-in-DT.patch + patches.megous/dt-bindings-sound-Add-jack-type-property-to-sun8i-a33-codec.patch + patches.megous/ASoC-sun8i-codec-Allow-the-jack-type-to-be-set-via-device-tree.patch + patches.megous/ASoC-sun8i-codec-define-button-keycodes.patch + patches.megous/ASoC-sun8i-codec-Add-debug-output-for-jack-detection.patch + patches.megous/ASoC-sun8i-codec-Set-jack_type-from-DT-in-probe.patch + patches.megous/ASoC-simple-card-Allow-to-define-pins-for-aux-jack-devices.patch + patches.megous/clk-sunxi-ng-a64-Increase-PLL_AUDIO-base-frequency.patch + patches.megous/dt-bindings-mfd-Add-codec-related-properties-to-AC100-PMIC.patch + patches.megous/sound-soc-ac100-codec-Support-analog-part-of-X-Powers-AC100-cod.patch + patches.megous/sound-soc-sun8i-codec-Add-support-for-digital-part-of-the-AC100.patch + patches.megous/ASoC-ec25-New-codec-driver-for-the-EC25-modem.patch + patches.megous/ASOC-sun9i-hdmi-audio-Initial-implementation.patch + patches.megous/ARM-dts-sunxi-h3-h5-Add-hdmi-sound-card.patch + patches.megous/ARM-dts-sun8i-h3-Enable-hdmi-sound-card-on-boards-with-hdmi.patch + patches.megous/ARM-dts-sun8i-h2-plus-bananapi-m2-zero-Enable-HDMI-audio.patch + patches.megous/ARM-dts-sun8i-a83t-Add-hdmi-sound-card.patch + patches.megous/ARM-dts-sun8i-a83t-Enable-hdmi-sound-card-on-boards-with-hdmi.patch + patches.megous/ARM-dts-sun8i-r40-Add-hdmi-sound-card.patch + patches.megous/ARM-dts-sun8i-r40-bananapi-m2-ultra-Enable-HDMI-audio.patch + patches.megous/ARM-dts-sun8i-v40-bananapi-m2-berry-Enable-HDMI-audio.patch + patches.megous/arm64-dts-allwinner-h6-Add-hdmi-sound-card.patch + patches.megous/arm64-dts-allwinner-h6-Enable-hdmi-sound-card-on-boards-with-hd.patch + patches.megous/arm64-dts-allwinner-a64-Add-hdmi-sound-card.patch + patches.megous/arm64-dts-allwinner-a64-Enable-hdmi-sound-card-on-boards-with-h.patch + patches.megous/arm64-dts-allwinner-h5-Enable-hdmi-sound-card-on-boards-with-hd.patch + patches.megous/Move-a-node-to-avoid-merge-conflict.patch + patches.megous/arm64-dts-sun50i-a64-Set-fifo-size-for-uarts.patch + patches.megous/ARM-dts-sun8i-a83t-Set-fifo-size-for-uarts.patch + patches.megous/Mark-some-slow-drivers-for-async-probe-with-PROBE_PREFER_ASYNCH.patch + patches.megous/arm64-xor-Select-32regs-without-benchmark-to-speed-up-boot.patch + patches.megous/clk-sunxi-ng-Mark-TWD-clocks-as-critical.patch + patches.megous/firmware-arm_scpi-Support-unidirectional-mailbox-channels.patch + patches.megous/ARM-dts-sunxi-a83t-Add-SCPI-protocol.patch + patches.megous/ARM-dts-sunxi-h3-h5-Add-SCPI-protocol.patch + patches.megous/ARM-dts-sun8i-a83t-tbs-a711-Give-Linux-more-privileges-over-SCP.patch + patches.megous/rtc-sun6i-Allow-RTC-wakeup-after-shutdown.patch + patches.megous/misc-modem-power-Power-manager-for-modems.patch + patches.megous/ARM-dts-sun8i-a83t-Add-missing-GPU-trip-point.patch + patches.megous/arm64-dts-sun50i-h5-Add-missing-GPU-trip-point.patch + patches.megous/arm64-dts-allwinner-a64-Fix-LRADC-compatible.patch + patches.megous/media-cedrus-Fix-failure-to-clean-up-hardware-on-probe-failure.patch + patches.megous/ASoC-rockchip-Fix-doubling-of-playback-speed-after-system-sleep.patch + patches.megous/usb-musb-sunxi-Avoid-enabling-host-side-code-on-SoCs-where-it-s.patch + patches.megous/arm64-dts-allwinner-Enforce-consistent-MMC-numbering.patch + patches.megous/ARM-dts-sunxi-Add-aliases-for-MMC.patch + patches.megous/drm-rockchip-Fix-panic-on-reboot-when-DRM-device-fails-to-bind.patch + patches.megous/usb-gadget-Fix-dangling-pointer-in-netdev-private-data.patch + patches.megous/mmc-dw-mmc-rockchip-fix-sdmmc-after-soft-reboot.patch + patches.megous/Revert-drm-sun4i-lvds-Invert-the-LVDS-polarity.patch + patches.megous/of-property-fw_devlink-Support-allwinner-sram-links.patch + patches.megous/Fix-broken-allwinner-sram-dependency-on-h616-h618.patch + patches.megous/arm64-dts-rockchip-rk356x-Fix-PCIe-register-map-and-ranges.patch + patches.megous/Fix-intptr_t-typedef.patch + patches.megous/mmc-sunxi-mmc-Remove-runtime-PM.patch + patches.megous/pci-Workaround-ITS-timeouts-on-poweroff-reboot-on-Orange-Pi-5-P.patch + patches.megous/usb-serial-option-add-reset_resume-callback-for-WWAN-devices.patch + patches.megous/media-ov5648-Fix-call-to-pm_runtime_set_suspended.patch + patches.megous/drm-rockchip-dw-mipi-dsi-rockchip-Fix-ISP1-PHY-initialization.patch + patches.megous/arm64-dts-rk3399-Add-dmc_opp_table.patch + patches.megous/arm64-dts-rockchip-rk3399-s-Add-DMC-table.patch + patches.megous/bluetooth-h5-Don-t-re-initialize-rtl8723cs-on-resume.patch + patches.megous/drm-sun4i-Mark-one-of-the-UI-planes-as-a-cursor-one.patch + patches.megous/drm-sun4i-Implement-gamma-correction.patch + patches.megous/drm-panel-st7703-Fix-xbd599-timings-to-make-refresh-rate-exactl.patch + patches.megous/drm-sun4i-Support-taking-over-display-pipeline-state-from-p-boo.patch + patches.megous/video-pwm_bl-Allow-to-change-lth_brightness-via-sysfs.patch + patches.megous/clk-sunxi-ng-sun50i-a64-Switch-parent-of-MIPI-DSI-to-periph0-1x.patch + patches.megous/drm-sun4i-tcon-Support-keeping-dclk-rate-upon-ancestor-clock-ch.patch + patches.megous/phy-allwinner-sun4i-usb-Add-support-for-usb_role_switch.patch + patches.megous/regulator-axp20x-Add-support-for-vin-supply-for-drivevbus.patch + patches.megous/regulator-axp20x-Turn-N_VBUSEN-to-input-on-x-powers-sense-vbus-.patch + patches.megous/drm-bridge-dw-hdmi-Allow-to-accept-HPD-status-from-other-driver.patch + patches.megous/drm-bridge-dw-hdmi-Report-HDMI-hotplug-events.patch + patches.megous/usb-typec-anx7688-Add-driver-for-ANX7688-USB-C-HDMI-bridge.patch + patches.megous/usb-typec-anx7688-Port-to-Linux-6.9.patch + patches.megous/usb-typec-anx7688-Port-to-Linux-6.10.patch + patches.megous/dt-bindings-axp20x-adc-allow-to-use-TS-pin-as-GPADC.patch + patches.megous/iio-adc-axp20x_adc-allow-to-set-TS-pin-to-GPADC-mode.patch + patches.megous/power-axp20x_battery-Allow-to-set-target-voltage-to-4.35V.patch + patches.megous/power-supply-axp20x_battery-Add-support-for-reporting-OCV.patch + patches.megous/regulator-axp20x-Enable-over-temperature-protection-and-16s-res.patch + patches.megous/power-supply-axp20x_battery-Setup-thermal-regulation-experiment.patch + patches.megous/power-supply-axp20x_battery-Fix-charging-done-detection.patch + patches.megous/mfd-axp20x-Add-battery-IRQ-resources.patch + patches.megous/power-supply-axp20x_battery-Send-uevents-for-status-changes.patch + patches.megous/power-supply-axp20x_battery-Monitor-battery-health.patch + patches.megous/power-supply-axp20x-usb-power-Change-Vbus-hold-voltage-to-4.5V.patch + patches.megous/power-axp803-Add-interrupts-for-low-battery-power-condition.patch + patches.megous/power-supply-axp20x-battery-Support-POWER_SUPPLY_PROP_CHARGE_BE.patch + patches.megous/power-supply-axp20x-battery-Enable-poweron-by-RTC-alarm.patch + patches.megous/power-supply-axp20x-battery-Add-support-for-POWER_SUPPLY_PROP_E.patch + patches.megous/power-supply-Add-support-for-USB_BC_ENABLED-and-USB_DCP_INPUT_C.patch + patches.megous/power-supply-axp20x-usb-power-Add-missing-interrupts.patch + patches.megous/power-supply-axp20x-battery-Improve-probe-error-reporting.patch + patches.megous/sunxi-Use-dev_err_probe-to-handle-EPROBE_DEFER-errors.patch + patches.megous/thermal-sun8i-Be-loud-when-probe-fails.patch + patches.megous/i2c-mv64xxx-Don-t-make-a-fuss-when-pinctrl-recovery-state-is-no.patch + patches.megous/iio-st_sensors-Don-t-report-error-when-the-device-is-not-presen.patch + patches.megous/opp-core-Avoid-confusing-error-when-no-regulator-is-defined-in-.patch + patches.megous/rtc-Print-which-error-caused-RTC-read-failure.patch + patches.megous/arm64-dts-allwinner-a64-pinetab-add-front-camera.patch + patches.megous/arm64-allwinner-dts-a64-enable-K101-IM2BYL02-panel-for-PineTab.patch + patches.megous/arm64-dts-sun50i-a64-pinetab-Name-sound-card-PineTab.patch + patches.megous/arm64-dts-sun50i-a64-pinetab-Add-accelerometer.patch + patches.megous/arm64-dts-sun50i-a64-pinetab-enable-RTL8723CS-bluetooth.patch + patches.megous/usb-typec-fusb302-Slightly-increase-wait-time-for-BC1.2-result.patch + patches.megous/usb-typec-fusb302-Set-the-current-before-enabling-pullups.patch + patches.megous/usb-typec-fusb302-Extend-debugging-interface-with-driver-state-.patch + patches.megous/usb-typec-fusb302-Retry-reading-of-CC-pins-status-if-activity-i.patch + patches.megous/usb-typec-fusb302-More-useful-of-logging-status-on-interrupt.patch + patches.megous/usb-typec-fusb302-Update-VBUS-state-even-if-VBUS-interrupt-is-n.patch + patches.megous/usb-typec-fusb302-Add-OF-extcon-support.patch + patches.megous/usb-typec-fusb302-Fix-register-definitions.patch + patches.megous/usb-typec-fusb302-Clear-interrupts-before-we-start-toggling.patch + patches.megous/usb-typec-typec-extcon-Add-typec-extcon-bridge-driver.patch + patches.megous/usb-typec-typec-extcon-Enable-debugging-for-now.patch + patches.megous/usb-typec-typec-extcon-Allow-to-force-reset-on-each-mux-change.patch + patches.megous/Revert-usb-typec-tcpm-unregister-existing-source-caps-before-re.patch + patches.megous/usb-typec-altmodes-displayport-Respect-DP_CAP_RECEPTACLE-bit.patch + patches.megous/usb-typec-tcpm-Unregister-altmodes-before-registering-new-ones.patch + patches.megous/usb-typec-tcpm-Fix-PD-devices-capabilities-registration.patch + patches.megous/usb-typec-tcpm-Improve-logs.patch + patches.megous/Make-microbuttons-on-Orange-Pi-PC-and-PC-2-work-as-power-off-bu.patch + patches.megous/Add-support-for-my-private-Sapomat-device.patch + patches.megous/ARM-dts-sun8i-h3-orange-pi-one-Enable-all-gpio-header-UARTs.patch + patches.megous/mtd-spi-nor-Add-Alliance-memory-support.patch + patches.megous/Add-README.md-with-information-and-u-boot-patches.patch + patches.megous/Defconfigs-for-all-my-devices.patch From a987fd3dbb11d451c834e7547cdda5c9f4302213 Mon Sep 17 00:00:00 2001 From: The-going <48602507+The-going@users.noreply.github.com> Date: Wed, 30 Apr 2025 12:37:10 +0300 Subject: [PATCH 2/7] sunxi-6.14: Add drm patches --- ...global-control-reg-for-pad-selection.patch | 41 ++ ...dts-allwinner-h616-Add-Mali-GPU-node.patch | 61 ++ ...-add-Display-Engine-3.3-DE33-support.patch | 86 +++ .../drm-panfrost-Add-PM-runtime-flags.patch | 111 ++++ ...-panfrost-add-h616-compatible-string.patch | 43 ++ ...panfrost-reorder-pd-clk-rst-sequence.patch | 106 ++++ ...n4i-add-sun50i-h616-hdmi-phy-support.patch | 108 ++++ ...-de2-Initialize-layer-fields-earlier.patch | 75 +++ ...rm-sun4i-de2-de3-Change-CSC-argument.patch | 178 ++++++ ...de2-de3-Merge-CSC-functions-into-one.patch | 221 +++++++ ...ic-blender-register-reference-functi.patch | 38 ++ ...sun4i-de2-de3-add-mixer-version-enum.patch | 257 ++++++++ ...de3-call-csc-setup-also-for-UI-layer.patch | 64 ++ ...e2-de3-refactor-mixer-initialisation.patch | 121 ++++ ...ic-register-reference-function-for-l.patch | 117 ++++ ...m-sun4i-de3-Add-YUV-formatter-module.patch | 164 +++++ ...drm-sun4i-de3-Implement-AFBC-support.patch | 569 ++++++++++++++++++ ...de3-add-YUV-support-to-the-DE3-mixer.patch | 126 ++++ ...un4i-de3-add-YUV-support-to-the-TCON.patch | 83 +++ ...rt-to-the-color-space-correction-mod.patch | 225 +++++++ ...ormat-enumeration-function-to-engine.patch | 63 ++ ...3-add-formatter-flag-to-mixer-config.patch | 53 ++ ...ine-reference-to-ccsc-setup-function.patch | 54 ++ ...-add-Display-Engine-3.3-DE33-support.patch | 156 +++++ ...-add-Display-Engine-3.3-DE33-support.patch | 75 +++ ...-add-Display-Engine-3.3-DE33-support.patch | 302 ++++++++++ ...-add-Display-Engine-3.3-DE33-support.patch | 77 +++ ...n4i-support-YUV-formats-in-VI-scaler.patch | 140 +++++ ...scaler-refactor-vi_scaler-enablement.patch | 100 +++ ...-allwinner-add-H616-DE33-bus-binding.patch | 35 ++ ...llwinner-add-H616-DE33-clock-binding.patch | 32 + ...llwinner-add-H616-DE33-mixer-binding.patch | 36 ++ ...ifrost-Add-Allwinner-H616-compatible.patch | 30 + ...power-Add-Allwinner-H6-H616-PRCM-PPU.patch | 67 +++ ...mdomain-sunxi-add-H6-PRCM-PPU-driver.patch | 249 ++++++++ ...tch-parent-of-MIPI-DSI-to-periph0-1x.patch | 16 +- ...w-to-change-lth_brightness-via-sysfs.patch | 47 +- patch/kernel/archive/sunxi-6.14/series.conf | 49 +- patch/kernel/archive/sunxi-6.14/series.drm | 40 ++ 39 files changed, 4399 insertions(+), 16 deletions(-) create mode 100644 patch/kernel/archive/sunxi-6.14/patches.drm/add-TCON-global-control-reg-for-pad-selection.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.drm/arm64-dts-allwinner-h616-Add-Mali-GPU-node.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.drm/clk-sunxi-ng-ccu-add-Display-Engine-3.3-DE33-support.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.drm/drm-panfrost-Add-PM-runtime-flags.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.drm/drm-panfrost-add-h616-compatible-string.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.drm/drm-panfrost-reorder-pd-clk-rst-sequence.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-add-sun50i-h616-hdmi-phy-support.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de2-Initialize-layer-fields-earlier.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de2-de3-Change-CSC-argument.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de2-de3-Merge-CSC-functions-into-one.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de2-de3-add-generic-blender-register-reference-functi.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de2-de3-add-mixer-version-enum.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de2-de3-call-csc-setup-also-for-UI-layer.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de2-de3-refactor-mixer-initialisation.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de2-de3-use-generic-register-reference-function-for-l.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de3-Add-YUV-formatter-module.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de3-Implement-AFBC-support.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de3-add-YUV-support-to-the-DE3-mixer.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de3-add-YUV-support-to-the-TCON.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de3-add-YUV-support-to-the-color-space-correction-mod.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de3-add-format-enumeration-function-to-engine.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de3-add-formatter-flag-to-mixer-config.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de3-pass-engine-reference-to-ccsc-setup-function.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de33-csc-add-Display-Engine-3.3-DE33-support.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de33-fmt-add-Display-Engine-3.3-DE33-support.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de33-mixer-add-Display-Engine-3.3-DE33-support.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de33-vi_scaler-add-Display-Engine-3.3-DE33-support.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-support-YUV-formats-in-VI-scaler.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-vi_scaler-refactor-vi_scaler-enablement.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.drm/dt-bindings-allwinner-add-H616-DE33-bus-binding.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.drm/dt-bindings-allwinner-add-H616-DE33-clock-binding.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.drm/dt-bindings-allwinner-add-H616-DE33-mixer-binding.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.drm/dt-bindings-gpu-mali-bifrost-Add-Allwinner-H616-compatible.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.drm/dt-bindings-power-Add-Allwinner-H6-H616-PRCM-PPU.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.drm/pmdomain-sunxi-add-H6-PRCM-PPU-driver.patch create mode 100644 patch/kernel/archive/sunxi-6.14/series.drm diff --git a/patch/kernel/archive/sunxi-6.14/patches.drm/add-TCON-global-control-reg-for-pad-selection.patch b/patch/kernel/archive/sunxi-6.14/patches.drm/add-TCON-global-control-reg-for-pad-selection.patch new file mode 100644 index 000000000000..c1a25e9690ff --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.drm/add-TCON-global-control-reg-for-pad-selection.patch @@ -0,0 +1,41 @@ +From 86ef31fcb116682d399b9723a7ab66c87e6f2cc5 Mon Sep 17 00:00:00 2001 +From: Stephen Graf +Date: Tue, 18 Feb 2025 05:12:44 +0000 +Subject: add TCON global control reg for pad selection + +Signed-off-by: Stephen Graf +--- + drivers/gpu/drm/sun4i/sun4i_tcon.c | 4 ++++ + drivers/gpu/drm/sun4i/sun4i_tcon.h | 1 + + 2 files changed, 5 insertions(+) + +diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c +index af67bf2e6e09..88984572f5c5 100644 +--- a/drivers/gpu/drm/sun4i/sun4i_tcon.c ++++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c +@@ -1308,6 +1308,10 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master, + goto err_free_dclk; + } + ++ regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG, ++ SUN4I_TCON_GCTL_PAD_SEL, ++ SUN4I_TCON_GCTL_PAD_SEL); ++ + if (tcon->quirks->has_channel_0) { + /* + * If we have an LVDS panel connected to the TCON, we should +diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.h b/drivers/gpu/drm/sun4i/sun4i_tcon.h +index bd4abc90062b..e8d28bad4060 100644 +--- a/drivers/gpu/drm/sun4i/sun4i_tcon.h ++++ b/drivers/gpu/drm/sun4i/sun4i_tcon.h +@@ -19,6 +19,7 @@ + + #define SUN4I_TCON_GCTL_REG 0x0 + #define SUN4I_TCON_GCTL_TCON_ENABLE BIT(31) ++#define SUN4I_TCON_GCTL_PAD_SEL BIT(1) + #define SUN4I_TCON_GCTL_IOMAP_MASK BIT(0) + #define SUN4I_TCON_GCTL_IOMAP_TCON1 (1 << 0) + #define SUN4I_TCON_GCTL_IOMAP_TCON0 (0 << 0) +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.drm/arm64-dts-allwinner-h616-Add-Mali-GPU-node.patch b/patch/kernel/archive/sunxi-6.14/patches.drm/arm64-dts-allwinner-h616-Add-Mali-GPU-node.patch new file mode 100644 index 000000000000..8a00dac93002 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.drm/arm64-dts-allwinner-h616-Add-Mali-GPU-node.patch @@ -0,0 +1,61 @@ +From 02dc976a5f62b393f378c056888f121ba1880fc5 Mon Sep 17 00:00:00 2001 +From: Andre Przywara +Date: Fri, 21 Feb 2025 00:58:01 +0000 +Subject: arm64: dts: allwinner: h616: Add Mali GPU node + +The Allwinner H616 SoC contains a Mali-G31 MP2 GPU, which is of the Mali +Bifrost family. There is a power domain specifically for that GPU, which +needs to be enabled to make use of the it. + +Add the DT nodes for those two devices, and link them together through +the "power-domains" property. +Any board wishing to use the GPU would need to enable the GPU node and +specify the "mali-supply" regulator. + +Signed-off-by: Andre Przywara +--- + .../arm64/boot/dts/allwinner/sun50i-h616.dtsi | 21 +++++++++++++++++++ + 1 file changed, 21 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi +index cdce3dcb8ec0..ceedae9e399b 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi +@@ -150,6 +150,21 @@ soc { + #size-cells = <1>; + ranges = <0x0 0x0 0x0 0x40000000>; + ++ gpu: gpu@1800000 { ++ compatible = "allwinner,sun50i-h616-mali", ++ "arm,mali-bifrost"; ++ reg = <0x1800000 0x40000>; ++ interrupts = , ++ , ++ ; ++ interrupt-names = "job", "mmu", "gpu"; ++ clocks = <&ccu CLK_GPU0>, <&ccu CLK_BUS_GPU>; ++ clock-names = "core", "bus"; ++ power-domains = <&prcm_ppu 2>; ++ resets = <&ccu RST_BUS_GPU>; ++ status = "disabled"; ++ }; ++ + crypto: crypto@1904000 { + compatible = "allwinner,sun50i-h616-crypto"; + reg = <0x01904000 0x800>; +@@ -874,6 +889,12 @@ r_ccu: clock@7010000 { + #reset-cells = <1>; + }; + ++ prcm_ppu: power-controller@7010250 { ++ compatible = "allwinner,sun50i-h616-prcm-ppu"; ++ reg = <0x07010250 0x10>; ++ #power-domain-cells = <1>; ++ }; ++ + nmi_intc: interrupt-controller@7010320 { + compatible = "allwinner,sun50i-h616-nmi", + "allwinner,sun9i-a80-nmi"; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.drm/clk-sunxi-ng-ccu-add-Display-Engine-3.3-DE33-support.patch b/patch/kernel/archive/sunxi-6.14/patches.drm/clk-sunxi-ng-ccu-add-Display-Engine-3.3-DE33-support.patch new file mode 100644 index 000000000000..6bd5c7665c81 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.drm/clk-sunxi-ng-ccu-add-Display-Engine-3.3-DE33-support.patch @@ -0,0 +1,86 @@ +From b41f5a9ec8841c0342f101585c64c292019543d2 Mon Sep 17 00:00:00 2001 +From: Ryan Walklin +Date: Sun, 29 Sep 2024 22:04:54 +1300 +Subject: clk: sunxi-ng: ccu: add Display Engine 3.3 (DE33) support + +The DE33 is a newer version of the Allwinner Display Engine IP block, +found in the H616, H618, H700 and T507 SoCs. DE2 and DE3 are already +supported by the mainline driver. + +The DE33 in the H616 has mixer0 and writeback units. The clocks +and resets required are identical to the H3 and H5 respectively, so use +those existing structs for the H616 description. + +There are two additional 32-bit registers (at offsets 0x24 and 0x28) +which require clearing and setting respectively to bring up the +hardware. The function of these registers is currently unknown, and the +values are taken from the out-of-tree driver. + +Add the required clock description struct and compatible string to the +DE2 driver. + +Signed-off-by: Ryan Walklin +--- + drivers/clk/sunxi-ng/ccu-sun8i-de2.c | 25 +++++++++++++++++++++++++ + 1 file changed, 25 insertions(+) + +diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-de2.c b/drivers/clk/sunxi-ng/ccu-sun8i-de2.c +index 7683ea08d8e3..83eab6f132aa 100644 +--- a/drivers/clk/sunxi-ng/ccu-sun8i-de2.c ++++ b/drivers/clk/sunxi-ng/ccu-sun8i-de2.c +@@ -5,6 +5,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -239,6 +240,16 @@ static const struct sunxi_ccu_desc sun50i_h5_de2_clk_desc = { + .num_resets = ARRAY_SIZE(sun50i_h5_de2_resets), + }; + ++static const struct sunxi_ccu_desc sun50i_h616_de33_clk_desc = { ++ .ccu_clks = sun8i_de2_ccu_clks, ++ .num_ccu_clks = ARRAY_SIZE(sun8i_de2_ccu_clks), ++ ++ .hw_clks = &sun8i_h3_de2_hw_clks, ++ ++ .resets = sun50i_h5_de2_resets, ++ .num_resets = ARRAY_SIZE(sun50i_h5_de2_resets), ++}; ++ + static int sunxi_de2_clk_probe(struct platform_device *pdev) + { + struct clk *bus_clk, *mod_clk; +@@ -291,6 +302,16 @@ static int sunxi_de2_clk_probe(struct platform_device *pdev) + goto err_disable_mod_clk; + } + ++ /* ++ * The DE33 requires these additional (unknown) registers set ++ * during initialisation. ++ */ ++ if (of_device_is_compatible(pdev->dev.of_node, ++ "allwinner,sun50i-h616-de33-clk")) { ++ writel(0, reg + 0x24); ++ writel(0x0000a980, reg + 0x28); ++ } ++ + ret = devm_sunxi_ccu_probe(&pdev->dev, reg, ccu_desc); + if (ret) + goto err_assert_reset; +@@ -335,6 +356,10 @@ static const struct of_device_id sunxi_de2_clk_ids[] = { + .compatible = "allwinner,sun50i-h6-de3-clk", + .data = &sun50i_h5_de2_clk_desc, + }, ++ { ++ .compatible = "allwinner,sun50i-h616-de33-clk", ++ .data = &sun50i_h616_de33_clk_desc, ++ }, + { } + }; + MODULE_DEVICE_TABLE(of, sunxi_de2_clk_ids); +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.drm/drm-panfrost-Add-PM-runtime-flags.patch b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-panfrost-Add-PM-runtime-flags.patch new file mode 100644 index 000000000000..7284fe0e0014 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-panfrost-Add-PM-runtime-flags.patch @@ -0,0 +1,111 @@ +From ce56f3f385e0830a1e242f9fca9a82c3067ba03f Mon Sep 17 00:00:00 2001 +From: Philippe Simons +Date: Thu, 13 Mar 2025 00:23:18 +0100 +Subject: drm/panfrost: Add PM runtime flags + +Allwinner H616 has a dedicated power domain for its Mali G31. + +Currently after probe, the GPU is put in runtime suspend which +disable the power domain. +On first usage of GPU, the power domain enable hangs the system. + +This series adds the necessary calls to enable the clocks and +deasserting the reset line after the power domain enabling and +asserting the reset line and disabling the clocks prior to the +power domain disabling. + +This allows to use the Mali GPU on all Allwinner H616 +boards and devices. + +When the GPU is the only device attached to a single power domain, +core genpd disable and enable it when gpu enter and leave runtime suspend. + +Some power-domain requires a sequence before disabled, +and the reverse when enabled. + +Add GPU_PM_RT flag, and implement in +panfrost_device_runtime_suspend/resume. + +Signed-off-by: Philippe Simons +--- + drivers/gpu/drm/panfrost/panfrost_device.c | 33 ++++++++++++++++++++++ + drivers/gpu/drm/panfrost/panfrost_device.h | 3 ++ + 2 files changed, 36 insertions(+) + +diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c +index a45e4addcc19..93d48e97ce10 100644 +--- a/drivers/gpu/drm/panfrost/panfrost_device.c ++++ b/drivers/gpu/drm/panfrost/panfrost_device.c +@@ -406,11 +406,36 @@ void panfrost_device_reset(struct panfrost_device *pfdev) + static int panfrost_device_runtime_resume(struct device *dev) + { + struct panfrost_device *pfdev = dev_get_drvdata(dev); ++ int ret; ++ ++ if (pfdev->comp->pm_features & BIT(GPU_PM_RT)) { ++ ret = reset_control_deassert(pfdev->rstc); ++ if (ret) ++ return ret; ++ ++ ret = clk_enable(pfdev->clock); ++ if (ret) ++ goto err_clk; ++ ++ if (pfdev->bus_clock) { ++ ret = clk_enable(pfdev->bus_clock); ++ if (ret) ++ goto err_bus_clk; ++ } ++ } + + panfrost_device_reset(pfdev); + panfrost_devfreq_resume(pfdev); + + return 0; ++ ++err_bus_clk: ++ if (pfdev->comp->pm_features & BIT(GPU_PM_RT)) ++ clk_disable(pfdev->clock); ++err_clk: ++ if (pfdev->comp->pm_features & BIT(GPU_PM_RT)) ++ reset_control_assert(pfdev->rstc); ++ return ret; + } + + static int panfrost_device_runtime_suspend(struct device *dev) +@@ -426,6 +451,14 @@ static int panfrost_device_runtime_suspend(struct device *dev) + panfrost_gpu_suspend_irq(pfdev); + panfrost_gpu_power_off(pfdev); + ++ if (pfdev->comp->pm_features & BIT(GPU_PM_RT)) { ++ if (pfdev->bus_clock) ++ clk_disable(pfdev->bus_clock); ++ ++ clk_disable(pfdev->clock); ++ reset_control_assert(pfdev->rstc); ++ } ++ + return 0; + } + +diff --git a/drivers/gpu/drm/panfrost/panfrost_device.h b/drivers/gpu/drm/panfrost/panfrost_device.h +index cffcb0ac7c11..861555ceea65 100644 +--- a/drivers/gpu/drm/panfrost/panfrost_device.h ++++ b/drivers/gpu/drm/panfrost/panfrost_device.h +@@ -36,10 +36,13 @@ enum panfrost_drv_comp_bits { + * enum panfrost_gpu_pm - Supported kernel power management features + * @GPU_PM_CLK_DIS: Allow disabling clocks during system suspend + * @GPU_PM_VREG_OFF: Allow turning off regulators during system suspend ++ * @GPU_PM_RT: Allow disabling clocks and asserting the reset control during ++ * system runtime suspend + */ + enum panfrost_gpu_pm { + GPU_PM_CLK_DIS, + GPU_PM_VREG_OFF, ++ GPU_PM_RT + }; + + struct panfrost_features { +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.drm/drm-panfrost-add-h616-compatible-string.patch b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-panfrost-add-h616-compatible-string.patch new file mode 100644 index 000000000000..57ea76e9a7fc --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-panfrost-add-h616-compatible-string.patch @@ -0,0 +1,43 @@ +From 16e3a927918e0c6349c2bfcbe468ee1690eaaaca Mon Sep 17 00:00:00 2001 +From: Philippe Simons +Date: Thu, 13 Mar 2025 00:23:19 +0100 +Subject: drm/panfrost: add h616 compatible string + +Tie the Allwinner compatible string to the GPU_PM_RT feature bits that will +toggle the clocks and the reset line whenever the power domain is changing +state. + +Signed-off-by: Philippe Simons +--- + drivers/gpu/drm/panfrost/panfrost_drv.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c +index 0f3935556ac7..9470c04c5487 100644 +--- a/drivers/gpu/drm/panfrost/panfrost_drv.c ++++ b/drivers/gpu/drm/panfrost/panfrost_drv.c +@@ -776,6 +776,13 @@ static const struct panfrost_compatible default_data = { + .pm_domain_names = NULL, + }; + ++static const struct panfrost_compatible allwinner_h616_data = { ++ .num_supplies = ARRAY_SIZE(default_supplies) - 1, ++ .supply_names = default_supplies, ++ .num_pm_domains = 1, ++ .pm_features = BIT(GPU_PM_RT), ++}; ++ + static const struct panfrost_compatible amlogic_data = { + .num_supplies = ARRAY_SIZE(default_supplies) - 1, + .supply_names = default_supplies, +@@ -859,6 +866,7 @@ static const struct of_device_id dt_match[] = { + { .compatible = "mediatek,mt8186-mali", .data = &mediatek_mt8186_data }, + { .compatible = "mediatek,mt8188-mali", .data = &mediatek_mt8188_data }, + { .compatible = "mediatek,mt8192-mali", .data = &mediatek_mt8192_data }, ++ { .compatible = "allwinner,sun50i-h616-mali", .data = &allwinner_h616_data }, + {} + }; + MODULE_DEVICE_TABLE(of, dt_match); +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.drm/drm-panfrost-reorder-pd-clk-rst-sequence.patch b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-panfrost-reorder-pd-clk-rst-sequence.patch new file mode 100644 index 000000000000..a63c07fef4a8 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-panfrost-reorder-pd-clk-rst-sequence.patch @@ -0,0 +1,106 @@ +From 37f8be7eaca786f85cf2a17dfd33227e2ff45780 Mon Sep 17 00:00:00 2001 +From: Philippe Simons +Date: Thu, 3 Apr 2025 07:52:10 +0200 +Subject: drm/panfrost: reorder pd/clk/rst sequence + +According to Mali manuals, the powerup sequence should be +enable pd, asserting the reset then enabling the clock and +the reverse for powerdown. + +Signed-off-by: Philippe Simons +--- + drivers/gpu/drm/panfrost/panfrost_device.c | 38 +++++++++++----------- + 1 file changed, 19 insertions(+), 19 deletions(-) + +diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c +index 93d48e97ce10..5d35076b2e6d 100644 +--- a/drivers/gpu/drm/panfrost/panfrost_device.c ++++ b/drivers/gpu/drm/panfrost/panfrost_device.c +@@ -209,10 +209,20 @@ int panfrost_device_init(struct panfrost_device *pfdev) + + spin_lock_init(&pfdev->cycle_counter.lock); + ++ err = panfrost_pm_domain_init(pfdev); ++ if (err) ++ return err; ++ ++ err = panfrost_reset_init(pfdev); ++ if (err) { ++ dev_err(pfdev->dev, "reset init failed %d\n", err); ++ goto out_pm_domain; ++ } ++ + err = panfrost_clk_init(pfdev); + if (err) { + dev_err(pfdev->dev, "clk init failed %d\n", err); +- return err; ++ goto out_reset; + } + + err = panfrost_devfreq_init(pfdev); +@@ -229,25 +239,15 @@ int panfrost_device_init(struct panfrost_device *pfdev) + goto out_devfreq; + } + +- err = panfrost_reset_init(pfdev); +- if (err) { +- dev_err(pfdev->dev, "reset init failed %d\n", err); +- goto out_regulator; +- } +- +- err = panfrost_pm_domain_init(pfdev); +- if (err) +- goto out_reset; +- + pfdev->iomem = devm_platform_ioremap_resource(pfdev->pdev, 0); + if (IS_ERR(pfdev->iomem)) { + err = PTR_ERR(pfdev->iomem); +- goto out_pm_domain; ++ goto out_regulator; + } + + err = panfrost_gpu_init(pfdev); + if (err) +- goto out_pm_domain; ++ goto out_regulator; + + err = panfrost_mmu_init(pfdev); + if (err) +@@ -268,16 +268,16 @@ int panfrost_device_init(struct panfrost_device *pfdev) + panfrost_mmu_fini(pfdev); + out_gpu: + panfrost_gpu_fini(pfdev); +-out_pm_domain: +- panfrost_pm_domain_fini(pfdev); +-out_reset: +- panfrost_reset_fini(pfdev); + out_regulator: + panfrost_regulator_fini(pfdev); + out_devfreq: + panfrost_devfreq_fini(pfdev); + out_clk: + panfrost_clk_fini(pfdev); ++out_reset: ++ panfrost_reset_fini(pfdev); ++out_pm_domain: ++ panfrost_pm_domain_fini(pfdev); + return err; + } + +@@ -287,11 +287,11 @@ void panfrost_device_fini(struct panfrost_device *pfdev) + panfrost_job_fini(pfdev); + panfrost_mmu_fini(pfdev); + panfrost_gpu_fini(pfdev); +- panfrost_pm_domain_fini(pfdev); +- panfrost_reset_fini(pfdev); + panfrost_devfreq_fini(pfdev); + panfrost_regulator_fini(pfdev); + panfrost_clk_fini(pfdev); ++ panfrost_reset_fini(pfdev); ++ panfrost_pm_domain_fini(pfdev); + } + + #define PANFROST_EXCEPTION(id) \ +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-add-sun50i-h616-hdmi-phy-support.patch b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-add-sun50i-h616-hdmi-phy-support.patch new file mode 100644 index 000000000000..d1e28132f06e --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-add-sun50i-h616-hdmi-phy-support.patch @@ -0,0 +1,108 @@ +From 6c8bbaf43b8eaf62d4682ce66a35fc7f341f4a13 Mon Sep 17 00:00:00 2001 +From: The-going <48602507+The-going@users.noreply.github.com> +Date: Mon, 10 Feb 2025 15:45:13 +0300 +Subject: [PATCH] drm: sun4i: add sun50i-h616-hdmi-phy support + +--- + drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c | 71 ++++++++++++++++++++++++++ + 1 file changed, 71 insertions(+) + +diff --git a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c +index 4fa69c463dc4..8a07052037c3 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c ++++ b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c +@@ -124,6 +124,66 @@ static const struct dw_hdmi_phy_config sun50i_h6_phy_config[] = { + { ~0UL, 0x0000, 0x0000, 0x0000} + }; + ++static const struct dw_hdmi_mpll_config sun50i_h616_mpll_cfg[] = { ++ { ++ 27000000, { ++ {0x00b3, 0x0003}, ++ {0x2153, 0x0003}, ++ {0x40f3, 0x0003}, ++ }, ++ }, { ++ 74250000, { ++ {0x0072, 0x0003}, ++ {0x2145, 0x0003}, ++ {0x4061, 0x0003}, ++ }, ++ }, { ++ 148500000, { ++ {0x0051, 0x0003}, ++ {0x214c, 0x0003}, ++ {0x4064, 0x0003}, ++ }, ++ }, { ++ 297000000, { ++ {0x0040, 0x0003}, ++ {0x3b4c, 0x0003}, ++ {0x5a64, 0x0003}, ++ }, ++ }, { ++ 594000000, { ++ {0x1a40, 0x0003}, ++ {0x3b4c, 0x0003}, ++ {0x5a64, 0x0003}, ++ }, ++ }, { ++ ~0UL, { ++ {0x0000, 0x0000}, ++ {0x0000, 0x0000}, ++ {0x0000, 0x0000}, ++ }, ++ } ++}; ++ ++static const struct dw_hdmi_curr_ctrl sun50i_h616_cur_ctr[] = { ++ /* pixelclk bpp8 bpp10 bpp12 */ ++ { 27000000, { 0x0012, 0x0000, 0x0000 }, }, ++ { 74250000, { 0x0013, 0x0013, 0x0013 }, }, ++ { 148500000, { 0x0019, 0x0019, 0x0019 }, }, ++ { 297000000, { 0x0019, 0x001b, 0x0019 }, }, ++ { 594000000, { 0x0010, 0x0010, 0x0010 }, }, ++ { ~0UL, { 0x0000, 0x0000, 0x0000 }, } ++}; ++ ++static const struct dw_hdmi_phy_config sun50i_h616_phy_config[] = { ++ /*pixelclk symbol term vlev*/ ++ {27000000, 0x8009, 0x0007, 0x02b0}, ++ {74250000, 0x8019, 0x0004, 0x0290}, ++ {148500000, 0x8019, 0x0004, 0x0290}, ++ {297000000, 0x8039, 0x0004, 0x022b}, ++ {594000000, 0x8029, 0x0000, 0x008a}, ++ {~0UL, 0x0000, 0x0000, 0x0000} ++}; ++ + static void sun8i_hdmi_phy_set_polarity(struct sun8i_hdmi_phy *phy, + const struct drm_display_mode *mode) + { +@@ -626,6 +686,13 @@ static const struct sun8i_hdmi_phy_variant sun50i_h6_hdmi_phy = { + .phy_init = &sun50i_hdmi_phy_init_h6, + }; + ++static const struct sun8i_hdmi_phy_variant sun50i_h616_hdmi_phy = { ++ .cur_ctr = sun50i_h616_cur_ctr, ++ .mpll_cfg = sun50i_h616_mpll_cfg, ++ .phy_cfg = sun50i_h616_phy_config, ++ .phy_init = &sun50i_hdmi_phy_init_h6, ++}; ++ + static const struct of_device_id sun8i_hdmi_phy_of_table[] = { + { + .compatible = "allwinner,sun8i-a83t-hdmi-phy", +@@ -647,6 +714,10 @@ static const struct of_device_id sun8i_hdmi_phy_of_table[] = { + .compatible = "allwinner,sun50i-h6-hdmi-phy", + .data = &sun50i_h6_hdmi_phy, + }, ++ { ++ .compatible = "allwinner,sun50i-h616-hdmi-phy", ++ .data = &sun50i_h616_hdmi_phy, ++ }, + { /* sentinel */ } + }; + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de2-Initialize-layer-fields-earlier.patch b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de2-Initialize-layer-fields-earlier.patch new file mode 100644 index 000000000000..f5555d4b0d2e --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de2-Initialize-layer-fields-earlier.patch @@ -0,0 +1,75 @@ +From 5c2859b3cccd1b1b3f1700fd70c06770f418247a Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sun, 29 Sep 2024 22:04:36 +1300 +Subject: drm: sun4i: de2: Initialize layer fields earlier + +drm_universal_plane_init() can already call some callbacks, like +format_mod_supported, during initialization. Because of that, fields +should be initialized beforehand. + +Signed-off-by: Jernej Skrabec +Co-developed-by: Ryan Walklin +Signed-off-by: Ryan Walklin +Reviewed-by: Chen-Yu Tsai +--- + drivers/gpu/drm/sun4i/sun8i_ui_layer.c | 9 +++++---- + drivers/gpu/drm/sun4i/sun8i_vi_layer.c | 9 +++++---- + 2 files changed, 10 insertions(+), 8 deletions(-) + +diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c +index aa987bca1dbb..cb9b694fef10 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c ++++ b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c +@@ -295,6 +295,11 @@ struct sun8i_layer *sun8i_ui_layer_init_one(struct drm_device *drm, + if (!layer) + return ERR_PTR(-ENOMEM); + ++ layer->mixer = mixer; ++ layer->type = SUN8I_LAYER_TYPE_UI; ++ layer->channel = channel; ++ layer->overlay = 0; ++ + if (index == 0) + type = DRM_PLANE_TYPE_PRIMARY; + +@@ -325,10 +330,6 @@ struct sun8i_layer *sun8i_ui_layer_init_one(struct drm_device *drm, + } + + drm_plane_helper_add(&layer->plane, &sun8i_ui_layer_helper_funcs); +- layer->mixer = mixer; +- layer->type = SUN8I_LAYER_TYPE_UI; +- layer->channel = channel; +- layer->overlay = 0; + + return layer; + } +diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c +index f3a5329351ca..3c657b069d1f 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c ++++ b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c +@@ -478,6 +478,11 @@ struct sun8i_layer *sun8i_vi_layer_init_one(struct drm_device *drm, + if (!layer) + return ERR_PTR(-ENOMEM); + ++ layer->mixer = mixer; ++ layer->type = SUN8I_LAYER_TYPE_VI; ++ layer->channel = index; ++ layer->overlay = 0; ++ + if (mixer->cfg->is_de3) { + formats = sun8i_vi_layer_de3_formats; + format_count = ARRAY_SIZE(sun8i_vi_layer_de3_formats); +@@ -536,10 +541,6 @@ struct sun8i_layer *sun8i_vi_layer_init_one(struct drm_device *drm, + } + + drm_plane_helper_add(&layer->plane, &sun8i_vi_layer_helper_funcs); +- layer->mixer = mixer; +- layer->type = SUN8I_LAYER_TYPE_VI; +- layer->channel = index; +- layer->overlay = 0; + + return layer; + } +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de2-de3-Change-CSC-argument.patch b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de2-de3-Change-CSC-argument.patch new file mode 100644 index 000000000000..9125e4fa06cb --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de2-de3-Change-CSC-argument.patch @@ -0,0 +1,178 @@ +From 54669ac67e47835b8cc3eea215026385a0050567 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sun, 29 Sep 2024 22:04:33 +1300 +Subject: drm: sun4i: de2/de3: Change CSC argument + +Currently, CSC module takes care only for converting YUV to RGB. +However, DE3 is more suited to work in YUV color space. Change CSC mode +argument to format type to be more neutral. New argument only tells +layer format type and doesn't imply output type. + +This commit doesn't make any functional change. + +Signed-off-by: Jernej Skrabec +Signed-off-by: Ryan Walklin +Reviewed-by: Andre Przywara +--- + drivers/gpu/drm/sun4i/sun8i_csc.c | 22 +++++++++++----------- + drivers/gpu/drm/sun4i/sun8i_csc.h | 10 +++++----- + drivers/gpu/drm/sun4i/sun8i_vi_layer.c | 16 ++++++++-------- + 3 files changed, 24 insertions(+), 24 deletions(-) + +diff --git a/drivers/gpu/drm/sun4i/sun8i_csc.c b/drivers/gpu/drm/sun4i/sun8i_csc.c +index 58480d8e4f70..6ebd1c3aa3ab 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_csc.c ++++ b/drivers/gpu/drm/sun4i/sun8i_csc.c +@@ -108,7 +108,7 @@ static const u32 yuv2rgb_de3[2][3][12] = { + }; + + static void sun8i_csc_set_coefficients(struct regmap *map, u32 base, +- enum sun8i_csc_mode mode, ++ enum format_type fmt_type, + enum drm_color_encoding encoding, + enum drm_color_range range) + { +@@ -118,12 +118,12 @@ static void sun8i_csc_set_coefficients(struct regmap *map, u32 base, + + table = yuv2rgb[range][encoding]; + +- switch (mode) { +- case SUN8I_CSC_MODE_YUV2RGB: ++ switch (fmt_type) { ++ case FORMAT_TYPE_YUV: + base_reg = SUN8I_CSC_COEFF(base, 0); + regmap_bulk_write(map, base_reg, table, 12); + break; +- case SUN8I_CSC_MODE_YVU2RGB: ++ case FORMAT_TYPE_YVU: + for (i = 0; i < 12; i++) { + if ((i & 3) == 1) + base_reg = SUN8I_CSC_COEFF(base, i + 1); +@@ -141,7 +141,7 @@ static void sun8i_csc_set_coefficients(struct regmap *map, u32 base, + } + + static void sun8i_de3_ccsc_set_coefficients(struct regmap *map, int layer, +- enum sun8i_csc_mode mode, ++ enum format_type fmt_type, + enum drm_color_encoding encoding, + enum drm_color_range range) + { +@@ -151,12 +151,12 @@ static void sun8i_de3_ccsc_set_coefficients(struct regmap *map, int layer, + + table = yuv2rgb_de3[range][encoding]; + +- switch (mode) { +- case SUN8I_CSC_MODE_YUV2RGB: ++ switch (fmt_type) { ++ case FORMAT_TYPE_YUV: + addr = SUN50I_MIXER_BLEND_CSC_COEFF(DE3_BLD_BASE, layer, 0); + regmap_bulk_write(map, addr, table, 12); + break; +- case SUN8I_CSC_MODE_YVU2RGB: ++ case FORMAT_TYPE_YVU: + for (i = 0; i < 12; i++) { + if ((i & 3) == 1) + addr = SUN50I_MIXER_BLEND_CSC_COEFF(DE3_BLD_BASE, +@@ -206,7 +206,7 @@ static void sun8i_de3_ccsc_enable(struct regmap *map, int layer, bool enable) + } + + void sun8i_csc_set_ccsc_coefficients(struct sun8i_mixer *mixer, int layer, +- enum sun8i_csc_mode mode, ++ enum format_type fmt_type, + enum drm_color_encoding encoding, + enum drm_color_range range) + { +@@ -214,14 +214,14 @@ void sun8i_csc_set_ccsc_coefficients(struct sun8i_mixer *mixer, int layer, + + if (mixer->cfg->is_de3) { + sun8i_de3_ccsc_set_coefficients(mixer->engine.regs, layer, +- mode, encoding, range); ++ fmt_type, encoding, range); + return; + } + + base = ccsc_base[mixer->cfg->ccsc][layer]; + + sun8i_csc_set_coefficients(mixer->engine.regs, base, +- mode, encoding, range); ++ fmt_type, encoding, range); + } + + void sun8i_csc_enable_ccsc(struct sun8i_mixer *mixer, int layer, bool enable) +diff --git a/drivers/gpu/drm/sun4i/sun8i_csc.h b/drivers/gpu/drm/sun4i/sun8i_csc.h +index 828b86fd0cab..7322770f39f0 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_csc.h ++++ b/drivers/gpu/drm/sun4i/sun8i_csc.h +@@ -22,14 +22,14 @@ struct sun8i_mixer; + + #define SUN8I_CSC_CTRL_EN BIT(0) + +-enum sun8i_csc_mode { +- SUN8I_CSC_MODE_OFF, +- SUN8I_CSC_MODE_YUV2RGB, +- SUN8I_CSC_MODE_YVU2RGB, ++enum format_type { ++ FORMAT_TYPE_RGB, ++ FORMAT_TYPE_YUV, ++ FORMAT_TYPE_YVU, + }; + + void sun8i_csc_set_ccsc_coefficients(struct sun8i_mixer *mixer, int layer, +- enum sun8i_csc_mode mode, ++ enum format_type fmt_type, + enum drm_color_encoding encoding, + enum drm_color_range range); + void sun8i_csc_enable_ccsc(struct sun8i_mixer *mixer, int layer, bool enable); +diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c +index 9c09d9c08496..8a80934e928f 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c ++++ b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c +@@ -193,19 +193,19 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel, + return 0; + } + +-static u32 sun8i_vi_layer_get_csc_mode(const struct drm_format_info *format) ++static u32 sun8i_vi_layer_get_format_type(const struct drm_format_info *format) + { + if (!format->is_yuv) +- return SUN8I_CSC_MODE_OFF; ++ return FORMAT_TYPE_RGB; + + switch (format->format) { + case DRM_FORMAT_YVU411: + case DRM_FORMAT_YVU420: + case DRM_FORMAT_YVU422: + case DRM_FORMAT_YVU444: +- return SUN8I_CSC_MODE_YVU2RGB; ++ return FORMAT_TYPE_YVU; + default: +- return SUN8I_CSC_MODE_YUV2RGB; ++ return FORMAT_TYPE_YUV; + } + } + +@@ -213,7 +213,7 @@ static int sun8i_vi_layer_update_formats(struct sun8i_mixer *mixer, int channel, + int overlay, struct drm_plane *plane) + { + struct drm_plane_state *state = plane->state; +- u32 val, ch_base, csc_mode, hw_fmt; ++ u32 val, ch_base, fmt_type, hw_fmt; + const struct drm_format_info *fmt; + int ret; + +@@ -231,9 +231,9 @@ static int sun8i_vi_layer_update_formats(struct sun8i_mixer *mixer, int channel, + SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay), + SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_MASK, val); + +- csc_mode = sun8i_vi_layer_get_csc_mode(fmt); +- if (csc_mode != SUN8I_CSC_MODE_OFF) { +- sun8i_csc_set_ccsc_coefficients(mixer, channel, csc_mode, ++ fmt_type = sun8i_vi_layer_get_format_type(fmt); ++ if (fmt_type != FORMAT_TYPE_RGB) { ++ sun8i_csc_set_ccsc_coefficients(mixer, channel, fmt_type, + state->color_encoding, + state->color_range); + sun8i_csc_enable_ccsc(mixer, channel, true); +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de2-de3-Merge-CSC-functions-into-one.patch b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de2-de3-Merge-CSC-functions-into-one.patch new file mode 100644 index 000000000000..a487d9d899f4 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de2-de3-Merge-CSC-functions-into-one.patch @@ -0,0 +1,221 @@ +From 09744193cdcf400e5a4c54d9309acf5aea3a591c Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sun, 29 Sep 2024 22:04:34 +1300 +Subject: drm: sun4i: de2/de3: Merge CSC functions into one + +At the moment the colour space conversion is handled by two functions: +one to setup the conversion parameters, and another one to enable the +conversion. Merging both into one gives more flexibility for upcoming +extensions to support whole YUV pipelines, in the DE33. + +Signed-off-by: Jernej Skrabec +Signed-off-by: Ryan Walklin +Reviewed-by: Andre Przywara +--- + drivers/gpu/drm/sun4i/sun8i_csc.c | 89 ++++++++++---------------- + drivers/gpu/drm/sun4i/sun8i_csc.h | 9 ++- + drivers/gpu/drm/sun4i/sun8i_vi_layer.c | 11 +--- + 3 files changed, 40 insertions(+), 69 deletions(-) + +diff --git a/drivers/gpu/drm/sun4i/sun8i_csc.c b/drivers/gpu/drm/sun4i/sun8i_csc.c +index 6ebd1c3aa3ab..0dcbc0866ae8 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_csc.c ++++ b/drivers/gpu/drm/sun4i/sun8i_csc.c +@@ -107,23 +107,28 @@ static const u32 yuv2rgb_de3[2][3][12] = { + }, + }; + +-static void sun8i_csc_set_coefficients(struct regmap *map, u32 base, +- enum format_type fmt_type, +- enum drm_color_encoding encoding, +- enum drm_color_range range) ++static void sun8i_csc_setup(struct regmap *map, u32 base, ++ enum format_type fmt_type, ++ enum drm_color_encoding encoding, ++ enum drm_color_range range) + { ++ u32 base_reg, val; + const u32 *table; +- u32 base_reg; + int i; + + table = yuv2rgb[range][encoding]; + + switch (fmt_type) { ++ case FORMAT_TYPE_RGB: ++ val = 0; ++ break; + case FORMAT_TYPE_YUV: ++ val = SUN8I_CSC_CTRL_EN; + base_reg = SUN8I_CSC_COEFF(base, 0); + regmap_bulk_write(map, base_reg, table, 12); + break; + case FORMAT_TYPE_YVU: ++ val = SUN8I_CSC_CTRL_EN; + for (i = 0; i < 12; i++) { + if ((i & 3) == 1) + base_reg = SUN8I_CSC_COEFF(base, i + 1); +@@ -135,28 +140,37 @@ static void sun8i_csc_set_coefficients(struct regmap *map, u32 base, + } + break; + default: ++ val = 0; + DRM_WARN("Wrong CSC mode specified.\n"); + return; + } ++ ++ regmap_write(map, SUN8I_CSC_CTRL(base), val); + } + +-static void sun8i_de3_ccsc_set_coefficients(struct regmap *map, int layer, +- enum format_type fmt_type, +- enum drm_color_encoding encoding, +- enum drm_color_range range) ++static void sun8i_de3_ccsc_setup(struct regmap *map, int layer, ++ enum format_type fmt_type, ++ enum drm_color_encoding encoding, ++ enum drm_color_range range) + { ++ u32 addr, val, mask; + const u32 *table; +- u32 addr; + int i; + ++ mask = SUN50I_MIXER_BLEND_CSC_CTL_EN(layer); + table = yuv2rgb_de3[range][encoding]; + + switch (fmt_type) { ++ case FORMAT_TYPE_RGB: ++ val = 0; ++ break; + case FORMAT_TYPE_YUV: ++ val = mask; + addr = SUN50I_MIXER_BLEND_CSC_COEFF(DE3_BLD_BASE, layer, 0); + regmap_bulk_write(map, addr, table, 12); + break; + case FORMAT_TYPE_YVU: ++ val = mask; + for (i = 0; i < 12; i++) { + if ((i & 3) == 1) + addr = SUN50I_MIXER_BLEND_CSC_COEFF(DE3_BLD_BASE, +@@ -173,67 +187,30 @@ static void sun8i_de3_ccsc_set_coefficients(struct regmap *map, int layer, + } + break; + default: ++ val = 0; + DRM_WARN("Wrong CSC mode specified.\n"); + return; + } +-} +- +-static void sun8i_csc_enable(struct regmap *map, u32 base, bool enable) +-{ +- u32 val; +- +- if (enable) +- val = SUN8I_CSC_CTRL_EN; +- else +- val = 0; +- +- regmap_update_bits(map, SUN8I_CSC_CTRL(base), SUN8I_CSC_CTRL_EN, val); +-} +- +-static void sun8i_de3_ccsc_enable(struct regmap *map, int layer, bool enable) +-{ +- u32 val, mask; +- +- mask = SUN50I_MIXER_BLEND_CSC_CTL_EN(layer); +- +- if (enable) +- val = mask; +- else +- val = 0; + + regmap_update_bits(map, SUN50I_MIXER_BLEND_CSC_CTL(DE3_BLD_BASE), + mask, val); + } + +-void sun8i_csc_set_ccsc_coefficients(struct sun8i_mixer *mixer, int layer, +- enum format_type fmt_type, +- enum drm_color_encoding encoding, +- enum drm_color_range range) +-{ +- u32 base; +- +- if (mixer->cfg->is_de3) { +- sun8i_de3_ccsc_set_coefficients(mixer->engine.regs, layer, +- fmt_type, encoding, range); +- return; +- } +- +- base = ccsc_base[mixer->cfg->ccsc][layer]; +- +- sun8i_csc_set_coefficients(mixer->engine.regs, base, +- fmt_type, encoding, range); +-} +- +-void sun8i_csc_enable_ccsc(struct sun8i_mixer *mixer, int layer, bool enable) ++void sun8i_csc_set_ccsc(struct sun8i_mixer *mixer, int layer, ++ enum format_type fmt_type, ++ enum drm_color_encoding encoding, ++ enum drm_color_range range) + { + u32 base; + + if (mixer->cfg->is_de3) { +- sun8i_de3_ccsc_enable(mixer->engine.regs, layer, enable); ++ sun8i_de3_ccsc_setup(mixer->engine.regs, layer, ++ fmt_type, encoding, range); + return; + } + + base = ccsc_base[mixer->cfg->ccsc][layer]; + +- sun8i_csc_enable(mixer->engine.regs, base, enable); ++ sun8i_csc_setup(mixer->engine.regs, base, ++ fmt_type, encoding, range); + } +diff --git a/drivers/gpu/drm/sun4i/sun8i_csc.h b/drivers/gpu/drm/sun4i/sun8i_csc.h +index 7322770f39f0..b7546e06e315 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_csc.h ++++ b/drivers/gpu/drm/sun4i/sun8i_csc.h +@@ -28,10 +28,9 @@ enum format_type { + FORMAT_TYPE_YVU, + }; + +-void sun8i_csc_set_ccsc_coefficients(struct sun8i_mixer *mixer, int layer, +- enum format_type fmt_type, +- enum drm_color_encoding encoding, +- enum drm_color_range range); +-void sun8i_csc_enable_ccsc(struct sun8i_mixer *mixer, int layer, bool enable); ++void sun8i_csc_set_ccsc(struct sun8i_mixer *mixer, int layer, ++ enum format_type fmt_type, ++ enum drm_color_encoding encoding, ++ enum drm_color_range range); + + #endif +diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c +index 8a80934e928f..f3a5329351ca 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c ++++ b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c +@@ -232,14 +232,9 @@ static int sun8i_vi_layer_update_formats(struct sun8i_mixer *mixer, int channel, + SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_MASK, val); + + fmt_type = sun8i_vi_layer_get_format_type(fmt); +- if (fmt_type != FORMAT_TYPE_RGB) { +- sun8i_csc_set_ccsc_coefficients(mixer, channel, fmt_type, +- state->color_encoding, +- state->color_range); +- sun8i_csc_enable_ccsc(mixer, channel, true); +- } else { +- sun8i_csc_enable_ccsc(mixer, channel, false); +- } ++ sun8i_csc_set_ccsc(mixer, channel, fmt_type, ++ state->color_encoding, ++ state->color_range); + + if (!fmt->is_yuv) + val = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de2-de3-add-generic-blender-register-reference-functi.patch b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de2-de3-add-generic-blender-register-reference-functi.patch new file mode 100644 index 000000000000..2757ba5e21fd --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de2-de3-add-generic-blender-register-reference-functi.patch @@ -0,0 +1,38 @@ +From 45d06599927825fe1fa3c374508d6d0c8c9f9f52 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sun, 29 Sep 2024 22:04:48 +1300 +Subject: drm: sun4i: de2/de3: add generic blender register reference function + +The DE2 and DE3 engines have a blender register range within the +mixer engine register map, whereas the DE33 separates this out into +a separate display group. + +Prepare for this by adding a function to look the blender reference up, +with a subsequent patch to add a conditional based on the DE type. + +Signed-off-by: Jernej Skrabec +Signed-off-by: Ryan Walklin +--- + drivers/gpu/drm/sun4i/sun8i_mixer.h | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h b/drivers/gpu/drm/sun4i/sun8i_mixer.h +index 82956cb97cfd..75facc7d1fa6 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_mixer.h ++++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h +@@ -224,6 +224,12 @@ sun8i_blender_base(struct sun8i_mixer *mixer) + return mixer->cfg->de_type == sun8i_mixer_de3 ? DE3_BLD_BASE : DE2_BLD_BASE; + } + ++static inline struct regmap * ++sun8i_blender_regmap(struct sun8i_mixer *mixer) ++{ ++ return mixer->engine.regs; ++} ++ + static inline u32 + sun8i_channel_base(struct sun8i_mixer *mixer, int channel) + { +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de2-de3-add-mixer-version-enum.patch b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de2-de3-add-mixer-version-enum.patch new file mode 100644 index 000000000000..0df4a242c2ee --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de2-de3-add-mixer-version-enum.patch @@ -0,0 +1,257 @@ +From b49e4fb3439c50eb6effc559366b3e88e2ac2f27 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sun, 29 Sep 2024 22:04:45 +1300 +Subject: drm: sun4i: de2/de3: add mixer version enum + +The Allwinner DE2 and DE3 display engine mixers are currently identified +by a simple boolean flag. This will not scale to support additional DE +variants. + +Convert the boolean flag to an enum. + +Signed-off-by: Jernej Skrabec +Signed-off-by: Ryan Walklin +Reviewed-by: Andre Przywara +--- + drivers/gpu/drm/sun4i/sun8i_csc.c | 2 +- + drivers/gpu/drm/sun4i/sun8i_mixer.c | 14 ++++++++++++-- + drivers/gpu/drm/sun4i/sun8i_mixer.h | 11 ++++++++--- + drivers/gpu/drm/sun4i/sun8i_ui_scaler.c | 2 +- + drivers/gpu/drm/sun4i/sun8i_vi_layer.c | 8 ++++---- + drivers/gpu/drm/sun4i/sun8i_vi_scaler.c | 4 ++-- + 6 files changed, 28 insertions(+), 13 deletions(-) + +diff --git a/drivers/gpu/drm/sun4i/sun8i_csc.c b/drivers/gpu/drm/sun4i/sun8i_csc.c +index e12a81fa9108..2d5a2cf7cba2 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_csc.c ++++ b/drivers/gpu/drm/sun4i/sun8i_csc.c +@@ -365,7 +365,7 @@ void sun8i_csc_set_ccsc(struct sun8i_mixer *mixer, int layer, + { + u32 base; + +- if (mixer->cfg->is_de3) { ++ if (mixer->cfg->de_type == sun8i_mixer_de3) { + sun8i_de3_ccsc_setup(&mixer->engine, layer, + fmt_type, encoding, range); + return; +diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c +index a50c583852ed..16e018aa4aae 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_mixer.c ++++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c +@@ -584,7 +584,7 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master, + base = sun8i_blender_base(mixer); + + /* Reset registers and disable unused sub-engines */ +- if (mixer->cfg->is_de3) { ++ if (mixer->cfg->de_type == sun8i_mixer_de3) { + for (i = 0; i < DE3_MIXER_UNIT_SIZE; i += 4) + regmap_write(mixer->engine.regs, i, 0); + +@@ -675,6 +675,7 @@ static void sun8i_mixer_remove(struct platform_device *pdev) + + static const struct sun8i_mixer_cfg sun8i_a83t_mixer0_cfg = { + .ccsc = CCSC_MIXER0_LAYOUT, ++ .de_type = sun8i_mixer_de2, + .scaler_mask = 0xf, + .scanline_yuv = 2048, + .ui_num = 3, +@@ -683,6 +684,7 @@ static const struct sun8i_mixer_cfg sun8i_a83t_mixer0_cfg = { + + static const struct sun8i_mixer_cfg sun8i_a83t_mixer1_cfg = { + .ccsc = CCSC_MIXER1_LAYOUT, ++ .de_type = sun8i_mixer_de2, + .scaler_mask = 0x3, + .scanline_yuv = 2048, + .ui_num = 1, +@@ -691,6 +693,7 @@ static const struct sun8i_mixer_cfg sun8i_a83t_mixer1_cfg = { + + static const struct sun8i_mixer_cfg sun8i_h3_mixer0_cfg = { + .ccsc = CCSC_MIXER0_LAYOUT, ++ .de_type = sun8i_mixer_de2, + .mod_rate = 432000000, + .scaler_mask = 0xf, + .scanline_yuv = 2048, +@@ -700,6 +703,7 @@ static const struct sun8i_mixer_cfg sun8i_h3_mixer0_cfg = { + + static const struct sun8i_mixer_cfg sun8i_r40_mixer0_cfg = { + .ccsc = CCSC_MIXER0_LAYOUT, ++ .de_type = sun8i_mixer_de2, + .mod_rate = 297000000, + .scaler_mask = 0xf, + .scanline_yuv = 2048, +@@ -709,6 +713,7 @@ static const struct sun8i_mixer_cfg sun8i_r40_mixer0_cfg = { + + static const struct sun8i_mixer_cfg sun8i_r40_mixer1_cfg = { + .ccsc = CCSC_MIXER1_LAYOUT, ++ .de_type = sun8i_mixer_de2, + .mod_rate = 297000000, + .scaler_mask = 0x3, + .scanline_yuv = 2048, +@@ -717,6 +722,7 @@ static const struct sun8i_mixer_cfg sun8i_r40_mixer1_cfg = { + }; + + static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = { ++ .de_type = sun8i_mixer_de2, + .vi_num = 2, + .ui_num = 1, + .scaler_mask = 0x3, +@@ -727,6 +733,7 @@ static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = { + + static const struct sun8i_mixer_cfg sun20i_d1_mixer0_cfg = { + .ccsc = CCSC_D1_MIXER0_LAYOUT, ++ .de_type = sun8i_mixer_de2, + .mod_rate = 297000000, + .scaler_mask = 0x3, + .scanline_yuv = 2048, +@@ -736,6 +743,7 @@ static const struct sun8i_mixer_cfg sun20i_d1_mixer0_cfg = { + + static const struct sun8i_mixer_cfg sun20i_d1_mixer1_cfg = { + .ccsc = CCSC_MIXER1_LAYOUT, ++ .de_type = sun8i_mixer_de2, + .mod_rate = 297000000, + .scaler_mask = 0x1, + .scanline_yuv = 1024, +@@ -745,6 +753,7 @@ static const struct sun8i_mixer_cfg sun20i_d1_mixer1_cfg = { + + static const struct sun8i_mixer_cfg sun50i_a64_mixer0_cfg = { + .ccsc = CCSC_MIXER0_LAYOUT, ++ .de_type = sun8i_mixer_de2, + .mod_rate = 297000000, + .scaler_mask = 0xf, + .scanline_yuv = 4096, +@@ -754,6 +763,7 @@ static const struct sun8i_mixer_cfg sun50i_a64_mixer0_cfg = { + + static const struct sun8i_mixer_cfg sun50i_a64_mixer1_cfg = { + .ccsc = CCSC_MIXER1_LAYOUT, ++ .de_type = sun8i_mixer_de2, + .mod_rate = 297000000, + .scaler_mask = 0x3, + .scanline_yuv = 2048, +@@ -763,7 +773,7 @@ static const struct sun8i_mixer_cfg sun50i_a64_mixer1_cfg = { + + static const struct sun8i_mixer_cfg sun50i_h6_mixer0_cfg = { + .ccsc = CCSC_MIXER0_LAYOUT, +- .is_de3 = true, ++ .de_type = sun8i_mixer_de3, + .has_formatter = 1, + .mod_rate = 600000000, + .scaler_mask = 0xf, +diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h b/drivers/gpu/drm/sun4i/sun8i_mixer.h +index 8417b8fef2e1..82956cb97cfd 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_mixer.h ++++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h +@@ -151,6 +151,11 @@ enum { + CCSC_D1_MIXER0_LAYOUT, + }; + ++enum sun8i_mixer_type { ++ sun8i_mixer_de2, ++ sun8i_mixer_de3, ++}; ++ + /** + * struct sun8i_mixer_cfg - mixer HW configuration + * @vi_num: number of VI channels +@@ -172,7 +177,7 @@ struct sun8i_mixer_cfg { + int scaler_mask; + int ccsc; + unsigned long mod_rate; +- unsigned int is_de3 : 1; ++ unsigned int de_type; + unsigned int has_formatter : 1; + unsigned int scanline_yuv; + }; +@@ -216,13 +221,13 @@ engine_to_sun8i_mixer(struct sunxi_engine *engine) + static inline u32 + sun8i_blender_base(struct sun8i_mixer *mixer) + { +- return mixer->cfg->is_de3 ? DE3_BLD_BASE : DE2_BLD_BASE; ++ return mixer->cfg->de_type == sun8i_mixer_de3 ? DE3_BLD_BASE : DE2_BLD_BASE; + } + + static inline u32 + sun8i_channel_base(struct sun8i_mixer *mixer, int channel) + { +- if (mixer->cfg->is_de3) ++ if (mixer->cfg->de_type == sun8i_mixer_de3) + return DE3_CH_BASE + channel * DE3_CH_SIZE; + else + return DE2_CH_BASE + channel * DE2_CH_SIZE; +diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_scaler.c b/drivers/gpu/drm/sun4i/sun8i_ui_scaler.c +index ae0806bccac7..504ffa0971a4 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_ui_scaler.c ++++ b/drivers/gpu/drm/sun4i/sun8i_ui_scaler.c +@@ -93,7 +93,7 @@ static u32 sun8i_ui_scaler_base(struct sun8i_mixer *mixer, int channel) + { + int vi_num = mixer->cfg->vi_num; + +- if (mixer->cfg->is_de3) ++ if (mixer->cfg->de_type == sun8i_mixer_de3) + return DE3_VI_SCALER_UNIT_BASE + + DE3_VI_SCALER_UNIT_SIZE * vi_num + + DE3_UI_SCALER_UNIT_SIZE * (channel - vi_num); +diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c +index 3c657b069d1f..4647e9bcccaa 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c ++++ b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c +@@ -25,7 +25,7 @@ static void sun8i_vi_layer_update_alpha(struct sun8i_mixer *mixer, int channel, + + ch_base = sun8i_channel_base(mixer, channel); + +- if (mixer->cfg->is_de3) { ++ if (mixer->cfg->de_type >= sun8i_mixer_de3) { + mask = SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MASK | + SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MODE_MASK; + val = SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA +@@ -483,7 +483,7 @@ struct sun8i_layer *sun8i_vi_layer_init_one(struct drm_device *drm, + layer->channel = index; + layer->overlay = 0; + +- if (mixer->cfg->is_de3) { ++ if (mixer->cfg->de_type >= sun8i_mixer_de3) { + formats = sun8i_vi_layer_de3_formats; + format_count = ARRAY_SIZE(sun8i_vi_layer_de3_formats); + } else { +@@ -507,7 +507,7 @@ struct sun8i_layer *sun8i_vi_layer_init_one(struct drm_device *drm, + + plane_cnt = mixer->cfg->ui_num + mixer->cfg->vi_num; + +- if (mixer->cfg->vi_num == 1 || mixer->cfg->is_de3) { ++ if (mixer->cfg->vi_num == 1 || mixer->cfg->de_type >= sun8i_mixer_de3) { + ret = drm_plane_create_alpha_property(&layer->plane); + if (ret) { + dev_err(drm->dev, "Couldn't add alpha property\n"); +@@ -524,7 +524,7 @@ struct sun8i_layer *sun8i_vi_layer_init_one(struct drm_device *drm, + + supported_encodings = BIT(DRM_COLOR_YCBCR_BT601) | + BIT(DRM_COLOR_YCBCR_BT709); +- if (mixer->cfg->is_de3) ++ if (mixer->cfg->de_type >= sun8i_mixer_de3) + supported_encodings |= BIT(DRM_COLOR_YCBCR_BT2020); + + supported_ranges = BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) | +diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c +index 2e49a6e5f1f1..aa346c3beb30 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c ++++ b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c +@@ -835,7 +835,7 @@ static const u32 bicubic4coefftab32[480] = { + + static u32 sun8i_vi_scaler_base(struct sun8i_mixer *mixer, int channel) + { +- if (mixer->cfg->is_de3) ++ if (mixer->cfg->de_type == sun8i_mixer_de3) + return DE3_VI_SCALER_UNIT_BASE + + DE3_VI_SCALER_UNIT_SIZE * channel; + else +@@ -982,7 +982,7 @@ void sun8i_vi_scaler_setup(struct sun8i_mixer *mixer, int layer, + cvphase = vphase; + } + +- if (mixer->cfg->is_de3) { ++ if (mixer->cfg->de_type >= sun8i_mixer_de3) { + u32 val; + + if (format->hsub == 1 && format->vsub == 1) +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de2-de3-call-csc-setup-also-for-UI-layer.patch b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de2-de3-call-csc-setup-also-for-UI-layer.patch new file mode 100644 index 000000000000..96847a05dce8 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de2-de3-call-csc-setup-also-for-UI-layer.patch @@ -0,0 +1,64 @@ +From 000c586a34ad82e4673e6dfda5457147b0d85606 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sun, 29 Sep 2024 22:04:35 +1300 +Subject: drm: sun4i: de2/de3: call csc setup also for UI layer + +Currently, only VI layer calls CSC setup function. This comes from DE2 +limitation, which doesn't have CSC unit for UI layers. However, DE3 has +separate CSC units for each layer. This allows display pipeline to make +output signal in different color spaces. To support both use cases, add +a call to CSC setup function also in UI layer code. For DE2, this will +be a no-op, but it will allow DE3 to output signal in multiple formats. + +Signed-off-by: Jernej Skrabec +Signed-off-by: Ryan Walklin +--- + drivers/gpu/drm/sun4i/sun8i_csc.c | 8 +++++--- + drivers/gpu/drm/sun4i/sun8i_ui_layer.c | 6 ++++++ + 2 files changed, 11 insertions(+), 3 deletions(-) + +diff --git a/drivers/gpu/drm/sun4i/sun8i_csc.c b/drivers/gpu/drm/sun4i/sun8i_csc.c +index 0dcbc0866ae8..68d955c63b05 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_csc.c ++++ b/drivers/gpu/drm/sun4i/sun8i_csc.c +@@ -209,8 +209,10 @@ void sun8i_csc_set_ccsc(struct sun8i_mixer *mixer, int layer, + return; + } + +- base = ccsc_base[mixer->cfg->ccsc][layer]; ++ if (layer < mixer->cfg->vi_num) { ++ base = ccsc_base[mixer->cfg->ccsc][layer]; + +- sun8i_csc_setup(mixer->engine.regs, base, +- fmt_type, encoding, range); ++ sun8i_csc_setup(mixer->engine.regs, base, ++ fmt_type, encoding, range); ++ } + } +diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c +index b90e5edef4e8..aa987bca1dbb 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c ++++ b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c +@@ -20,6 +20,7 @@ + #include + #include + ++#include "sun8i_csc.h" + #include "sun8i_mixer.h" + #include "sun8i_ui_layer.h" + #include "sun8i_ui_scaler.h" +@@ -135,6 +136,11 @@ static int sun8i_ui_layer_update_formats(struct sun8i_mixer *mixer, int channel, + SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch_base, overlay), + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val); + ++ /* Note: encoding and range arguments are ignored for RGB */ ++ sun8i_csc_set_ccsc(mixer, channel, FORMAT_TYPE_RGB, ++ DRM_COLOR_YCBCR_BT601, ++ DRM_COLOR_YCBCR_FULL_RANGE); ++ + return 0; + } + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de2-de3-refactor-mixer-initialisation.patch b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de2-de3-refactor-mixer-initialisation.patch new file mode 100644 index 000000000000..4357b5b775ab --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de2-de3-refactor-mixer-initialisation.patch @@ -0,0 +1,121 @@ +From 18890b5c9dbf9270b7f0e42875d6b8bd14ee6624 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sun, 29 Sep 2024 22:04:46 +1300 +Subject: drm: sun4i: de2/de3: refactor mixer initialisation + +Now that the DE variant can be selected by enum, take the oppportunity +to factor out some common initialisation code to a separate function. + +Signed-off-by: Jernej Skrabec +Signed-off-by: Ryan Walklin +Reviewed-by: Andre Przywara +--- + drivers/gpu/drm/sun4i/sun8i_mixer.c | 64 +++++++++++++++-------------- + 1 file changed, 34 insertions(+), 30 deletions(-) + +diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c +index 16e018aa4aae..18745af08954 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_mixer.c ++++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c +@@ -468,6 +468,38 @@ static int sun8i_mixer_of_get_id(struct device_node *node) + return of_ep.id; + } + ++static void sun8i_mixer_init(struct sun8i_mixer *mixer) ++{ ++ unsigned int base = sun8i_blender_base(mixer); ++ int plane_cnt, i; ++ ++ /* Enable the mixer */ ++ regmap_write(mixer->engine.regs, SUN8I_MIXER_GLOBAL_CTL, ++ SUN8I_MIXER_GLOBAL_CTL_RT_EN); ++ ++ /* Set background color to black */ ++ regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_BKCOLOR(base), ++ SUN8I_MIXER_BLEND_COLOR_BLACK); ++ ++ /* ++ * Set fill color of bottom plane to black. Generally not needed ++ * except when VI plane is at bottom (zpos = 0) and enabled. ++ */ ++ regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL(base), ++ SUN8I_MIXER_BLEND_PIPE_CTL_FC_EN(0)); ++ regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_ATTR_FCOLOR(base, 0), ++ SUN8I_MIXER_BLEND_COLOR_BLACK); ++ ++ plane_cnt = mixer->cfg->vi_num + mixer->cfg->ui_num; ++ for (i = 0; i < plane_cnt; i++) ++ regmap_write(mixer->engine.regs, ++ SUN8I_MIXER_BLEND_MODE(base, i), ++ SUN8I_MIXER_BLEND_MODE_DEF); ++ ++ regmap_update_bits(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL(base), ++ SUN8I_MIXER_BLEND_PIPE_CTL_EN_MSK, 0); ++} ++ + static int sun8i_mixer_bind(struct device *dev, struct device *master, + void *data) + { +@@ -476,8 +508,6 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master, + struct sun4i_drv *drv = drm->dev_private; + struct sun8i_mixer *mixer; + void __iomem *regs; +- unsigned int base; +- int plane_cnt; + int i, ret; + + /* +@@ -581,8 +611,6 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master, + + list_add_tail(&mixer->engine.list, &drv->engine_list); + +- base = sun8i_blender_base(mixer); +- + /* Reset registers and disable unused sub-engines */ + if (mixer->cfg->de_type == sun8i_mixer_de3) { + for (i = 0; i < DE3_MIXER_UNIT_SIZE; i += 4) +@@ -598,7 +626,7 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master, + regmap_write(mixer->engine.regs, SUN50I_MIXER_FMT_EN, 0); + regmap_write(mixer->engine.regs, SUN50I_MIXER_CDC0_EN, 0); + regmap_write(mixer->engine.regs, SUN50I_MIXER_CDC1_EN, 0); +- } else { ++ } else if (mixer->cfg->de_type == sun8i_mixer_de2) { + for (i = 0; i < DE2_MIXER_UNIT_SIZE; i += 4) + regmap_write(mixer->engine.regs, i, 0); + +@@ -611,31 +639,7 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master, + regmap_write(mixer->engine.regs, SUN8I_MIXER_DCSC_EN, 0); + } + +- /* Enable the mixer */ +- regmap_write(mixer->engine.regs, SUN8I_MIXER_GLOBAL_CTL, +- SUN8I_MIXER_GLOBAL_CTL_RT_EN); +- +- /* Set background color to black */ +- regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_BKCOLOR(base), +- SUN8I_MIXER_BLEND_COLOR_BLACK); +- +- /* +- * Set fill color of bottom plane to black. Generally not needed +- * except when VI plane is at bottom (zpos = 0) and enabled. +- */ +- regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL(base), +- SUN8I_MIXER_BLEND_PIPE_CTL_FC_EN(0)); +- regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_ATTR_FCOLOR(base, 0), +- SUN8I_MIXER_BLEND_COLOR_BLACK); +- +- plane_cnt = mixer->cfg->vi_num + mixer->cfg->ui_num; +- for (i = 0; i < plane_cnt; i++) +- regmap_write(mixer->engine.regs, +- SUN8I_MIXER_BLEND_MODE(base, i), +- SUN8I_MIXER_BLEND_MODE_DEF); +- +- regmap_update_bits(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL(base), +- SUN8I_MIXER_BLEND_PIPE_CTL_EN_MSK, 0); ++ sun8i_mixer_init(mixer); + + return 0; + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de2-de3-use-generic-register-reference-function-for-l.patch b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de2-de3-use-generic-register-reference-function-for-l.patch new file mode 100644 index 000000000000..ae503d937cb6 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de2-de3-use-generic-register-reference-function-for-l.patch @@ -0,0 +1,117 @@ +From a756d6b4ac645ac3c18d5758faec068b3c8819a6 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sun, 29 Sep 2024 22:04:49 +1300 +Subject: drm: sun4i: de2/de3: use generic register reference function for + layer configuration + +Use the new blender register lookup function where required in the layer +commit and update code. + +Signed-off-by: Jernej Skrabec +Signed-off-by: Ryan Walklin +--- + drivers/gpu/drm/sun4i/sun8i_mixer.c | 5 +++-- + drivers/gpu/drm/sun4i/sun8i_ui_layer.c | 7 +++++-- + drivers/gpu/drm/sun4i/sun8i_vi_layer.c | 6 ++++-- + 3 files changed, 12 insertions(+), 6 deletions(-) + +diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c +index 18745af08954..600084286b39 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_mixer.c ++++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c +@@ -277,6 +277,7 @@ static void sun8i_mixer_commit(struct sunxi_engine *engine, + { + struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine); + u32 bld_base = sun8i_blender_base(mixer); ++ struct regmap *bld_regs = sun8i_blender_regmap(mixer); + struct drm_plane_state *plane_state; + struct drm_plane *plane; + u32 route = 0, pipe_en = 0; +@@ -316,8 +317,8 @@ static void sun8i_mixer_commit(struct sunxi_engine *engine, + pipe_en |= SUN8I_MIXER_BLEND_PIPE_CTL_EN(zpos); + } + +- regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_ROUTE(bld_base), route); +- regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL(bld_base), ++ regmap_write(bld_regs, SUN8I_MIXER_BLEND_ROUTE(bld_base), route); ++ regmap_write(bld_regs, SUN8I_MIXER_BLEND_PIPE_CTL(bld_base), + pipe_en | SUN8I_MIXER_BLEND_PIPE_CTL_FC_EN(0)); + + regmap_write(engine->regs, SUN8I_MIXER_GLOBAL_DBUFF, +diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c +index cb9b694fef10..7f1231cf0f01 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c ++++ b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c +@@ -24,6 +24,7 @@ + #include "sun8i_mixer.h" + #include "sun8i_ui_layer.h" + #include "sun8i_ui_scaler.h" ++#include "sun8i_vi_scaler.h" + + static void sun8i_ui_layer_update_alpha(struct sun8i_mixer *mixer, int channel, + int overlay, struct drm_plane *plane) +@@ -52,6 +53,7 @@ static int sun8i_ui_layer_update_coord(struct sun8i_mixer *mixer, int channel, + { + struct drm_plane_state *state = plane->state; + u32 src_w, src_h, dst_w, dst_h; ++ struct regmap *bld_regs; + u32 bld_base, ch_base; + u32 outsize, insize; + u32 hphase, vphase; +@@ -60,6 +62,7 @@ static int sun8i_ui_layer_update_coord(struct sun8i_mixer *mixer, int channel, + channel, overlay); + + bld_base = sun8i_blender_base(mixer); ++ bld_regs = sun8i_blender_regmap(mixer); + ch_base = sun8i_channel_base(mixer, channel); + + src_w = drm_rect_width(&state->src) >> 16; +@@ -104,10 +107,10 @@ static int sun8i_ui_layer_update_coord(struct sun8i_mixer *mixer, int channel, + DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n", + state->dst.x1, state->dst.y1); + DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h); +- regmap_write(mixer->engine.regs, ++ regmap_write(bld_regs, + SUN8I_MIXER_BLEND_ATTR_COORD(bld_base, zpos), + SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1)); +- regmap_write(mixer->engine.regs, ++ regmap_write(bld_regs, + SUN8I_MIXER_BLEND_ATTR_INSIZE(bld_base, zpos), + outsize); + +diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c +index e348fd0a3d81..d19349eecc9d 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c ++++ b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c +@@ -55,6 +55,7 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel, + struct drm_plane_state *state = plane->state; + const struct drm_format_info *format = state->fb->format; + u32 src_w, src_h, dst_w, dst_h; ++ struct regmap *bld_regs; + u32 bld_base, ch_base; + u32 outsize, insize; + u32 hphase, vphase; +@@ -66,6 +67,7 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel, + channel, overlay); + + bld_base = sun8i_blender_base(mixer); ++ bld_regs = sun8i_blender_regmap(mixer); + ch_base = sun8i_channel_base(mixer, channel); + + src_w = drm_rect_width(&state->src) >> 16; +@@ -182,10 +184,10 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel, + DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n", + state->dst.x1, state->dst.y1); + DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h); +- regmap_write(mixer->engine.regs, ++ regmap_write(bld_regs, + SUN8I_MIXER_BLEND_ATTR_COORD(bld_base, zpos), + SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1)); +- regmap_write(mixer->engine.regs, ++ regmap_write(bld_regs, + SUN8I_MIXER_BLEND_ATTR_INSIZE(bld_base, zpos), + outsize); + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de3-Add-YUV-formatter-module.patch b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de3-Add-YUV-formatter-module.patch new file mode 100644 index 000000000000..14f17b6d5d69 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de3-Add-YUV-formatter-module.patch @@ -0,0 +1,164 @@ +From 8bdcc131fedb576a8db65bb6e87ca8742660add0 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sun, 29 Sep 2024 22:04:37 +1300 +Subject: drm: sun4i: de3: Add YUV formatter module + +The display engine formatter (FMT) module is present in the DE3 engine +and provides YUV444 to YUV422/YUV420 conversion, format re-mapping and +color depth conversion. + +Add support for this module. + +Signed-off-by: Jernej Skrabec +Signed-off-by: Ryan Walklin +--- + drivers/gpu/drm/sun4i/Makefile | 3 +- + drivers/gpu/drm/sun4i/sun50i_fmt.c | 82 ++++++++++++++++++++++++++++++ + drivers/gpu/drm/sun4i/sun50i_fmt.h | 32 ++++++++++++ + 3 files changed, 116 insertions(+), 1 deletion(-) + create mode 100644 drivers/gpu/drm/sun4i/sun50i_fmt.c + create mode 100644 drivers/gpu/drm/sun4i/sun50i_fmt.h + +diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile +index bad7497a0d11..3f516329f51e 100644 +--- a/drivers/gpu/drm/sun4i/Makefile ++++ b/drivers/gpu/drm/sun4i/Makefile +@@ -16,7 +16,8 @@ sun8i-drm-hdmi-y += sun8i_hdmi_phy_clk.o + + sun8i-mixer-y += sun8i_mixer.o sun8i_ui_layer.o \ + sun8i_vi_layer.o sun8i_ui_scaler.o \ +- sun8i_vi_scaler.o sun8i_csc.o ++ sun8i_vi_scaler.o sun8i_csc.o \ ++ sun50i_fmt.o + + sun4i-tcon-y += sun4i_crtc.o + sun4i-tcon-y += sun4i_tcon_dclk.o +diff --git a/drivers/gpu/drm/sun4i/sun50i_fmt.c b/drivers/gpu/drm/sun4i/sun50i_fmt.c +new file mode 100644 +index 000000000000..050a8716ae86 +--- /dev/null ++++ b/drivers/gpu/drm/sun4i/sun50i_fmt.c +@@ -0,0 +1,82 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * Copyright (C) Jernej Skrabec ++ */ ++ ++#include ++ ++#include "sun50i_fmt.h" ++ ++static bool sun50i_fmt_is_10bit(u32 format) ++{ ++ switch (format) { ++ case MEDIA_BUS_FMT_RGB101010_1X30: ++ case MEDIA_BUS_FMT_YUV10_1X30: ++ case MEDIA_BUS_FMT_UYYVYY10_0_5X30: ++ case MEDIA_BUS_FMT_UYVY10_1X20: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++static u32 sun50i_fmt_get_colorspace(u32 format) ++{ ++ switch (format) { ++ case MEDIA_BUS_FMT_UYYVYY8_0_5X24: ++ case MEDIA_BUS_FMT_UYYVYY10_0_5X30: ++ return SUN50I_FMT_CS_YUV420; ++ case MEDIA_BUS_FMT_UYVY8_1X16: ++ case MEDIA_BUS_FMT_UYVY10_1X20: ++ return SUN50I_FMT_CS_YUV422; ++ default: ++ return SUN50I_FMT_CS_YUV444RGB; ++ } ++} ++ ++static void sun50i_fmt_de3_limits(u32 *limits, u32 colorspace, bool bit10) ++{ ++ if (colorspace != SUN50I_FMT_CS_YUV444RGB) { ++ limits[0] = SUN50I_FMT_LIMIT(64, 940); ++ limits[1] = SUN50I_FMT_LIMIT(64, 960); ++ limits[2] = SUN50I_FMT_LIMIT(64, 960); ++ } else if (bit10) { ++ limits[0] = SUN50I_FMT_LIMIT(0, 1023); ++ limits[1] = SUN50I_FMT_LIMIT(0, 1023); ++ limits[2] = SUN50I_FMT_LIMIT(0, 1023); ++ } else { ++ limits[0] = SUN50I_FMT_LIMIT(0, 1021); ++ limits[1] = SUN50I_FMT_LIMIT(0, 1021); ++ limits[2] = SUN50I_FMT_LIMIT(0, 1021); ++ } ++} ++ ++void sun50i_fmt_setup(struct sun8i_mixer *mixer, u16 width, ++ u16 height, u32 format) ++{ ++ u32 colorspace, limit[3], base; ++ struct regmap *regs; ++ bool bit10; ++ ++ colorspace = sun50i_fmt_get_colorspace(format); ++ bit10 = sun50i_fmt_is_10bit(format); ++ base = SUN50I_FMT_DE3; ++ regs = sun8i_blender_regmap(mixer); ++ ++ sun50i_fmt_de3_limits(limit, colorspace, bit10); ++ ++ regmap_write(regs, SUN50I_FMT_CTRL(base), 0); ++ ++ regmap_write(regs, SUN50I_FMT_SIZE(base), ++ SUN8I_MIXER_SIZE(width, height)); ++ regmap_write(regs, SUN50I_FMT_SWAP(base), 0); ++ regmap_write(regs, SUN50I_FMT_DEPTH(base), bit10); ++ regmap_write(regs, SUN50I_FMT_FORMAT(base), colorspace); ++ regmap_write(regs, SUN50I_FMT_COEF(base), 0); ++ ++ regmap_write(regs, SUN50I_FMT_LMT_Y(base), limit[0]); ++ regmap_write(regs, SUN50I_FMT_LMT_C0(base), limit[1]); ++ regmap_write(regs, SUN50I_FMT_LMT_C1(base), limit[2]); ++ ++ regmap_write(regs, SUN50I_FMT_CTRL(base), 1); ++} +diff --git a/drivers/gpu/drm/sun4i/sun50i_fmt.h b/drivers/gpu/drm/sun4i/sun50i_fmt.h +new file mode 100644 +index 000000000000..4127f7206aad +--- /dev/null ++++ b/drivers/gpu/drm/sun4i/sun50i_fmt.h +@@ -0,0 +1,32 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * Copyright (C) Jernej Skrabec ++ */ ++ ++#ifndef _SUN50I_FMT_H_ ++#define _SUN50I_FMT_H_ ++ ++#include "sun8i_mixer.h" ++ ++#define SUN50I_FMT_DE3 0xa8000 ++ ++#define SUN50I_FMT_CTRL(base) ((base) + 0x00) ++#define SUN50I_FMT_SIZE(base) ((base) + 0x04) ++#define SUN50I_FMT_SWAP(base) ((base) + 0x08) ++#define SUN50I_FMT_DEPTH(base) ((base) + 0x0c) ++#define SUN50I_FMT_FORMAT(base) ((base) + 0x10) ++#define SUN50I_FMT_COEF(base) ((base) + 0x14) ++#define SUN50I_FMT_LMT_Y(base) ((base) + 0x20) ++#define SUN50I_FMT_LMT_C0(base) ((base) + 0x24) ++#define SUN50I_FMT_LMT_C1(base) ((base) + 0x28) ++ ++#define SUN50I_FMT_LIMIT(low, high) (((high) << 16) | (low)) ++ ++#define SUN50I_FMT_CS_YUV444RGB 0 ++#define SUN50I_FMT_CS_YUV422 1 ++#define SUN50I_FMT_CS_YUV420 2 ++ ++void sun50i_fmt_setup(struct sun8i_mixer *mixer, u16 width, ++ u16 height, u32 format); ++ ++#endif +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de3-Implement-AFBC-support.patch b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de3-Implement-AFBC-support.patch new file mode 100644 index 000000000000..e4d21564c020 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de3-Implement-AFBC-support.patch @@ -0,0 +1,569 @@ +From 0788787d1240dba85ecbbdb559cb46d413975656 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sun, 29 Sep 2024 22:04:50 +1300 +Subject: drm: sun4i: de3: Implement AFBC support + +Buffers, compressed with AFBC, are supported by the DE3 and above, and +are generally more efficient for memory transfers. Add support for them. + +Currently it's implemented only for VI layers, but vendor code and +documentation suggest UI layers can have them too. However, I haven't +observed any SoC with such feature. + +Signed-off-by: Jernej Skrabec +Signed-off-by: Ryan Walklin +--- + drivers/gpu/drm/sun4i/Makefile | 2 +- + drivers/gpu/drm/sun4i/sun50i_afbc.c | 250 +++++++++++++++++++++++++ + drivers/gpu/drm/sun4i/sun50i_afbc.h | 87 +++++++++ + drivers/gpu/drm/sun4i/sun8i_vi_layer.c | 84 +++++++-- + 4 files changed, 409 insertions(+), 14 deletions(-) + create mode 100644 drivers/gpu/drm/sun4i/sun50i_afbc.c + create mode 100644 drivers/gpu/drm/sun4i/sun50i_afbc.h + +diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile +index 3f516329f51e..78290f1660fb 100644 +--- a/drivers/gpu/drm/sun4i/Makefile ++++ b/drivers/gpu/drm/sun4i/Makefile +@@ -17,7 +17,7 @@ sun8i-drm-hdmi-y += sun8i_hdmi_phy_clk.o + sun8i-mixer-y += sun8i_mixer.o sun8i_ui_layer.o \ + sun8i_vi_layer.o sun8i_ui_scaler.o \ + sun8i_vi_scaler.o sun8i_csc.o \ +- sun50i_fmt.o ++ sun50i_fmt.o sun50i_afbc.o + + sun4i-tcon-y += sun4i_crtc.o + sun4i-tcon-y += sun4i_tcon_dclk.o +diff --git a/drivers/gpu/drm/sun4i/sun50i_afbc.c b/drivers/gpu/drm/sun4i/sun50i_afbc.c +new file mode 100644 +index 000000000000..b55e1c553371 +--- /dev/null ++++ b/drivers/gpu/drm/sun4i/sun50i_afbc.c +@@ -0,0 +1,250 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * Copyright (C) Jernej Skrabec ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "sun50i_afbc.h" ++#include "sun8i_mixer.h" ++ ++static u32 sun50i_afbc_get_base(struct sun8i_mixer *mixer, unsigned int channel) ++{ ++ u32 base = sun8i_channel_base(mixer, channel); ++ ++ if (mixer->cfg->de_type == sun8i_mixer_de3) ++ return base + SUN50I_AFBC_CH_OFFSET; ++ ++ return base + 0x4000; ++} ++ ++bool sun50i_afbc_format_mod_supported(struct sun8i_mixer *mixer, ++ u32 format, u64 modifier) ++{ ++ u64 mode; ++ ++ if (modifier == DRM_FORMAT_MOD_INVALID) ++ return false; ++ ++ if (modifier == DRM_FORMAT_MOD_LINEAR) { ++ if (format == DRM_FORMAT_YUV420_8BIT || ++ format == DRM_FORMAT_YUV420_10BIT || ++ format == DRM_FORMAT_Y210) ++ return false; ++ return true; ++ } ++ ++ if (mixer->cfg->de_type == sun8i_mixer_de2) ++ return false; ++ ++ mode = AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | ++ AFBC_FORMAT_MOD_SPARSE | ++ AFBC_FORMAT_MOD_SPLIT; ++ ++ switch (format) { ++ case DRM_FORMAT_RGBA8888: ++ case DRM_FORMAT_RGB888: ++ case DRM_FORMAT_RGB565: ++ case DRM_FORMAT_RGBA4444: ++ case DRM_FORMAT_RGBA5551: ++ case DRM_FORMAT_RGBA1010102: ++ mode |= AFBC_FORMAT_MOD_YTR; ++ break; ++ case DRM_FORMAT_YUYV: ++ case DRM_FORMAT_Y210: ++ case DRM_FORMAT_YUV420_8BIT: ++ case DRM_FORMAT_YUV420_10BIT: ++ break; ++ default: ++ return false; ++ } ++ ++ return modifier == DRM_FORMAT_MOD_ARM_AFBC(mode); ++} ++ ++void sun50i_afbc_atomic_update(struct sun8i_mixer *mixer, unsigned int channel, ++ struct drm_plane *plane) ++{ ++ struct drm_plane_state *state = plane->state; ++ struct drm_framebuffer *fb = state->fb; ++ const struct drm_format_info *format = fb->format; ++ struct drm_gem_dma_object *gem; ++ u32 base, val, src_w, src_h; ++ u32 def_color0, def_color1; ++ struct regmap *regs; ++ dma_addr_t dma_addr; ++ ++ base = sun50i_afbc_get_base(mixer, channel); ++ regs = mixer->engine.regs; ++ ++ src_w = drm_rect_width(&state->src) >> 16; ++ src_h = drm_rect_height(&state->src) >> 16; ++ ++ val = SUN50I_FBD_SIZE_HEIGHT(src_h); ++ val |= SUN50I_FBD_SIZE_WIDTH(src_w); ++ regmap_write(regs, SUN50I_FBD_SIZE(base), val); ++ ++ val = SUN50I_FBD_BLK_SIZE_HEIGHT(DIV_ROUND_UP(src_h, 16)); ++ val = SUN50I_FBD_BLK_SIZE_WIDTH(DIV_ROUND_UP(src_w, 16)); ++ regmap_write(regs, SUN50I_FBD_BLK_SIZE(base), val); ++ ++ val = SUN50I_FBD_SRC_CROP_TOP(0); ++ val |= SUN50I_FBD_SRC_CROP_LEFT(0); ++ regmap_write(regs, SUN50I_FBD_SRC_CROP(base), val); ++ ++ val = SUN50I_FBD_LAY_CROP_TOP(state->src.y1 >> 16); ++ val |= SUN50I_FBD_LAY_CROP_LEFT(state->src.x1 >> 16); ++ regmap_write(regs, SUN50I_FBD_LAY_CROP(base), val); ++ ++ /* ++ * Default color is always set to white, in colorspace and bitness ++ * that coresponds to used format. If it is actually used or not ++ * depends on AFBC buffer. At least in Cedrus it can be turned on ++ * or off. ++ * NOTE: G and B channels are off by 1 (up). It's unclear if this ++ * is because HW need such value or it is due to good enough code ++ * in vendor driver and HW clips the value anyway. ++ */ ++ def_color0 = 0; ++ def_color1 = 0; ++ ++ val = 0; ++ switch (format->format) { ++ case DRM_FORMAT_YUYV: ++ case DRM_FORMAT_YUV420_10BIT: ++ val |= SUN50I_FBD_FMT_SBS1(2); ++ val |= SUN50I_FBD_FMT_SBS0(1); ++ break; ++ case DRM_FORMAT_Y210: ++ val |= SUN50I_FBD_FMT_SBS1(3); ++ val |= SUN50I_FBD_FMT_SBS0(2); ++ break; ++ default: ++ val |= SUN50I_FBD_FMT_SBS1(1); ++ val |= SUN50I_FBD_FMT_SBS0(1); ++ break; ++ } ++ switch (format->format) { ++ case DRM_FORMAT_RGBA8888: ++ val |= SUN50I_FBD_FMT_YUV_TRAN; ++ val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_RGBA_8888); ++ def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(255) | ++ SUN50I_FBD_DEFAULT_COLOR0_YR(255); ++ def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(256) | ++ SUN50I_FBD_DEFAULT_COLOR1_VB(256); ++ break; ++ case DRM_FORMAT_RGB888: ++ val |= SUN50I_FBD_FMT_YUV_TRAN; ++ val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_RGB_888); ++ def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(0) | ++ SUN50I_FBD_DEFAULT_COLOR0_YR(255); ++ def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(256) | ++ SUN50I_FBD_DEFAULT_COLOR1_VB(256); ++ break; ++ case DRM_FORMAT_RGB565: ++ val |= SUN50I_FBD_FMT_YUV_TRAN; ++ val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_RGB_565); ++ def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(0) | ++ SUN50I_FBD_DEFAULT_COLOR0_YR(31); ++ def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(64) | ++ SUN50I_FBD_DEFAULT_COLOR1_VB(32); ++ break; ++ case DRM_FORMAT_RGBA4444: ++ val |= SUN50I_FBD_FMT_YUV_TRAN; ++ val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_RGBA_4444); ++ def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(15) | ++ SUN50I_FBD_DEFAULT_COLOR0_YR(15); ++ def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(16) | ++ SUN50I_FBD_DEFAULT_COLOR1_VB(16); ++ break; ++ case DRM_FORMAT_RGBA5551: ++ val |= SUN50I_FBD_FMT_YUV_TRAN; ++ val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_RGBA_5551); ++ def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(1) | ++ SUN50I_FBD_DEFAULT_COLOR0_YR(31); ++ def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(32) | ++ SUN50I_FBD_DEFAULT_COLOR1_VB(32); ++ break; ++ case DRM_FORMAT_RGBA1010102: ++ val |= SUN50I_FBD_FMT_YUV_TRAN; ++ val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_RGBA1010102); ++ def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(3) | ++ SUN50I_FBD_DEFAULT_COLOR0_YR(1023); ++ def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(1024) | ++ SUN50I_FBD_DEFAULT_COLOR1_VB(1024); ++ break; ++ case DRM_FORMAT_YUV420_8BIT: ++ val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_YUV420); ++ def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(0) | ++ SUN50I_FBD_DEFAULT_COLOR0_YR(255); ++ def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(128) | ++ SUN50I_FBD_DEFAULT_COLOR1_VB(128); ++ break; ++ case DRM_FORMAT_YUYV: ++ val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_YUV422); ++ def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(0) | ++ SUN50I_FBD_DEFAULT_COLOR0_YR(255); ++ def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(128) | ++ SUN50I_FBD_DEFAULT_COLOR1_VB(128); ++ break; ++ case DRM_FORMAT_YUV420_10BIT: ++ val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_P010); ++ def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(0) | ++ SUN50I_FBD_DEFAULT_COLOR0_YR(1023); ++ def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(512) | ++ SUN50I_FBD_DEFAULT_COLOR1_VB(512); ++ break; ++ case DRM_FORMAT_Y210: ++ val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_P210); ++ def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(0) | ++ SUN50I_FBD_DEFAULT_COLOR0_YR(1023); ++ def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(512) | ++ SUN50I_FBD_DEFAULT_COLOR1_VB(512); ++ break; ++ } ++ regmap_write(regs, SUN50I_FBD_FMT(base), val); ++ ++ /* Get the physical address of the buffer in memory */ ++ gem = drm_fb_dma_get_gem_obj(fb, 0); ++ ++ DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->dma_addr); ++ ++ /* Compute the start of the displayed memory */ ++ dma_addr = gem->dma_addr + fb->offsets[0]; ++ ++ regmap_write(regs, SUN50I_FBD_LADDR(base), lower_32_bits(dma_addr)); ++ regmap_write(regs, SUN50I_FBD_HADDR(base), upper_32_bits(dma_addr)); ++ ++ val = SUN50I_FBD_OVL_SIZE_HEIGHT(src_h); ++ val |= SUN50I_FBD_OVL_SIZE_WIDTH(src_w); ++ regmap_write(regs, SUN50I_FBD_OVL_SIZE(base), val); ++ ++ val = SUN50I_FBD_OVL_COOR_Y(0); ++ val |= SUN50I_FBD_OVL_COOR_X(0); ++ regmap_write(regs, SUN50I_FBD_OVL_COOR(base), val); ++ ++ regmap_write(regs, SUN50I_FBD_OVL_BG_COLOR(base), ++ SUN8I_MIXER_BLEND_COLOR_BLACK); ++ regmap_write(regs, SUN50I_FBD_DEFAULT_COLOR0(base), def_color0); ++ regmap_write(regs, SUN50I_FBD_DEFAULT_COLOR1(base), def_color1); ++ ++ val = SUN50I_FBD_CTL_GLB_ALPHA(state->alpha >> 16); ++ val |= SUN50I_FBD_CTL_CLK_GATE; ++ val |= (state->alpha == DRM_BLEND_ALPHA_OPAQUE) ? ++ SUN50I_FBD_CTL_ALPHA_MODE_PIXEL : ++ SUN50I_FBD_CTL_ALPHA_MODE_COMBINED; ++ val |= SUN50I_FBD_CTL_FBD_EN; ++ regmap_write(regs, SUN50I_FBD_CTL(base), val); ++} ++ ++void sun50i_afbc_disable(struct sun8i_mixer *mixer, unsigned int channel) ++{ ++ u32 base = sun50i_afbc_get_base(mixer, channel); ++ ++ regmap_write(mixer->engine.regs, SUN50I_FBD_CTL(base), 0); ++} +diff --git a/drivers/gpu/drm/sun4i/sun50i_afbc.h b/drivers/gpu/drm/sun4i/sun50i_afbc.h +new file mode 100644 +index 000000000000..cea685c86855 +--- /dev/null ++++ b/drivers/gpu/drm/sun4i/sun50i_afbc.h +@@ -0,0 +1,87 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * Copyright (C) Jernej Skrabec ++ */ ++ ++#ifndef _SUN50I_AFBC_H_ ++#define _SUN50I_AFBC_H_ ++ ++#include ++ ++#define SUN50I_AFBC_CH_OFFSET 0x300 ++ ++#define SUN50I_AFBC_RGBA_8888 0x02 ++#define SUN50I_AFBC_RGB_888 0x08 ++#define SUN50I_AFBC_RGB_565 0x0a ++#define SUN50I_AFBC_RGBA_4444 0x0e ++#define SUN50I_AFBC_RGBA_5551 0x12 ++#define SUN50I_AFBC_RGBA1010102 0x16 ++#define SUN50I_AFBC_YUV422 0x26 ++#define SUN50I_AFBC_YUV420 0x2a ++#define SUN50I_AFBC_P010 0x30 ++#define SUN50I_AFBC_P210 0x32 ++ ++#define SUN50I_FBD_CTL(base) ((base) + 0x00) ++#define SUN50I_FBD_CTL_GLB_ALPHA(v) ((v) << 24) ++#define SUN50I_FBD_CTL_CLK_GATE BIT(4) ++#define SUN50I_FBD_CTL_ALPHA_MODE_PIXEL ((0) << 2) ++#define SUN50I_FBD_CTL_ALPHA_MODE_LAYER ((1) << 2) ++#define SUN50I_FBD_CTL_ALPHA_MODE_COMBINED ((2) << 2) ++#define SUN50I_FBD_CTL_FBD_FCEN BIT(1) ++#define SUN50I_FBD_CTL_FBD_EN BIT(0) ++ ++#define SUN50I_FBD_SIZE(base) ((base) + 0x08) ++#define SUN50I_FBD_SIZE_HEIGHT(v) (((v) - 1) << 16) ++#define SUN50I_FBD_SIZE_WIDTH(v) (((v) - 1) << 0) ++ ++#define SUN50I_FBD_BLK_SIZE(base) ((base) + 0x0c) ++#define SUN50I_FBD_BLK_SIZE_HEIGHT(v) ((v) << 16) ++#define SUN50I_FBD_BLK_SIZE_WIDTH(v) ((v) << 0) ++ ++#define SUN50I_FBD_SRC_CROP(base) ((base) + 0x10) ++#define SUN50I_FBD_SRC_CROP_TOP(v) ((v) << 16) ++#define SUN50I_FBD_SRC_CROP_LEFT(v) ((v) << 0) ++ ++#define SUN50I_FBD_LAY_CROP(base) ((base) + 0x14) ++#define SUN50I_FBD_LAY_CROP_TOP(v) ((v) << 16) ++#define SUN50I_FBD_LAY_CROP_LEFT(v) ((v) << 0) ++ ++#define SUN50I_FBD_FMT(base) ((base) + 0x18) ++#define SUN50I_FBD_FMT_SBS1(v) ((v) << 18) ++#define SUN50I_FBD_FMT_SBS0(v) ((v) << 16) ++#define SUN50I_FBD_FMT_YUV_TRAN BIT(7) ++#define SUN50I_FBD_FMT_IN_FMT(v) ((v) << 0) ++ ++#define SUN50I_FBD_LADDR(base) ((base) + 0x20) ++#define SUN50I_FBD_HADDR(base) ((base) + 0x24) ++ ++#define SUN50I_FBD_OVL_SIZE(base) ((base) + 0x30) ++#define SUN50I_FBD_OVL_SIZE_HEIGHT(v) (((v) - 1) << 16) ++#define SUN50I_FBD_OVL_SIZE_WIDTH(v) (((v) - 1) << 0) ++ ++#define SUN50I_FBD_OVL_COOR(base) ((base) + 0x34) ++#define SUN50I_FBD_OVL_COOR_Y(v) ((v) << 16) ++#define SUN50I_FBD_OVL_COOR_X(v) ((v) << 0) ++ ++#define SUN50I_FBD_OVL_BG_COLOR(base) ((base) + 0x38) ++#define SUN50I_FBD_OVL_FILL_COLOR(base) ((base) + 0x3c) ++ ++#define SUN50I_FBD_DEFAULT_COLOR0(base) ((base) + 0x50) ++#define SUN50I_FBD_DEFAULT_COLOR0_ALPHA(v) ((v) << 16) ++#define SUN50I_FBD_DEFAULT_COLOR0_YR(v) ((v) << 0) ++ ++#define SUN50I_FBD_DEFAULT_COLOR1(base) ((base) + 0x54) ++#define SUN50I_FBD_DEFAULT_COLOR1_VB(v) ((v) << 16) ++#define SUN50I_FBD_DEFAULT_COLOR1_UG(v) ((v) << 0) ++ ++struct sun8i_mixer; ++struct drm_plane; ++ ++bool sun50i_afbc_format_mod_supported(struct sun8i_mixer *mixer, ++ u32 format, u64 modifier); ++ ++void sun50i_afbc_atomic_update(struct sun8i_mixer *mixer, unsigned int channel, ++ struct drm_plane *plane); ++void sun50i_afbc_disable(struct sun8i_mixer *mixer, unsigned int channel); ++ ++#endif +diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c +index d19349eecc9d..84f8917e2dd8 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c ++++ b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c +@@ -11,8 +11,10 @@ + #include + #include + #include ++#include + #include + ++#include "sun50i_afbc.h" + #include "sun8i_csc.h" + #include "sun8i_mixer.h" + #include "sun8i_vi_layer.h" +@@ -50,7 +52,7 @@ static void sun8i_vi_layer_update_alpha(struct sun8i_mixer *mixer, int channel, + + static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel, + int overlay, struct drm_plane *plane, +- unsigned int zpos) ++ unsigned int zpos, bool afbc) + { + struct drm_plane_state *state = plane->state; + const struct drm_format_info *format = state->fb->format; +@@ -135,7 +137,7 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel, + + required = src_h * 100 / dst_h; + +- if (ability < required) { ++ if (!afbc && ability < required) { + DRM_DEBUG_DRIVER("Using vertical coarse scaling\n"); + vm = src_h; + vn = (u32)ability * dst_h / 100; +@@ -145,7 +147,7 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel, + /* it seems that every RGB scaler has buffer for 2048 pixels */ + scanline = subsampled ? mixer->cfg->scanline_yuv : 2048; + +- if (src_w > scanline) { ++ if (!afbc && src_w > scanline) { + DRM_DEBUG_DRIVER("Using horizontal coarse scaling\n"); + hm = src_w; + hn = scanline; +@@ -308,6 +310,15 @@ static int sun8i_vi_layer_update_buffer(struct sun8i_mixer *mixer, int channel, + return 0; + } + ++static void sun8i_vi_layer_prepare_non_linear(struct sun8i_mixer *mixer, ++ int channel, int overlay) ++{ ++ u32 base = sun8i_channel_base(mixer, channel); ++ ++ regmap_write(mixer->engine.regs, ++ SUN8I_MIXER_CHAN_VI_LAYER_ATTR(base, overlay), 0); ++} ++ + static int sun8i_vi_layer_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state) + { +@@ -348,18 +359,45 @@ static void sun8i_vi_layer_atomic_update(struct drm_plane *plane, + struct sun8i_layer *layer = plane_to_sun8i_layer(plane); + unsigned int zpos = new_state->normalized_zpos; + struct sun8i_mixer *mixer = layer->mixer; ++ struct drm_framebuffer *fb = plane->state->fb; ++ bool afbc = drm_is_afbc(fb->modifier); + +- if (!new_state->crtc || !new_state->visible) ++ if (!new_state->crtc || !new_state->visible) { ++ if (mixer->cfg->de_type >= sun8i_mixer_de3) ++ sun50i_afbc_disable(mixer, layer->channel); + return; ++ } + + sun8i_vi_layer_update_coord(mixer, layer->channel, +- layer->overlay, plane, zpos); +- sun8i_vi_layer_update_alpha(mixer, layer->channel, +- layer->overlay, plane); +- sun8i_vi_layer_update_formats(mixer, layer->channel, +- layer->overlay, plane); +- sun8i_vi_layer_update_buffer(mixer, layer->channel, +- layer->overlay, plane); ++ layer->overlay, plane, zpos, afbc); ++ ++ if (afbc) { ++ u32 fmt_type; ++ ++ sun8i_vi_layer_prepare_non_linear(mixer, layer->channel, ++ layer->overlay); ++ sun50i_afbc_atomic_update(mixer, layer->channel, plane); ++ ++ fmt_type = sun8i_vi_layer_get_format_type(fb->format); ++ sun8i_csc_set_ccsc(mixer, layer->channel, fmt_type, ++ plane->state->color_encoding, ++ plane->state->color_range); ++ } else { ++ sun8i_vi_layer_update_alpha(mixer, layer->channel, ++ layer->overlay, plane); ++ sun8i_vi_layer_update_formats(mixer, layer->channel, ++ layer->overlay, plane); ++ sun8i_vi_layer_update_buffer(mixer, layer->channel, ++ layer->overlay, plane); ++ } ++} ++ ++static bool sun8i_vi_layer_format_mod_supported(struct drm_plane *plane, ++ u32 format, u64 modifier) ++{ ++ struct sun8i_layer *layer = plane_to_sun8i_layer(plane); ++ ++ return sun50i_afbc_format_mod_supported(layer->mixer, format, modifier); + } + + static const struct drm_plane_helper_funcs sun8i_vi_layer_helper_funcs = { +@@ -374,6 +412,7 @@ static const struct drm_plane_funcs sun8i_vi_layer_funcs = { + .disable_plane = drm_atomic_helper_disable_plane, + .reset = drm_atomic_helper_plane_reset, + .update_plane = drm_atomic_helper_update_plane, ++ .format_mod_supported = sun8i_vi_layer_format_mod_supported, + }; + + /* +@@ -457,6 +496,11 @@ static const u32 sun8i_vi_layer_de3_formats[] = { + DRM_FORMAT_YVU411, + DRM_FORMAT_YVU420, + DRM_FORMAT_YVU422, ++ ++ /* AFBC only formats */ ++ DRM_FORMAT_YUV420_8BIT, ++ DRM_FORMAT_YUV420_10BIT, ++ DRM_FORMAT_Y210, + }; + + static const uint64_t sun8i_layer_modifiers[] = { +@@ -464,6 +508,18 @@ static const uint64_t sun8i_layer_modifiers[] = { + DRM_FORMAT_MOD_INVALID + }; + ++static const uint64_t sun50i_layer_de3_modifiers[] = { ++ DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | ++ AFBC_FORMAT_MOD_SPARSE | ++ AFBC_FORMAT_MOD_SPLIT), ++ DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | ++ AFBC_FORMAT_MOD_YTR | ++ AFBC_FORMAT_MOD_SPARSE | ++ AFBC_FORMAT_MOD_SPLIT), ++ DRM_FORMAT_MOD_LINEAR, ++ DRM_FORMAT_MOD_INVALID ++}; ++ + struct sun8i_layer *sun8i_vi_layer_init_one(struct drm_device *drm, + struct sun8i_mixer *mixer, + int index) +@@ -472,6 +528,7 @@ struct sun8i_layer *sun8i_vi_layer_init_one(struct drm_device *drm, + u32 supported_encodings, supported_ranges; + unsigned int plane_cnt, format_count; + struct sun8i_layer *layer; ++ const uint64_t *modifiers; + const u32 *formats; + int ret; + +@@ -487,9 +544,11 @@ struct sun8i_layer *sun8i_vi_layer_init_one(struct drm_device *drm, + if (mixer->cfg->de_type >= sun8i_mixer_de3) { + formats = sun8i_vi_layer_de3_formats; + format_count = ARRAY_SIZE(sun8i_vi_layer_de3_formats); ++ modifiers = sun50i_layer_de3_modifiers; + } else { + formats = sun8i_vi_layer_formats; + format_count = ARRAY_SIZE(sun8i_vi_layer_formats); ++ modifiers = sun8i_layer_modifiers; + } + + if (!mixer->cfg->ui_num && index == 0) +@@ -499,8 +558,7 @@ struct sun8i_layer *sun8i_vi_layer_init_one(struct drm_device *drm, + ret = drm_universal_plane_init(drm, &layer->plane, 0, + &sun8i_vi_layer_funcs, + formats, format_count, +- sun8i_layer_modifiers, +- type, NULL); ++ modifiers, type, NULL); + if (ret) { + dev_err(drm->dev, "Couldn't initialize layer\n"); + return ERR_PTR(ret); +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de3-add-YUV-support-to-the-DE3-mixer.patch b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de3-add-YUV-support-to-the-DE3-mixer.patch new file mode 100644 index 000000000000..8a814bcaf3db --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de3-add-YUV-support-to-the-DE3-mixer.patch @@ -0,0 +1,126 @@ +From 3b6462ebad249f4762acfd8e262442bb0cda95b4 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sun, 29 Sep 2024 22:04:40 +1300 +Subject: drm: sun4i: de3: add YUV support to the DE3 mixer + +The mixer in the DE3 display engine supports YUV 8 and 10 bit +formats in addition to 8-bit RGB. Add the required register +configuration and format enumeration callback functions to the mixer, +and store the in-use output format (defaulting to RGB) and color +encoding in engine variables. + +Signed-off-by: Jernej Skrabec +Signed-off-by: Ryan Walklin +--- + drivers/gpu/drm/sun4i/sun8i_mixer.c | 53 ++++++++++++++++++++++++++-- + drivers/gpu/drm/sun4i/sunxi_engine.h | 5 +++ + 2 files changed, 55 insertions(+), 3 deletions(-) + +diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c +index 252827715de1..a50c583852ed 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_mixer.c ++++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c +@@ -23,7 +23,10 @@ + #include + #include + ++#include ++ + #include "sun4i_drv.h" ++#include "sun50i_fmt.h" + #include "sun8i_mixer.h" + #include "sun8i_ui_layer.h" + #include "sun8i_vi_layer.h" +@@ -390,12 +393,52 @@ static void sun8i_mixer_mode_set(struct sunxi_engine *engine, + + DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n", + interlaced ? "on" : "off"); ++ ++ if (engine->format == MEDIA_BUS_FMT_RGB888_1X24) ++ val = SUN8I_MIXER_BLEND_COLOR_BLACK; ++ else ++ val = 0xff108080; ++ ++ regmap_write(mixer->engine.regs, ++ SUN8I_MIXER_BLEND_BKCOLOR(bld_base), val); ++ regmap_write(mixer->engine.regs, ++ SUN8I_MIXER_BLEND_ATTR_FCOLOR(bld_base, 0), val); ++ ++ if (mixer->cfg->has_formatter) ++ sun50i_fmt_setup(mixer, mode->hdisplay, ++ mode->vdisplay, mixer->engine.format); ++} ++ ++static u32 *sun8i_mixer_get_supported_fmts(struct sunxi_engine *engine, u32 *num) ++{ ++ struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine); ++ u32 *formats, count; ++ ++ count = 0; ++ ++ formats = kcalloc(5, sizeof(*formats), GFP_KERNEL); ++ if (!formats) ++ return NULL; ++ ++ if (mixer->cfg->has_formatter) { ++ formats[count++] = MEDIA_BUS_FMT_UYYVYY10_0_5X30; ++ formats[count++] = MEDIA_BUS_FMT_YUV8_1X24; ++ formats[count++] = MEDIA_BUS_FMT_UYVY8_1X16; ++ formats[count++] = MEDIA_BUS_FMT_UYYVYY8_0_5X24; ++ } ++ ++ formats[count++] = MEDIA_BUS_FMT_RGB888_1X24; ++ ++ *num = count; ++ ++ return formats; + } + + static const struct sunxi_engine_ops sun8i_engine_ops = { +- .commit = sun8i_mixer_commit, +- .layers_init = sun8i_layers_init, +- .mode_set = sun8i_mixer_mode_set, ++ .commit = sun8i_mixer_commit, ++ .layers_init = sun8i_layers_init, ++ .mode_set = sun8i_mixer_mode_set, ++ .get_supported_fmts = sun8i_mixer_get_supported_fmts, + }; + + static const struct regmap_config sun8i_mixer_regmap_config = { +@@ -456,6 +499,10 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master, + dev_set_drvdata(dev, mixer); + mixer->engine.ops = &sun8i_engine_ops; + mixer->engine.node = dev->of_node; ++ /* default output format, supported by all mixers */ ++ mixer->engine.format = MEDIA_BUS_FMT_RGB888_1X24; ++ /* default color encoding, ignored with RGB I/O */ ++ mixer->engine.encoding = DRM_COLOR_YCBCR_BT601; + + if (of_property_present(dev->of_node, "iommus")) { + /* +diff --git a/drivers/gpu/drm/sun4i/sunxi_engine.h b/drivers/gpu/drm/sun4i/sunxi_engine.h +index c48cbc1aceb8..ffafc29b3a0c 100644 +--- a/drivers/gpu/drm/sun4i/sunxi_engine.h ++++ b/drivers/gpu/drm/sun4i/sunxi_engine.h +@@ -6,6 +6,8 @@ + #ifndef _SUNXI_ENGINE_H_ + #define _SUNXI_ENGINE_H_ + ++#include ++ + struct drm_plane; + struct drm_crtc; + struct drm_device; +@@ -151,6 +153,9 @@ struct sunxi_engine { + + int id; + ++ u32 format; ++ enum drm_color_encoding encoding; ++ + /* Engine list management */ + struct list_head list; + }; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de3-add-YUV-support-to-the-TCON.patch b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de3-add-YUV-support-to-the-TCON.patch new file mode 100644 index 000000000000..5fb39d4f07d6 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de3-add-YUV-support-to-the-TCON.patch @@ -0,0 +1,83 @@ +From ff794822d56721795fec59dea66164cc19ba792c Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sun, 29 Sep 2024 22:04:43 +1300 +Subject: drm: sun4i: de3: add YUV support to the TCON + +Account for U/V channel subsampling by reducing the dot clock and +resolution with a divider in the DE3 timing controller if a YUV format +is selected. + +Signed-off-by: Jernej Skrabec +Signed-off-by: Ryan Walklin +--- + drivers/gpu/drm/sun4i/sun4i_tcon.c | 26 +++++++++++++++++++------- + 1 file changed, 19 insertions(+), 7 deletions(-) + +diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c +index 3675c87461e9..af67bf2e6e09 100644 +--- a/drivers/gpu/drm/sun4i/sun4i_tcon.c ++++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c +@@ -649,14 +649,26 @@ static void sun4i_tcon0_mode_set_rgb(struct sun4i_tcon *tcon, + static void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon, + const struct drm_display_mode *mode) + { +- unsigned int bp, hsync, vsync, vtotal; ++ unsigned int bp, hsync, vsync, vtotal, div; ++ struct sun4i_crtc *scrtc = tcon->crtc; ++ struct sunxi_engine *engine = scrtc->engine; + u8 clk_delay; + u32 val; + + WARN_ON(!tcon->quirks->has_channel_1); + ++ switch (engine->format) { ++ case MEDIA_BUS_FMT_UYYVYY8_0_5X24: ++ case MEDIA_BUS_FMT_UYYVYY10_0_5X30: ++ div = 2; ++ break; ++ default: ++ div = 1; ++ break; ++ } ++ + /* Configure the dot clock */ +- clk_set_rate(tcon->sclk1, mode->crtc_clock * 1000); ++ clk_set_rate(tcon->sclk1, mode->crtc_clock * 1000 / div); + + /* Adjust clock delay */ + clk_delay = sun4i_tcon_get_clk_delay(mode, 1); +@@ -675,17 +687,17 @@ static void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon, + + /* Set the input resolution */ + regmap_write(tcon->regs, SUN4I_TCON1_BASIC0_REG, +- SUN4I_TCON1_BASIC0_X(mode->crtc_hdisplay) | ++ SUN4I_TCON1_BASIC0_X(mode->crtc_hdisplay / div) | + SUN4I_TCON1_BASIC0_Y(mode->crtc_vdisplay)); + + /* Set the upscaling resolution */ + regmap_write(tcon->regs, SUN4I_TCON1_BASIC1_REG, +- SUN4I_TCON1_BASIC1_X(mode->crtc_hdisplay) | ++ SUN4I_TCON1_BASIC1_X(mode->crtc_hdisplay / div) | + SUN4I_TCON1_BASIC1_Y(mode->crtc_vdisplay)); + + /* Set the output resolution */ + regmap_write(tcon->regs, SUN4I_TCON1_BASIC2_REG, +- SUN4I_TCON1_BASIC2_X(mode->crtc_hdisplay) | ++ SUN4I_TCON1_BASIC2_X(mode->crtc_hdisplay / div) | + SUN4I_TCON1_BASIC2_Y(mode->crtc_vdisplay)); + + /* Set horizontal display timings */ +@@ -693,8 +705,8 @@ static void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon, + DRM_DEBUG_DRIVER("Setting horizontal total %d, backporch %d\n", + mode->htotal, bp); + regmap_write(tcon->regs, SUN4I_TCON1_BASIC3_REG, +- SUN4I_TCON1_BASIC3_H_TOTAL(mode->crtc_htotal) | +- SUN4I_TCON1_BASIC3_H_BACKPORCH(bp)); ++ SUN4I_TCON1_BASIC3_H_TOTAL(mode->crtc_htotal / div) | ++ SUN4I_TCON1_BASIC3_H_BACKPORCH(bp / div)); + + bp = mode->crtc_vtotal - mode->crtc_vsync_start; + DRM_DEBUG_DRIVER("Setting vertical total %d, backporch %d\n", +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de3-add-YUV-support-to-the-color-space-correction-mod.patch b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de3-add-YUV-support-to-the-color-space-correction-mod.patch new file mode 100644 index 000000000000..68774b67bf48 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de3-add-YUV-support-to-the-color-space-correction-mod.patch @@ -0,0 +1,225 @@ +From a23ed976ee720c2445791716d975f040ef576c2b Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sun, 29 Sep 2024 22:04:42 +1300 +Subject: drm: sun4i: de3: add YUV support to the color space correction module + +Add coefficients and support for YUV formats to the display engine +colorspace and dynamic range correction submodule. + +Signed-off-by: Jernej Skrabec +Signed-off-by: Ryan Walklin +--- + drivers/gpu/drm/sun4i/sun8i_csc.c | 164 +++++++++++++++++++++++++++++- + 1 file changed, 162 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/sun4i/sun8i_csc.c b/drivers/gpu/drm/sun4i/sun8i_csc.c +index 8a336ccb27d3..e12a81fa9108 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_csc.c ++++ b/drivers/gpu/drm/sun4i/sun8i_csc.c +@@ -5,6 +5,8 @@ + + #include + ++#include ++ + #include "sun8i_csc.h" + #include "sun8i_mixer.h" + +@@ -107,6 +109,135 @@ static const u32 yuv2rgb_de3[2][3][12] = { + }, + }; + ++/* always convert to limited mode */ ++static const u32 rgb2yuv_de3[3][12] = { ++ [DRM_COLOR_YCBCR_BT601] = { ++ 0x0000837A, 0x0001021D, 0x00003221, 0x00000040, ++ 0xFFFFB41C, 0xFFFF6B03, 0x0000E0E1, 0x00000200, ++ 0x0000E0E1, 0xFFFF43B1, 0xFFFFDB6E, 0x00000200, ++ }, ++ [DRM_COLOR_YCBCR_BT709] = { ++ 0x00005D7C, 0x00013A7C, 0x00001FBF, 0x00000040, ++ 0xFFFFCC78, 0xFFFF52A7, 0x0000E0E1, 0x00000200, ++ 0x0000E0E1, 0xFFFF33BE, 0xFFFFEB61, 0x00000200, ++ }, ++ [DRM_COLOR_YCBCR_BT2020] = { ++ 0x00007384, 0x00012A21, 0x00001A13, 0x00000040, ++ 0xFFFFC133, 0xFFFF5DEC, 0x0000E0E1, 0x00000200, ++ 0x0000E0E1, 0xFFFF3135, 0xFFFFEDEA, 0x00000200, ++ }, ++}; ++ ++/* always convert to limited mode */ ++static const u32 yuv2yuv_de3[2][3][3][12] = { ++ [DRM_COLOR_YCBCR_LIMITED_RANGE] = { ++ [DRM_COLOR_YCBCR_BT601] = { ++ [DRM_COLOR_YCBCR_BT601] = { ++ 0x00020000, 0x00000000, 0x00000000, 0x00000000, ++ 0x00000000, 0x00020000, 0x00000000, 0x00000000, ++ 0x00000000, 0x00000000, 0x00020000, 0x00000000, ++ }, ++ [DRM_COLOR_YCBCR_BT709] = { ++ 0x00020000, 0xFFFFC4D7, 0xFFFF9589, 0xFFC00040, ++ 0x00000000, 0x0002098B, 0x00003AAF, 0xFE000200, ++ 0x00000000, 0x0000266D, 0x00020CF8, 0xFE000200, ++ }, ++ [DRM_COLOR_YCBCR_BT2020] = { ++ 0x00020000, 0xFFFFBFCE, 0xFFFFC5FF, 0xFFC00040, ++ 0x00000000, 0x00020521, 0x00001F89, 0xFE000200, ++ 0x00000000, 0x00002C87, 0x00020F07, 0xFE000200, ++ }, ++ }, ++ [DRM_COLOR_YCBCR_BT709] = { ++ [DRM_COLOR_YCBCR_BT601] = { ++ 0x00020000, 0x000032D9, 0x00006226, 0xFFC00040, ++ 0x00000000, 0x0001FACE, 0xFFFFC759, 0xFE000200, ++ 0x00000000, 0xFFFFDAE7, 0x0001F780, 0xFE000200, ++ }, ++ [DRM_COLOR_YCBCR_BT709] = { ++ 0x00020000, 0x00000000, 0x00000000, 0x00000000, ++ 0x00000000, 0x00020000, 0x00000000, 0x00000000, ++ 0x00000000, 0x00000000, 0x00020000, 0x00000000, ++ }, ++ [DRM_COLOR_YCBCR_BT2020] = { ++ 0x00020000, 0xFFFFF782, 0x00003036, 0xFFC00040, ++ 0x00000000, 0x0001FD99, 0xFFFFE5CA, 0xFE000200, ++ 0x00000000, 0x000005E4, 0x0002015A, 0xFE000200, ++ }, ++ }, ++ [DRM_COLOR_YCBCR_BT2020] = { ++ [DRM_COLOR_YCBCR_BT601] = { ++ 0x00020000, 0x00003B03, 0x000034D2, 0xFFC00040, ++ 0x00000000, 0x0001FD8C, 0xFFFFE183, 0xFE000200, ++ 0x00000000, 0xFFFFD4F3, 0x0001F3FA, 0xFE000200, ++ }, ++ [DRM_COLOR_YCBCR_BT709] = { ++ 0x00020000, 0x00000916, 0xFFFFD061, 0xFFC00040, ++ 0x00000000, 0x0002021C, 0x00001A40, 0xFE000200, ++ 0x00000000, 0xFFFFFA19, 0x0001FE5A, 0xFE000200, ++ }, ++ [DRM_COLOR_YCBCR_BT2020] = { ++ 0x00020000, 0x00000000, 0x00000000, 0x00000000, ++ 0x00000000, 0x00020000, 0x00000000, 0x00000000, ++ 0x00000000, 0x00000000, 0x00020000, 0x00000000, ++ }, ++ }, ++ }, ++ [DRM_COLOR_YCBCR_FULL_RANGE] = { ++ [DRM_COLOR_YCBCR_BT601] = { ++ [DRM_COLOR_YCBCR_BT601] = { ++ 0x0001B7B8, 0x00000000, 0x00000000, 0x00000040, ++ 0x00000000, 0x0001C1C2, 0x00000000, 0xFE000200, ++ 0x00000000, 0x00000000, 0x0001C1C2, 0xFE000200, ++ }, ++ [DRM_COLOR_YCBCR_BT709] = { ++ 0x0001B7B8, 0xFFFFCC08, 0xFFFFA27B, 0x00000040, ++ 0x00000000, 0x0001CA24, 0x0000338D, 0xFE000200, ++ 0x00000000, 0x000021C1, 0x0001CD26, 0xFE000200, ++ }, ++ [DRM_COLOR_YCBCR_BT2020] = { ++ 0x0001B7B8, 0xFFFFC79C, 0xFFFFCD0C, 0x00000040, ++ 0x00000000, 0x0001C643, 0x00001BB4, 0xFE000200, ++ 0x00000000, 0x0000271D, 0x0001CEF5, 0xFE000200, ++ }, ++ }, ++ [DRM_COLOR_YCBCR_BT709] = { ++ [DRM_COLOR_YCBCR_BT601] = { ++ 0x0001B7B8, 0x00002CAB, 0x00005638, 0x00000040, ++ 0x00000000, 0x0001BD32, 0xFFFFCE3C, 0xFE000200, ++ 0x00000000, 0xFFFFDF6A, 0x0001BA4A, 0xFE000200, ++ }, ++ [DRM_COLOR_YCBCR_BT709] = { ++ 0x0001B7B8, 0x00000000, 0x00000000, 0x00000040, ++ 0x00000000, 0x0001C1C2, 0x00000000, 0xFE000200, ++ 0x00000000, 0x00000000, 0x0001C1C2, 0xFE000200, ++ }, ++ [DRM_COLOR_YCBCR_BT2020] = { ++ 0x0001B7B8, 0xFFFFF88A, 0x00002A5A, 0x00000040, ++ 0x00000000, 0x0001BFA5, 0xFFFFE8FA, 0xFE000200, ++ 0x00000000, 0x0000052D, 0x0001C2F1, 0xFE000200, ++ }, ++ }, ++ [DRM_COLOR_YCBCR_BT2020] = { ++ [DRM_COLOR_YCBCR_BT601] = { ++ 0x0001B7B8, 0x000033D6, 0x00002E66, 0x00000040, ++ 0x00000000, 0x0001BF9A, 0xFFFFE538, 0xFE000200, ++ 0x00000000, 0xFFFFDA2F, 0x0001B732, 0xFE000200, ++ }, ++ [DRM_COLOR_YCBCR_BT709] = { ++ 0x0001B7B8, 0x000007FB, 0xFFFFD62B, 0x00000040, ++ 0x00000000, 0x0001C39D, 0x0000170F, 0xFE000200, ++ 0x00000000, 0xFFFFFAD1, 0x0001C04F, 0xFE000200, ++ }, ++ [DRM_COLOR_YCBCR_BT2020] = { ++ 0x0001B7B8, 0x00000000, 0x00000000, 0x00000040, ++ 0x00000000, 0x0001C1C2, 0x00000000, 0xFE000200, ++ 0x00000000, 0x00000000, 0x0001C1C2, 0xFE000200, ++ }, ++ }, ++ }, ++}; ++ + static void sun8i_csc_setup(struct regmap *map, u32 base, + enum format_type fmt_type, + enum drm_color_encoding encoding, +@@ -148,12 +279,27 @@ static void sun8i_csc_setup(struct regmap *map, u32 base, + regmap_write(map, SUN8I_CSC_CTRL(base), val); + } + ++static const u32 *sun8i_csc_get_de3_yuv_table(enum drm_color_encoding in_enc, ++ enum drm_color_range in_range, ++ u32 out_format, ++ enum drm_color_encoding out_enc) ++{ ++ if (out_format == MEDIA_BUS_FMT_RGB888_1X24) ++ return yuv2rgb_de3[in_range][in_enc]; ++ ++ /* check for identity transformation */ ++ if (in_range == DRM_COLOR_YCBCR_LIMITED_RANGE && out_enc == in_enc) ++ return NULL; ++ ++ return yuv2yuv_de3[in_range][in_enc][out_enc]; ++} ++ + static void sun8i_de3_ccsc_setup(struct sunxi_engine *engine, int layer, + enum format_type fmt_type, + enum drm_color_encoding encoding, + enum drm_color_range range) + { +- u32 addr, val, mask; ++ u32 addr, val = 0, mask; + struct regmap *map; + const u32 *table; + int i; +@@ -164,14 +310,28 @@ static void sun8i_de3_ccsc_setup(struct sunxi_engine *engine, int layer, + + switch (fmt_type) { + case FORMAT_TYPE_RGB: +- val = 0; ++ if (engine->format == MEDIA_BUS_FMT_RGB888_1X24) ++ break; ++ val = mask; ++ addr = SUN50I_MIXER_BLEND_CSC_COEFF(DE3_BLD_BASE, layer, 0); ++ regmap_bulk_write(map, addr, rgb2yuv_de3[engine->encoding], 12); + break; + case FORMAT_TYPE_YUV: ++ table = sun8i_csc_get_de3_yuv_table(encoding, range, ++ engine->format, ++ engine->encoding); ++ if (!table) ++ break; + val = mask; + addr = SUN50I_MIXER_BLEND_CSC_COEFF(DE3_BLD_BASE, layer, 0); + regmap_bulk_write(map, addr, table, 12); + break; + case FORMAT_TYPE_YVU: ++ table = sun8i_csc_get_de3_yuv_table(encoding, range, ++ engine->format, ++ engine->encoding); ++ if (!table) ++ table = yuv2yuv_de3[range][encoding][encoding]; + val = mask; + for (i = 0; i < 12; i++) { + if ((i & 3) == 1) +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de3-add-format-enumeration-function-to-engine.patch b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de3-add-format-enumeration-function-to-engine.patch new file mode 100644 index 000000000000..581242c382e9 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de3-add-format-enumeration-function-to-engine.patch @@ -0,0 +1,63 @@ +From 99d327853acbc5d6c6d4140f004f82fcd5c40ea1 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sun, 29 Sep 2024 22:04:38 +1300 +Subject: drm: sun4i: de3: add format enumeration function to engine + +The DE3 display engine supports YUV formats in addition to RGB. + +Add an optional format enumeration function to the engine. + +Signed-off-by: Jernej Skrabec +Signed-off-by: Ryan Walklin +--- + drivers/gpu/drm/sun4i/sunxi_engine.h | 29 ++++++++++++++++++++++++++++ + 1 file changed, 29 insertions(+) + +diff --git a/drivers/gpu/drm/sun4i/sunxi_engine.h b/drivers/gpu/drm/sun4i/sunxi_engine.h +index ec0c4932f15c..c48cbc1aceb8 100644 +--- a/drivers/gpu/drm/sun4i/sunxi_engine.h ++++ b/drivers/gpu/drm/sun4i/sunxi_engine.h +@@ -123,6 +123,17 @@ struct sunxi_engine_ops { + */ + void (*mode_set)(struct sunxi_engine *engine, + const struct drm_display_mode *mode); ++ ++ /** ++ * @get_supported_fmts ++ * ++ * This callback is used to enumerate all supported output ++ * formats by the engine. They are used for bridge format ++ * negotiation. ++ * ++ * This function is optional. ++ */ ++ u32 *(*get_supported_fmts)(struct sunxi_engine *engine, u32 *num); + }; + + /** +@@ -215,4 +226,22 @@ sunxi_engine_mode_set(struct sunxi_engine *engine, + if (engine->ops && engine->ops->mode_set) + engine->ops->mode_set(engine, mode); + } ++ ++/** ++ * sunxi_engine_get_supported_formats - Provide array of supported formats ++ * @engine: pointer to the engine ++ * @num: pointer to variable, which will hold number of formats ++ * ++ * This list can be used for format negotiation by bridge. ++ */ ++static inline u32 * ++sunxi_engine_get_supported_formats(struct sunxi_engine *engine, u32 *num) ++{ ++ if (engine->ops && engine->ops->get_supported_fmts) ++ return engine->ops->get_supported_fmts(engine, num); ++ ++ *num = 0; ++ ++ return NULL; ++} + #endif /* _SUNXI_ENGINE_H_ */ +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de3-add-formatter-flag-to-mixer-config.patch b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de3-add-formatter-flag-to-mixer-config.patch new file mode 100644 index 000000000000..90f86befd415 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de3-add-formatter-flag-to-mixer-config.patch @@ -0,0 +1,53 @@ +From f9a39553dcf5e87eba968d2aac4f5acf52baa392 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sun, 29 Sep 2024 22:04:39 +1300 +Subject: drm: sun4i: de3: add formatter flag to mixer config + +Only the DE3 (and newer) display engines have a formatter module. This +could be inferred from the is_de3 flag alone, however this will not +scale with addition of future DE versions in subsequent patches. + +Add a separate flag to signal this in the mixer configuration. + +Signed-off-by: Jernej Skrabec +Signed-off-by: Ryan Walklin +--- + drivers/gpu/drm/sun4i/sun8i_mixer.c | 1 + + drivers/gpu/drm/sun4i/sun8i_mixer.h | 2 ++ + 2 files changed, 3 insertions(+) + +diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c +index bd0fe2c6624e..252827715de1 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_mixer.c ++++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c +@@ -717,6 +717,7 @@ static const struct sun8i_mixer_cfg sun50i_a64_mixer1_cfg = { + static const struct sun8i_mixer_cfg sun50i_h6_mixer0_cfg = { + .ccsc = CCSC_MIXER0_LAYOUT, + .is_de3 = true, ++ .has_formatter = 1, + .mod_rate = 600000000, + .scaler_mask = 0xf, + .scanline_yuv = 4096, +diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h b/drivers/gpu/drm/sun4i/sun8i_mixer.h +index d7898c9c9cc0..8417b8fef2e1 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_mixer.h ++++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h +@@ -163,6 +163,7 @@ enum { + * @mod_rate: module clock rate that needs to be set in order to have + * a functional block. + * @is_de3: true, if this is next gen display engine 3.0, false otherwise. ++ * @has_formatter: true, if mixer has formatter core, for 10-bit and YUV handling + * @scaline_yuv: size of a scanline for VI scaler for YUV formats. + */ + struct sun8i_mixer_cfg { +@@ -172,6 +173,7 @@ struct sun8i_mixer_cfg { + int ccsc; + unsigned long mod_rate; + unsigned int is_de3 : 1; ++ unsigned int has_formatter : 1; + unsigned int scanline_yuv; + }; + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de3-pass-engine-reference-to-ccsc-setup-function.patch b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de3-pass-engine-reference-to-ccsc-setup-function.patch new file mode 100644 index 000000000000..41bd023772e6 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de3-pass-engine-reference-to-ccsc-setup-function.patch @@ -0,0 +1,54 @@ +From 2d7c88fc2af6d07ccadc99b157753638b4940293 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sun, 29 Sep 2024 22:04:41 +1300 +Subject: drm: sun4i: de3: pass engine reference to ccsc setup function + +Configuration of the DE3 colorspace and dynamic range correction module +requires knowledge of the current video format and encoding. + +Pass the display engine by reference to the csc setup function, rather +than the register map alone, to allow access to this information. + +Signed-off-by: Jernej Skrabec +Signed-off-by: Ryan Walklin +--- + drivers/gpu/drm/sun4i/sun8i_csc.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/sun4i/sun8i_csc.c b/drivers/gpu/drm/sun4i/sun8i_csc.c +index 68d955c63b05..8a336ccb27d3 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_csc.c ++++ b/drivers/gpu/drm/sun4i/sun8i_csc.c +@@ -148,17 +148,19 @@ static void sun8i_csc_setup(struct regmap *map, u32 base, + regmap_write(map, SUN8I_CSC_CTRL(base), val); + } + +-static void sun8i_de3_ccsc_setup(struct regmap *map, int layer, ++static void sun8i_de3_ccsc_setup(struct sunxi_engine *engine, int layer, + enum format_type fmt_type, + enum drm_color_encoding encoding, + enum drm_color_range range) + { + u32 addr, val, mask; ++ struct regmap *map; + const u32 *table; + int i; + + mask = SUN50I_MIXER_BLEND_CSC_CTL_EN(layer); + table = yuv2rgb_de3[range][encoding]; ++ map = engine->regs; + + switch (fmt_type) { + case FORMAT_TYPE_RGB: +@@ -204,7 +206,7 @@ void sun8i_csc_set_ccsc(struct sun8i_mixer *mixer, int layer, + u32 base; + + if (mixer->cfg->is_de3) { +- sun8i_de3_ccsc_setup(mixer->engine.regs, layer, ++ sun8i_de3_ccsc_setup(&mixer->engine, layer, + fmt_type, encoding, range); + return; + } +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de33-csc-add-Display-Engine-3.3-DE33-support.patch b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de33-csc-add-Display-Engine-3.3-DE33-support.patch new file mode 100644 index 000000000000..3479d96ff100 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de33-csc-add-Display-Engine-3.3-DE33-support.patch @@ -0,0 +1,156 @@ +From 0d003a88bcacf5f405f3922b0c56b7eecdb68386 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sun, 29 Sep 2024 22:04:58 +1300 +Subject: drm: sun4i: de33: csc: add Display Engine 3.3 (DE33) support + +Like earlier DE versions, the DE33 has a CSC (Color Space Correction) +module. which provides color space conversion between BT2020/BT709, and +dynamic range conversion between SDR/ST2084/HLG. + +Add support for the DE33. + +Signed-off-by: Jernej Skrabec +Signed-off-by: Ryan Walklin +--- + drivers/gpu/drm/sun4i/sun8i_csc.c | 96 +++++++++++++++++++++++++++++++ + drivers/gpu/drm/sun4i/sun8i_csc.h | 3 + + 2 files changed, 99 insertions(+) + +diff --git a/drivers/gpu/drm/sun4i/sun8i_csc.c b/drivers/gpu/drm/sun4i/sun8i_csc.c +index 2d5a2cf7cba2..45bd1ca06400 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_csc.c ++++ b/drivers/gpu/drm/sun4i/sun8i_csc.c +@@ -238,6 +238,14 @@ static const u32 yuv2yuv_de3[2][3][3][12] = { + }, + }; + ++static u32 sun8i_csc_base(struct sun8i_mixer *mixer, int layer) ++{ ++ if (mixer->cfg->de_type == sun8i_mixer_de33) ++ return sun8i_channel_base(mixer, layer) - 0x800; ++ else ++ return ccsc_base[mixer->cfg->ccsc][layer]; ++} ++ + static void sun8i_csc_setup(struct regmap *map, u32 base, + enum format_type fmt_type, + enum drm_color_encoding encoding, +@@ -358,6 +366,90 @@ static void sun8i_de3_ccsc_setup(struct sunxi_engine *engine, int layer, + mask, val); + } + ++/* extract constant from high word and invert sign if necessary */ ++static u32 sun8i_de33_ccsc_get_constant(u32 value) ++{ ++ value >>= 16; ++ ++ if (value & BIT(15)) ++ return 0x400 - (value & 0x3ff); ++ ++ return value; ++} ++ ++static void sun8i_de33_convert_table(const u32 *src, u32 *dst) ++{ ++ dst[0] = sun8i_de33_ccsc_get_constant(src[3]); ++ dst[1] = sun8i_de33_ccsc_get_constant(src[7]); ++ dst[2] = sun8i_de33_ccsc_get_constant(src[11]); ++ memcpy(&dst[3], src, sizeof(u32) * 12); ++ dst[6] &= 0xffff; ++ dst[10] &= 0xffff; ++ dst[14] &= 0xffff; ++} ++ ++static void sun8i_de33_ccsc_setup(struct sun8i_mixer *mixer, int layer, ++ enum format_type fmt_type, ++ enum drm_color_encoding encoding, ++ enum drm_color_range range) ++{ ++ u32 addr, val = 0, base, csc[15]; ++ struct sunxi_engine *engine; ++ struct regmap *map; ++ const u32 *table; ++ int i; ++ ++ table = yuv2rgb_de3[range][encoding]; ++ base = sun8i_csc_base(mixer, layer); ++ engine = &mixer->engine; ++ map = engine->regs; ++ ++ switch (fmt_type) { ++ case FORMAT_TYPE_RGB: ++ if (engine->format == MEDIA_BUS_FMT_RGB888_1X24) ++ break; ++ val = SUN8I_CSC_CTRL_EN; ++ sun8i_de33_convert_table(rgb2yuv_de3[engine->encoding], csc); ++ regmap_bulk_write(map, SUN50I_CSC_COEFF(base, 0), csc, 15); ++ break; ++ case FORMAT_TYPE_YUV: ++ table = sun8i_csc_get_de3_yuv_table(encoding, range, ++ engine->format, ++ engine->encoding); ++ if (!table) ++ break; ++ val = SUN8I_CSC_CTRL_EN; ++ sun8i_de33_convert_table(table, csc); ++ regmap_bulk_write(map, SUN50I_CSC_COEFF(base, 0), csc, 15); ++ break; ++ case FORMAT_TYPE_YVU: ++ table = sun8i_csc_get_de3_yuv_table(encoding, range, ++ engine->format, ++ engine->encoding); ++ if (!table) ++ table = yuv2yuv_de3[range][encoding][encoding]; ++ val = SUN8I_CSC_CTRL_EN; ++ sun8i_de33_convert_table(table, csc); ++ for (i = 0; i < 15; i++) { ++ addr = SUN50I_CSC_COEFF(base, i); ++ if (i > 3) { ++ if (((i - 3) & 3) == 1) ++ addr = SUN50I_CSC_COEFF(base, i + 1); ++ else if (((i - 3) & 3) == 2) ++ addr = SUN50I_CSC_COEFF(base, i - 1); ++ } ++ regmap_write(map, addr, csc[i]); ++ } ++ break; ++ default: ++ val = 0; ++ DRM_WARN("Wrong CSC mode specified.\n"); ++ return; ++ } ++ ++ regmap_write(map, SUN8I_CSC_CTRL(base), val); ++} ++ + void sun8i_csc_set_ccsc(struct sun8i_mixer *mixer, int layer, + enum format_type fmt_type, + enum drm_color_encoding encoding, +@@ -369,6 +461,10 @@ void sun8i_csc_set_ccsc(struct sun8i_mixer *mixer, int layer, + sun8i_de3_ccsc_setup(&mixer->engine, layer, + fmt_type, encoding, range); + return; ++ } else if (mixer->cfg->de_type == sun8i_mixer_de33) { ++ sun8i_de33_ccsc_setup(mixer, layer, fmt_type, ++ encoding, range); ++ return; + } + + if (layer < mixer->cfg->vi_num) { +diff --git a/drivers/gpu/drm/sun4i/sun8i_csc.h b/drivers/gpu/drm/sun4i/sun8i_csc.h +index b7546e06e315..2b762cb79f02 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_csc.h ++++ b/drivers/gpu/drm/sun4i/sun8i_csc.h +@@ -20,6 +20,9 @@ struct sun8i_mixer; + #define SUN8I_CSC_CTRL(base) ((base) + 0x0) + #define SUN8I_CSC_COEFF(base, i) ((base) + 0x10 + 4 * (i)) + ++#define SUN50I_CSC_COEFF(base, i) ((base) + 0x04 + 4 * (i)) ++#define SUN50I_CSC_ALPHA(base) ((base) + 0x40) ++ + #define SUN8I_CSC_CTRL_EN BIT(0) + + enum format_type { +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de33-fmt-add-Display-Engine-3.3-DE33-support.patch b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de33-fmt-add-Display-Engine-3.3-DE33-support.patch new file mode 100644 index 000000000000..cd060b36718e --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de33-fmt-add-Display-Engine-3.3-DE33-support.patch @@ -0,0 +1,75 @@ +From 792b816c952bcf5dbf7c3ac7d90937bc71f0a7cd Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sun, 29 Sep 2024 22:04:57 +1300 +Subject: drm: sun4i: de33: fmt: add Display Engine 3.3 (DE33) support + +Like the DE3, the DE33 has a FMT (formatter) module, which +provides YUV444 to YUV422/YUV420 conversion, format re-mapping and color +depth conversion, although the DE33 module appears significantly more +capable, including up to 4K video support. + +Add support for the DE33. + +Signed-off-by: Jernej Skrabec +Signed-off-by: Ryan Walklin +--- + drivers/gpu/drm/sun4i/sun50i_fmt.c | 21 +++++++++++++++++++-- + drivers/gpu/drm/sun4i/sun50i_fmt.h | 1 + + 2 files changed, 20 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/sun4i/sun50i_fmt.c b/drivers/gpu/drm/sun4i/sun50i_fmt.c +index 050a8716ae86..39682d4e6d20 100644 +--- a/drivers/gpu/drm/sun4i/sun50i_fmt.c ++++ b/drivers/gpu/drm/sun4i/sun50i_fmt.c +@@ -51,6 +51,19 @@ static void sun50i_fmt_de3_limits(u32 *limits, u32 colorspace, bool bit10) + } + } + ++static void sun50i_fmt_de33_limits(u32 *limits, u32 colorspace) ++{ ++ if (colorspace == SUN50I_FMT_CS_YUV444RGB) { ++ limits[0] = SUN50I_FMT_LIMIT(0, 4095); ++ limits[1] = SUN50I_FMT_LIMIT(0, 4095); ++ limits[2] = SUN50I_FMT_LIMIT(0, 4095); ++ } else { ++ limits[0] = SUN50I_FMT_LIMIT(256, 3840); ++ limits[1] = SUN50I_FMT_LIMIT(256, 3840); ++ limits[2] = SUN50I_FMT_LIMIT(256, 3840); ++ } ++} ++ + void sun50i_fmt_setup(struct sun8i_mixer *mixer, u16 width, + u16 height, u32 format) + { +@@ -60,10 +73,14 @@ void sun50i_fmt_setup(struct sun8i_mixer *mixer, u16 width, + + colorspace = sun50i_fmt_get_colorspace(format); + bit10 = sun50i_fmt_is_10bit(format); +- base = SUN50I_FMT_DE3; ++ base = mixer->cfg->de_type == sun8i_mixer_de3 ? ++ SUN50I_FMT_DE3 : SUN50I_FMT_DE33; + regs = sun8i_blender_regmap(mixer); + +- sun50i_fmt_de3_limits(limit, colorspace, bit10); ++ if (mixer->cfg->de_type == sun8i_mixer_de3) ++ sun50i_fmt_de3_limits(limit, colorspace, bit10); ++ else ++ sun50i_fmt_de33_limits(limit, colorspace); + + regmap_write(regs, SUN50I_FMT_CTRL(base), 0); + +diff --git a/drivers/gpu/drm/sun4i/sun50i_fmt.h b/drivers/gpu/drm/sun4i/sun50i_fmt.h +index 4127f7206aad..3e60d5c788b3 100644 +--- a/drivers/gpu/drm/sun4i/sun50i_fmt.h ++++ b/drivers/gpu/drm/sun4i/sun50i_fmt.h +@@ -9,6 +9,7 @@ + #include "sun8i_mixer.h" + + #define SUN50I_FMT_DE3 0xa8000 ++#define SUN50I_FMT_DE33 0x5000 + + #define SUN50I_FMT_CTRL(base) ((base) + 0x00) + #define SUN50I_FMT_SIZE(base) ((base) + 0x04) +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de33-mixer-add-Display-Engine-3.3-DE33-support.patch b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de33-mixer-add-Display-Engine-3.3-DE33-support.patch new file mode 100644 index 000000000000..84e9458fb3fd --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de33-mixer-add-Display-Engine-3.3-DE33-support.patch @@ -0,0 +1,302 @@ +From 66c111f3315d2c34c2f9bfb39de61bf3ec46a5f6 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sun, 29 Sep 2024 22:04:55 +1300 +Subject: drm: sun4i: de33: mixer: add Display Engine 3.3 (DE33) support + +The DE33 is a newer version of the Allwinner Display Engine IP block, +found in the H616, H618, H700 and T507 SoCs. DE2 and DE3 are already +supported by the mainline driver. + +Notable features (from the H616 datasheet and implemented): +- 4096 x 2048 (4K) output support +- AFBC ARM Frame Buffer Compression support +- YUV420 input support + +The DE2 and DE3 engines have a blender register range within the +mixer engine register map, whereas the DE33 separates this out into +a separate display group, and adds a top register map. + +Extend the mixer to support the DE33. + +Signed-off-by: Jernej Skrabec +Signed-off-by: Ryan Walklin +--- + drivers/gpu/drm/sun4i/sun8i_mixer.c | 109 ++++++++++++++++++++++++---- + drivers/gpu/drm/sun4i/sun8i_mixer.h | 16 +++- + 2 files changed, 108 insertions(+), 17 deletions(-) + +diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c +index 600084286b39..204fc8055b32 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_mixer.c ++++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c +@@ -321,8 +321,12 @@ static void sun8i_mixer_commit(struct sunxi_engine *engine, + regmap_write(bld_regs, SUN8I_MIXER_BLEND_PIPE_CTL(bld_base), + pipe_en | SUN8I_MIXER_BLEND_PIPE_CTL_FC_EN(0)); + +- regmap_write(engine->regs, SUN8I_MIXER_GLOBAL_DBUFF, +- SUN8I_MIXER_GLOBAL_DBUFF_ENABLE); ++ if (mixer->cfg->de_type == sun8i_mixer_de33) ++ regmap_write(mixer->top_regs, SUN50I_MIXER_GLOBAL_DBUFF, ++ SUN8I_MIXER_GLOBAL_DBUFF_ENABLE); ++ else ++ regmap_write(engine->regs, SUN8I_MIXER_GLOBAL_DBUFF, ++ SUN8I_MIXER_GLOBAL_DBUFF_ENABLE); + } + + static struct drm_plane **sun8i_layers_init(struct drm_device *drm, +@@ -371,25 +375,33 @@ static void sun8i_mixer_mode_set(struct sunxi_engine *engine, + const struct drm_display_mode *mode) + { + struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine); ++ struct regmap *bld_regs, *disp_regs; + u32 bld_base, size, val; + bool interlaced; + + bld_base = sun8i_blender_base(mixer); ++ bld_regs = sun8i_blender_regmap(mixer); + interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE); + size = SUN8I_MIXER_SIZE(mode->hdisplay, mode->vdisplay); + + DRM_DEBUG_DRIVER("Updating global size W: %u H: %u\n", + mode->hdisplay, mode->vdisplay); + +- regmap_write(engine->regs, SUN8I_MIXER_GLOBAL_SIZE, size); +- regmap_write(engine->regs, SUN8I_MIXER_BLEND_OUTSIZE(bld_base), size); ++ if (mixer->cfg->de_type == sun8i_mixer_de33) { ++ disp_regs = mixer->disp_regs; ++ regmap_write(mixer->top_regs, SUN50I_MIXER_GLOBAL_SIZE, size); ++ } else { ++ disp_regs = mixer->engine.regs; ++ regmap_write(mixer->engine.regs, SUN8I_MIXER_GLOBAL_SIZE, size); ++ } ++ regmap_write(bld_regs, SUN8I_MIXER_BLEND_OUTSIZE(bld_base), size); + + if (interlaced) + val = SUN8I_MIXER_BLEND_OUTCTL_INTERLACED; + else + val = 0; + +- regmap_update_bits(engine->regs, SUN8I_MIXER_BLEND_OUTCTL(bld_base), ++ regmap_update_bits(bld_regs, SUN8I_MIXER_BLEND_OUTCTL(bld_base), + SUN8I_MIXER_BLEND_OUTCTL_INTERLACED, val); + + DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n", +@@ -400,10 +412,8 @@ static void sun8i_mixer_mode_set(struct sunxi_engine *engine, + else + val = 0xff108080; + +- regmap_write(mixer->engine.regs, +- SUN8I_MIXER_BLEND_BKCOLOR(bld_base), val); +- regmap_write(mixer->engine.regs, +- SUN8I_MIXER_BLEND_ATTR_FCOLOR(bld_base, 0), val); ++ regmap_write(disp_regs, SUN8I_MIXER_BLEND_BKCOLOR(bld_base), val); ++ regmap_write(disp_regs, SUN8I_MIXER_BLEND_ATTR_FCOLOR(bld_base, 0), val); + + if (mixer->cfg->has_formatter) + sun50i_fmt_setup(mixer, mode->hdisplay, +@@ -443,12 +453,29 @@ static const struct sunxi_engine_ops sun8i_engine_ops = { + }; + + static const struct regmap_config sun8i_mixer_regmap_config = { ++ .name = "layers", + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0xffffc, /* guessed */ + }; + ++static const struct regmap_config sun8i_top_regmap_config = { ++ .name = "top", ++ .reg_bits = 32, ++ .val_bits = 32, ++ .reg_stride = 4, ++ .max_register = 0x3c, ++}; ++ ++static const struct regmap_config sun8i_disp_regmap_config = { ++ .name = "display", ++ .reg_bits = 32, ++ .val_bits = 32, ++ .reg_stride = 4, ++ .max_register = 0x20000, ++}; ++ + static int sun8i_mixer_of_get_id(struct device_node *node) + { + struct device_node *ep, *remote; +@@ -471,33 +498,45 @@ static int sun8i_mixer_of_get_id(struct device_node *node) + + static void sun8i_mixer_init(struct sun8i_mixer *mixer) + { ++ struct regmap *top_regs, *disp_regs; + unsigned int base = sun8i_blender_base(mixer); + int plane_cnt, i; + ++ if (mixer->cfg->de_type == sun8i_mixer_de33) { ++ top_regs = mixer->top_regs; ++ disp_regs = mixer->disp_regs; ++ } else { ++ top_regs = mixer->engine.regs; ++ disp_regs = mixer->engine.regs; ++ } ++ + /* Enable the mixer */ +- regmap_write(mixer->engine.regs, SUN8I_MIXER_GLOBAL_CTL, ++ regmap_write(top_regs, SUN8I_MIXER_GLOBAL_CTL, + SUN8I_MIXER_GLOBAL_CTL_RT_EN); + ++ if (mixer->cfg->de_type == sun8i_mixer_de33) ++ regmap_write(top_regs, SUN50I_MIXER_GLOBAL_CLK, 1); ++ + /* Set background color to black */ +- regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_BKCOLOR(base), ++ regmap_write(disp_regs, SUN8I_MIXER_BLEND_BKCOLOR(base), + SUN8I_MIXER_BLEND_COLOR_BLACK); + + /* + * Set fill color of bottom plane to black. Generally not needed + * except when VI plane is at bottom (zpos = 0) and enabled. + */ +- regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL(base), ++ regmap_write(disp_regs, SUN8I_MIXER_BLEND_PIPE_CTL(base), + SUN8I_MIXER_BLEND_PIPE_CTL_FC_EN(0)); +- regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_ATTR_FCOLOR(base, 0), ++ regmap_write(disp_regs, SUN8I_MIXER_BLEND_ATTR_FCOLOR(base, 0), + SUN8I_MIXER_BLEND_COLOR_BLACK); + + plane_cnt = mixer->cfg->vi_num + mixer->cfg->ui_num; + for (i = 0; i < plane_cnt; i++) +- regmap_write(mixer->engine.regs, ++ regmap_write(disp_regs, + SUN8I_MIXER_BLEND_MODE(base, i), + SUN8I_MIXER_BLEND_MODE_DEF); + +- regmap_update_bits(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL(base), ++ regmap_update_bits(disp_regs, SUN8I_MIXER_BLEND_PIPE_CTL(base), + SUN8I_MIXER_BLEND_PIPE_CTL_EN_MSK, 0); + } + +@@ -573,6 +612,30 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master, + return PTR_ERR(mixer->engine.regs); + } + ++ if (mixer->cfg->de_type == sun8i_mixer_de33) { ++ regs = devm_platform_ioremap_resource(pdev, 1); ++ if (IS_ERR(regs)) ++ return PTR_ERR(regs); ++ ++ mixer->top_regs = devm_regmap_init_mmio(dev, regs, ++ &sun8i_top_regmap_config); ++ if (IS_ERR(mixer->top_regs)) { ++ dev_err(dev, "Couldn't create the top regmap\n"); ++ return PTR_ERR(mixer->top_regs); ++ } ++ ++ regs = devm_platform_ioremap_resource(pdev, 2); ++ if (IS_ERR(regs)) ++ return PTR_ERR(regs); ++ ++ mixer->disp_regs = devm_regmap_init_mmio(dev, regs, ++ &sun8i_disp_regmap_config); ++ if (IS_ERR(mixer->disp_regs)) { ++ dev_err(dev, "Couldn't create the disp regmap\n"); ++ return PTR_ERR(mixer->disp_regs); ++ } ++ } ++ + mixer->reset = devm_reset_control_get(dev, NULL); + if (IS_ERR(mixer->reset)) { + dev_err(dev, "Couldn't get our reset line\n"); +@@ -787,6 +850,18 @@ static const struct sun8i_mixer_cfg sun50i_h6_mixer0_cfg = { + .vi_num = 1, + }; + ++static const struct sun8i_mixer_cfg sun50i_h616_mixer0_cfg = { ++ .ccsc = CCSC_MIXER0_LAYOUT, ++ .de_type = sun8i_mixer_de33, ++ .has_formatter = 1, ++ .mod_rate = 600000000, ++ .scaler_mask = 0xf, ++ .scanline_yuv = 4096, ++ .ui_num = 3, ++ .vi_num = 1, ++ .map = {0, 6, 7, 8}, ++}; ++ + static const struct of_device_id sun8i_mixer_of_table[] = { + { + .compatible = "allwinner,sun8i-a83t-de2-mixer-0", +@@ -832,6 +907,10 @@ static const struct of_device_id sun8i_mixer_of_table[] = { + .compatible = "allwinner,sun50i-h6-de3-mixer-0", + .data = &sun50i_h6_mixer0_cfg, + }, ++ { ++ .compatible = "allwinner,sun50i-h616-de33-mixer-0", ++ .data = &sun50i_h616_mixer0_cfg, ++ }, + { } + }; + MODULE_DEVICE_TABLE(of, sun8i_mixer_of_table); +diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h b/drivers/gpu/drm/sun4i/sun8i_mixer.h +index 75facc7d1fa6..26b001164647 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_mixer.h ++++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h +@@ -21,6 +21,10 @@ + #define SUN8I_MIXER_GLOBAL_DBUFF 0x8 + #define SUN8I_MIXER_GLOBAL_SIZE 0xc + ++#define SUN50I_MIXER_GLOBAL_SIZE 0x8 ++#define SUN50I_MIXER_GLOBAL_CLK 0xc ++#define SUN50I_MIXER_GLOBAL_DBUFF 0x10 ++ + #define SUN8I_MIXER_GLOBAL_CTL_RT_EN BIT(0) + + #define SUN8I_MIXER_GLOBAL_DBUFF_ENABLE BIT(0) +@@ -154,6 +158,7 @@ enum { + enum sun8i_mixer_type { + sun8i_mixer_de2, + sun8i_mixer_de3, ++ sun8i_mixer_de33, + }; + + /** +@@ -180,6 +185,7 @@ struct sun8i_mixer_cfg { + unsigned int de_type; + unsigned int has_formatter : 1; + unsigned int scanline_yuv; ++ unsigned int map[6]; + }; + + struct sun8i_mixer { +@@ -191,6 +197,9 @@ struct sun8i_mixer { + + struct clk *bus_clk; + struct clk *mod_clk; ++ ++ struct regmap *top_regs; ++ struct regmap *disp_regs; + }; + + enum { +@@ -227,13 +236,16 @@ sun8i_blender_base(struct sun8i_mixer *mixer) + static inline struct regmap * + sun8i_blender_regmap(struct sun8i_mixer *mixer) + { +- return mixer->engine.regs; ++ return mixer->cfg->de_type == sun8i_mixer_de33 ? ++ mixer->disp_regs : mixer->engine.regs; + } + + static inline u32 + sun8i_channel_base(struct sun8i_mixer *mixer, int channel) + { +- if (mixer->cfg->de_type == sun8i_mixer_de3) ++ if (mixer->cfg->de_type == sun8i_mixer_de33) ++ return mixer->cfg->map[channel] * 0x20000 + DE2_CH_SIZE; ++ else if (mixer->cfg->de_type == sun8i_mixer_de3) + return DE3_CH_BASE + channel * DE3_CH_SIZE; + else + return DE2_CH_BASE + channel * DE2_CH_SIZE; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de33-vi_scaler-add-Display-Engine-3.3-DE33-support.patch b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de33-vi_scaler-add-Display-Engine-3.3-DE33-support.patch new file mode 100644 index 000000000000..9028d40a3256 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-de33-vi_scaler-add-Display-Engine-3.3-DE33-support.patch @@ -0,0 +1,77 @@ +From 412294545ec91452cc3eccff746a4243879b4cde Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sun, 29 Sep 2024 22:04:56 +1300 +Subject: drm: sun4i: de33: vi_scaler: add Display Engine 3.3 (DE33) support + +The vi_scaler appears to be used in preference to the ui_scaler module +for hardware video scaling in the DE33. + +Enable support for this scaler. + +Signed-off-by: Jernej Skrabec +Signed-off-by: Ryan Walklin +--- + drivers/gpu/drm/sun4i/sun8i_ui_layer.c | 19 +++++++++++++++---- + drivers/gpu/drm/sun4i/sun8i_vi_scaler.c | 7 ++++++- + 2 files changed, 21 insertions(+), 5 deletions(-) + +diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c +index 7f1231cf0f01..180be9d67d9c 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c ++++ b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c +@@ -95,12 +95,23 @@ static int sun8i_ui_layer_update_coord(struct sun8i_mixer *mixer, int channel, + hscale = state->src_w / state->crtc_w; + vscale = state->src_h / state->crtc_h; + +- sun8i_ui_scaler_setup(mixer, channel, src_w, src_h, dst_w, +- dst_h, hscale, vscale, hphase, vphase); +- sun8i_ui_scaler_enable(mixer, channel, true); ++ if (mixer->cfg->de_type == sun8i_mixer_de33) { ++ sun8i_vi_scaler_setup(mixer, channel, src_w, src_h, ++ dst_w, dst_h, hscale, vscale, ++ hphase, vphase, ++ state->fb->format); ++ } else { ++ sun8i_ui_scaler_setup(mixer, channel, src_w, src_h, ++ dst_w, dst_h, hscale, vscale, ++ hphase, vphase); ++ sun8i_ui_scaler_enable(mixer, channel, true); ++ } + } else { + DRM_DEBUG_DRIVER("HW scaling is not needed\n"); +- sun8i_ui_scaler_enable(mixer, channel, false); ++ if (mixer->cfg->de_type == sun8i_mixer_de33) ++ sun8i_vi_scaler_disable(mixer, channel); ++ else ++ sun8i_ui_scaler_enable(mixer, channel, false); + } + + /* Set base coordinates */ +diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c +index e7242301b312..9c7f6e7d71d5 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c ++++ b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c +@@ -835,7 +835,9 @@ static const u32 bicubic4coefftab32[480] = { + + static u32 sun8i_vi_scaler_base(struct sun8i_mixer *mixer, int channel) + { +- if (mixer->cfg->de_type == sun8i_mixer_de3) ++ if (mixer->cfg->de_type == sun8i_mixer_de33) ++ return sun8i_channel_base(mixer, channel) + 0x3000; ++ else if (mixer->cfg->de_type == sun8i_mixer_de3) + return DE3_VI_SCALER_UNIT_BASE + + DE3_VI_SCALER_UNIT_SIZE * channel; + else +@@ -845,6 +847,9 @@ static u32 sun8i_vi_scaler_base(struct sun8i_mixer *mixer, int channel) + + static bool sun8i_vi_scaler_is_vi_plane(struct sun8i_mixer *mixer, int channel) + { ++ if (mixer->cfg->de_type == sun8i_mixer_de33) ++ return mixer->cfg->map[channel] < mixer->cfg->vi_num; ++ + return true; + } + +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-support-YUV-formats-in-VI-scaler.patch b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-support-YUV-formats-in-VI-scaler.patch new file mode 100644 index 000000000000..1aff0b4bcde5 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-support-YUV-formats-in-VI-scaler.patch @@ -0,0 +1,140 @@ +From e0de25f60a3535d345b33dcc541c814499151788 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sun, 29 Sep 2024 22:04:44 +1300 +Subject: drm: sun4i: support YUV formats in VI scaler + +Now that YUV formats are available, enable support in the VI scaler. + +Signed-off-by: Jernej Skrabec +Signed-off-by: Ryan Walklin + +Changelog v4..v5: +- Add commit description +--- + drivers/gpu/drm/sun4i/sun8i_vi_scaler.c | 85 +++++++++++++++++-------- + 1 file changed, 58 insertions(+), 27 deletions(-) + +diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c +index 7ba75011adf9..2e49a6e5f1f1 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c ++++ b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c +@@ -843,6 +843,11 @@ static u32 sun8i_vi_scaler_base(struct sun8i_mixer *mixer, int channel) + DE2_VI_SCALER_UNIT_SIZE * channel; + } + ++static bool sun8i_vi_scaler_is_vi_plane(struct sun8i_mixer *mixer, int channel) ++{ ++ return true; ++} ++ + static int sun8i_vi_scaler_coef_index(unsigned int step) + { + unsigned int scale, int_part, float_part; +@@ -867,44 +872,65 @@ static int sun8i_vi_scaler_coef_index(unsigned int step) + } + } + +-static void sun8i_vi_scaler_set_coeff(struct regmap *map, u32 base, +- u32 hstep, u32 vstep, +- const struct drm_format_info *format) ++static void sun8i_vi_scaler_set_coeff_vi(struct regmap *map, u32 base, ++ u32 hstep, u32 vstep, ++ const struct drm_format_info *format) + { + const u32 *ch_left, *ch_right, *cy; +- int offset, i; ++ int offset; + +- if (format->hsub == 1 && format->vsub == 1) { +- ch_left = lan3coefftab32_left; +- ch_right = lan3coefftab32_right; +- cy = lan2coefftab32; +- } else { ++ if (format->is_yuv) { + ch_left = bicubic8coefftab32_left; + ch_right = bicubic8coefftab32_right; + cy = bicubic4coefftab32; ++ } else { ++ ch_left = lan3coefftab32_left; ++ ch_right = lan3coefftab32_right; ++ cy = lan2coefftab32; + } + + offset = sun8i_vi_scaler_coef_index(hstep) * + SUN8I_VI_SCALER_COEFF_COUNT; +- for (i = 0; i < SUN8I_VI_SCALER_COEFF_COUNT; i++) { +- regmap_write(map, SUN8I_SCALER_VSU_YHCOEFF0(base, i), +- lan3coefftab32_left[offset + i]); +- regmap_write(map, SUN8I_SCALER_VSU_YHCOEFF1(base, i), +- lan3coefftab32_right[offset + i]); +- regmap_write(map, SUN8I_SCALER_VSU_CHCOEFF0(base, i), +- ch_left[offset + i]); +- regmap_write(map, SUN8I_SCALER_VSU_CHCOEFF1(base, i), +- ch_right[offset + i]); +- } ++ regmap_bulk_write(map, SUN8I_SCALER_VSU_YHCOEFF0(base, 0), ++ &lan3coefftab32_left[offset], ++ SUN8I_VI_SCALER_COEFF_COUNT); ++ regmap_bulk_write(map, SUN8I_SCALER_VSU_YHCOEFF1(base, 0), ++ &lan3coefftab32_right[offset], ++ SUN8I_VI_SCALER_COEFF_COUNT); ++ regmap_bulk_write(map, SUN8I_SCALER_VSU_CHCOEFF0(base, 0), ++ &ch_left[offset], SUN8I_VI_SCALER_COEFF_COUNT); ++ regmap_bulk_write(map, SUN8I_SCALER_VSU_CHCOEFF1(base, 0), ++ &ch_right[offset], SUN8I_VI_SCALER_COEFF_COUNT); + + offset = sun8i_vi_scaler_coef_index(hstep) * + SUN8I_VI_SCALER_COEFF_COUNT; +- for (i = 0; i < SUN8I_VI_SCALER_COEFF_COUNT; i++) { +- regmap_write(map, SUN8I_SCALER_VSU_YVCOEFF(base, i), +- lan2coefftab32[offset + i]); +- regmap_write(map, SUN8I_SCALER_VSU_CVCOEFF(base, i), +- cy[offset + i]); +- } ++ regmap_bulk_write(map, SUN8I_SCALER_VSU_YVCOEFF(base, 0), ++ &lan2coefftab32[offset], SUN8I_VI_SCALER_COEFF_COUNT); ++ regmap_bulk_write(map, SUN8I_SCALER_VSU_CVCOEFF(base, 0), ++ &cy[offset], SUN8I_VI_SCALER_COEFF_COUNT); ++} ++ ++static void sun8i_vi_scaler_set_coeff_ui(struct regmap *map, u32 base, ++ u32 hstep, u32 vstep, ++ const struct drm_format_info *format) ++{ ++ const u32 *table; ++ int offset; ++ ++ offset = sun8i_vi_scaler_coef_index(hstep) * ++ SUN8I_VI_SCALER_COEFF_COUNT; ++ regmap_bulk_write(map, SUN8I_SCALER_VSU_YHCOEFF0(base, 0), ++ &lan2coefftab32[offset], SUN8I_VI_SCALER_COEFF_COUNT); ++ offset = sun8i_vi_scaler_coef_index(vstep) * ++ SUN8I_VI_SCALER_COEFF_COUNT; ++ regmap_bulk_write(map, SUN8I_SCALER_VSU_YVCOEFF(base, 0), ++ &lan2coefftab32[offset], SUN8I_VI_SCALER_COEFF_COUNT); ++ ++ table = format->is_yuv ? bicubic4coefftab32 : lan2coefftab32; ++ offset = sun8i_vi_scaler_coef_index(hstep) * ++ SUN8I_VI_SCALER_COEFF_COUNT; ++ regmap_bulk_write(map, SUN8I_SCALER_VSU_CHCOEFF0(base, 0), ++ &table[offset], SUN8I_VI_SCALER_COEFF_COUNT); + } + + void sun8i_vi_scaler_enable(struct sun8i_mixer *mixer, int layer, bool enable) +@@ -994,6 +1020,11 @@ void sun8i_vi_scaler_setup(struct sun8i_mixer *mixer, int layer, + SUN8I_SCALER_VSU_CHPHASE(base), chphase); + regmap_write(mixer->engine.regs, + SUN8I_SCALER_VSU_CVPHASE(base), cvphase); +- sun8i_vi_scaler_set_coeff(mixer->engine.regs, base, +- hscale, vscale, format); ++ ++ if (sun8i_vi_scaler_is_vi_plane(mixer, layer)) ++ sun8i_vi_scaler_set_coeff_vi(mixer->engine.regs, base, ++ hscale, vscale, format); ++ else ++ sun8i_vi_scaler_set_coeff_ui(mixer->engine.regs, base, ++ hscale, vscale, format); + } +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-vi_scaler-refactor-vi_scaler-enablement.patch b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-vi_scaler-refactor-vi_scaler-enablement.patch new file mode 100644 index 000000000000..59a56dd02497 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.drm/drm-sun4i-vi_scaler-refactor-vi_scaler-enablement.patch @@ -0,0 +1,100 @@ +From 0c10a80b8e37d9a7fc57d8bf968c70419423065a Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sun, 29 Sep 2024 22:04:47 +1300 +Subject: drm: sun4i: vi_scaler refactor vi_scaler enablement + +If the video scaler is required, then it is obligatory to set the +relevant register to enable it, so move this to the +sun8i_vi_scaler_setup() function. + +This simplifies the alternate case (scaler not required) so replace the +vi_scaler_enable() function with a vi_scaler_disable() function. + +Signed-off-by: Jernej Skrabec +Signed-off-by: Ryan Walklin +--- + drivers/gpu/drm/sun4i/sun8i_vi_layer.c | 3 +-- + drivers/gpu/drm/sun4i/sun8i_vi_scaler.c | 21 +++++++++++---------- + drivers/gpu/drm/sun4i/sun8i_vi_scaler.h | 2 +- + 3 files changed, 13 insertions(+), 13 deletions(-) + +diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c +index 4647e9bcccaa..e348fd0a3d81 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c ++++ b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c +@@ -156,10 +156,9 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel, + sun8i_vi_scaler_setup(mixer, channel, src_w, src_h, dst_w, + dst_h, hscale, vscale, hphase, vphase, + format); +- sun8i_vi_scaler_enable(mixer, channel, true); + } else { + DRM_DEBUG_DRIVER("HW scaling is not needed\n"); +- sun8i_vi_scaler_enable(mixer, channel, false); ++ sun8i_vi_scaler_disable(mixer, channel); + } + + regmap_write(mixer->engine.regs, +diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c +index aa346c3beb30..e7242301b312 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c ++++ b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c +@@ -933,20 +933,13 @@ static void sun8i_vi_scaler_set_coeff_ui(struct regmap *map, u32 base, + &table[offset], SUN8I_VI_SCALER_COEFF_COUNT); + } + +-void sun8i_vi_scaler_enable(struct sun8i_mixer *mixer, int layer, bool enable) ++void sun8i_vi_scaler_disable(struct sun8i_mixer *mixer, int layer) + { +- u32 val, base; ++ u32 base; + + base = sun8i_vi_scaler_base(mixer, layer); + +- if (enable) +- val = SUN8I_SCALER_VSU_CTRL_EN | +- SUN8I_SCALER_VSU_CTRL_COEFF_RDY; +- else +- val = 0; +- +- regmap_write(mixer->engine.regs, +- SUN8I_SCALER_VSU_CTRL(base), val); ++ regmap_write(mixer->engine.regs, SUN8I_SCALER_VSU_CTRL(base), 0); + } + + void sun8i_vi_scaler_setup(struct sun8i_mixer *mixer, int layer, +@@ -982,6 +975,9 @@ void sun8i_vi_scaler_setup(struct sun8i_mixer *mixer, int layer, + cvphase = vphase; + } + ++ regmap_write(mixer->engine.regs, SUN8I_SCALER_VSU_CTRL(base), ++ SUN8I_SCALER_VSU_CTRL_EN); ++ + if (mixer->cfg->de_type >= sun8i_mixer_de3) { + u32 val; + +@@ -1027,4 +1023,9 @@ void sun8i_vi_scaler_setup(struct sun8i_mixer *mixer, int layer, + else + sun8i_vi_scaler_set_coeff_ui(mixer->engine.regs, base, + hscale, vscale, format); ++ ++ if (mixer->cfg->de_type <= sun8i_mixer_de3) ++ regmap_write(mixer->engine.regs, SUN8I_SCALER_VSU_CTRL(base), ++ SUN8I_SCALER_VSU_CTRL_EN | ++ SUN8I_SCALER_VSU_CTRL_COEFF_RDY); + } +diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.h b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.h +index 68f6593b369a..e801bc7a4189 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.h ++++ b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.h +@@ -69,7 +69,7 @@ + #define SUN50I_SCALER_VSU_ANGLE_SHIFT(x) (((x) << 16) & 0xF) + #define SUN50I_SCALER_VSU_ANGLE_OFFSET(x) ((x) & 0xFF) + +-void sun8i_vi_scaler_enable(struct sun8i_mixer *mixer, int layer, bool enable); ++void sun8i_vi_scaler_disable(struct sun8i_mixer *mixer, int layer); + void sun8i_vi_scaler_setup(struct sun8i_mixer *mixer, int layer, + u32 src_w, u32 src_h, u32 dst_w, u32 dst_h, + u32 hscale, u32 vscale, u32 hphase, u32 vphase, +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.drm/dt-bindings-allwinner-add-H616-DE33-bus-binding.patch b/patch/kernel/archive/sunxi-6.14/patches.drm/dt-bindings-allwinner-add-H616-DE33-bus-binding.patch new file mode 100644 index 000000000000..86b0c0cb30ca --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.drm/dt-bindings-allwinner-add-H616-DE33-bus-binding.patch @@ -0,0 +1,35 @@ +From b8344d8eb9d000bc2984a5fcafc25b527f361b5b Mon Sep 17 00:00:00 2001 +From: Ryan Walklin +Date: Sun, 29 Sep 2024 22:04:51 +1300 +Subject: dt-bindings: allwinner: add H616 DE33 bus binding + +The Allwinner H616 and variants have a new display engine revision +(DE33). + +Add a display engine bus binding for the DE33. + +Signed-off-by: Ryan Walklin +Acked-by: Conor Dooley +Reviewed-by: Chen-Yu Tsai +--- + .../devicetree/bindings/bus/allwinner,sun50i-a64-de2.yaml | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/Documentation/devicetree/bindings/bus/allwinner,sun50i-a64-de2.yaml b/Documentation/devicetree/bindings/bus/allwinner,sun50i-a64-de2.yaml +index 9845a187bdf6..ea7ee89158c6 100644 +--- a/Documentation/devicetree/bindings/bus/allwinner,sun50i-a64-de2.yaml ++++ b/Documentation/devicetree/bindings/bus/allwinner,sun50i-a64-de2.yaml +@@ -24,7 +24,9 @@ properties: + oneOf: + - const: allwinner,sun50i-a64-de2 + - items: +- - const: allwinner,sun50i-h6-de3 ++ - enum: ++ - allwinner,sun50i-h6-de3 ++ - allwinner,sun50i-h616-de33 + - const: allwinner,sun50i-a64-de2 + + reg: +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.drm/dt-bindings-allwinner-add-H616-DE33-clock-binding.patch b/patch/kernel/archive/sunxi-6.14/patches.drm/dt-bindings-allwinner-add-H616-DE33-clock-binding.patch new file mode 100644 index 000000000000..65411eb517a8 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.drm/dt-bindings-allwinner-add-H616-DE33-clock-binding.patch @@ -0,0 +1,32 @@ +From 58a606c8136c57d23b4e35f0f50cb140f1d65d9b Mon Sep 17 00:00:00 2001 +From: Ryan Walklin +Date: Sun, 29 Sep 2024 22:04:52 +1300 +Subject: dt-bindings: allwinner: add H616 DE33 clock binding + +The Allwinner H616 and variants have a new display engine revision +(DE33). + +Add a clock binding for the DE33. + +Signed-off-by: Ryan Walklin +Acked-by: Conor Dooley +Reviewed-by: Chen-Yu Tsai +--- + .../devicetree/bindings/clock/allwinner,sun8i-a83t-de2-clk.yaml | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/Documentation/devicetree/bindings/clock/allwinner,sun8i-a83t-de2-clk.yaml b/Documentation/devicetree/bindings/clock/allwinner,sun8i-a83t-de2-clk.yaml +index 70369bd633e4..7fcd55d468d4 100644 +--- a/Documentation/devicetree/bindings/clock/allwinner,sun8i-a83t-de2-clk.yaml ++++ b/Documentation/devicetree/bindings/clock/allwinner,sun8i-a83t-de2-clk.yaml +@@ -25,6 +25,7 @@ properties: + - const: allwinner,sun50i-a64-de2-clk + - const: allwinner,sun50i-h5-de2-clk + - const: allwinner,sun50i-h6-de3-clk ++ - const: allwinner,sun50i-h616-de33-clk + - items: + - const: allwinner,sun8i-r40-de2-clk + - const: allwinner,sun8i-h3-de2-clk +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.drm/dt-bindings-allwinner-add-H616-DE33-mixer-binding.patch b/patch/kernel/archive/sunxi-6.14/patches.drm/dt-bindings-allwinner-add-H616-DE33-mixer-binding.patch new file mode 100644 index 000000000000..d3a92736cc03 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.drm/dt-bindings-allwinner-add-H616-DE33-mixer-binding.patch @@ -0,0 +1,36 @@ +From 12d7983166ed867dd72c023af036b1397aec66ba Mon Sep 17 00:00:00 2001 +From: Ryan Walklin +Date: Sun, 29 Sep 2024 22:04:53 +1300 +Subject: dt-bindings: allwinner: add H616 DE33 mixer binding + +The Allwinner H616 and variants have a new display engine revision +(DE33). + +The mixer configuration registers are significantly different to the DE3 +and DE2 revisions, being split into separate top and display blocks, +therefore a fallback for the mixer compatible is not provided. + +Add a display engine mixer binding for the DE33. + +Signed-off-by: Ryan Walklin +Acked-by: Conor Dooley +Reviewed-by: Chen-Yu Tsai +--- + .../bindings/display/allwinner,sun8i-a83t-de2-mixer.yaml | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/Documentation/devicetree/bindings/display/allwinner,sun8i-a83t-de2-mixer.yaml b/Documentation/devicetree/bindings/display/allwinner,sun8i-a83t-de2-mixer.yaml +index b75c1ec686ad..c37eb8ae1b8e 100644 +--- a/Documentation/devicetree/bindings/display/allwinner,sun8i-a83t-de2-mixer.yaml ++++ b/Documentation/devicetree/bindings/display/allwinner,sun8i-a83t-de2-mixer.yaml +@@ -24,6 +24,7 @@ properties: + - allwinner,sun50i-a64-de2-mixer-0 + - allwinner,sun50i-a64-de2-mixer-1 + - allwinner,sun50i-h6-de3-mixer-0 ++ - allwinner,sun50i-h616-de33-mixer-0 + + reg: + maxItems: 1 +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.drm/dt-bindings-gpu-mali-bifrost-Add-Allwinner-H616-compatible.patch b/patch/kernel/archive/sunxi-6.14/patches.drm/dt-bindings-gpu-mali-bifrost-Add-Allwinner-H616-compatible.patch new file mode 100644 index 000000000000..73e24e0afa40 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.drm/dt-bindings-gpu-mali-bifrost-Add-Allwinner-H616-compatible.patch @@ -0,0 +1,30 @@ +From 7f350d05012873c129458241792d005ef1344d9a Mon Sep 17 00:00:00 2001 +From: Andre Przywara +Date: Fri, 21 Feb 2025 00:58:00 +0000 +Subject: dt-bindings: gpu: mali-bifrost: Add Allwinner H616 compatible + +The Allwinner H616 SoC has a Mali-G31 MP2 GPU, which is of the Mali +Bifrost family. +Add the SoC specific compatible string and pair it with the bifrost +fallback compatible. + +Signed-off-by: Andre Przywara +--- + Documentation/devicetree/bindings/gpu/arm,mali-bifrost.yaml | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/Documentation/devicetree/bindings/gpu/arm,mali-bifrost.yaml b/Documentation/devicetree/bindings/gpu/arm,mali-bifrost.yaml +index 735c7f06c24e..439d5c59daa2 100644 +--- a/Documentation/devicetree/bindings/gpu/arm,mali-bifrost.yaml ++++ b/Documentation/devicetree/bindings/gpu/arm,mali-bifrost.yaml +@@ -17,6 +17,7 @@ properties: + oneOf: + - items: + - enum: ++ - allwinner,sun50i-h616-mali + - amlogic,meson-g12a-mali + - mediatek,mt8183-mali + - mediatek,mt8183b-mali +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.drm/dt-bindings-power-Add-Allwinner-H6-H616-PRCM-PPU.patch b/patch/kernel/archive/sunxi-6.14/patches.drm/dt-bindings-power-Add-Allwinner-H6-H616-PRCM-PPU.patch new file mode 100644 index 000000000000..87ec283f9e11 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.drm/dt-bindings-power-Add-Allwinner-H6-H616-PRCM-PPU.patch @@ -0,0 +1,67 @@ +From af0be61ac5ff0f86567ddbf3924c46eaa211e07f Mon Sep 17 00:00:00 2001 +From: Andre Przywara +Date: Fri, 21 Feb 2025 00:57:58 +0000 +Subject: dt-bindings: power: Add Allwinner H6/H616 PRCM PPU + +The Allwinner H6 and some later SoCs contain some bits in the PRCM (Power +Reset Clock Management) block that control some power domains. +Those power domains include the one for the GPU, the PLLs and some +analogue circuits. + +Signed-off-by: Andre Przywara +--- + .../power/allwinner,sun50i-h6-prcm-ppu.yaml | 42 +++++++++++++++++++ + 1 file changed, 42 insertions(+) + create mode 100644 Documentation/devicetree/bindings/power/allwinner,sun50i-h6-prcm-ppu.yaml + +diff --git a/Documentation/devicetree/bindings/power/allwinner,sun50i-h6-prcm-ppu.yaml b/Documentation/devicetree/bindings/power/allwinner,sun50i-h6-prcm-ppu.yaml +new file mode 100644 +index 000000000000..7eaff9baf726 +--- /dev/null ++++ b/Documentation/devicetree/bindings/power/allwinner,sun50i-h6-prcm-ppu.yaml +@@ -0,0 +1,42 @@ ++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/power/allwinner,sun50i-h6-prcm-ppu.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Allwinner SoCs PRCM power domain controller ++ ++maintainers: ++ - Andre Przywara ++ ++description: ++ The Allwinner Power Reset Clock Management (PRCM) unit contains bits to ++ control a few power domains. ++ ++properties: ++ compatible: ++ enum: ++ - allwinner,sun50i-h6-prcm-ppu ++ - allwinner,sun50i-h616-prcm-ppu ++ - allwinner,sun55i-a523-prcm-ppu ++ ++ reg: ++ maxItems: 1 ++ ++ '#power-domain-cells': ++ const: 1 ++ ++required: ++ - compatible ++ - reg ++ - '#power-domain-cells' ++ ++additionalProperties: false ++ ++examples: ++ - | ++ prcm_ppu: power-controller@7010210 { ++ compatible = "allwinner,sun50i-h616-prcm-ppu"; ++ reg = <0x07010250 0x10>; ++ #power-domain-cells = <1>; ++ }; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.drm/pmdomain-sunxi-add-H6-PRCM-PPU-driver.patch b/patch/kernel/archive/sunxi-6.14/patches.drm/pmdomain-sunxi-add-H6-PRCM-PPU-driver.patch new file mode 100644 index 000000000000..2e7eccbb339a --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.drm/pmdomain-sunxi-add-H6-PRCM-PPU-driver.patch @@ -0,0 +1,249 @@ +From 9fb1314045a676823428efa503b581c2910169d7 Mon Sep 17 00:00:00 2001 +From: Andre Przywara +Date: Fri, 21 Feb 2025 00:57:59 +0000 +Subject: pmdomain: sunxi: add H6 PRCM PPU driver + +The Allwinner Power Reset Clock Management (RPCM) block contains a few +bits that control some power domains. The most prominent one is the one +for the Mali GPU. On the Allwinner H6 this domain is enabled at reset, so +we didn't care about it so far, but the H616 defaults to it being disabled. + +Add a power domain driver for those bits. Some BSP code snippets and +some spare documentation describe three bits, slightly different between +the H6 and H616, so add three power domains for each SoC, connected to +their compatible string. + +Signed-off-by: Andre Przywara +--- + drivers/pmdomain/sunxi/Kconfig | 10 + + drivers/pmdomain/sunxi/Makefile | 1 + + drivers/pmdomain/sunxi/sun50i-h6-prcm-ppu.c | 191 ++++++++++++++++++++ + 3 files changed, 202 insertions(+) + create mode 100644 drivers/pmdomain/sunxi/sun50i-h6-prcm-ppu.c + +diff --git a/drivers/pmdomain/sunxi/Kconfig b/drivers/pmdomain/sunxi/Kconfig +index 17781bf8d86d..43eecb3ea981 100644 +--- a/drivers/pmdomain/sunxi/Kconfig ++++ b/drivers/pmdomain/sunxi/Kconfig +@@ -8,3 +8,13 @@ config SUN20I_PPU + help + Say y to enable the PPU power domain driver. This saves power + when certain peripherals, such as the video engine, are idle. ++ ++config SUN50I_H6_PRCM_PPU ++ tristate "Allwinner H6 PRCM power domain driver" ++ depends on ARCH_SUNXI || COMPILE_TEST ++ depends on PM ++ select PM_GENERIC_DOMAINS ++ help ++ Say y to enable the Allwinner H6/H616 PRCM power domain driver. ++ This is required to enable the Mali GPU in the H616 SoC, it is ++ optional for the H6. +diff --git a/drivers/pmdomain/sunxi/Makefile b/drivers/pmdomain/sunxi/Makefile +index ec1d7a2fb21d..c1343e123759 100644 +--- a/drivers/pmdomain/sunxi/Makefile ++++ b/drivers/pmdomain/sunxi/Makefile +@@ -1,2 +1,3 @@ + # SPDX-License-Identifier: GPL-2.0-only + obj-$(CONFIG_SUN20I_PPU) += sun20i-ppu.o ++obj-$(CONFIG_SUN50I_H6_PRCM_PPU) += sun50i-h6-prcm-ppu.o +diff --git a/drivers/pmdomain/sunxi/sun50i-h6-prcm-ppu.c b/drivers/pmdomain/sunxi/sun50i-h6-prcm-ppu.c +new file mode 100644 +index 000000000000..1c6b0c78b222 +--- /dev/null ++++ b/drivers/pmdomain/sunxi/sun50i-h6-prcm-ppu.c +@@ -0,0 +1,191 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Copyright (C) Arm Ltd. 2024 ++ * ++ * Allwinner H6/H616 PRCM power domain driver. ++ * This covers a few registers inside the PRCM (Power Reset Clock Management) ++ * block that control some power rails, most prominently for the Mali GPU. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * The PRCM block covers multiple devices, starting with some clocks, ++ * then followed by the power rails. ++ * The clocks are covered by a different driver, so this driver's MMIO range ++ * starts later in the PRCM MMIO frame, not at the beginning of it. ++ * To keep the register offsets consistent with other PRCM documentation, ++ * express the registers relative to the beginning of the whole PRCM, and ++ * subtract the PPU offset this driver is bound to. ++ */ ++#define PD_H6_PPU_OFFSET 0x250 ++#define PD_H6_VDD_SYS_REG 0x250 ++#define PD_H616_ANA_VDD_GATE BIT(4) ++#define PD_H6_CPUS_VDD_GATE BIT(3) ++#define PD_H6_AVCC_VDD_GATE BIT(2) ++#define PD_H6_GPU_REG 0x254 ++#define PD_H6_GPU_GATE BIT(0) ++ ++struct sun50i_h6_ppu_pd { ++ struct generic_pm_domain genpd; ++ void __iomem *reg; ++ u32 gate_mask; ++ bool negated; ++}; ++ ++#define FLAG_PPU_ALWAYS_ON BIT(0) ++#define FLAG_PPU_NEGATED BIT(1) ++ ++struct sun50i_h6_ppu_desc { ++ const char *name; ++ u32 offset; ++ u32 mask; ++ unsigned int flags; ++}; ++ ++struct sun50i_h6_ppu_desc sun50i_h6_ppus[] = { ++ { "AVCC", PD_H6_VDD_SYS_REG, PD_H6_AVCC_VDD_GATE }, ++ { "CPUS", PD_H6_VDD_SYS_REG, PD_H6_CPUS_VDD_GATE }, ++ { "GPU", PD_H6_GPU_REG, PD_H6_GPU_GATE }, ++ {} ++}; ++ ++struct sun50i_h6_ppu_desc sun50i_h616_ppus[] = { ++ { "PLL", PD_H6_VDD_SYS_REG, PD_H6_AVCC_VDD_GATE, ++ FLAG_PPU_ALWAYS_ON | FLAG_PPU_NEGATED }, ++ { "ANA", PD_H6_VDD_SYS_REG, PD_H616_ANA_VDD_GATE, FLAG_PPU_ALWAYS_ON }, ++ { "GPU", PD_H6_GPU_REG, PD_H6_GPU_GATE, FLAG_PPU_NEGATED }, ++ {} ++}; ++#define to_sun50i_h6_ppu_pd(_genpd) \ ++ container_of(_genpd, struct sun50i_h6_ppu_pd, genpd) ++ ++static bool sun50i_h6_ppu_power_status(const struct sun50i_h6_ppu_pd *pd) ++{ ++ bool bit = readl(pd->reg) & pd->gate_mask; ++ ++ return bit ^ pd->negated; ++} ++ ++static int sun50i_h6_ppu_pd_set_power(const struct sun50i_h6_ppu_pd *pd, ++ bool set_bit) ++{ ++ u32 reg = readl(pd->reg); ++ ++ if (set_bit) ++ writel(reg | pd->gate_mask, pd->reg); ++ else ++ writel(reg & ~pd->gate_mask, pd->reg); ++ ++ return 0; ++} ++ ++static int sun50i_h6_ppu_pd_power_on(struct generic_pm_domain *genpd) ++{ ++ const struct sun50i_h6_ppu_pd *pd = to_sun50i_h6_ppu_pd(genpd); ++ ++ return sun50i_h6_ppu_pd_set_power(pd, !pd->negated); ++} ++ ++static int sun50i_h6_ppu_pd_power_off(struct generic_pm_domain *genpd) ++{ ++ const struct sun50i_h6_ppu_pd *pd = to_sun50i_h6_ppu_pd(genpd); ++ ++ return sun50i_h6_ppu_pd_set_power(pd, pd->negated); ++} ++ ++static int sun50i_h6_ppu_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct genpd_onecell_data *ppu; ++ struct sun50i_h6_ppu_pd *pds; ++ const struct sun50i_h6_ppu_desc *desc; ++ void __iomem *base; ++ int ret, i, count; ++ ++ desc = of_device_get_match_data(dev); ++ if (!desc) ++ return -EINVAL; ++ ++ for (count = 0; desc[count].name; count++) ++ ; ++ ++ pds = devm_kcalloc(dev, count, sizeof(*pds), GFP_KERNEL); ++ if (!pds) ++ return -ENOMEM; ++ ++ ppu = devm_kzalloc(dev, sizeof(*ppu), GFP_KERNEL); ++ if (!ppu) ++ return -ENOMEM; ++ ++ ppu->num_domains = count; ++ ppu->domains = devm_kcalloc(dev, count, sizeof(*ppu->domains), ++ GFP_KERNEL); ++ if (!ppu->domains) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, ppu); ++ ++ base = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ for (i = 0; i < count; i++) { ++ struct sun50i_h6_ppu_pd *pd = &pds[i]; ++ ++ pd->genpd.name = desc[i].name; ++ pd->genpd.power_off = sun50i_h6_ppu_pd_power_off; ++ pd->genpd.power_on = sun50i_h6_ppu_pd_power_on; ++ if (desc[i].flags & FLAG_PPU_ALWAYS_ON) ++ pd->genpd.flags = GENPD_FLAG_ALWAYS_ON; ++ pd->negated = !!(desc[i].flags & FLAG_PPU_NEGATED); ++ pd->reg = base + desc[i].offset - PD_H6_PPU_OFFSET; ++ pd->gate_mask = desc[i].mask; ++ ++ ret = pm_genpd_init(&pd->genpd, NULL, ++ !sun50i_h6_ppu_power_status(pd)); ++ if (ret) { ++ dev_warn(dev, "Failed to add GPU power domain: %d\n", ret); ++ return ret; ++ } ++ ppu->domains[i] = &pd->genpd; ++ } ++ ++ ret = of_genpd_add_provider_onecell(dev->of_node, ppu); ++ if (ret) ++ dev_warn(dev, "Failed to add provider: %d\n", ret); ++ ++ return 0; ++} ++ ++static const struct of_device_id sun50i_h6_ppu_of_match[] = { ++ { .compatible = "allwinner,sun50i-h6-prcm-ppu", ++ .data = &sun50i_h6_ppus }, ++ { .compatible = "allwinner,sun50i-h616-prcm-ppu", ++ .data = &sun50i_h616_ppus }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, sun50i_h6_ppu_of_match); ++ ++static struct platform_driver sun50i_h6_ppu_driver = { ++ .probe = sun50i_h6_ppu_probe, ++ .driver = { ++ .name = "sun50i-h6-prcm-ppu", ++ .of_match_table = sun50i_h6_ppu_of_match, ++ /* Power domains cannot be removed while they are in use. */ ++ .suppress_bind_attrs = true, ++ }, ++}; ++module_platform_driver(sun50i_h6_ppu_driver); ++ ++MODULE_AUTHOR("Andre Przywara "); ++MODULE_DESCRIPTION("Allwinner H6 PRCM power domain driver"); ++MODULE_LICENSE("GPL"); +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/clk-sunxi-ng-sun50i-a64-Switch-parent-of-MIPI-DSI-to-periph0-1x.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/clk-sunxi-ng-sun50i-a64-Switch-parent-of-MIPI-DSI-to-periph0-1x.patch index b7876a623520..b12fb911ef18 100644 --- a/patch/kernel/archive/sunxi-6.14/patches.megous/clk-sunxi-ng-sun50i-a64-Switch-parent-of-MIPI-DSI-to-periph0-1x.patch +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/clk-sunxi-ng-sun50i-a64-Switch-parent-of-MIPI-DSI-to-periph0-1x.patch @@ -13,7 +13,7 @@ Signed-off-by: Ondrej Jirman 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-a64.c b/drivers/clk/sunxi-ng/ccu-sun50i-a64.c -index c8cdb342b1a0..e76a1c10f390 100644 +index 4b3068bdfdf2..e76a1c10f390 100644 --- a/drivers/clk/sunxi-ng/ccu-sun50i-a64.c +++ b/drivers/clk/sunxi-ng/ccu-sun50i-a64.c @@ -962,6 +962,8 @@ static struct ccu_mux_nb sun50i_a64_cpu_nb = { @@ -25,24 +25,24 @@ index c8cdb342b1a0..e76a1c10f390 100644 static int sun50i_a64_ccu_probe(struct platform_device *pdev) { void __iomem *reg; -@@ -981,9 +983,16 @@ static int sun50i_a64_ccu_probe(struct platform_device *pdev) +@@ -980,7 +982,16 @@ static int sun50i_a64_ccu_probe(struct platform_device *pdev) + /* Decrease the PLL AUDIO bias current to reduce noise. */ writel(0x10040000, reg + SUN50I_A64_PLL_AUDIO_BIAS_REG); - ret = of_property_read_u32_index(of_chosen, "p-boot,framebuffer-start", 0, &val); -- if (ret) +- writel(0x515, reg + SUN50I_A64_PLL_MIPI_REG); ++ ret = of_property_read_u32_index(of_chosen, "p-boot,framebuffer-start", 0, &val); + if (ret) { - writel(0x515, reg + SUN50I_A64_PLL_MIPI_REG); - ++ writel(0x515, reg + SUN50I_A64_PLL_MIPI_REG); ++ + /* Set MIPI-DSI clock parent to periph0(1x), so that video0(1x) is free to change. */ + val = readl(reg + CCU_MIPI_DSI_CLK); + val &= 0x30f; + val |= (2 << 8) | ((4 - 1) << 0); /* M-1 */ + writel(val, reg + CCU_MIPI_DSI_CLK); + } -+ + ret = devm_sunxi_ccu_probe(&pdev->dev, reg, &sun50i_a64_ccu_desc); if (ret) - return ret; -- 2.35.3 diff --git a/patch/kernel/archive/sunxi-6.14/patches.megous/video-pwm_bl-Allow-to-change-lth_brightness-via-sysfs.patch b/patch/kernel/archive/sunxi-6.14/patches.megous/video-pwm_bl-Allow-to-change-lth_brightness-via-sysfs.patch index c49c442432bf..8923f26425a9 100644 --- a/patch/kernel/archive/sunxi-6.14/patches.megous/video-pwm_bl-Allow-to-change-lth_brightness-via-sysfs.patch +++ b/patch/kernel/archive/sunxi-6.14/patches.megous/video-pwm_bl-Allow-to-change-lth_brightness-via-sysfs.patch @@ -25,7 +25,7 @@ Signed-off-by: Ondrej Jirman 1 file changed, 70 insertions(+), 2 deletions(-) diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c -index f7d9d8813d92..8a7a158dc1f6 100644 +index 237d3d3f3bb1..9f96bdd82a87 100644 --- a/drivers/video/backlight/pwm_bl.c +++ b/drivers/video/backlight/pwm_bl.c @@ -437,6 +437,61 @@ static int pwm_backlight_initial_power_state(const struct pwm_bl_data *pb) @@ -90,15 +90,29 @@ index f7d9d8813d92..8a7a158dc1f6 100644 static int pwm_backlight_probe(struct platform_device *pdev) { struct platform_pwm_backlight_data *data = dev_get_platdata(&pdev->dev); -@@ -445,6 +500,7 @@ static int pwm_backlight_probe(struct platform_device *pdev) +@@ -444,7 +499,8 @@ static int pwm_backlight_probe(struct platform_device *pdev) + struct backlight_properties props; struct backlight_device *bl; struct pwm_bl_data *pb; - struct pwm_state state, state_real; +- struct pwm_state state; ++ struct pwm_state state, state_real; + u32 lth_brightness; unsigned int i; int ret; -@@ -584,8 +640,20 @@ static int pwm_backlight_probe(struct platform_device *pdev) +@@ -509,6 +565,11 @@ static int pwm_backlight_probe(struct platform_device *pdev) + /* Sync up PWM state. */ + pwm_init_state(pb->pwm, &state); + ++ /* Read real state, but only if the PWM is enabled. */ ++ pwm_get_state(pb->pwm, &state_real); ++ if (state_real.enabled) ++ state = state_real; ++ + /* + * The DT case will set the pwm_period_ns field to 0 and store the + * period, parsed from the DT, in the PWM device. For the non-DT case, +@@ -579,8 +640,20 @@ static int pwm_backlight_probe(struct platform_device *pdev) pb->scale = data->max_brightness; } @@ -121,6 +135,31 @@ index f7d9d8813d92..8a7a158dc1f6 100644 props.type = BACKLIGHT_RAW; props.max_brightness = data->max_brightness; +@@ -601,6 +674,24 @@ static int pwm_backlight_probe(struct platform_device *pdev) + + bl->props.brightness = data->dft_brightness; + bl->props.power = pwm_backlight_initial_power_state(pb); ++ if (bl->props.power == FB_BLANK_UNBLANK && pb->levels) { ++ u64 level; ++ ++ /* If the backlight is already on, determine the default ++ * brightness from PWM duty cycle instead of forcing ++ * the brightness determined by the driver ++ */ ++ pwm_get_state(pb->pwm, &state); ++ level = (u64)state.duty_cycle * pb->scale; ++ do_div(level, (u64)state.period); ++ ++ for (i = 0; i <= data->max_brightness; i++) { ++ if (data->levels[i] > level) { ++ bl->props.brightness = i; ++ break; ++ } ++ } ++ } + backlight_update_status(bl); + + platform_set_drvdata(pdev, bl); -- 2.35.3 diff --git a/patch/kernel/archive/sunxi-6.14/series.conf b/patch/kernel/archive/sunxi-6.14/series.conf index 9b2f89584aa6..d21952e30b38 100644 --- a/patch/kernel/archive/sunxi-6.14/series.conf +++ b/patch/kernel/archive/sunxi-6.14/series.conf @@ -180,10 +180,10 @@ patches.megous/arm64-dts-rk3399-Add-dmc_opp_table.patch patches.megous/arm64-dts-rockchip-rk3399-s-Add-DMC-table.patch patches.megous/bluetooth-h5-Don-t-re-initialize-rtl8723cs-on-resume.patch - patches.megous/drm-sun4i-Mark-one-of-the-UI-planes-as-a-cursor-one.patch - patches.megous/drm-sun4i-Implement-gamma-correction.patch +- patches.megous/drm-sun4i-Mark-one-of-the-UI-planes-as-a-cursor-one.patch +- patches.megous/drm-sun4i-Implement-gamma-correction.patch patches.megous/drm-panel-st7703-Fix-xbd599-timings-to-make-refresh-rate-exactl.patch - patches.megous/drm-sun4i-Support-taking-over-display-pipeline-state-from-p-boo.patch +- patches.megous/drm-sun4i-Support-taking-over-display-pipeline-state-from-p-boo.patch patches.megous/video-pwm_bl-Allow-to-change-lth_brightness-via-sysfs.patch patches.megous/clk-sunxi-ng-sun50i-a64-Switch-parent-of-MIPI-DSI-to-periph0-1x.patch patches.megous/drm-sun4i-tcon-Support-keeping-dclk-rate-upon-ancestor-clock-ch.patch @@ -245,5 +245,46 @@ patches.megous/Add-support-for-my-private-Sapomat-device.patch patches.megous/ARM-dts-sun8i-h3-orange-pi-one-Enable-all-gpio-header-UARTs.patch patches.megous/mtd-spi-nor-Add-Alliance-memory-support.patch - patches.megous/Add-README.md-with-information-and-u-boot-patches.patch +- patches.megous/Add-README.md-with-information-and-u-boot-patches.patch patches.megous/Defconfigs-for-all-my-devices.patch + +################################################################################ +# +# drivers/gpu/drm/sun4i/ +# +################################################################################ + patches.drm/drm-sun4i-de2-de3-Change-CSC-argument.patch + patches.drm/drm-sun4i-de2-de3-Merge-CSC-functions-into-one.patch + patches.drm/drm-sun4i-de2-de3-call-csc-setup-also-for-UI-layer.patch + patches.drm/drm-sun4i-de2-Initialize-layer-fields-earlier.patch + patches.drm/drm-sun4i-de3-Add-YUV-formatter-module.patch + patches.drm/drm-sun4i-de3-add-format-enumeration-function-to-engine.patch + patches.drm/drm-sun4i-de3-add-formatter-flag-to-mixer-config.patch + patches.drm/drm-sun4i-de3-add-YUV-support-to-the-DE3-mixer.patch + patches.drm/drm-sun4i-de3-pass-engine-reference-to-ccsc-setup-function.patch + patches.drm/drm-sun4i-de3-add-YUV-support-to-the-color-space-correction-mod.patch + patches.drm/drm-sun4i-de3-add-YUV-support-to-the-TCON.patch + patches.drm/drm-sun4i-support-YUV-formats-in-VI-scaler.patch + patches.drm/drm-sun4i-de2-de3-add-mixer-version-enum.patch + patches.drm/drm-sun4i-de2-de3-refactor-mixer-initialisation.patch + patches.drm/drm-sun4i-vi_scaler-refactor-vi_scaler-enablement.patch + patches.drm/drm-sun4i-de2-de3-add-generic-blender-register-reference-functi.patch + patches.drm/drm-sun4i-de2-de3-use-generic-register-reference-function-for-l.patch + patches.drm/drm-sun4i-de3-Implement-AFBC-support.patch + patches.drm/dt-bindings-allwinner-add-H616-DE33-bus-binding.patch + patches.drm/dt-bindings-allwinner-add-H616-DE33-clock-binding.patch + patches.drm/dt-bindings-allwinner-add-H616-DE33-mixer-binding.patch + patches.drm/clk-sunxi-ng-ccu-add-Display-Engine-3.3-DE33-support.patch + patches.drm/drm-sun4i-de33-mixer-add-Display-Engine-3.3-DE33-support.patch + patches.drm/drm-sun4i-de33-vi_scaler-add-Display-Engine-3.3-DE33-support.patch + patches.drm/drm-sun4i-de33-fmt-add-Display-Engine-3.3-DE33-support.patch + patches.drm/drm-sun4i-de33-csc-add-Display-Engine-3.3-DE33-support.patch + patches.drm/drm-sun4i-add-sun50i-h616-hdmi-phy-support.patch + patches.drm/add-TCON-global-control-reg-for-pad-selection.patch + patches.drm/dt-bindings-power-Add-Allwinner-H6-H616-PRCM-PPU.patch + patches.drm/pmdomain-sunxi-add-H6-PRCM-PPU-driver.patch + patches.drm/dt-bindings-gpu-mali-bifrost-Add-Allwinner-H616-compatible.patch + patches.drm/arm64-dts-allwinner-h616-Add-Mali-GPU-node.patch + patches.drm/drm-panfrost-Add-PM-runtime-flags.patch + patches.drm/drm-panfrost-add-h616-compatible-string.patch + patches.drm/drm-panfrost-reorder-pd-clk-rst-sequence.patch diff --git a/patch/kernel/archive/sunxi-6.14/series.drm b/patch/kernel/archive/sunxi-6.14/series.drm new file mode 100644 index 000000000000..f6693debad04 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/series.drm @@ -0,0 +1,40 @@ +################################################################################ +# +# drivers/gpu/drm/sun4i/ +# +################################################################################ + patches.drm/drm-sun4i-de2-de3-Change-CSC-argument.patch + patches.drm/drm-sun4i-de2-de3-Merge-CSC-functions-into-one.patch + patches.drm/drm-sun4i-de2-de3-call-csc-setup-also-for-UI-layer.patch + patches.drm/drm-sun4i-de2-Initialize-layer-fields-earlier.patch + patches.drm/drm-sun4i-de3-Add-YUV-formatter-module.patch + patches.drm/drm-sun4i-de3-add-format-enumeration-function-to-engine.patch + patches.drm/drm-sun4i-de3-add-formatter-flag-to-mixer-config.patch + patches.drm/drm-sun4i-de3-add-YUV-support-to-the-DE3-mixer.patch + patches.drm/drm-sun4i-de3-pass-engine-reference-to-ccsc-setup-function.patch + patches.drm/drm-sun4i-de3-add-YUV-support-to-the-color-space-correction-mod.patch + patches.drm/drm-sun4i-de3-add-YUV-support-to-the-TCON.patch + patches.drm/drm-sun4i-support-YUV-formats-in-VI-scaler.patch + patches.drm/drm-sun4i-de2-de3-add-mixer-version-enum.patch + patches.drm/drm-sun4i-de2-de3-refactor-mixer-initialisation.patch + patches.drm/drm-sun4i-vi_scaler-refactor-vi_scaler-enablement.patch + patches.drm/drm-sun4i-de2-de3-add-generic-blender-register-reference-functi.patch + patches.drm/drm-sun4i-de2-de3-use-generic-register-reference-function-for-l.patch + patches.drm/drm-sun4i-de3-Implement-AFBC-support.patch + patches.drm/dt-bindings-allwinner-add-H616-DE33-bus-binding.patch + patches.drm/dt-bindings-allwinner-add-H616-DE33-clock-binding.patch + patches.drm/dt-bindings-allwinner-add-H616-DE33-mixer-binding.patch + patches.drm/clk-sunxi-ng-ccu-add-Display-Engine-3.3-DE33-support.patch + patches.drm/drm-sun4i-de33-mixer-add-Display-Engine-3.3-DE33-support.patch + patches.drm/drm-sun4i-de33-vi_scaler-add-Display-Engine-3.3-DE33-support.patch + patches.drm/drm-sun4i-de33-fmt-add-Display-Engine-3.3-DE33-support.patch + patches.drm/drm-sun4i-de33-csc-add-Display-Engine-3.3-DE33-support.patch + patches.drm/drm-sun4i-add-sun50i-h616-hdmi-phy-support.patch + patches.drm/add-TCON-global-control-reg-for-pad-selection.patch + patches.drm/dt-bindings-power-Add-Allwinner-H6-H616-PRCM-PPU.patch + patches.drm/pmdomain-sunxi-add-H6-PRCM-PPU-driver.patch + patches.drm/dt-bindings-gpu-mali-bifrost-Add-Allwinner-H616-compatible.patch + patches.drm/arm64-dts-allwinner-h616-Add-Mali-GPU-node.patch + patches.drm/drm-panfrost-Add-PM-runtime-flags.patch + patches.drm/drm-panfrost-add-h616-compatible-string.patch + patches.drm/drm-panfrost-reorder-pd-clk-rst-sequence.patch From 76eb5bb9e7fdf6e58ef556b1fc9a5ab96a439678 Mon Sep 17 00:00:00 2001 From: The-going <48602507+The-going@users.noreply.github.com> Date: Wed, 30 Apr 2025 12:53:25 +0300 Subject: [PATCH 3/7] sunxi-6.14: Add media patches --- ...ma-sun6i-dma-add-sun50i-h616-support.patch | 53 ++++++ ...V12-and-P010-AFBC-compressed-formats.patch | 45 +++++ ...-cedrus-Don-t-CPU-map-source-buffers.patch | 29 +++ ...plement-AFBC-YUV420-formats-for-H265.patch | 169 ++++++++++++++++++ .../media-cedrus-Increase-H6-clock-rate.patch | 29 +++ ...tering-based-on-depth-and-src-format.patch | 50 ++++++ patch/kernel/archive/sunxi-6.14/series.conf | 12 ++ patch/kernel/archive/sunxi-6.14/series.media | 11 ++ 8 files changed, 398 insertions(+) create mode 100644 patch/kernel/archive/sunxi-6.14/patches.media/dma-sun6i-dma-add-sun50i-h616-support.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.media/media-Add-NV12-and-P010-AFBC-compressed-formats.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.media/media-cedrus-Don-t-CPU-map-source-buffers.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.media/media-cedrus-Implement-AFBC-YUV420-formats-for-H265.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.media/media-cedrus-Increase-H6-clock-rate.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.media/media-cedrus-add-format-filtering-based-on-depth-and-src-format.patch create mode 100644 patch/kernel/archive/sunxi-6.14/series.media diff --git a/patch/kernel/archive/sunxi-6.14/patches.media/dma-sun6i-dma-add-sun50i-h616-support.patch b/patch/kernel/archive/sunxi-6.14/patches.media/dma-sun6i-dma-add-sun50i-h616-support.patch new file mode 100644 index 000000000000..ffedd6b0c354 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.media/dma-sun6i-dma-add-sun50i-h616-support.patch @@ -0,0 +1,53 @@ +From f4671b6d4842701e71b6a1ba6a93fbfb9dabb188 Mon Sep 17 00:00:00 2001 +From: The-going <48602507+The-going@users.noreply.github.com> +Date: Mon, 7 Apr 2025 21:32:25 +0300 +Subject: dma: sun6i-dma: add sun50i-h616 support + +--- + drivers/dma/sun6i-dma.c | 23 +++++++++++++++++++++++ + 1 file changed, 23 insertions(+) + +diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c +index 95ecb12caaa5..ae2b5509b5b4 100644 +--- a/drivers/dma/sun6i-dma.c ++++ b/drivers/dma/sun6i-dma.c +@@ -1243,6 +1243,28 @@ static struct sun6i_dma_config sun50i_h6_dma_cfg = { + .has_mbus_clk = true, + }; + ++/* ++ * The H616 binding uses the number of dma channels from the ++ * device tree node. ++ */ ++static struct sun6i_dma_config sun50i_h616_dma_cfg = { ++ .clock_autogate_enable = sun6i_enable_clock_autogate_h3, ++ .set_burst_length = sun6i_set_burst_length_h3, ++ .set_drq = sun6i_set_drq_h6, ++ .set_mode = sun6i_set_mode_h6, ++ .src_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16), ++ .dst_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16), ++ .src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | ++ BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | ++ BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | ++ BIT(DMA_SLAVE_BUSWIDTH_8_BYTES), ++ .dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | ++ BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | ++ BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | ++ BIT(DMA_SLAVE_BUSWIDTH_8_BYTES), ++ .has_mbus_clk = true, ++}; ++ + /* + * The V3s have only 8 physical channels, a maximum DRQ port id of 23, + * and a total of 24 usable source and destination endpoints. +@@ -1276,6 +1298,7 @@ static const struct of_device_id sun6i_dma_match[] = { + { .compatible = "allwinner,sun50i-a64-dma", .data = &sun50i_a64_dma_cfg }, + { .compatible = "allwinner,sun50i-a100-dma", .data = &sun50i_a100_dma_cfg }, + { .compatible = "allwinner,sun50i-h6-dma", .data = &sun50i_h6_dma_cfg }, ++ { .compatible = "allwinner,sun50i-h616-dma", .data = &sun50i_h616_dma_cfg }, + { /* sentinel */ } + }; + MODULE_DEVICE_TABLE(of, sun6i_dma_match); +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.media/media-Add-NV12-and-P010-AFBC-compressed-formats.patch b/patch/kernel/archive/sunxi-6.14/patches.media/media-Add-NV12-and-P010-AFBC-compressed-formats.patch new file mode 100644 index 000000000000..113377c08405 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.media/media-Add-NV12-and-P010-AFBC-compressed-formats.patch @@ -0,0 +1,45 @@ +From eae101ec576943cc34c4718b360bbe6091792e8b Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sat, 7 Oct 2023 09:43:35 +0200 +Subject: media: Add NV12 and P010 AFBC compressed formats + +Cedrus supports AFBC compressed NV12 and P010 formats, which +considerably speed up H265 decoding. Add them. + +Signed-off-by: Jernej Skrabec +--- + drivers/media/v4l2-core/v4l2-ioctl.c | 2 ++ + include/uapi/linux/videodev2.h | 4 ++++ + 2 files changed, 6 insertions(+) + +diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c +index 0304daa8471d..6c7f3abb4959 100644 +--- a/drivers/media/v4l2-core/v4l2-ioctl.c ++++ b/drivers/media/v4l2-core/v4l2-ioctl.c +@@ -1546,6 +1546,8 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) + case V4L2_PIX_FMT_PISP_COMP2_GBRG: descr = "PiSP 8b GBGB/RGRG mode2 compr"; break; + case V4L2_PIX_FMT_PISP_COMP2_BGGR: descr = "PiSP 8b BGBG/GRGR mode2 compr"; break; + case V4L2_PIX_FMT_PISP_COMP2_MONO: descr = "PiSP 8b monochrome mode2 compr"; break; ++ case V4L2_PIX_FMT_YUV420_8_AFBC_16X16_SPLIT: descr = "YUV 4:2:0 (AFBC 16x16)"; break; ++ case V4L2_PIX_FMT_YUV420_10_AFBC_16X16_SPLIT: descr = "10-bit YUV 4:2:0 (AFBC 16x16)"; break; + default: + if (fmt->description[0]) + return; +diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h +index e7c4dce39007..8d423df66f61 100644 +--- a/include/uapi/linux/videodev2.h ++++ b/include/uapi/linux/videodev2.h +@@ -688,6 +688,10 @@ struct v4l2_pix_format { + #define V4L2_PIX_FMT_NV12M_8L128 v4l2_fourcc('N', 'A', '1', '2') /* Y/CbCr 4:2:0 8x128 tiles */ + #define V4L2_PIX_FMT_NV12M_10BE_8L128 v4l2_fourcc_be('N', 'T', '1', '2') /* Y/CbCr 4:2:0 10-bit 8x128 tiles */ + ++/* AFBC YUV formats */ ++#define V4L2_PIX_FMT_YUV420_8_AFBC_16X16_SPLIT v4l2_fourcc('A', 'S', '1', '2') /* YUV420 AFBC compressed, 16x16 macroblocks, split */ ++#define V4L2_PIX_FMT_YUV420_10_AFBC_16X16_SPLIT v4l2_fourcc('A', 'S', '0', '1') /* YUV420 10-bit AFBC compressed, 16x16 macroblocks, split */ ++ + /* Bayer formats - see http://www.siliconimaging.com/RGB%20Bayer.htm */ + #define V4L2_PIX_FMT_SBGGR8 v4l2_fourcc('B', 'A', '8', '1') /* 8 BGBG.. GRGR.. */ + #define V4L2_PIX_FMT_SGBRG8 v4l2_fourcc('G', 'B', 'R', 'G') /* 8 GBGB.. RGRG.. */ +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.media/media-cedrus-Don-t-CPU-map-source-buffers.patch b/patch/kernel/archive/sunxi-6.14/patches.media/media-cedrus-Don-t-CPU-map-source-buffers.patch new file mode 100644 index 000000000000..15649d82c556 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.media/media-cedrus-Don-t-CPU-map-source-buffers.patch @@ -0,0 +1,29 @@ +From 80b16fcddf1837c631796c25f3123b7adbbf815a Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Tue, 4 Oct 2022 20:36:18 +0200 +Subject: media: cedrus: Don't CPU map source buffers + +There is no need to access source buffers via CPU, so let's disable +that. This will lower amount of virtual memory needed on 32-bit ARM +SoCs. + +Signed-off-by: Jernej Skrabec +--- + drivers/staging/media/sunxi/cedrus/cedrus_video.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_video.c b/drivers/staging/media/sunxi/cedrus/cedrus_video.c +index 77f78266f406..7f5a6a2363a5 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_video.c ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_video.c +@@ -580,6 +580,7 @@ int cedrus_queue_init(void *priv, struct vb2_queue *src_vq, + + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; ++ src_vq->dma_attrs = DMA_ATTR_NO_KERNEL_MAPPING; + src_vq->drv_priv = ctx; + src_vq->buf_struct_size = sizeof(struct cedrus_buffer); + src_vq->ops = &cedrus_qops; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.media/media-cedrus-Implement-AFBC-YUV420-formats-for-H265.patch b/patch/kernel/archive/sunxi-6.14/patches.media/media-cedrus-Implement-AFBC-YUV420-formats-for-H265.patch new file mode 100644 index 000000000000..e9ca743f9e7b --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.media/media-cedrus-Implement-AFBC-YUV420-formats-for-H265.patch @@ -0,0 +1,169 @@ +From 81241983ba12f281a839a530da460088170cca2e Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sun, 8 Oct 2023 12:44:59 +0200 +Subject: media: cedrus: Implement AFBC YUV420 formats for H265 + +AFBC output formats are more performant, since they are optimized for +more efficient memory operations and transfers. + +Add support for them. + +Signed-off-by: Jernej Skrabec +--- + drivers/staging/media/sunxi/cedrus/cedrus.h | 11 ++++++ + .../staging/media/sunxi/cedrus/cedrus_h265.c | 3 +- + .../staging/media/sunxi/cedrus/cedrus_hw.c | 16 +++++++++ + .../staging/media/sunxi/cedrus/cedrus_regs.h | 6 ++++ + .../staging/media/sunxi/cedrus/cedrus_video.c | 36 +++++++++++++++++++ + 5 files changed, 71 insertions(+), 1 deletion(-) + +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.h b/drivers/staging/media/sunxi/cedrus/cedrus.h +index 522c184e2afc..c7ec4dee8630 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus.h ++++ b/drivers/staging/media/sunxi/cedrus/cedrus.h +@@ -268,6 +268,17 @@ cedrus_is_capable(struct cedrus_ctx *ctx, unsigned int capabilities) + return (ctx->dev->capabilities & capabilities) == capabilities; + } + ++static inline bool is_afbc_format(u32 format) ++{ ++ switch (format) { ++ case V4L2_PIX_FMT_YUV420_8_AFBC_16X16_SPLIT: ++ case V4L2_PIX_FMT_YUV420_10_AFBC_16X16_SPLIT: ++ return true; ++ default: ++ return false; ++ } ++} ++ + void *cedrus_find_control_data(struct cedrus_ctx *ctx, u32 id); + u32 cedrus_get_num_of_controls(struct cedrus_ctx *ctx, u32 id); + +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c +index 780da4a8b5af..1a7b0600cc51 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c +@@ -120,7 +120,8 @@ static void cedrus_h265_frame_info_write_single(struct cedrus_ctx *ctx, + { + struct cedrus_dev *dev = ctx->dev; + dma_addr_t dst_luma_addr = cedrus_dst_buf_addr(ctx, buf, 0); +- dma_addr_t dst_chroma_addr = cedrus_dst_buf_addr(ctx, buf, 1); ++ dma_addr_t dst_chroma_addr = is_afbc_format(ctx->dst_fmt.pixelformat) ? ++ 0 : cedrus_dst_buf_addr(ctx, buf, 1); + dma_addr_t mv_col_buf_addr[2] = { + cedrus_h265_frame_info_mv_col_buf_addr(buf, 0), + cedrus_h265_frame_info_mv_col_buf_addr(buf, field_pic ? 1 : 0) +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c +index 32af0e96e762..5d769a124439 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c +@@ -65,6 +65,18 @@ int cedrus_engine_enable(struct cedrus_ctx *ctx) + reg |= VE_MODE_PIC_WIDTH_IS_4096; + if (ctx->src_fmt.width > 2048) + reg |= VE_MODE_PIC_WIDTH_MORE_2048; ++ /* ++ * NOTE: Not sure if RGB default color feature is part of official ++ * AFBC standard or not and if it is, which feature that is. However, ++ * in order to render it properly with display engine, default color ++ * has to be set to white there. ++ */ ++ if (is_afbc_format(ctx->dst_fmt.pixelformat)) ++ reg |= VE_MODE_COMPRESS_EN | ++ VE_MODE_MIN_VAL_WRAP_EN | ++ VE_MODE_RGB_DEF_COLOR_EN | ++ VE_MODE_BODYBUF_1K_ALIGNED | ++ VE_MODE_COMPRESS_MODE_AFBC; + + cedrus_write(ctx->dev, VE_MODE, reg); + +@@ -85,6 +97,10 @@ void cedrus_dst_format_set(struct cedrus_dev *dev, + u32 reg; + + switch (fmt->pixelformat) { ++ case V4L2_PIX_FMT_YUV420_8_AFBC_16X16_SPLIT: ++ case V4L2_PIX_FMT_YUV420_10_AFBC_16X16_SPLIT: ++ /* format is already set in cedrus_engine_enable() */ ++ break; + case V4L2_PIX_FMT_NV12: + chroma_size = ALIGN(width, 16) * ALIGN(height, 16) / 2; + +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_regs.h b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h +index 05e6cbc548ab..c3dcd93a29eb 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_regs.h ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h +@@ -35,12 +35,18 @@ + + #define VE_MODE 0x00 + ++#define VE_MODE_COMPRESS_EN BIT(29) ++#define VE_MODE_MIN_VAL_WRAP_EN BIT(27) ++#define VE_MODE_RGB_DEF_COLOR_EN BIT(26) + #define VE_MODE_PIC_WIDTH_IS_4096 BIT(22) + #define VE_MODE_PIC_WIDTH_MORE_2048 BIT(21) + #define VE_MODE_REC_WR_MODE_2MB (0x01 << 20) + #define VE_MODE_REC_WR_MODE_1MB (0x00 << 20) + #define VE_MODE_DDR_MODE_BW_128 (0x03 << 16) + #define VE_MODE_DDR_MODE_BW_256 (0x02 << 16) ++#define VE_MODE_BODYBUF_1K_ALIGNED BIT(12) ++#define VE_MODE_COMPRESS_MODE_LOSSLESS (0x00 << 4) ++#define VE_MODE_COMPRESS_MODE_AFBC (0x01 << 4) + #define VE_MODE_DISABLED (0x07 << 0) + #define VE_MODE_DEC_H265 (0x04 << 0) + #define VE_MODE_DEC_H264 (0x01 << 0) +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_video.c b/drivers/staging/media/sunxi/cedrus/cedrus_video.c +index 53ec3066274d..99011066e7d3 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_video.c ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_video.c +@@ -55,6 +55,22 @@ static struct cedrus_format cedrus_formats[] = { + .directions = CEDRUS_DECODE_SRC, + .capabilities = CEDRUS_CAPABILITY_VP8_DEC, + }, ++ { ++ .pixelformat = V4L2_PIX_FMT_YUV420_10_AFBC_16X16_SPLIT, ++ .directions = CEDRUS_DECODE_DST, ++ .capabilities = CEDRUS_CAPABILITY_UNTILED | ++ CEDRUS_CAPABILITY_H265_10_DEC, ++ .depth = 10, ++ .src_format = V4L2_PIX_FMT_HEVC_SLICE, ++ }, ++ { ++ .pixelformat = V4L2_PIX_FMT_YUV420_8_AFBC_16X16_SPLIT, ++ .directions = CEDRUS_DECODE_DST, ++ .capabilities = CEDRUS_CAPABILITY_UNTILED | ++ CEDRUS_CAPABILITY_H265_10_DEC, ++ .depth = 8, ++ .src_format = V4L2_PIX_FMT_HEVC_SLICE, ++ }, + { + .pixelformat = V4L2_PIX_FMT_NV12, + .directions = CEDRUS_DECODE_DST, +@@ -160,6 +176,26 @@ void cedrus_prepare_format(struct v4l2_pix_format *pix_fmt) + sizeimage += bytesperline * height / 2; + + break; ++ ++ case V4L2_PIX_FMT_YUV420_10_AFBC_16X16_SPLIT: ++ /* Zero bytes per line for compressed destination. */ ++ bytesperline = 0; ++ ++ sizeimage = DIV_ROUND_UP(width, 16) * ++ DIV_ROUND_UP(height + 4, 16) * (512 + 16) + ++ 32 + SZ_1K; ++ ++ break; ++ ++ case V4L2_PIX_FMT_YUV420_8_AFBC_16X16_SPLIT: ++ /* Zero bytes per line for compressed destination. */ ++ bytesperline = 0; ++ ++ sizeimage = DIV_ROUND_UP(width, 16) * ++ DIV_ROUND_UP(height + 4, 16) * (384 + 16) + ++ 32 + SZ_1K; ++ ++ break; + } + + pix_fmt->width = width; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.media/media-cedrus-Increase-H6-clock-rate.patch b/patch/kernel/archive/sunxi-6.14/patches.media/media-cedrus-Increase-H6-clock-rate.patch new file mode 100644 index 000000000000..1b6cdd1b8daa --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.media/media-cedrus-Increase-H6-clock-rate.patch @@ -0,0 +1,29 @@ +From c0dffc32c3a65a1c25bf04f979faf8b6a82b88f8 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Mon, 9 Oct 2023 20:16:27 +0200 +Subject: media: cedrus: Increase H6 clock rate + +Vendor driver runs Cedrus at 648 MHz, supposedly to be able to decode +4k HEVC at 60 fps. + +Signed-off-by: Jernej Skrabec +--- + drivers/staging/media/sunxi/cedrus/cedrus.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c +index b91174246e58..08f8d994c99c 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus.c ++++ b/drivers/staging/media/sunxi/cedrus/cedrus.c +@@ -648,7 +648,7 @@ static const struct cedrus_variant sun50i_h6_cedrus_variant = { + CEDRUS_CAPABILITY_H265_DEC | + CEDRUS_CAPABILITY_H265_10_DEC | + CEDRUS_CAPABILITY_VP8_DEC, +- .mod_rate = 600000000, ++ .mod_rate = 648000000, + }; + + static const struct of_device_id cedrus_dt_match[] = { +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.media/media-cedrus-add-format-filtering-based-on-depth-and-src-format.patch b/patch/kernel/archive/sunxi-6.14/patches.media/media-cedrus-add-format-filtering-based-on-depth-and-src-format.patch new file mode 100644 index 000000000000..462e3d712544 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.media/media-cedrus-add-format-filtering-based-on-depth-and-src-format.patch @@ -0,0 +1,50 @@ +From be75f442cae72a4e646e1f5d7374f579ee026c3d Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sat, 7 Oct 2023 09:45:25 +0200 +Subject: media: cedrus: add format filtering based on depth and src format + +Some Cedrus variant, like that found in H6, support special output +formats only with specific codecs, like H265. + +Add extra filtering fields based on bit depth and source format. + +Signed-off-by: Jernej Skrabec +--- + drivers/staging/media/sunxi/cedrus/cedrus_video.c | 7 +++++++ + drivers/staging/media/sunxi/cedrus/cedrus_video.h | 2 ++ + 2 files changed, 9 insertions(+) + +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_video.c b/drivers/staging/media/sunxi/cedrus/cedrus_video.c +index 7f5a6a2363a5..53ec3066274d 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_video.c ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_video.c +@@ -87,6 +87,13 @@ static struct cedrus_format *cedrus_find_format(struct cedrus_ctx *ctx, + !(fmt->directions & directions)) + continue; + ++ if (fmt->depth && fmt->depth != ctx->bit_depth) ++ continue; ++ ++ if (fmt->src_format && ++ fmt->src_format != ctx->src_fmt.pixelformat) ++ continue; ++ + if (fmt->pixelformat == pixelformat) + break; + +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_video.h b/drivers/staging/media/sunxi/cedrus/cedrus_video.h +index 8e1afc16a6a1..c8e9909ecdee 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_video.h ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_video.h +@@ -20,6 +20,8 @@ struct cedrus_format { + u32 pixelformat; + u32 directions; + unsigned int capabilities; ++ unsigned int depth; ++ u32 src_format; + }; + + extern const struct v4l2_ioctl_ops cedrus_ioctl_ops; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/series.conf b/patch/kernel/archive/sunxi-6.14/series.conf index d21952e30b38..01aee915e783 100644 --- a/patch/kernel/archive/sunxi-6.14/series.conf +++ b/patch/kernel/archive/sunxi-6.14/series.conf @@ -288,3 +288,15 @@ patches.drm/drm-panfrost-Add-PM-runtime-flags.patch patches.drm/drm-panfrost-add-h616-compatible-string.patch patches.drm/drm-panfrost-reorder-pd-clk-rst-sequence.patch + +################################################################################ +# +# media patches +# +################################################################################ + patches.media/media-cedrus-Don-t-CPU-map-source-buffers.patch + patches.media/media-Add-NV12-and-P010-AFBC-compressed-formats.patch + patches.media/media-cedrus-add-format-filtering-based-on-depth-and-src-format.patch + patches.media/media-cedrus-Implement-AFBC-YUV420-formats-for-H265.patch + patches.media/media-cedrus-Increase-H6-clock-rate.patch + patches.media/dma-sun6i-dma-add-sun50i-h616-support.patch diff --git a/patch/kernel/archive/sunxi-6.14/series.media b/patch/kernel/archive/sunxi-6.14/series.media new file mode 100644 index 000000000000..d93c20840b00 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/series.media @@ -0,0 +1,11 @@ +# +# Automatically generated by the script mk_format_patch +# +# git@github.com:The-going/linux-sf.git +# + patches.media/media-cedrus-Don-t-CPU-map-source-buffers.patch + patches.media/media-Add-NV12-and-P010-AFBC-compressed-formats.patch + patches.media/media-cedrus-add-format-filtering-based-on-depth-and-src-format.patch + patches.media/media-cedrus-Implement-AFBC-YUV420-formats-for-H265.patch + patches.media/media-cedrus-Increase-H6-clock-rate.patch + patches.media/dma-sun6i-dma-add-sun50i-h616-support.patch From 04c459244cc1ae82aac2ef32b94a2772e1d3a12f Mon Sep 17 00:00:00 2001 From: The-going <48602507+The-going@users.noreply.github.com> Date: Wed, 30 Apr 2025 13:43:40 +0300 Subject: [PATCH 4/7] sunxi-6.14: Add armbian patches --- ...nanopiduo2-Use-key-0-as-power-button.patch | 31 + ...dts-sun8i-nanopiduo2-enable-ethernet.patch | 31 + ...0i-h616-BigTreeTech-CB1-Enable-EMAC1.patch | 42 + ...50i-h616-BigTreeTech-CB1-Enable-HDMI.patch | 57 + .../ASoC-AC200-Initial-driver.patch | 842 +++ .../Add-BananaPi-BPI-M4-Zero-overlays.patch | 377 ++ .../Add-BananaPi-BPI-M4-Zero-pinctrl.patch | 81 + .../Add-FB_TFT-ST7796S-driver.patch | 154 + .../Add-board-BananaPi-BPI-M4-Zero.patch | 407 ++ ...d-dump_reg-and-sunxi-sysinfo-drivers.patch | 1548 +++++ ...to-fix-uwe5622-bluetooth-MAC-address.patch | 609 ++ .../Add-wifi-nodes-for-Inovato-Quadra.patch | 88 + ...ws2812-RGB-driver-for-allwinner-H616.patch | 273 + ...o-mode-adjustment-and-ws2812-rgb_val.patch | 58 + .../Compile-the-pwm-overlay.patch | 24 + ...ce-number-as-referenced-in-the-Allwi.patch | 29 + ...ding-for-DWC3-controller-on-Allwinne.patch | 63 + ...Allwinner-A10-EMAC-which-already-exi.patch | 26 + .../Enable-creation-of-__symbols__-node.patch | 29 + ...-ghost-touches-on-tsc2007-tft-screen.patch | 211 + .../Fix-include-uapi-spi-spidev-module.patch | 26 + ...p20x-pek-allow-wakeup-after-shutdown.patch | 48 + ...us_heartbeat-arch-arm64-boot-dts-all.patch | 39 + ...FIG_SHELL-fix-for-builddeb-packaging.patch | 27 + ...i-h6-pwm-settings-to-its-own-overlay.patch | 70 + ...C2007-touchscreen-add-polling-method.patch | 195 + ...sun4i-hdmi-switch-to-struct-drm_edid.patch | 49 + .../Sound-for-H616-H618-Allwinner-SOCs.patch | 4239 ++++++++++++++ ...-boot-dts-allwinner-sun50i-a64-pinep.patch | 53 + .../add-dtb-overlay-for-zero2w.patch | 135 + ...dd-initial-support-for-orangepi3-lts.patch | 431 ++ ...i-info-sunxi-addr-and-sunxi-dump-reg.patch | 73 + ...rm-arm64-dts-Add-leds-axp20x-charger.patch | 79 + ...-Add-sun8i-h2-plus-nanopi-duo-device.patch | 196 + ...Add-sun8i-h2-plus-sunvell-r69-device.patch | 257 + ...ubietruck-green-LED-mmc0-default-tri.patch | 38 + ...-orangepi-and-mini-fix-phy-mode-hdmi.patch | 86 + ...3-nanopi-neo-Add-regulator-leds-mmc2.patch | 80 + ...dd-regulator-camera-wifi-bluetooth-o.patch | 196 + ...-h3-orangepi-2-Add-regulator-vdd-cpu.patch | 59 + ...m-dts-overlay-Add-Overlays-for-sunxi.patch | 4964 +++++++++++++++++ ...lay-sun8i-h3-cpu-clock-add-overclock.patch | 209 + ...-Add-panel-lcd-olinuxino-4.3-needed-.patch | 133 + ...no-micro-add-panel-lcd-olinuxino-4.3.patch | 106 + ...OB-IRQ-for-brcm-wifi-on-Cubietruck-a.patch | 99 + ...-a20-bananapro-add-AXP209-regulators.patch | 72 + ...-a20-bananapro-add-hdmi-connector-de.patch | 78 + ...sun7i-a20-cubietruck-add-alias-uart2.patch | 24 + ...m-204-evb-olinuxino-micro-decrease-d.patch | 55 + ...0-olinuxino-lime2-enable-audio-codec.patch | 27 + ...linuxino-lime2-enable-ldo3-always-on.patch | 28 + ...-olinuxino-micro-emmc-Add-vqmmc-node.patch | 24 + ...us-orangepi-zero-fix-usb_otg-dr_mode.patch | 25 + ...s-orangepi-zero-fix-xradio-interrupt.patch | 35 + .../arm-dts-sun8i-h3-add-thermal-zones.patch | 104 + ...-h3-bananapi-m2-plus-add-wifi_pwrseq.patch | 25 + ...ts-sun8i-h3-nanopi-add-leds-pio-pins.patch | 60 + ...-h3-orangepi-pc-plus-add-wifi_pwrseq.patch | 33 + ...-microvolt-to-prevent-not-supported-.patch | 48 + ...-r40-add-clk_out_a-fix-bananam2ultra.patch | 40 + ...0-bananapi-m2-ultra-add-codec-analog.patch | 63 + ...i-v3s-s3-pinecube-enable-sound-codec.patch | 63 + ...arm-dts-sun9i-a80-add-thermal-sensor.patch | 41 + .../arm-dts-sun9i-a80-add-thermal-zone.patch | 54 + ...-sunxi-h3-h5.dtsi-add-i2s0-i2s1-pins.patch | 33 + ...unxi-h3-h5.dtsi-force-mmc0-bus-width.patch | 24 + ...e-ASAP-after-writing-new-instruction.patch | 63 + ...un50i-h618-bananapi-m4-berry-support.patch | 353 ++ ...-Add-sun50i-h5-nanopi-k1-plus-device.patch | 429 ++ ...Add-sun50i-h5-nanopi-m1-plus2-device.patch | 272 + ...dd-sun50i-h5-nanopi-neo-core2-device.patch | 242 + ...dd-sun50i-h5-nanopi-neo2-v1.1-device.patch | 212 + ...64-olinuxino-add-regulator-audio-mmc.patch | 99 + ...64-dts-add-sun50i-h618-cpu-dvfs.dtsi.patch | 260 + ...arm64-dts-allwinner-Add-axp313a.dtsi.patch | 83 + ...ts-allwinner-h6-Add-AC200-EPHY-nodes.patch | 144 + ...s-allwinner-h6-add-AC200-codec-nodes.patch | 87 + ...-dts-allwinner-h6-enable-AC200-codec.patch | 142 + ...s-allwinner-h6-tanix-enable-Ethernet.patch | 97 + ...angepi-zero2-Enable-expansion-board-.patch | 48 + ...ner-overlay-Add-Overlays-for-sunxi64.patch | 2471 ++++++++ ...-dts-allwinner-sun50i-h6-Fix-H6-emmc.patch | 26 + ...s-allwinner-sun50i-h616-Add-VPU-node.patch | 55 + ...dts-h616-8-Add-overlays-i2c-pwm-uart.patch | 369 ++ ...add-hdmi-support-for-zero2-and-zero3.patch | 339 ++ ...pport-for-orange-pi-zero-2-and-zero3.patch | 68 + ...i-a64-set-right-phy-mode-to-rgmii-id.patch | 29 + ...-overlay-sun50i-a64-pine64-7inch-lcd.patch | 144 + ...n50i-h5-add-gpio-regulator-overclock.patch | 220 + ...-dts-sun50i-a64-force-mmc0-bus-width.patch | 24 + ...ino-1Ge16GW-Disable-clock-phase-and-.patch | 28 + ...4-olinuxino-1Ge16GW-enable-bluetooth.patch | 41 + ...-dts-sun50i-a64-olinuxino-add-boards.patch | 603 ++ ...-a64-olinuxino-emmc-enable-bluetooth.patch | 38 + ...64-orangepi-win-add-aliase-ethernet1.patch | 24 + ...arm64-dts-sun50i-a64-pine64-add-spi0.patch | 49 + ...s-sun50i-a64-pine64-enable-Bluetooth.patch | 40 + ...s-sun50i-a64-pine64-enable-wifi-mmc1.patch | 52 + ...i-a64-sopine-baseboard-Add-i2s2-mmc1.patch | 44 + ...64-sopine-baseboard-enable-Bluetooth.patch | 44 + ...-a64.dtsi-adjust-thermal-trip-points.patch | 42 + .../arm64-dts-sun50i-h313-x96q-lpddr3.patch | 441 ++ ...arm64-dts-sun50i-h5-add-cpu-opp-refs.patch | 122 + ...arm64-dts-sun50i-h5-add-termal-zones.patch | 102 + ...able-power-button-for-orangepi-prime.patch | 27 + ...nanopi-neo2-add-regulator-led-triger.patch | 65 + ...h5-nanopi-r1s-h5-add-rtl8153-support.patch | 40 + ...sun50i-h5-orangepi-pc2-add-spi-flash.patch | 51 + ...n50i-h5-orangepi-prime-add-regulator.patch | 92 + ...n50i-h5-orangepi-prime-add-rtl8723cs.patch | 35 + ...-h5-orangepi-zero-plus-add-regulator.patch | 67 + ...ts-sun50i-h6-Add-r_uart-uart2-3-pins.patch | 119 + ...n50i-h6-orangepi-3-add-r_uart-aliase.patch | 24 + ...un50i-h6-orangepi-3-delete-node-spi0.patch | 25 + ...-sun50i-h6-orangepi-add-cpu-opp-refs.patch | 35 + ...i-enable-higher-clock-regulator-max-.patch | 26 + ...ngepi-lite2-spi0-usb3phy-dwc3-enable.patch | 60 + ...rangepi.dtsi-Rollback-r_rsb-to-r_i2c.patch | 40 + ...-sun50i-h6-pine-h64-add-dwc3-usb3phy.patch | 36 + ...un50i-h6-pine-h64-add-wifi-rtl8723cs.patch | 82 + ...50i-h6.dtsi-add-pinctrl-pins-for-spi.patch | 34 + ...-dts-sun50i-h6.dtsi-improve-thermals.patch | 111 + ...ts-sun50i-h616-add-pwm-nodes-support.patch | 133 + ...-sun50i-h616-bigtreetech-cb1-sd-emmc.patch | 491 ++ ...-h616-orangepi-zero2-Enable-GPU-mali.patch | 29 + ...angepi-zero2-reg_usb1_vbus-status-ok.patch | 24 + ...i-h616-x96-mate-T95-eth-sd-card-hack.patch | 103 + ...64-dts-sun50i-h616-x96-mate-add-hdmi.patch | 64 + ...6.dtsi-reserved-memory-512K-for-BL31.patch | 31 + ...18-orangepi-zero2w-Add-missing-nodes.patch | 434 ++ ...-h618-orangepi-zero3-Enable-GPU-mali.patch | 28 + ...50i-h616-Add-i2c-2-3-4-uart-2-5-pins.patch | 107 + .../patches.armbian/cb1-overlay.patch | 491 ++ ...e-add-support-for-regmap-based-gates.patch | 251 + .../driver-allwinner-h618-emac.patch | 4514 +++++++++++++++ ...q-sun8i-a33-mbus-disable-autorefresh.patch | 27 + ...dd-pwm-sunxi-enhance-driver-for-h616.patch | 1315 +++++ ...urce-arm_arch_timer-fix-a64-timejump.patch | 48 + ...gem-dma-Export-with-handle-allocator.patch | 51 + ...simple-Add-compability-olinuxino-lcd.patch | 169 + .../drv-gpu-drm-sun4i-Add-GEM-allocator.patch | 103 + ...4i-Add-HDMI-audio-sun4i-hdmi-encoder.patch | 633 +++ ...rm-sun4i-sun8i_mixer.c-add-h3-mixer1.patch | 42 + ...rm64-dts-axp803-hwmon-enable-thermal.patch | 184 + ...-touchscreen-sun4i-ts-Enable-parsing.patch | 63 + ...dvb-frontends-si2168-fix-cmd-timeout.patch | 28 + .../drv-mfd-axp20x-add-sysfs-interface.patch | 658 +++ ...ble-DDR52-mode-on-all-A20-based-boar.patch | 27 + ...ost-sunxi-mmc-add-h5-emmc-compatible.patch | 38 + ...-nand_ids.c-add-H27UBG8T2BTR-BC-nand.patch | 27 + ...mac-sun8i-second-EMAC-clock-register.patch | 64 + ...-nvmem-sunxi_sid-Support-SID-on-H616.patch | 41 + ...vice-Tree-Overlay-ConfigFS-interface.patch | 343 ++ ...4i-usb-Allow-reset-line-to-be-shared.patch | 36 + ...nctrl-sun50i-a64-disable_strict_mode.patch | 30 + ...sun50i-h6.c-GPIO-disable_strict_mode.patch | 24 + ...rtc-sun6i-Add-Allwinner-H616-support.patch | 45 + ...-support-RTCs-without-external-LOSCs.patch | 68 + ...sunxi-sram-Add-SRAM-C1-H616-handling.patch | 41 + ...v-spi-spi-sun4i.c-spi-bug-low-on-sck.patch | 36 + ...pidev-Add-armbian-spi-dev-compatible.patch | 37 + ...-media-sunxi-cedrus-add-H616-variant.patch | 44 + .../drv-staging-rtl8723bs-AP-bugfix.patch | 25 + ...name-gadget-serial-console-manufactu.patch | 27 + ...nable-TV-Output-on-OrangePi-Zero-LTE.patch | 647 +++ .../fix-cpu-opp-table-sun8i-a83t.patch | 115 + ...fourcc-add-ARM-tiled-format-modifier.patch | 35 + ...pport-for-X-Powers-AC200-EPHY-syscon.patch | 373 ++ .../mfd-Add-support-for-X-Powers-AC200.patch | 266 + .../mmc-host-sunxi-mmc-Fix-H6-emmc.patch | 43 + .../net-phy-Add-support-for-AC200-EPHY.patch | 143 + ...-r8152-add-LED-configuration-from-OF.patch | 79 + ...unxi_get_soc_chipid-sunxi_get_serial.patch | 58 + ...ipts-add-overlay-compilation-support.patch | 86 + ...the-early-load-of-sun8i-codec-analog.patch | 27 + ...4i-codec-adcis-select-capture-source.patch | 120 + ...unxi-sun8i-codec-analog-enable-sound.patch | 31 + .../kernel/archive/sunxi-6.14/series.armbian | 182 + patch/kernel/archive/sunxi-6.14/series.conf | 183 + 179 files changed, 40100 insertions(+) create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/ARM-dts-sun8i-nanopiduo2-Use-key-0-as-power-button.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/ARM-dts-sun8i-nanopiduo2-enable-ethernet.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/ARM64-dts-sun50i-h616-BigTreeTech-CB1-Enable-EMAC1.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/ARM64-dts-sun50i-h616-BigTreeTech-CB1-Enable-HDMI.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/ASoC-AC200-Initial-driver.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/Add-BananaPi-BPI-M4-Zero-overlays.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/Add-BananaPi-BPI-M4-Zero-pinctrl.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/Add-FB_TFT-ST7796S-driver.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/Add-board-BananaPi-BPI-M4-Zero.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/Add-dump_reg-and-sunxi-sysinfo-drivers.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/Add-sunxi-addr-driver-Used-to-fix-uwe5622-bluetooth-MAC-address.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/Add-wifi-nodes-for-Inovato-Quadra.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/Add-ws2812-RGB-driver-for-allwinner-H616.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/BigTreeTech-CB1-dts-i2c-gpio-mode-adjustment-and-ws2812-rgb_val.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/Compile-the-pwm-overlay.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/Correct-perf-interrupt-source-number-as-referenced-in-the-Allwi.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/Doc-dt-bindings-usb-add-binding-for-DWC3-controller-on-Allwinne.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/Enable-DMA-support-for-the-Allwinner-A10-EMAC-which-already-exi.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/Enable-creation-of-__symbols__-node.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/Fix-ghost-touches-on-tsc2007-tft-screen.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/Fix-include-uapi-spi-spidev-module.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/Input-axp20x-pek-allow-wakeup-after-shutdown.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/LED-green_power_on-red_status_heartbeat-arch-arm64-boot-dts-all.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/Makefile-CONFIG_SHELL-fix-for-builddeb-packaging.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/Move-sun50i-h6-pwm-settings-to-its-own-overlay.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/Optimize-TSC2007-touchscreen-add-polling-method.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/Revert-drm-sun4i-hdmi-switch-to-struct-drm_edid.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/Sound-for-H616-H618-Allwinner-SOCs.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/Temp_fix-mailbox-arch-arm64-boot-dts-allwinner-sun50i-a64-pinep.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/add-dtb-overlay-for-zero2w.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/add-initial-support-for-orangepi3-lts.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/add-nodes-for-sunxi-info-sunxi-addr-and-sunxi-dump-reg.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm-arm64-dts-Add-leds-axp20x-charger.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm-dts-Add-sun8i-h2-plus-nanopi-duo-device.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm-dts-Add-sun8i-h2-plus-sunvell-r69-device.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm-dts-a10-cubiebord-a20-cubietruck-green-LED-mmc0-default-tri.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm-dts-a20-orangepi-and-mini-fix-phy-mode-hdmi.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm-dts-h3-nanopi-neo-Add-regulator-leds-mmc2.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm-dts-h3-nanopi-neo-air-Add-regulator-camera-wifi-bluetooth-o.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm-dts-h3-orangepi-2-Add-regulator-vdd-cpu.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm-dts-overlay-Add-Overlays-for-sunxi.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm-dts-overlay-sun8i-h3-cpu-clock-add-overclock.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm-dts-sun5i-a13-olinuxino-Add-panel-lcd-olinuxino-4.3-needed-.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm-dts-sun5i-a13-olinuxino-micro-add-panel-lcd-olinuxino-4.3.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm-dts-sun7i-a20-Disable-OOB-IRQ-for-brcm-wifi-on-Cubietruck-a.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm-dts-sun7i-a20-bananapro-add-AXP209-regulators.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm-dts-sun7i-a20-bananapro-add-hdmi-connector-de.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm-dts-sun7i-a20-cubietruck-add-alias-uart2.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm-dts-sun7i-a20-olimex-som-204-evb-olinuxino-micro-decrease-d.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm-dts-sun7i-a20-olinuxino-lime2-enable-audio-codec.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm-dts-sun7i-a20-olinuxino-lime2-enable-ldo3-always-on.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm-dts-sun7i-a20-olinuxino-micro-emmc-Add-vqmmc-node.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm-dts-sun8i-h2-plus-orangepi-zero-fix-usb_otg-dr_mode.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm-dts-sun8i-h2-plus-orangepi-zero-fix-xradio-interrupt.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm-dts-sun8i-h3-add-thermal-zones.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm-dts-sun8i-h3-bananapi-m2-plus-add-wifi_pwrseq.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm-dts-sun8i-h3-nanopi-add-leds-pio-pins.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm-dts-sun8i-h3-orangepi-pc-plus-add-wifi_pwrseq.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm-dts-sun8i-h3-reduce-opp-microvolt-to-prevent-not-supported-.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm-dts-sun8i-r40-add-clk_out_a-fix-bananam2ultra.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm-dts-sun8i-r40-bananapi-m2-ultra-add-codec-analog.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm-dts-sun8i-v3s-s3-pinecube-enable-sound-codec.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm-dts-sun9i-a80-add-thermal-sensor.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm-dts-sun9i-a80-add-thermal-zone.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm-dts-sunxi-h3-h5.dtsi-add-i2s0-i2s1-pins.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm-dts-sunxi-h3-h5.dtsi-force-mmc0-bus-width.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm-patch-call-flush_icache-ASAP-after-writing-new-instruction.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-allwinner-Add-sun50i-h618-bananapi-m4-berry-support.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-Add-sun50i-h5-nanopi-k1-plus-device.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-Add-sun50i-h5-nanopi-m1-plus2-device.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-Add-sun50i-h5-nanopi-neo-core2-device.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-Add-sun50i-h5-nanopi-neo2-v1.1-device.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-FIXME-a64-olinuxino-add-regulator-audio-mmc.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-add-sun50i-h618-cpu-dvfs.dtsi.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-allwinner-Add-axp313a.dtsi.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-allwinner-h6-Add-AC200-EPHY-nodes.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-allwinner-h6-add-AC200-codec-nodes.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-allwinner-h6-enable-AC200-codec.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-allwinner-h6-tanix-enable-Ethernet.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-allwinner-h616-orangepi-zero2-Enable-expansion-board-.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-allwinner-overlay-Add-Overlays-for-sunxi64.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-allwinner-sun50i-h6-Fix-H6-emmc.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-allwinner-sun50i-h616-Add-VPU-node.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-h616-8-Add-overlays-i2c-pwm-uart.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-h616-add-hdmi-support-for-zero2-and-zero3.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-h616-add-wifi-support-for-orange-pi-zero-2-and-zero3.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-nanopi-a64-set-right-phy-mode-to-rgmii-id.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-overlay-sun50i-a64-pine64-7inch-lcd.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-overlay-sun50i-h5-add-gpio-regulator-overclock.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-a64-force-mmc0-bus-width.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-a64-olinuxino-1Ge16GW-Disable-clock-phase-and-.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-a64-olinuxino-1Ge16GW-enable-bluetooth.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-a64-olinuxino-add-boards.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-a64-olinuxino-emmc-enable-bluetooth.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-a64-orangepi-win-add-aliase-ethernet1.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-a64-pine64-add-spi0.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-a64-pine64-enable-Bluetooth.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-a64-pine64-enable-wifi-mmc1.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-a64-sopine-baseboard-Add-i2s2-mmc1.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-a64-sopine-baseboard-enable-Bluetooth.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-a64.dtsi-adjust-thermal-trip-points.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-h313-x96q-lpddr3.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-h5-add-cpu-opp-refs.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-h5-add-termal-zones.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-h5-enable-power-button-for-orangepi-prime.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-h5-nanopi-neo2-add-regulator-led-triger.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-h5-nanopi-r1s-h5-add-rtl8153-support.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-h5-orangepi-pc2-add-spi-flash.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-h5-orangepi-prime-add-regulator.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-h5-orangepi-prime-add-rtl8723cs.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-h5-orangepi-zero-plus-add-regulator.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-h6-Add-r_uart-uart2-3-pins.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-h6-orangepi-3-add-r_uart-aliase.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-h6-orangepi-3-delete-node-spi0.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-h6-orangepi-add-cpu-opp-refs.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-h6-orangepi-enable-higher-clock-regulator-max-.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-h6-orangepi-lite2-spi0-usb3phy-dwc3-enable.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-h6-orangepi.dtsi-Rollback-r_rsb-to-r_i2c.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-h6-pine-h64-add-dwc3-usb3phy.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-h6-pine-h64-add-wifi-rtl8723cs.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-h6.dtsi-add-pinctrl-pins-for-spi.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-h6.dtsi-improve-thermals.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-h616-add-pwm-nodes-support.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-h616-bigtreetech-cb1-sd-emmc.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-h616-orangepi-zero2-Enable-GPU-mali.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-h616-orangepi-zero2-reg_usb1_vbus-status-ok.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-h616-x96-mate-T95-eth-sd-card-hack.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-h616-x96-mate-add-hdmi.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-h616.dtsi-reserved-memory-512K-for-BL31.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-h618-orangepi-zero2w-Add-missing-nodes.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-dts-sun50i-h618-orangepi-zero3-Enable-GPU-mali.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/arm64-sun50i-h616-Add-i2c-2-3-4-uart-2-5-pins.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/cb1-overlay.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/clk-gate-add-support-for-regmap-based-gates.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/driver-allwinner-h618-emac.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/drivers-devfreq-sun8i-a33-mbus-disable-autorefresh.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/drivers-pwm-Add-pwm-sunxi-enhance-driver-for-h616.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/drv-clocksource-arm_arch_timer-fix-a64-timejump.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/drv-gpu-drm-gem-dma-Export-with-handle-allocator.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/drv-gpu-drm-panel-simple-Add-compability-olinuxino-lcd.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/drv-gpu-drm-sun4i-Add-GEM-allocator.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/drv-gpu-drm-sun4i-Add-HDMI-audio-sun4i-hdmi-encoder.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/drv-gpu-drm-sun4i-sun8i_mixer.c-add-h3-mixer1.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/drv-iio-adc-axp20x_adc-arm64-dts-axp803-hwmon-enable-thermal.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/drv-input-touchscreen-sun4i-ts-Enable-parsing.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/drv-media-dvb-frontends-si2168-fix-cmd-timeout.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/drv-mfd-axp20x-add-sysfs-interface.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/drv-mmc-host-sunxi-mmc-Disable-DDR52-mode-on-all-A20-based-boar.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/drv-mmc-host-sunxi-mmc-add-h5-emmc-compatible.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/drv-mtd-nand-raw-nand_ids.c-add-H27UBG8T2BTR-BC-nand.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/drv-net-stmmac-dwmac-sun8i-second-EMAC-clock-register.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/drv-nvmem-sunxi_sid-Support-SID-on-H616.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/drv-of-Device-Tree-Overlay-ConfigFS-interface.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/drv-phy-sun4i-usb-Allow-reset-line-to-be-shared.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/drv-pinctrl-pinctrl-sun50i-a64-disable_strict_mode.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/drv-pinctrl-sunxi-pinctrl-sun50i-h6.c-GPIO-disable_strict_mode.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/drv-rtc-sun6i-Add-Allwinner-H616-support.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/drv-rtc-sun6i-support-RTCs-without-external-LOSCs.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/drv-soc-sunxi-sram-Add-SRAM-C1-H616-handling.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/drv-spi-spi-sun4i.c-spi-bug-low-on-sck.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/drv-spi-spidev-Add-armbian-spi-dev-compatible.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/drv-staging-media-sunxi-cedrus-add-H616-variant.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/drv-staging-rtl8723bs-AP-bugfix.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/drv-usb-gadget-composite-rename-gadget-serial-console-manufactu.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/enable-TV-Output-on-OrangePi-Zero-LTE.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/fix-cpu-opp-table-sun8i-a83t.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/include-uapi-drm_fourcc-add-ARM-tiled-format-modifier.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/mfd-Add-support-for-X-Powers-AC200-EPHY-syscon.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/mfd-Add-support-for-X-Powers-AC200.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/mmc-host-sunxi-mmc-Fix-H6-emmc.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/net-phy-Add-support-for-AC200-EPHY.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/net-usb-r8152-add-LED-configuration-from-OF.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/nvmem-sunxi_sid-add-sunxi_get_soc_chipid-sunxi_get_serial.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/scripts-add-overlay-compilation-support.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/sound-soc-sunxi-Provoke-the-early-load-of-sun8i-codec-analog.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/sound-soc-sunxi-sun4i-codec-adcis-select-capture-source.patch create mode 100644 patch/kernel/archive/sunxi-6.14/patches.armbian/sound-soc-sunxi-sun8i-codec-analog-enable-sound.patch create mode 100644 patch/kernel/archive/sunxi-6.14/series.armbian diff --git a/patch/kernel/archive/sunxi-6.14/patches.armbian/ARM-dts-sun8i-nanopiduo2-Use-key-0-as-power-button.patch b/patch/kernel/archive/sunxi-6.14/patches.armbian/ARM-dts-sun8i-nanopiduo2-Use-key-0-as-power-button.patch new file mode 100644 index 000000000000..efa313c9425a --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.armbian/ARM-dts-sun8i-nanopiduo2-Use-key-0-as-power-button.patch @@ -0,0 +1,31 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Gunjan Gupta +Date: Mon, 26 Jun 2023 13:29:46 +0000 +Subject: ARM: dts: sun8i: nanopiduo2: Use key-0 as power button + +The onboard button key-0 was not marked as power button. This meant +that once the board was suspended, there was no way to bring it back +to life. Mark key-0 as power button so that it can be used to bring +the board back to life +--- + arch/arm/boot/dts/allwinner/sun8i-h3-nanopi-duo2.dts | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/arch/arm/boot/dts/allwinner/sun8i-h3-nanopi-duo2.dts b/arch/arm/boot/dts/allwinner/sun8i-h3-nanopi-duo2.dts +index 111111111111..222222222222 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-h3-nanopi-duo2.dts ++++ b/arch/arm/boot/dts/allwinner/sun8i-h3-nanopi-duo2.dts +@@ -42,8 +42,9 @@ gpio-keys { + + key-0 { + label = "k1"; +- linux,code = ; ++ linux,code = ; + gpios = <&r_pio 0 3 GPIO_ACTIVE_LOW>; /* PL3 */ ++ wakeup-source; + }; + }; + +-- +Armbian + diff --git a/patch/kernel/archive/sunxi-6.14/patches.armbian/ARM-dts-sun8i-nanopiduo2-enable-ethernet.patch b/patch/kernel/archive/sunxi-6.14/patches.armbian/ARM-dts-sun8i-nanopiduo2-enable-ethernet.patch new file mode 100644 index 000000000000..c60ef86c06e1 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.armbian/ARM-dts-sun8i-nanopiduo2-enable-ethernet.patch @@ -0,0 +1,31 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Gunjan Gupta +Date: Mon, 26 Jun 2023 13:53:14 +0000 +Subject: ARM: dts: sun8i: nanopiduo2: enable ethernet + +NanoPi Duo2 has pinout for ethernet. Lets enable the same in dts +--- + arch/arm/boot/dts/allwinner/sun8i-h3-nanopi-duo2.dts | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/arch/arm/boot/dts/allwinner/sun8i-h3-nanopi-duo2.dts b/arch/arm/boot/dts/allwinner/sun8i-h3-nanopi-duo2.dts +index 111111111111..222222222222 100644 +--- a/arch/arm/boot/dts/allwinner/sun8i-h3-nanopi-duo2.dts ++++ b/arch/arm/boot/dts/allwinner/sun8i-h3-nanopi-duo2.dts +@@ -105,6 +105,13 @@ &ehci0 { + status = "okay"; + }; + ++&emac { ++ phy-handle = <&int_mii_phy>; ++ phy-mode = "mii"; ++ allwinner,leds-active-low; ++ status = "okay"; ++}; ++ + &mmc0 { + bus-width = <4>; + cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ +-- +Armbian + diff --git a/patch/kernel/archive/sunxi-6.14/patches.armbian/ARM64-dts-sun50i-h616-BigTreeTech-CB1-Enable-EMAC1.patch b/patch/kernel/archive/sunxi-6.14/patches.armbian/ARM64-dts-sun50i-h616-BigTreeTech-CB1-Enable-EMAC1.patch new file mode 100644 index 000000000000..3d9c87c93025 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.armbian/ARM64-dts-sun50i-h616-BigTreeTech-CB1-Enable-EMAC1.patch @@ -0,0 +1,42 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: JohnTheCoolingFan +Date: Thu, 13 Jun 2024 11:50:55 +0000 +Subject: ARM64: dts: sun50i-h616: BigTreeTech CB1: Enable EMAC1 + +Signed-off-by: JohnTheCoolingFan +--- + arch/arm64/boot/dts/allwinner/sun50i-h616-bigtreetech-cb1.dtsi | 18 ++++++++++ + 1 file changed, 18 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h616-bigtreetech-cb1.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h616-bigtreetech-cb1.dtsi +index 111111111111..222222222222 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-h616-bigtreetech-cb1.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-h616-bigtreetech-cb1.dtsi +@@ -149,6 +149,24 @@ &cpu0 { + cpu-supply = <®_dcdc2>; + }; + ++&emac1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&rmii_pins>; ++ phy-mode = "rmii"; ++ phy-handle = <&rmii_phy>; ++ phy-supply = <®_dldo1>; ++ allwinner,rx-delay-ps = <3100>; ++ allwinner,tx-delay-ps = <700>; ++ status = "okay"; ++}; ++ ++&mdio1 { ++ rmii_phy: ethernet-phy@1 { ++ compatible = "ethernet-phy-ieee802.3-c22"; ++ reg = <1>; ++ }; ++}; ++ + &mmc0 { + vmmc-supply = <®_dldo1>; + broken-cd; +-- +Armbian + diff --git a/patch/kernel/archive/sunxi-6.14/patches.armbian/ARM64-dts-sun50i-h616-BigTreeTech-CB1-Enable-HDMI.patch b/patch/kernel/archive/sunxi-6.14/patches.armbian/ARM64-dts-sun50i-h616-BigTreeTech-CB1-Enable-HDMI.patch new file mode 100644 index 000000000000..895e4563401a --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.armbian/ARM64-dts-sun50i-h616-BigTreeTech-CB1-Enable-HDMI.patch @@ -0,0 +1,57 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: JohnTheCoolingFan +Date: Thu, 13 Jun 2024 11:07:35 +0000 +Subject: ARM64: dts: sun50i-h616: BigTreeTech CB1: Enable HDMI + +Signed-off-by: JohnTheCoolingFan +--- + arch/arm64/boot/dts/allwinner/sun50i-h616-bigtreetech-cb1.dtsi | 26 ++++++++++ + 1 file changed, 26 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h616-bigtreetech-cb1.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h616-bigtreetech-cb1.dtsi +index 111111111111..222222222222 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-h616-bigtreetech-cb1.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-h616-bigtreetech-cb1.dtsi +@@ -26,6 +26,17 @@ chosen { + stdout-path = "serial0:115200n8"; + }; + ++ connector { ++ compatible = "hdmi-connector"; ++ type = "d"; ++ ++ port { ++ hdmi_con_in: endpoint { ++ remote-endpoint = <&hdmi_out_con>; ++ }; ++ }; ++ }; ++ + leds { + compatible = "gpio-leds"; + +@@ -262,6 +273,21 @@ reg_dldo1: dldo1 { + }; + }; + ++&de { ++ status = "okay"; ++}; ++ ++&hdmi { ++ hvcc-supply = <®_aldo1>; ++ status = "okay"; ++}; ++ ++&hdmi_out { ++ hdmi_out_con: endpoint { ++ remote-endpoint = <&hdmi_con_in>; ++ }; ++}; ++ + &cpu0 { + cpu-supply = <®_dcdc2>; + status = "okay"; +-- +Armbian + diff --git a/patch/kernel/archive/sunxi-6.14/patches.armbian/ASoC-AC200-Initial-driver.patch b/patch/kernel/archive/sunxi-6.14/patches.armbian/ASoC-AC200-Initial-driver.patch new file mode 100644 index 000000000000..b634e51426f3 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.armbian/ASoC-AC200-Initial-driver.patch @@ -0,0 +1,842 @@ +From 2ace8ce7a3eb989f415186c74e6fbec9d2196c20 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Thu, 1 Sep 2022 17:36:53 +0200 +Subject: ASoC: AC200: Initial driver + +Signed-off-by: Jernej Skrabec +--- + sound/soc/codecs/Kconfig | 10 + + sound/soc/codecs/Makefile | 2 + + sound/soc/codecs/ac200.c | 772 ++++++++++++++++++++++++++++++++++++++ + 3 files changed, 784 insertions(+) + create mode 100644 sound/soc/codecs/ac200.c + +diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig +index 45737cfc5966..1b7b38e3b5b9 100644 +--- a/sound/soc/codecs/Kconfig ++++ b/sound/soc/codecs/Kconfig +@@ -16,6 +16,7 @@ config SND_SOC_ALL_CODECS + depends on COMPILE_TEST + imply SND_SOC_88PM860X + imply SND_SOC_AB8500_CODEC ++ imply SND_SOC_AC200_CODEC + imply SND_SOC_AC97_CODEC + imply SND_SOC_AD1836 + imply SND_SOC_AD193X_SPI +@@ -417,6 +418,15 @@ config SND_SOC_AB8500_CODEC + tristate + depends on ABX500_CORE + ++config SND_SOC_AC200_CODEC ++ tristate "AC200 Codec" ++ depends on MFD_AC200 ++ help ++ Enable support for X-Powers AC200 analog audio codec. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called snd-soc-ac200. ++ + config SND_SOC_AC97_CODEC + tristate "Build generic ASoC AC97 CODEC driver" + select SND_AC97_CODEC +diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile +index 7e12f3ae4f7d..eaae2fc19fbd 100644 +--- a/sound/soc/codecs/Makefile ++++ b/sound/soc/codecs/Makefile +@@ -1,6 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0 + snd-soc-88pm860x-y := 88pm860x-codec.o + snd-soc-ab8500-codec-y := ab8500-codec.o ++snd-soc-ac200-y := ac200.o + snd-soc-ac97-y := ac97.o + snd-soc-ad1836-y := ad1836.o + snd-soc-ad193x-y := ad193x.o +@@ -418,6 +419,7 @@ snd-soc-simple-mux-y := simple-mux.o + + obj-$(CONFIG_SND_SOC_88PM860X) += snd-soc-88pm860x.o + obj-$(CONFIG_SND_SOC_AB8500_CODEC) += snd-soc-ab8500-codec.o ++obj-$(CONFIG_SND_SOC_AC200_CODEC) += snd-soc-ac200.o + obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o + obj-$(CONFIG_SND_SOC_AD1836) += snd-soc-ad1836.o + obj-$(CONFIG_SND_SOC_AD193X) += snd-soc-ad193x.o +diff --git a/sound/soc/codecs/ac200.c b/sound/soc/codecs/ac200.c +new file mode 100644 +index 000000000000..662de230dc83 +--- /dev/null ++++ b/sound/soc/codecs/ac200.c +@@ -0,0 +1,772 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * X-Powers AC200 Codec Driver ++ * ++ * Copyright (C) 2022 Jernej Skrabec ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define AC200_CODEC_RATES (SNDRV_PCM_RATE_8000 | \ ++ SNDRV_PCM_RATE_11025 | \ ++ SNDRV_PCM_RATE_16000 | \ ++ SNDRV_PCM_RATE_22050 | \ ++ SNDRV_PCM_RATE_32000 | \ ++ SNDRV_PCM_RATE_44100 | \ ++ SNDRV_PCM_RATE_48000 | \ ++ SNDRV_PCM_RATE_96000 | \ ++ SNDRV_PCM_RATE_192000 | \ ++ SNDRV_PCM_RATE_KNOT) ++ ++#define AC200_CODEC_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ ++ SNDRV_PCM_FMTBIT_S16_LE | \ ++ SNDRV_PCM_FMTBIT_S20_LE | \ ++ SNDRV_PCM_FMTBIT_S24_LE | \ ++ SNDRV_PCM_FMTBIT_S32_LE) ++ ++#define AC200_SYS_AUDIO_CTL0 0x0010 ++#define AC200_SYS_AUDIO_CTL0_MCLK_GATING BIT(1) ++#define AC200_SYS_AUDIO_CTL0_RST_INVALID BIT(0) ++#define AC200_SYS_AUDIO_CTL1 0x0012 ++#define AC200_SYS_AUDIO_CTL1_I2S_IO_EN BIT(0) ++ ++#define AC200_SYS_CLK_CTL 0x2000 ++#define AC200_SYS_CLK_CTL_I2S 15 ++#define AC200_SYS_CLK_CTL_ADC 3 ++#define AC200_SYS_CLK_CTL_DAC 2 ++#define AC200_SYS_MOD_RST 0x2002 ++#define AC200_SYS_MOD_RST_I2S 15 ++#define AC200_SYS_MOD_RST_ADC 3 ++#define AC200_SYS_MOD_RST_DAC 2 ++#define AC200_SYS_SR_CTL 0x2004 ++#define AC200_SYS_SR_CTL_SR_MASK GENMASK(3, 0) ++#define AC200_SYS_SR_CTL_SR(x) (x) ++#define AC200_I2S_CTL 0x2100 ++#define AC200_I2S_CTL_SDO_EN 3 ++#define AC200_I2S_CTL_TX_EN 2 ++#define AC200_I2S_CTL_RX_EN 1 ++#define AC200_I2S_CTL_GEN 0 ++#define AC200_I2S_CLK 0x2102 ++#define AC200_I2S_CLK_BCLK_OUT BIT(15) ++#define AC200_I2S_CLK_LRCK_OUT BIT(14) ++#define AC200_I2S_CLK_BCLKDIV_MASK GENMASK(13, 10) ++#define AC200_I2S_CLK_BCLKDIV(x) ((x) << 10) ++#define AC200_I2S_CLK_LRCK_MASK GENMASK(9, 0) ++#define AC200_I2S_CLK_LRCK(x) ((x) - 1) ++#define AC200_I2S_FMT0 0x2104 ++#define AC200_I2S_FMT0_MODE_MASK GENMASK(15, 14) ++#define AC200_I2S_FMT0_MODE(x) ((x) << 14) ++#define AC200_I2S_FMT0_MODE_PCM 0 ++#define AC200_I2S_FMT0_MODE_LEFT 1 ++#define AC200_I2S_FMT0_MODE_RIGHT 2 ++#define AC200_I2S_FMT0_TX_OFFSET_MASK GENMASK(11, 10) ++#define AC200_I2S_FMT0_TX_OFFSET(x) ((x) << 10) ++#define AC200_I2S_FMT0_RX_OFFSET_MASK GENMASK(9, 8) ++#define AC200_I2S_FMT0_RX_OFFSET(x) ((x) << 8) ++#define AC200_I2S_FMT0_SR_MASK GENMASK(6, 4) ++#define AC200_I2S_FMT0_SR(x) ((x) << 4) ++#define AC200_I2S_FMT0_SW_MASK GENMASK(3, 1) ++#define AC200_I2S_FMT0_SW(x) ((x) << 1) ++#define AC200_I2S_FMT1 0x2108 ++#define AC200_I2S_FMT1_BCLK_POL_INVERT BIT(15) ++#define AC200_I2S_FMT1_LRCK_POL_INVERT BIT(14) ++#define AC200_I2S_MIX_SRC 0x2114 ++#define AC200_I2S_MIX_SRC_LMIX_DAC 13 ++#define AC200_I2S_MIX_SRC_LMIX_ADC 12 ++#define AC200_I2S_MIX_SRC_RMIX_DAC 9 ++#define AC200_I2S_MIX_SRC_RMIX_ADC 8 ++#define AC200_I2S_MIX_GAIN 0x2116 ++#define AC200_I2S_MIX_GAIN_LMIX_DAC 13 ++#define AC200_I2S_MIX_GAIN_LMIX_ADC 12 ++#define AC200_I2S_MIX_GAIN_RMIX_DAC 9 ++#define AC200_I2S_MIX_GAIN_RMIX_ADC 8 ++#define AC200_I2S_DAC_VOL 0x2118 ++#define AC200_I2S_DAC_VOL_LEFT 8 ++#define AC200_I2S_DAC_VOL_RIGHT 0 ++#define AC200_I2S_ADC_VOL 0x211A ++#define AC200_I2S_ADC_VOL_LEFT 8 ++#define AC200_I2S_ADC_VOL_RIGHT 0 ++#define AC200_DAC_CTL 0x2200 ++#define AC200_DAC_CTL_DAC_EN 15 ++#define AC200_DAC_MIX_SRC 0x2202 ++#define AC200_DAC_MIX_SRC_LMIX_DAC 13 ++#define AC200_DAC_MIX_SRC_LMIX_ADC 12 ++#define AC200_DAC_MIX_SRC_RMIX_DAC 9 ++#define AC200_DAC_MIX_SRC_RMIX_ADC 8 ++#define AC200_DAC_MIX_GAIN 0x2204 ++#define AC200_DAC_MIX_GAIN_LMIX_DAC 13 ++#define AC200_DAC_MIX_GAIN_LMIX_ADC 12 ++#define AC200_DAC_MIX_GAIN_RMIX_DAC 9 ++#define AC200_DAC_MIX_GAIN_RMIX_ADC 8 ++#define AC200_OUT_MIX_CTL 0x2220 ++#define AC200_OUT_MIX_CTL_RDAC_EN 15 ++#define AC200_OUT_MIX_CTL_LDAC_EN 14 ++#define AC200_OUT_MIX_CTL_RMIX_EN 13 ++#define AC200_OUT_MIX_CTL_LMIX_EN 12 ++#define AC200_OUT_MIX_CTL_MIC1_VOL 4 ++#define AC200_OUT_MIX_CTL_MIC2_VOL 0 ++#define AC200_OUT_MIX_SRC 0x2222 ++#define AC200_OUT_MIX_SRC_RMIX_MIC1 14 ++#define AC200_OUT_MIX_SRC_RMIX_MIC2 13 ++#define AC200_OUT_MIX_SRC_RMIX_RDAC 9 ++#define AC200_OUT_MIX_SRC_RMIX_LDAC 8 ++#define AC200_OUT_MIX_SRC_LMIX_MIC1 6 ++#define AC200_OUT_MIX_SRC_LMIX_MIC2 5 ++#define AC200_OUT_MIX_SRC_LMIX_RDAC 1 ++#define AC200_OUT_MIX_SRC_LMIX_LDAC 0 ++#define AC200_LINEOUT_CTL 0x2224 ++#define AC200_LINEOUT_CTL_EN 15 ++#define AC200_LINEOUT_CTL_LEN 14 ++#define AC200_LINEOUT_CTL_REN 13 ++#define AC200_LINEOUT_CTL_LMONO 12 ++#define AC200_LINEOUT_CTL_RMONO 11 ++#define AC200_LINEOUT_CTL_VOL 0 ++#define AC200_ADC_CTL 0x2300 ++#define AC200_ADC_CTL_ADC_EN 15 ++#define AC200_MBIAS_CTL 0x2310 ++#define AC200_MBIAS_CTL_MBIAS_EN 15 ++#define AC200_MBIAS_CTL_ADDA_BIAS_EN 3 ++#define AC200_ADC_MIC_CTL 0x2320 ++#define AC200_ADC_MIC_CTL_RADC_EN 15 ++#define AC200_ADC_MIC_CTL_LADC_EN 14 ++#define AC200_ADC_MIC_CTL_ADC_VOL 8 ++#define AC200_ADC_MIC_CTL_MIC1_GAIN_EN 7 ++#define AC200_ADC_MIC_CTL_MIC1_BOOST 4 ++#define AC200_ADC_MIC_CTL_MIC2_GAIN_EN 3 ++#define AC200_ADC_MIC_CTL_MIC2_BOOST 0 ++#define AC200_ADC_MIX_SRC 0x2322 ++#define AC200_ADC_MIX_SRC_RMIX_MIC1 14 ++#define AC200_ADC_MIX_SRC_RMIX_MIC2 13 ++#define AC200_ADC_MIX_SRC_RMIX_RMIX 9 ++#define AC200_ADC_MIX_SRC_RMIX_LMIX 8 ++#define AC200_ADC_MIX_SRC_LMIX_MIC1 6 ++#define AC200_ADC_MIX_SRC_LMIX_MIC2 5 ++#define AC200_ADC_MIX_SRC_LMIX_LMIX 1 ++#define AC200_ADC_MIX_SRC_LMIX_RMIX 0 ++ ++struct ac200_codec { ++ struct regmap *regmap; ++ unsigned int format; ++}; ++ ++struct ac200_map { ++ int match; ++ int value; ++}; ++ ++static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(mixer_scale, -600, 600, 0); ++static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(gain_scale, -450, 150, 0); ++static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(lineout_scale, -4650, 150, 1); ++static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(codec_scale, -12000, 75, 1); ++static const unsigned int mic_scale[] = { ++ TLV_DB_RANGE_HEAD(2), ++ 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), ++ 1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0), ++}; ++ ++static const struct snd_kcontrol_new ac200_codec_controls[] = { ++ SOC_DOUBLE_TLV("Master Playback Volume", AC200_I2S_DAC_VOL, ++ AC200_I2S_DAC_VOL_LEFT, AC200_I2S_DAC_VOL_RIGHT, ++ 0xff, 0, codec_scale), ++ SOC_DOUBLE_TLV("Master Capture Volume", AC200_I2S_ADC_VOL, ++ AC200_I2S_ADC_VOL_LEFT, AC200_I2S_ADC_VOL_RIGHT, ++ 0xff, 0, codec_scale), ++ SOC_DOUBLE_TLV("I2S ADC Capture Volume", AC200_I2S_MIX_GAIN, ++ AC200_I2S_MIX_GAIN_LMIX_ADC, AC200_I2S_MIX_GAIN_RMIX_ADC, ++ 0x1, 1, mixer_scale), ++ SOC_DOUBLE_TLV("I2S DAC Capture Volume", AC200_I2S_MIX_GAIN, ++ AC200_I2S_MIX_GAIN_LMIX_DAC, AC200_I2S_MIX_GAIN_RMIX_DAC, ++ 0x1, 1, mixer_scale), ++ SOC_DOUBLE_TLV("DAC I2S Playback Volume", AC200_DAC_MIX_GAIN, ++ AC200_DAC_MIX_GAIN_LMIX_DAC, AC200_DAC_MIX_GAIN_RMIX_DAC, ++ 0x1, 1, mixer_scale), ++ SOC_DOUBLE_TLV("ADC Playback Volume", AC200_DAC_MIX_GAIN, ++ AC200_DAC_MIX_GAIN_LMIX_ADC, AC200_DAC_MIX_GAIN_RMIX_ADC, ++ 0x1, 1, mixer_scale), ++ SOC_SINGLE_TLV("MIC1 Playback Volume", AC200_OUT_MIX_CTL, ++ AC200_OUT_MIX_CTL_MIC1_VOL, 0x7, 0, gain_scale), ++ SOC_SINGLE_TLV("MIC2 Playback Volume", AC200_OUT_MIX_CTL, ++ AC200_OUT_MIX_CTL_MIC2_VOL, 0x7, 0, gain_scale), ++ SOC_SINGLE_TLV("ADC Volume", AC200_ADC_MIC_CTL, ++ AC200_ADC_MIC_CTL_ADC_VOL, 0x07, 0, gain_scale), ++ SOC_SINGLE_TLV("Line Out Playback Volume", AC200_LINEOUT_CTL, ++ AC200_LINEOUT_CTL_VOL, 0x1f, 0, lineout_scale), ++ SOC_SINGLE_TLV("MIC1 Boost Volume", AC200_ADC_MIC_CTL, ++ AC200_ADC_MIC_CTL_MIC1_BOOST, 0x07, 0, mic_scale), ++ SOC_SINGLE_TLV("MIC2 Boost Volume", AC200_ADC_MIC_CTL, ++ AC200_ADC_MIC_CTL_MIC2_BOOST, 0x07, 0, mic_scale), ++ SOC_DOUBLE("Line Out Playback Switch", AC200_LINEOUT_CTL, ++ AC200_LINEOUT_CTL_LEN, AC200_LINEOUT_CTL_REN, 1, 0), ++}; ++ ++static const struct snd_kcontrol_new i2s_mixer[] = { ++ SOC_DAPM_DOUBLE("I2S DAC Capture Switch", AC200_I2S_MIX_SRC, ++ AC200_I2S_MIX_SRC_LMIX_DAC, ++ AC200_I2S_MIX_SRC_RMIX_DAC, 1, 0), ++ SOC_DAPM_DOUBLE("I2S ADC Capture Switch", AC200_I2S_MIX_SRC, ++ AC200_I2S_MIX_SRC_LMIX_ADC, ++ AC200_I2S_MIX_SRC_RMIX_ADC, 1, 0), ++}; ++ ++static const struct snd_kcontrol_new dac_mixer[] = { ++ SOC_DAPM_DOUBLE("DAC I2S Playback Switch", AC200_DAC_MIX_SRC, ++ AC200_DAC_MIX_SRC_LMIX_DAC, ++ AC200_DAC_MIX_SRC_RMIX_DAC, 1, 0), ++ SOC_DAPM_DOUBLE("ADC Playback Switch", AC200_DAC_MIX_SRC, ++ AC200_DAC_MIX_SRC_LMIX_ADC, ++ AC200_DAC_MIX_SRC_RMIX_ADC, 1, 0), ++}; ++ ++static const struct snd_kcontrol_new output_mixer[] = { ++ SOC_DAPM_DOUBLE("MIC1 Playback Switch", AC200_OUT_MIX_SRC, ++ AC200_OUT_MIX_SRC_LMIX_MIC1, ++ AC200_OUT_MIX_SRC_RMIX_MIC1, 1, 0), ++ SOC_DAPM_DOUBLE("MIC2 Playback Switch", AC200_OUT_MIX_SRC, ++ AC200_OUT_MIX_SRC_LMIX_MIC2, ++ AC200_OUT_MIX_SRC_RMIX_MIC2, 1, 0), ++ SOC_DAPM_DOUBLE("DAC Playback Switch", AC200_OUT_MIX_SRC, ++ AC200_OUT_MIX_SRC_LMIX_LDAC, ++ AC200_OUT_MIX_SRC_RMIX_RDAC, 1, 0), ++ SOC_DAPM_DOUBLE("DAC Reversed Playback Switch", AC200_OUT_MIX_SRC, ++ AC200_OUT_MIX_SRC_LMIX_RDAC, ++ AC200_OUT_MIX_SRC_RMIX_LDAC, 1, 0), ++}; ++ ++static const struct snd_kcontrol_new input_mixer[] = { ++ SOC_DAPM_DOUBLE("MIC1 Capture Switch", AC200_ADC_MIX_SRC, ++ AC200_ADC_MIX_SRC_LMIX_MIC1, ++ AC200_ADC_MIX_SRC_RMIX_MIC1, 1, 0), ++ SOC_DAPM_DOUBLE("MIC2 Capture Switch", AC200_ADC_MIX_SRC, ++ AC200_ADC_MIX_SRC_LMIX_MIC2, ++ AC200_ADC_MIX_SRC_RMIX_MIC2, 1, 0), ++ SOC_DAPM_DOUBLE("Output Mixer Capture Switch", AC200_ADC_MIX_SRC, ++ AC200_ADC_MIX_SRC_LMIX_LMIX, ++ AC200_ADC_MIX_SRC_RMIX_RMIX, 1, 0), ++ SOC_DAPM_DOUBLE("Output Mixer Reverse Capture Switch", ++ AC200_ADC_MIX_SRC, ++ AC200_ADC_MIX_SRC_LMIX_RMIX, ++ AC200_ADC_MIX_SRC_RMIX_LMIX, 1, 0), ++}; ++ ++const char * const lineout_mux_enum_text[] = { ++ "Stereo", "Mono", ++}; ++ ++static SOC_ENUM_DOUBLE_DECL(lineout_mux_enum, AC200_LINEOUT_CTL, ++ AC200_LINEOUT_CTL_LMONO, AC200_LINEOUT_CTL_RMONO, ++ lineout_mux_enum_text); ++ ++static const struct snd_kcontrol_new lineout_mux = ++ SOC_DAPM_ENUM("Line Out Source Playback Route", lineout_mux_enum); ++ ++static const struct snd_soc_dapm_widget ac200_codec_dapm_widgets[] = { ++ /* Regulator */ ++ SND_SOC_DAPM_REGULATOR_SUPPLY("avcc", 0, 0), ++ ++ /* System clocks */ ++ SND_SOC_DAPM_SUPPLY("CLK SYS I2S", AC200_SYS_CLK_CTL, ++ AC200_SYS_CLK_CTL_I2S, 0, NULL, 0), ++ SND_SOC_DAPM_SUPPLY("CLK SYS DAC", AC200_SYS_CLK_CTL, ++ AC200_SYS_CLK_CTL_DAC, 0, NULL, 0), ++ SND_SOC_DAPM_SUPPLY("CLK SYS ADC", AC200_SYS_CLK_CTL, ++ AC200_SYS_CLK_CTL_ADC, 0, NULL, 0), ++ ++ /* Module resets */ ++ SND_SOC_DAPM_SUPPLY("RST SYS I2S", AC200_SYS_MOD_RST, ++ AC200_SYS_MOD_RST_I2S, 0, NULL, 0), ++ SND_SOC_DAPM_SUPPLY("RST SYS DAC", AC200_SYS_MOD_RST, ++ AC200_SYS_MOD_RST_DAC, 0, NULL, 0), ++ SND_SOC_DAPM_SUPPLY("RST SYS ADC", AC200_SYS_MOD_RST, ++ AC200_SYS_MOD_RST_DAC, 0, NULL, 0), ++ ++ /* I2S gates */ ++ SND_SOC_DAPM_SUPPLY("CLK I2S GEN", AC200_I2S_CTL, ++ AC200_I2S_CTL_GEN, 0, NULL, 0), ++ SND_SOC_DAPM_SUPPLY("CLK I2S SDO", AC200_I2S_CTL, ++ AC200_I2S_CTL_SDO_EN, 0, NULL, 0), ++ SND_SOC_DAPM_SUPPLY("CLK I2S TX", AC200_I2S_CTL, ++ AC200_I2S_CTL_TX_EN, 0, NULL, 0), ++ SND_SOC_DAPM_SUPPLY("CLK I2S RX", AC200_I2S_CTL, ++ AC200_I2S_CTL_RX_EN, 0, NULL, 0), ++ ++ /* Module supplies */ ++ SND_SOC_DAPM_SUPPLY("ADC Enable", AC200_ADC_CTL, ++ AC200_ADC_CTL_ADC_EN, 0, NULL, 0), ++ SND_SOC_DAPM_SUPPLY("DAC Enable", AC200_DAC_CTL, ++ AC200_DAC_CTL_DAC_EN, 0, NULL, 0), ++ SND_SOC_DAPM_SUPPLY("Line Out Enable", AC200_LINEOUT_CTL, ++ AC200_LINEOUT_CTL_EN, 0, NULL, 0), ++ ++ /* Bias */ ++ SND_SOC_DAPM_SUPPLY("MIC Bias", AC200_MBIAS_CTL, ++ AC200_MBIAS_CTL_MBIAS_EN, 0, NULL, 0), ++ SND_SOC_DAPM_SUPPLY("ADDA Bias", AC200_MBIAS_CTL, ++ AC200_MBIAS_CTL_ADDA_BIAS_EN, 0, NULL, 0), ++ ++ /* DAC */ ++ SND_SOC_DAPM_DAC("Left DAC", "Playback", AC200_OUT_MIX_CTL, ++ AC200_OUT_MIX_CTL_LDAC_EN, 0), ++ SND_SOC_DAPM_DAC("Right DAC", "Playback", AC200_OUT_MIX_CTL, ++ AC200_OUT_MIX_CTL_RDAC_EN, 0), ++ ++ /* ADC */ ++ SND_SOC_DAPM_ADC("Left ADC", "Capture", AC200_ADC_MIC_CTL, ++ AC200_ADC_MIC_CTL_LADC_EN, 0), ++ SND_SOC_DAPM_ADC("Right ADC", "Capture", AC200_ADC_MIC_CTL, ++ AC200_ADC_MIC_CTL_RADC_EN, 0), ++ ++ /* Mixers */ ++ SND_SOC_DAPM_MIXER("Left Output Mixer", AC200_OUT_MIX_CTL, ++ AC200_OUT_MIX_CTL_LMIX_EN, 0, ++ output_mixer, ARRAY_SIZE(output_mixer)), ++ SND_SOC_DAPM_MIXER("Right Output Mixer", AC200_OUT_MIX_CTL, ++ AC200_OUT_MIX_CTL_RMIX_EN, 0, ++ output_mixer, ARRAY_SIZE(output_mixer)), ++ ++ SND_SOC_DAPM_MIXER("Left Input Mixer", SND_SOC_NOPM, 0, 0, ++ input_mixer, ARRAY_SIZE(input_mixer)), ++ SND_SOC_DAPM_MIXER("Right Input Mixer", SND_SOC_NOPM, 0, 0, ++ input_mixer, ARRAY_SIZE(input_mixer)), ++ ++ SND_SOC_DAPM_MIXER("Left DAC Mixer", SND_SOC_NOPM, 0, 0, ++ dac_mixer, ARRAY_SIZE(dac_mixer)), ++ SND_SOC_DAPM_MIXER("Right DAC Mixer", SND_SOC_NOPM, 0, 0, ++ dac_mixer, ARRAY_SIZE(dac_mixer)), ++ ++ SND_SOC_DAPM_MIXER("Left I2S Mixer", SND_SOC_NOPM, 0, 0, ++ i2s_mixer, ARRAY_SIZE(i2s_mixer)), ++ SND_SOC_DAPM_MIXER("Right I2S Mixer", SND_SOC_NOPM, 0, 0, ++ i2s_mixer, ARRAY_SIZE(i2s_mixer)), ++ ++ /* Muxes */ ++ SND_SOC_DAPM_MUX("Line Out Source Playback Route", ++ SND_SOC_NOPM, 0, 0, &lineout_mux), ++ ++ /* Gain/attenuation */ ++ SND_SOC_DAPM_PGA("MIC1 Amplifier", AC200_ADC_MIC_CTL, ++ AC200_ADC_MIC_CTL_MIC1_GAIN_EN, 0, NULL, 0), ++ SND_SOC_DAPM_PGA("MIC2 Amplifier", AC200_ADC_MIC_CTL, ++ AC200_ADC_MIC_CTL_MIC2_GAIN_EN, 0, NULL, 0), ++ ++ /* Inputs */ ++ SND_SOC_DAPM_INPUT("MIC1"), ++ SND_SOC_DAPM_INPUT("MIC2"), ++ ++ /* Outputs */ ++ SND_SOC_DAPM_OUTPUT("LINEOUT"), ++}; ++ ++static const struct snd_soc_dapm_route ac200_codec_dapm_routes[] = { ++ { "RST SYS I2S", NULL, "CLK SYS I2S" }, ++ { "RST SYS ADC", NULL, "CLK SYS ADC" }, ++ { "RST SYS DAC", NULL, "CLK SYS DAC" }, ++ ++ { "CLK I2S GEN", NULL, "RST SYS I2S" }, ++ { "CLK I2S SDO", NULL, "CLK I2S GEN" }, ++ { "CLK I2S TX", NULL, "CLK I2S SDO" }, ++ { "CLK I2S RX", NULL, "CLK I2S SDO" }, ++ ++ { "ADC Enable", NULL, "RST SYS ADC" }, ++ { "ADC Enable", NULL, "ADDA Bias" }, ++ { "ADC Enable", NULL, "avcc" }, ++ { "DAC Enable", NULL, "RST SYS DAC" }, ++ { "DAC Enable", NULL, "ADDA Bias" }, ++ { "DAC Enable", NULL, "avcc" }, ++ ++ { "Left DAC", NULL, "DAC Enable" }, ++ { "Left DAC", NULL, "CLK I2S RX" }, ++ { "Right DAC", NULL, "DAC Enable" }, ++ { "Right DAC", NULL, "CLK I2S RX" }, ++ ++ { "Left ADC", NULL, "ADC Enable" }, ++ { "Left ADC", NULL, "CLK I2S TX" }, ++ { "Right ADC", NULL, "ADC Enable" }, ++ { "Right ADC", NULL, "CLK I2S TX" }, ++ ++ { "Left Output Mixer", "MIC1 Playback Switch", "MIC1 Amplifier" }, ++ { "Left Output Mixer", "MIC2 Playback Switch", "MIC2 Amplifier" }, ++ { "Left Output Mixer", "DAC Playback Switch", "Left DAC Mixer" }, ++ { "Left Output Mixer", "DAC Reversed Playback Switch", "Right DAC Mixer" }, ++ ++ { "Right Output Mixer", "MIC1 Playback Switch", "MIC1 Amplifier" }, ++ { "Right Output Mixer", "MIC2 Playback Switch", "MIC2 Amplifier" }, ++ { "Right Output Mixer", "DAC Playback Switch", "Right DAC Mixer" }, ++ { "Right Output Mixer", "DAC Reversed Playback Switch", "Left DAC Mixer" }, ++ ++ { "Left Input Mixer", "MIC1 Capture Switch", "MIC1 Amplifier" }, ++ { "Left Input Mixer", "MIC2 Capture Switch", "MIC2 Amplifier" }, ++ { "Left Input Mixer", "Output Mixer Capture Switch", "Left Output Mixer" }, ++ { "Left Input Mixer", "Output Mixer Reverse Capture Switch", "Right Output Mixer" }, ++ ++ { "Right Input Mixer", "MIC1 Capture Switch", "MIC1 Amplifier" }, ++ { "Right Input Mixer", "MIC2 Capture Switch", "MIC2 Amplifier" }, ++ { "Right Input Mixer", "Output Mixer Capture Switch", "Right Output Mixer" }, ++ { "Right Input Mixer", "Output Mixer Reverse Capture Switch", "Left Output Mixer" }, ++ ++ { "Left I2S Mixer", "I2S DAC Capture Switch", "Left DAC" }, ++ { "Left I2S Mixer", "I2S ADC Capture Switch", "Left Input Mixer" }, ++ { "Right I2S Mixer", "I2S DAC Capture Switch", "Right DAC" }, ++ { "Right I2S Mixer", "I2S ADC Capture Switch", "Right Input Mixer" }, ++ ++ { "Left DAC Mixer", "DAC I2S Playback Switch", "Left DAC" }, ++ { "Left DAC Mixer", "ADC Playback Switch", "Left Input Mixer" }, ++ { "Right DAC Mixer", "DAC I2S Playback Switch", "Right DAC" }, ++ { "Right DAC Mixer", "ADC Playback Switch", "Right Input Mixer" }, ++ ++ { "Line Out Source Playback Route", "Stereo", "Left Output Mixer" }, ++ { "Line Out Source Playback Route", "Stereo", "Right Output Mixer" }, ++ { "Line Out Source Playback Route", "Mono", "Right Output Mixer" }, ++ { "Line Out Source Playback Route", "Mono", "Left Output Mixer" }, ++ ++ { "Left ADC", NULL, "Left I2S Mixer" }, ++ { "Right ADC", NULL, "Right I2S Mixer" }, ++ ++ { "LINEOUT", NULL, "Line Out Enable", }, ++ { "LINEOUT", NULL, "Line Out Source Playback Route" }, ++ ++ { "MIC1", NULL, "MIC Bias" }, ++ { "MIC2", NULL, "MIC Bias" }, ++ { "MIC1 Amplifier", NULL, "MIC1" }, ++ { "MIC2 Amplifier", NULL, "MIC2" }, ++}; ++ ++static int ac200_get_sr_sw(unsigned int width) ++{ ++ switch (width) { ++ case 8: ++ return 1; ++ case 12: ++ return 2; ++ case 16: ++ return 3; ++ case 20: ++ return 4; ++ case 24: ++ return 5; ++ case 28: ++ return 6; ++ case 32: ++ return 7; ++ } ++ ++ return -EINVAL; ++} ++ ++static const struct ac200_map ac200_bclk_div_map[] = { ++ { .match = 1, .value = 1 }, ++ { .match = 2, .value = 2 }, ++ { .match = 4, .value = 3 }, ++ { .match = 6, .value = 4 }, ++ { .match = 8, .value = 5 }, ++ { .match = 12, .value = 6 }, ++ { .match = 16, .value = 7 }, ++ { .match = 24, .value = 8 }, ++ { .match = 32, .value = 9 }, ++ { .match = 48, .value = 10 }, ++ { .match = 64, .value = 11 }, ++ { .match = 96, .value = 12 }, ++ { .match = 128, .value = 13 }, ++ { .match = 176, .value = 14 }, ++ { .match = 192, .value = 15 }, ++}; ++ ++static int ac200_get_bclk_div(unsigned int sample_rate, unsigned int period) ++{ ++ unsigned int sysclk_rate = (sample_rate % 4000) ? 22579200 : 24576000; ++ unsigned int div = sysclk_rate / sample_rate / period; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(ac200_bclk_div_map); i++) { ++ const struct ac200_map *bdiv = &ac200_bclk_div_map[i]; ++ ++ if (bdiv->match == div) ++ return bdiv->value; ++ } ++ ++ return -EINVAL; ++} ++ ++static const struct ac200_map ac200_ssr_map[] = { ++ { .match = 8000, .value = 0 }, ++ { .match = 11025, .value = 1 }, ++ { .match = 12000, .value = 2 }, ++ { .match = 16000, .value = 3 }, ++ { .match = 22050, .value = 4 }, ++ { .match = 24000, .value = 5 }, ++ { .match = 32000, .value = 6 }, ++ { .match = 44100, .value = 7 }, ++ { .match = 48000, .value = 8 }, ++ { .match = 96000, .value = 9 }, ++ { .match = 192000, .value = 10 }, ++}; ++ ++static int ac200_get_ssr(unsigned int sample_rate) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(ac200_ssr_map); i++) { ++ const struct ac200_map *ssr = &ac200_ssr_map[i]; ++ ++ if (ssr->match == sample_rate) ++ return ssr->value; ++ } ++ ++ return -EINVAL; ++} ++ ++static int ac200_codec_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, ++ struct snd_soc_dai *dai) ++{ ++ struct ac200_codec *priv = snd_soc_dai_get_drvdata(dai); ++ unsigned int slot_width = params_physical_width(params); ++ unsigned int sample_rate = params_rate(params); ++ int sr, period, sw, bclkdiv, ssr; ++ ++ sr = ac200_get_sr_sw(params_width(params)); ++ if (sr < 0) ++ return sr; ++ ++ sw = ac200_get_sr_sw(slot_width); ++ if (sw < 0) ++ return sw; ++ ++ regmap_update_bits(priv->regmap, AC200_I2S_FMT0, ++ AC200_I2S_FMT0_SR_MASK | ++ AC200_I2S_FMT0_SW_MASK, ++ AC200_I2S_FMT0_SR(sr) | ++ AC200_I2S_FMT0_SW(sw)); ++ ++ switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) { ++ case SND_SOC_DAIFMT_I2S: ++ case SND_SOC_DAIFMT_RIGHT_J: ++ case SND_SOC_DAIFMT_LEFT_J: ++ period = slot_width; ++ break; ++ case SND_SOC_DAIFMT_DSP_A: ++ case SND_SOC_DAIFMT_DSP_B: ++ period = slot_width * 2; ++ break; ++ } ++ ++ bclkdiv = ac200_get_bclk_div(sample_rate, period); ++ if (bclkdiv < 0) ++ return bclkdiv; ++ ++ regmap_update_bits(priv->regmap, AC200_I2S_CLK, ++ AC200_I2S_CLK_LRCK_MASK | ++ AC200_I2S_CLK_BCLKDIV_MASK, ++ AC200_I2S_CLK_LRCK(period) | ++ AC200_I2S_CLK_BCLKDIV(bclkdiv)); ++ ++ ssr = ac200_get_ssr(sample_rate); ++ if (ssr < 0) ++ return ssr; ++ ++ regmap_update_bits(priv->regmap, AC200_SYS_SR_CTL, ++ AC200_SYS_SR_CTL_SR_MASK, ++ AC200_SYS_SR_CTL_SR(ssr)); ++ ++ return 0; ++} ++ ++static int ac200_codec_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) ++{ ++ struct ac200_codec *priv = snd_soc_dai_get_drvdata(dai); ++ unsigned long offset, mode, value; ++ ++ priv->format = fmt; ++ ++ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { ++ case SND_SOC_DAIFMT_CBP_CFP: ++ value = AC200_I2S_CLK_BCLK_OUT | AC200_I2S_CLK_LRCK_OUT; ++ break; ++ case SND_SOC_DAIFMT_CBC_CFP: ++ value = AC200_I2S_CLK_LRCK_OUT; ++ break; ++ case SND_SOC_DAIFMT_CBP_CFC: ++ value = AC200_I2S_CLK_BCLK_OUT; ++ break; ++ case SND_SOC_DAIFMT_CBC_CFC: ++ value = 0; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ regmap_update_bits(priv->regmap, AC200_I2S_CLK, ++ AC200_I2S_CLK_BCLK_OUT | ++ AC200_I2S_CLK_LRCK_OUT, value); ++ ++ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { ++ case SND_SOC_DAIFMT_I2S: ++ mode = AC200_I2S_FMT0_MODE_LEFT; ++ offset = 1; ++ break; ++ case SND_SOC_DAIFMT_RIGHT_J: ++ mode = AC200_I2S_FMT0_MODE_RIGHT; ++ offset = 0; ++ break; ++ case SND_SOC_DAIFMT_LEFT_J: ++ mode = AC200_I2S_FMT0_MODE_LEFT; ++ offset = 0; ++ break; ++ case SND_SOC_DAIFMT_DSP_A: ++ mode = AC200_I2S_FMT0_MODE_PCM; ++ offset = 1; ++ break; ++ case SND_SOC_DAIFMT_DSP_B: ++ mode = AC200_I2S_FMT0_MODE_PCM; ++ offset = 0; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ regmap_update_bits(priv->regmap, AC200_I2S_FMT0, ++ AC200_I2S_FMT0_MODE_MASK | ++ AC200_I2S_FMT0_TX_OFFSET_MASK | ++ AC200_I2S_FMT0_RX_OFFSET_MASK, ++ AC200_I2S_FMT0_MODE(mode) | ++ AC200_I2S_FMT0_TX_OFFSET(offset) | ++ AC200_I2S_FMT0_RX_OFFSET(offset)); ++ ++ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { ++ case SND_SOC_DAIFMT_NB_NF: ++ value = 0; ++ break; ++ case SND_SOC_DAIFMT_NB_IF: ++ value = AC200_I2S_FMT1_LRCK_POL_INVERT; ++ break; ++ case SND_SOC_DAIFMT_IB_NF: ++ value = AC200_I2S_FMT1_BCLK_POL_INVERT; ++ break; ++ case SND_SOC_DAIFMT_IB_IF: ++ value = AC200_I2S_FMT1_BCLK_POL_INVERT | ++ AC200_I2S_FMT1_LRCK_POL_INVERT; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ regmap_update_bits(priv->regmap, AC200_I2S_FMT1, ++ AC200_I2S_FMT1_BCLK_POL_INVERT | ++ AC200_I2S_FMT1_LRCK_POL_INVERT, value); ++ ++ ++ return 0; ++} ++ ++static const struct snd_soc_dai_ops ac200_codec_dai_ops = { ++ .hw_params = ac200_codec_hw_params, ++ .set_fmt = ac200_codec_set_fmt, ++}; ++ ++static struct snd_soc_dai_driver ac200_codec_dai = { ++ .name = "ac200-dai", ++ .playback = { ++ .stream_name = "Playback", ++ .channels_min = 2, ++ .channels_max = 2, ++ .rates = AC200_CODEC_RATES, ++ .formats = AC200_CODEC_FORMATS, ++ }, ++ .capture = { ++ .stream_name = "Capture", ++ .channels_min = 1, ++ .channels_max = 2, ++ .rates = AC200_CODEC_RATES, ++ .formats = AC200_CODEC_FORMATS, ++ }, ++ .ops = &ac200_codec_dai_ops, ++ .symmetric_rate = 1, ++ .symmetric_sample_bits = 1, ++}; ++ ++static int ac200_codec_component_probe(struct snd_soc_component *component) ++{ ++ struct ac200_codec *priv = snd_soc_component_get_drvdata(component); ++ ++ snd_soc_component_init_regmap(component, priv->regmap); ++ ++ return 0; ++} ++ ++static struct snd_soc_component_driver ac200_soc_component = { ++ .controls = ac200_codec_controls, ++ .num_controls = ARRAY_SIZE(ac200_codec_controls), ++ .dapm_widgets = ac200_codec_dapm_widgets, ++ .num_dapm_widgets = ARRAY_SIZE(ac200_codec_dapm_widgets), ++ .dapm_routes = ac200_codec_dapm_routes, ++ .num_dapm_routes = ARRAY_SIZE(ac200_codec_dapm_routes), ++ .probe = ac200_codec_component_probe, ++}; ++ ++static int ac200_codec_probe(struct platform_device *pdev) ++{ ++ struct ac200_codec *priv; ++ int ret; ++ ++ priv = devm_kzalloc(&pdev->dev, sizeof(struct ac200_codec), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->regmap = dev_get_regmap(pdev->dev.parent, NULL); ++ if (!priv->regmap) ++ return -EPROBE_DEFER; ++ ++ platform_set_drvdata(pdev, priv); ++ ++ ret = regmap_write(priv->regmap, AC200_SYS_AUDIO_CTL0, ++ AC200_SYS_AUDIO_CTL0_RST_INVALID | ++ AC200_SYS_AUDIO_CTL0_MCLK_GATING); ++ if (ret) ++ return ret; ++ ++ ret = regmap_write(priv->regmap, AC200_SYS_AUDIO_CTL1, ++ AC200_SYS_AUDIO_CTL1_I2S_IO_EN); ++ if (ret) ++ return ret; ++ ++ ret = devm_snd_soc_register_component(&pdev->dev, &ac200_soc_component, ++ &ac200_codec_dai, 1); ++ ++ if (ret) ++ dev_err(&pdev->dev, "Failed to register codec: %d\n", ret); ++ ++ return ret; ++} ++ ++static void ac200_codec_remove(struct platform_device *pdev) ++{ ++ struct ac200_codec *priv = dev_get_drvdata(&pdev->dev); ++ ++ regmap_write(priv->regmap, AC200_SYS_AUDIO_CTL0, 0); ++ regmap_write(priv->regmap, AC200_SYS_AUDIO_CTL1, 0); ++} ++ ++static const struct of_device_id ac200_codec_match[] = { ++ { .compatible = "x-powers,ac200-codec" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, ac200_codec_match); ++ ++static struct platform_driver ac200_codec_driver = { ++ .driver = { ++ .name = "ac200-codec", ++ .of_match_table = ac200_codec_match, ++ }, ++ .probe = ac200_codec_probe, ++ .remove = ac200_codec_remove, ++}; ++module_platform_driver(ac200_codec_driver); ++ ++MODULE_DESCRIPTION("X-Powers AC200 Codec Driver"); ++MODULE_AUTHOR("Jernej Skrabec "); ++MODULE_LICENSE("GPL"); +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.armbian/Add-BananaPi-BPI-M4-Zero-overlays.patch b/patch/kernel/archive/sunxi-6.14/patches.armbian/Add-BananaPi-BPI-M4-Zero-overlays.patch new file mode 100644 index 000000000000..00642052e852 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.armbian/Add-BananaPi-BPI-M4-Zero-overlays.patch @@ -0,0 +1,377 @@ +From 43a7177563683a1e7d192138f65073d687ed068b Mon Sep 17 00:00:00 2001 +From: Patrick Yavitz +Date: Tue, 7 Jan 2025 06:39:30 -0500 +Subject: Add BananaPi BPI-M4-Zero overlays + +Signed-off-by: Patrick Yavitz +--- + .../arm64/boot/dts/allwinner/overlay/Makefile | 13 ++++++ + ...sun50i-h616-bananapi-m4-pg-15-16-i2c4.dtso | 13 ++++++ + ...sun50i-h616-bananapi-m4-pg-17-18-i2c3.dtso | 13 ++++++ + .../sun50i-h616-bananapi-m4-pg-6-7-uart1.dtso | 13 ++++++ + ...h616-bananapi-m4-pg-8-9-rts-cts-uart1.dtso | 16 +++++++ + .../sun50i-h616-bananapi-m4-ph-2-3-uart5.dtso | 13 ++++++ + ...un50i-h616-bananapi-m4-pi-13-14-uart4.dtso | 13 ++++++ + ...16-bananapi-m4-pi-15-16-rts-cts-uart4.dtso | 16 +++++++ + .../sun50i-h616-bananapi-m4-pi-5-6-i2c0.dtso | 13 ++++++ + .../sun50i-h616-bananapi-m4-pi-7-8-i2c1.dtso | 13 ++++++ + .../sun50i-h616-bananapi-m4-sdio-wifi-bt.dtso | 44 +++++++++++++++++++ + ...-h616-bananapi-m4-spi1-cs0-cs1-spidev.dtso | 32 ++++++++++++++ + ...n50i-h616-bananapi-m4-spi1-cs0-spidev.dtso | 24 ++++++++++ + ...n50i-h616-bananapi-m4-spi1-cs1-spidev.dtso | 13 ++++++ + 14 files changed, 249 insertions(+) + create mode 100644 arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-pg-15-16-i2c4.dtso + create mode 100644 arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-pg-17-18-i2c3.dtso + create mode 100644 arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-pg-6-7-uart1.dtso + create mode 100644 arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-pg-8-9-rts-cts-uart1.dtso + create mode 100644 arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-ph-2-3-uart5.dtso + create mode 100644 arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-pi-13-14-uart4.dtso + create mode 100644 arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-pi-15-16-rts-cts-uart4.dtso + create mode 100644 arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-pi-5-6-i2c0.dtso + create mode 100644 arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-pi-7-8-i2c1.dtso + create mode 100644 arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-sdio-wifi-bt.dtso + create mode 100644 arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-spi1-cs0-cs1-spidev.dtso + create mode 100644 arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-spi1-cs0-spidev.dtso + create mode 100644 arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-spi1-cs1-spidev.dtso + +diff --git a/arch/arm64/boot/dts/allwinner/overlay/Makefile b/arch/arm64/boot/dts/allwinner/overlay/Makefile +index ccb89b5bf495..3e87d21c9d14 100644 +--- a/arch/arm64/boot/dts/allwinner/overlay/Makefile ++++ b/arch/arm64/boot/dts/allwinner/overlay/Makefile +@@ -49,6 +49,19 @@ dtb-$(CONFIG_ARCH_SUNXI) += \ + sun50i-h6-uart2.dtbo \ + sun50i-h6-uart3.dtbo \ + sun50i-h6-w1-gpio.dtbo \ ++ sun50i-h616-bananapi-m4-pg-6-7-uart1.dtbo \ ++ sun50i-h616-bananapi-m4-pg-8-9-rts-cts-uart1.dtbo \ ++ sun50i-h616-bananapi-m4-pg-15-16-i2c4.dtbo \ ++ sun50i-h616-bananapi-m4-pg-17-18-i2c3.dtbo \ ++ sun50i-h616-bananapi-m4-ph-2-3-uart5.dtbo \ ++ sun50i-h616-bananapi-m4-pi-13-14-uart4.dtbo \ ++ sun50i-h616-bananapi-m4-pi-15-16-rts-cts-uart4.dtbo \ ++ sun50i-h616-bananapi-m4-pi-5-6-i2c0.dtbo \ ++ sun50i-h616-bananapi-m4-pi-7-8-i2c1.dtbo \ ++ sun50i-h616-bananapi-m4-sdio-wifi-bt.dtbo \ ++ sun50i-h616-bananapi-m4-spi1-cs0-cs1-spidev.dtbo \ ++ sun50i-h616-bananapi-m4-spi1-cs0-spidev.dtbo \ ++ sun50i-h616-bananapi-m4-spi1-cs1-spidev.dtbo \ + sun50i-h616-gpu.dtbo \ + sun50i-h616-i2c0-pi.dtbo \ + sun50i-h616-i2c1-pi.dtbo \ +diff --git a/arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-pg-15-16-i2c4.dtso b/arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-pg-15-16-i2c4.dtso +new file mode 100644 +index 000000000000..4e78aa8f1f27 +--- /dev/null ++++ b/arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-pg-15-16-i2c4.dtso +@@ -0,0 +1,13 @@ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "sinovoip,bpi-m4-zero", "allwinner,sun50i-h616", "allwinner,sun50i-h618"; ++ ++ fragment@0 { ++ target = <&i2c4>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++}; +diff --git a/arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-pg-17-18-i2c3.dtso b/arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-pg-17-18-i2c3.dtso +new file mode 100644 +index 000000000000..3419eee0b70b +--- /dev/null ++++ b/arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-pg-17-18-i2c3.dtso +@@ -0,0 +1,13 @@ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "sinovoip,bpi-m4-zero", "allwinner,sun50i-h616", "allwinner,sun50i-h618"; ++ ++ fragment@0 { ++ target = <&i2c3>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++}; +diff --git a/arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-pg-6-7-uart1.dtso b/arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-pg-6-7-uart1.dtso +new file mode 100644 +index 000000000000..7001781f42d2 +--- /dev/null ++++ b/arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-pg-6-7-uart1.dtso +@@ -0,0 +1,13 @@ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "sinovoip,bpi-m4-zero", "allwinner,sun50i-h616", "allwinner,sun50i-h618"; ++ ++ fragment@0 { ++ target = <&uart1>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++}; +diff --git a/arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-pg-8-9-rts-cts-uart1.dtso b/arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-pg-8-9-rts-cts-uart1.dtso +new file mode 100644 +index 000000000000..1317a9b3b52f +--- /dev/null ++++ b/arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-pg-8-9-rts-cts-uart1.dtso +@@ -0,0 +1,16 @@ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "sinovoip,bpi-m4-zero", "allwinner,sun50i-h616", "allwinner,sun50i-h618"; ++ ++ fragment@0 { ++ target = <&uart1>; ++ __overlay__ { ++ status = "okay"; ++ pinctrl-0 = <&uart1_pins>, <&uart1_rts_cts_pins>; ++ pinctrl-names = "default"; ++ uart-has-rtscts; ++ }; ++ }; ++}; +diff --git a/arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-ph-2-3-uart5.dtso b/arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-ph-2-3-uart5.dtso +new file mode 100644 +index 000000000000..aaa96e46d708 +--- /dev/null ++++ b/arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-ph-2-3-uart5.dtso +@@ -0,0 +1,13 @@ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "sinovoip,bpi-m4-zero", "allwinner,sun50i-h616", "allwinner,sun50i-h618"; ++ ++ fragment@0 { ++ target = <&uart5>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++}; +diff --git a/arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-pi-13-14-uart4.dtso b/arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-pi-13-14-uart4.dtso +new file mode 100644 +index 000000000000..0373f7d25449 +--- /dev/null ++++ b/arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-pi-13-14-uart4.dtso +@@ -0,0 +1,13 @@ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "sinovoip,bpi-m4-zero", "allwinner,sun50i-h616", "allwinner,sun50i-h618"; ++ ++ fragment@0 { ++ target = <&uart4>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++}; +diff --git a/arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-pi-15-16-rts-cts-uart4.dtso b/arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-pi-15-16-rts-cts-uart4.dtso +new file mode 100644 +index 000000000000..ef9394c8519c +--- /dev/null ++++ b/arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-pi-15-16-rts-cts-uart4.dtso +@@ -0,0 +1,16 @@ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "sinovoip,bpi-m4-zero", "allwinner,sun50i-h616", "allwinner,sun50i-h618"; ++ ++ fragment@0 { ++ target = <&uart4>; ++ __overlay__ { ++ status = "okay"; ++ pinctrl-0 = <&uart4_pi_pins>, <&uart4_pi_rts_cts_pins>; ++ pinctrl-names = "default"; ++ uart-has-rtscts; ++ }; ++ }; ++}; +diff --git a/arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-pi-5-6-i2c0.dtso b/arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-pi-5-6-i2c0.dtso +new file mode 100644 +index 000000000000..60c75e4d61b5 +--- /dev/null ++++ b/arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-pi-5-6-i2c0.dtso +@@ -0,0 +1,13 @@ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "sinovoip,bpi-m4-zero", "allwinner,sun50i-h616", "allwinner,sun50i-h618"; ++ ++ fragment@0 { ++ target = <&i2c0>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++}; +diff --git a/arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-pi-7-8-i2c1.dtso b/arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-pi-7-8-i2c1.dtso +new file mode 100644 +index 000000000000..99c7e2b8c5f6 +--- /dev/null ++++ b/arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-pi-7-8-i2c1.dtso +@@ -0,0 +1,13 @@ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "sinovoip,bpi-m4-zero", "allwinner,sun50i-h616", "allwinner,sun50i-h618"; ++ ++ fragment@0 { ++ target = <&i2c1>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++}; +diff --git a/arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-sdio-wifi-bt.dtso b/arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-sdio-wifi-bt.dtso +new file mode 100644 +index 000000000000..307f6e5e921c +--- /dev/null ++++ b/arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-sdio-wifi-bt.dtso +@@ -0,0 +1,44 @@ ++/dts-v1/; ++/plugin/; ++ ++#include ++ ++/ { ++ compatible = "sinovoip,bpi-m4-zero", "allwinner,sun50i-h616", "allwinner,sun50i-h618"; ++ ++ fragment@0 { ++ target-path = "/"; ++ __overlay__ { ++ model = "BananaPi BPI-M4-Zero v2"; ++ }; ++ }; ++ ++ /* SDIO WIFI */ ++ fragment@1 { ++ target = <&mmc1>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ /* BLUETOOTH */ ++ fragment@2 { ++ target = <&uart1>; ++ __overlay__ { ++ status = "okay"; ++ pinctrl-0 = <&uart1_pins>, <&uart1_rts_cts_pins>; ++ pinctrl-names = "default"; ++ uart-has-rtscts; ++ ++ bluetooth { ++ compatible = "brcm,bcm43540-bt"; ++ host-wakeup-gpios = <&pio 6 16 GPIO_ACTIVE_HIGH>; ++ device-wakeup-gpios = <&pio 6 17 GPIO_ACTIVE_HIGH>; ++ shutdown-gpios = <&pio 6 19 GPIO_ACTIVE_HIGH>; ++ max-speed = <1500000>; ++ vbat-supply = <®_vcc3v3>; ++ vddio-supply = <®_vcc1v8>; ++ }; ++ }; ++ }; ++}; +diff --git a/arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-spi1-cs0-cs1-spidev.dtso b/arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-spi1-cs0-cs1-spidev.dtso +new file mode 100644 +index 000000000000..7fa3b94bcc8d +--- /dev/null ++++ b/arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-spi1-cs0-cs1-spidev.dtso +@@ -0,0 +1,32 @@ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "sinovoip,bpi-m4-zero", "allwinner,sun50i-h616", "allwinner,sun50i-h618"; ++ ++ fragment@0 { ++ target = <&spi1>; ++ __overlay__ { ++ status = "okay"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi1_pins>, <&spi1_cs0_pin>, <&spi1_cs1_pin>; ++ ++ spidev@0 { ++ compatible = "rohm,dh2228fv"; ++ status = "okay"; ++ reg = <0>; ++ spi-max-frequency = <50000000>; ++ }; ++ ++ spidev@1 { ++ compatible = "rohm,dh2228fv"; ++ status = "okay"; ++ reg = <1>; ++ spi-max-frequency = <50000000>; ++ }; ++ }; ++ }; ++}; +diff --git a/arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-spi1-cs0-spidev.dtso b/arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-spi1-cs0-spidev.dtso +new file mode 100644 +index 000000000000..fef73f1afa52 +--- /dev/null ++++ b/arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-spi1-cs0-spidev.dtso +@@ -0,0 +1,24 @@ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "sinovoip,bpi-m4-zero", "allwinner,sun50i-h616", "allwinner,sun50i-h618"; ++ ++ fragment@0 { ++ target = <&spi1>; ++ __overlay__ { ++ status = "okay"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi1_pins>, <&spi1_cs0_pin>; ++ spidev@0 { ++ compatible = "rohm,dh2228fv"; ++ status = "okay"; ++ reg = <0>; ++ spi-max-frequency = <1000000>; ++ }; ++ }; ++ }; ++}; +diff --git a/arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-spi1-cs1-spidev.dtso b/arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-spi1-cs1-spidev.dtso +new file mode 100644 +index 000000000000..840357f2e9e0 +--- /dev/null ++++ b/arch/arm64/boot/dts/allwinner/overlay/sun50i-h616-bananapi-m4-spi1-cs1-spidev.dtso +@@ -0,0 +1,13 @@ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "sinovoip,bpi-m4-zero", "allwinner,sun50i-h616", "allwinner,sun50i-h618"; ++ ++ fragment@0 { ++ target = <&spi1>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++}; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.armbian/Add-BananaPi-BPI-M4-Zero-pinctrl.patch b/patch/kernel/archive/sunxi-6.14/patches.armbian/Add-BananaPi-BPI-M4-Zero-pinctrl.patch new file mode 100644 index 000000000000..42e2ef571923 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.armbian/Add-BananaPi-BPI-M4-Zero-pinctrl.patch @@ -0,0 +1,81 @@ +From 78a53cd64db4002e9c9a4f46406e7182b3f3b934 Mon Sep 17 00:00:00 2001 +From: Patrick Yavitz +Date: Thu, 12 Dec 2024 06:49:59 -0500 +Subject: Add BananaPi BPI-M4-Zero pinctrl + +Signed-off-by: Patrick Yavitz +--- + .../arm64/boot/dts/allwinner/sun50i-h616.dtsi | 36 +++++++++++++++++++ + 1 file changed, 36 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi +index 954c8eab2c44..004e824dfe0b 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi +@@ -409,6 +409,12 @@ i2c0_pins: i2c0-pins { + function = "i2c0"; + }; + ++ /omit-if-no-ref/ ++ i2c1_pi_pins: i2c1-pi-pins { ++ pins = "PI7", "PI8"; ++ function = "i2c1"; ++ }; ++ + /omit-if-no-ref/ + i2c2_ph_pins: i2c2-ph-pins { + pins = "PH2", "PH3"; +@@ -571,6 +577,12 @@ spi1_cs0_pin: spi1-cs0-pin { + function = "spi1"; + }; + ++ /omit-if-no-ref/ ++ spi1_cs1_pin: spi1-cs1-pin { ++ pins = "PH9"; ++ function = "spi1"; ++ }; ++ + spdif_tx_pin: spdif-tx-pin { + pins = "PH4"; + function = "spdif"; +@@ -593,6 +605,12 @@ uart1_rts_cts_pins: uart1-rts-cts-pins { + function = "uart1"; + }; + ++ /omit-if-no-ref/ ++ uart2_pi_pins: uart2-pi-pins { ++ pins = "PI5", "PI6"; ++ function = "uart2"; ++ }; ++ + /omit-if-no-ref/ + uart2_pg_pins: uart2-pg-pins { + pins = "PG15", "PG16"; +@@ -617,6 +635,24 @@ uart2_ph_rts_cts_pins: uart2-ph-rts-cts-pins { + function = "uart2"; + }; + ++ /omit-if-no-ref/ ++ uart3_pi_pins: uart3-pi-pins { ++ pins = "PI9", "PI10"; ++ function = "uart3"; ++ }; ++ ++ /omit-if-no-ref/ ++ uart4_pi_pins: uart4-pi-pins { ++ pins = "PI13", "PI14"; ++ function = "uart4"; ++ }; ++ ++ /omit-if-no-ref/ ++ uart4_pi_rts_cts_pins: uart4-pi-rts-cts-pins { ++ pins = "PI15", "PI16"; ++ function = "uart4"; ++ }; ++ + /omit-if-no-ref/ + uart5_pins: uart5-pins { + pins = "PH2", "PH3"; +-- +2.35.3 + diff --git a/patch/kernel/archive/sunxi-6.14/patches.armbian/Add-FB_TFT-ST7796S-driver.patch b/patch/kernel/archive/sunxi-6.14/patches.armbian/Add-FB_TFT-ST7796S-driver.patch new file mode 100644 index 000000000000..7abbc213124b --- /dev/null +++ b/patch/kernel/archive/sunxi-6.14/patches.armbian/Add-FB_TFT-ST7796S-driver.patch @@ -0,0 +1,154 @@ +From 0987e0158d63711b01a2435fa515d80a5b540422 Mon Sep 17 00:00:00 2001 +From: Alan +Date: Sat, 20 May 2023 14:33:52 +0800 +Subject: Add: FB_TFT ST7796S driver + +--- + drivers/staging/fbtft/Kconfig | 10 +++ + drivers/staging/fbtft/Makefile | 1 + + drivers/staging/fbtft/fb_st7796s.c | 100 +++++++++++++++++++++++++++++ + 3 files changed, 111 insertions(+) + create mode 100644 drivers/staging/fbtft/fb_st7796s.c + +diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig +index dcf6a70455cc..42fe4b73aa3f 100644 +--- a/drivers/staging/fbtft/Kconfig ++++ b/drivers/staging/fbtft/Kconfig +@@ -169,6 +169,16 @@ config FB_TFT_ST7789V + + Say Y if you have such a display that utilizes this controller. + ++config FB_TFT_ST7796S ++ tristate "FB driver for the ST7796S LCD Controller" ++ depends on FB_TFT ++ help ++ This enables generic framebuffer support for the Sitronix ST7796S ++ display controller. The controller is intended for small color ++ displays with a resolution of up to 480x320 pixels. ++ ++ Say Y if you have such a display that utilizes this controller. ++ + config FB_TFT_TINYLCD + tristate "FB driver for tinylcd.com display" + depends on FB_TFT +diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile +index e9cdf0f0a7da..7b2098b8a1bd 100644 +--- a/drivers/staging/fbtft/Makefile ++++ b/drivers/staging/fbtft/Makefile +@@ -31,6 +31,7 @@ obj-$(CONFIG_FB_TFT_SSD1331) += fb_ssd1331.o + obj-$(CONFIG_FB_TFT_SSD1351) += fb_ssd1351.o + obj-$(CONFIG_FB_TFT_ST7735R) += fb_st7735r.o + obj-$(CONFIG_FB_TFT_ST7789V) += fb_st7789v.o ++obj-$(CONFIG_FB_TFT_ST7796S) += fb_st7796s.o + obj-$(CONFIG_FB_TFT_TINYLCD) += fb_tinylcd.o + obj-$(CONFIG_FB_TFT_TLS8204) += fb_tls8204.o + obj-$(CONFIG_FB_TFT_UC1611) += fb_uc1611.o +diff --git a/drivers/staging/fbtft/fb_st7796s.c b/drivers/staging/fbtft/fb_st7796s.c +new file mode 100644 +index 000000000000..cad489cef595 +--- /dev/null ++++ b/drivers/staging/fbtft/fb_st7796s.c +@@ -0,0 +1,100 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * FB driver for the ST7796S LCD Controller ++ * ++ * Copyright (c) 2023 Alan Ma ++ * Copyright (c) 2014 Petr Olivka ++ * Copyright (c) 2013 Noralf Tronnes ++ */ ++ ++#include ++#include ++#include ++#include ++#include