From 6da383de61f845e48b1d20c549bbe555501dd89c Mon Sep 17 00:00:00 2001 From: Gerben Jan Dijkman Date: Mon, 10 Jan 2022 13:52:54 +0100 Subject: [PATCH] Prepration for PPP and 5.16 support --- sys-kernel/pinephone-sources/Manifest | 3 - ...inner-pinephone-stop-LEDs-on-suspend.patch | 40 - ...ap-order-of-search-for-connection-to.patch | 35 + ...rivers-usb-add-reset_resume-callback.patch | 24 - ...collect-fbdev-scrolling-acceleration.patch | 1038 + ...-PRivacy-BLE-device-fails-to-connect.patch | 67 - ...s-Add-USB_QUIRK_RESET-for-Quectel-EG.patch | 28 - ...ts-add-pinetab-dev-old-display-panel.patch | 61 - ...sued-softback_lines-cursor-argument.patch} | 0 ...rt-fbcon-remove-soft-scrollback-code.patch | 497 - ...-sdhci-arasan-Add-runtime-PM-support.patch | 152 + ...-to-set-RPA-timeout-when-unsupported.patch | 48 - .../files/0003-Bluetooth-btusb.patch | 40 - ...port-SCLK_CIF_OUT_SRC-to-device-tree.patch | 37 + ...wan-provide-wrapper-for-reset_resume.patch | 51 - ...fbcon-remove-no-op-fbcon_set_origin.patch} | 0 ...wdm-provide-wrapper-for-reset_resume.patch | 65 - ...t-fix-memory-leak-in-efivarfs_create.patch | 58 - ...04-media-rockchip-rga-Fix-probe-bugs.patch | 39 + ...t-fbcon-remove-soft-scrollback-code.patch} | 0 ...rockchip-Ensure-that-lane-is-properl.patch | 34 + ...b-qmi_wwan-set-the-DTR-when-resuming.patch | 34 - ...cdc-wdm-send-HUP-if-we-are-resetting.patch | 27 - ...mipi-dsi-Fix-missing-clk_disable_unp.patch | 48 + ...pi-dsi-Fix-enable-disable-of-dsi-con.patch | 145 + ...rockchip-Never-allow-lane-bandwidth-.patch | 28 + ...-cdn-dp-Disable-CDN-DP-on-disconnect.patch | 23 + ...-Add-pine64-pinephone-to-compat-list.patch | 56 - ...dd-events-for-early-fb-event-support.patch | 69 + ...011-dts-pinetab-hardcode-mmc-numbers.patch | 29 - ...818-Configure-rk808-clkout2-function.patch | 40 + .../0012-pinephone-fix-pogopin-i2c.patch | 29 - ...battery-Add-battery-driver-for-RK818.patch | 3964 + ...18-battery-Use-a-more-propper-compat.patch | 46 + ...e-Don-t-ignore-max_current-of-0-when.patch | 85 + ...18-charger-Implement-charger-driver-.patch | 699 + ...2-Set-the-current-before-enabling-pu.patch | 47 + ...2-Extend-debugging-interface-with-dr.patch | 108 + ...2-Retry-reading-of-CC-pins-status-if.patch | 72 + ...2-More-useful-of-logging-status-on-i.patch | 186 + ...2-Update-VBUS-state-even-if-VBUS-int.patch | 39 + ...2-Make-tcpm-fusb302-logs-less-pollut.patch | 160 + ...-typec-fusb302-Add-OF-extcon-support.patch | 34 + ...pec-fusb302-Fix-register-definitions.patch | 45 + ...2-Clear-interrupts-before-we-start-t.patch | 37 + ...extcon-Add-typec-extcon-bridge-drive.patch | 388 + ...ec-Make-sure-the-plug-orientation-is.patch | 64 + ...mx258-Add-support-for-powerdown-gpio.patch | 56 + ...-Don-t-be-too-strict-about-clock-rat.patch | 41 + ...2c-imx258-Add-support-for-reset-gpio.patch | 57 + ...mx258-Add-support-for-power-supplies.patch | 100 + ...-more-framerates-to-the-driver-some-.patch | 52 + ...eriment-Try-to-disable-denoising-sha.patch | 47 + ...ep-after-poweroff-to-ensure-next-pow.patch | 25 + ...-t-powerup-the-sensor-during-driver-.patch | 86 + ...35-media-ov5640-Implement-autofocus.patch} | 38 +- ...a-ov5640-set-default-ae-target-lower.patch | 23 + ...-Add-driver-for-HX8394-based-HannSta.patch | 464 + ...-Improve-the-panel-driver-make-it-wo.patch | 226 + ...-Fix-mode-clock-for-the-pinephone-pr.patch | 29 + ...-option-to-power-off-the-controller-.patch | 97 + ...-t-disable-regulators-during-suspend.patch | 49 + ...n-goodix-Respect-IRQ-flags-from-DT-w.patch | 53 + ...screen-goodix-Add-support-for-GT1158.patch | 23 + ...-pinephone-pro-Add-support-for-Pinep.patch | 1074 + ...-pinephone-pro-Fixup-DT-validation-i.patch | 101 + ...-pinephone-pro-Make-charging-and-per.patch | 34 + ...-pinephone-pro-Fix-goodix-toucscreen.patch | 30 + ...-pinephone-pro-Correct-the-pmu1830-i.patch | 26 + ...-pinephone-pro-Power-off-goodix-touc.patch | 25 + ...-pinephone-pro-Add-support-for-both-.patch | 198 + ...-pinephone-pro-Fix-SD-card-power-sup.patch | 71 + ...-pinephone-pro-Correct-the-battery-s.patch | 44 + ...-pinephone-pro-Cleanup-some-USB-node.patch | 32 + ...-pinephone-pro-Fix-PDOs-to-be-more-r.patch | 33 + ...-pinephone-pro-Add-chassis-type-hand.patch | 24 + ...-pinephone-pro-Add-mmc-aliases-to-ge.patch | 29 + ...-pinephone-pro-Use-a-new-rk818-batte.patch | 23 + ...-pinephone-pro-Full-support-for-Type.patch | 294 + ...-pinephone-pro-Use-DCLK_VOP-_FRAC-to.patch | 37 + ...-pinephone-pro-Add-support-for-power.patch | 78 + ...3399-pinephone-pro-Add-audio-support.patch | 139 + ...-pinephone-pro-Add-flash-and-fix-led.patch | 105 + ...k3399-pinephone-pro-add-modem-RI-pin.patch | 48 + ...9-pinephone-pro-improve-sound-device.patch | 90 + ...l-org-bug-210681-firmware_rome_error.patch | 12 - ...pio-make-max_brightness-configurable.patch | 49 - ...8i-codec-fix-headphone-jack-pin-name.patch | 25 - ...r-pinephone-improve-device-tree-5.12.patch | 130 - ...winner-pinephone-improve-device-tree.patch | 116 - .../pinephone-sources/files/5.11.3.patch | 31655 - .../pinephone-sources/files/5.11.5.patch | 1526 - .../pinephone-sources/files/5.15.1.patch | 512 - .../pinephone-sources/files/90-linux.hook | 11 - .../Multigenerational-LRU-Framework.patch | 6357 - ...-4-HID-add-Apple-Magic-Mouse-2-support.txt | 118 - ...ouse-add-Apple-Magic-Mouse-2-support.patch | 247 - ...se-fix-3-button-emulation-of-Mouse-2.patch | 134 - ...se-fix-reconnection-of-Magic-Mouse-2.patch | 265 - ...-input-map-battery-capacity-00850065.patch | 155 - .../PATCH-v1-00-14-Multigenerational-LRU.txt | 330 - ...rn-in-page_memcg_rcu-if-CONFIG_MEMCG.patch | 129 - ...fine-next_memory_node-if-CONFIG_NUMA.patch | 113 - ...o_pmd-if-CONFIG_TRANSPARENT_HUGEPAGE.patch | 118 - ...e-linux-cgroup.h-export-cgroup_mutex.patch | 140 - ...05-14-mm-swap.c-export-activate_page.patch | 178 - ...e-access-bit-on-non-leaf-PMD-entries.patch | 202 - ...entry_post-for-post-order-traversals.patch | 144 - ...-14-mm-vmscan.c-refactor-shrink_node.patch | 311 - ...multigenerational-lru-mm_struct-list.patch | 749 - ...-10-14-mm-multigenerational-lru-core.patch | 1930 - ...ultigenerational-lru-page-activation.patch | 612 - ...enerational-lru-user-space-interface.patch | 492 - ...-14-mm-multigenerational-lru-Kconfig.patch | 137 - ...-multigenerational-lru-documentation.patch | 329 - ...rn-in-page_memcg_rcu-if-CONFIG_MEMCG.patch | 146 - ...fine-next_memory_node-if-CONFIG_NUMA.patch | 124 - ...o_pmd-if-CONFIG_TRANSPARENT_HUGEPAGE.patch | 130 - ...e-linux-cgroup.h-export-cgroup_mutex.patch | 151 - ...05-16-mm-swap.c-export-activate_page.patch | 190 - ...e-access-bit-on-non-leaf-PMD-entries.patch | 214 - ...-16-mm-vmscan.c-refactor-shrink_node.patch | 324 - ...-mm-multigenerational-lru-groundwork.patch | 1030 - ...-mm-multigenerational-lru-activation.patch | 940 - ...multigenerational-lru-mm_struct-list.patch | 814 - ...11-16-mm-multigenerational-lru-aging.patch | 853 - ...16-mm-multigenerational-lru-eviction.patch | 474 - ...m-multigenerational-lru-page-reclaim.patch | 479 - ...multigenerational-lru-user-interface.patch | 575 - ...-16-mm-multigenerational-lru-Kconfig.patch | 175 - ...-multigenerational-lru-documentation.patch | 322 - ...0-14-Multigenerational-LRU-Framework.patch | 391 - ...rn-in-page_memcg_rcu-if-CONFIG_MEMCG.patch | 146 - ...fine-next_memory_node-if-CONFIG_NUMA.patch | 124 - ...e-linux-cgroup.h-export-cgroup_mutex.patch | 150 - ...e-access-bit-on-non-leaf-PMD-entries.patch | 214 - ...-14-mm-vmscan.c-refactor-shrink_node.patch | 323 - ...factor-pack_shadow-and-unpack_shadow.patch | 233 - ...-mm-multigenerational-lru-groundwork.patch | 1025 - ...-mm-multigenerational-lru-activation.patch | 686 - ...multigenerational-lru-mm_struct-list.patch | 789 - ...10-14-mm-multigenerational-lru-aging.patch | 1004 - ...14-mm-multigenerational-lru-eviction.patch | 768 - ...multigenerational-lru-user-interface.patch | 572 - ...-14-mm-multigenerational-lru-Kconfig.patch | 177 - ...-multigenerational-lru-documentation.patch | 273 - .../pinephone-sources/files/all-5.11.4.patch | 475856 --------------- .../files/camera-added-bggr-bayer-mode.patch | 30 - .../files/camera-autofocus.patch | 390 - ...ent-clocks-to-lower-speed-oscillator.patch | 49 - .../pinephone-sources/files/config-5.10.14 | 5849 - .../pinephone-sources/files/config-5.10.15 | 5737 - .../pinephone-sources/files/config-5.11.0 | 5786 - .../pinephone-sources/files/config-5.11.6 | 5787 - .../pinephone-sources/files/config-5.12 | 6528 - .../pinephone-sources/files/config-5.12.17 | 6528 - .../pinephone-sources/files/config-5.13 | 5861 - .../pinephone-sources/files/config-5.13.13 | 5836 - .../pinephone-sources/files/config-5.13.2 | 6578 - .../pinephone-sources/files/config-5.13.4 | 5818 - .../pinephone-sources/files/config-5.13.5 | 5818 - .../pinephone-sources/files/config-5.13.5-2 | 6531 - .../pinephone-sources/files/config-5.13.7 | 5839 - .../pinephone-sources/files/config-5.13.8 | 5839 - .../pinephone-sources/files/config-5.14.17 | 5878 - .../pinephone-sources/files/config-5.14.2 | 5876 - .../pinephone-sources/files/config-5.14.9 | 5877 - .../files/{config => config-5.16} | 897 +- .../pinephone-sources/files/config-5.9.11 | 6359 - .../config-postmarketos-allwinner.aarch64 | 5962 - .../drm-lima-add-LIMA_BO_FLAG_FORCE_VA.patch | 280 - .../files/dts-headphone-jack-detection.patch | 18 - .../dts-pinephone-drop-modem-power-node.patch | 175 - .../files/enable-hdmi-output-pinetab.patch | 57 - .../files/enable-jack-detection-pinetab.patch | 38 - .../files/improve-brightness.patch | 67 - ...improve-jack-button-handling-and-mic.patch | 56 - .../files/improve-pinephone-brightness.patch | 67 - .../files/linux-pinephone.install | 9 - .../pinephone-sources/files/linux.preset | 11 - ...n-firmware-for-autofocus-isnt-loaded.patch | 11 - .../megi-fix-mipi-dsi-panel-framerate.patch | 36 - .../files/megi-fix-xbd599-timings.patch | 37 - ...-recover-from-occasional-HW-failures.patch | 66 - .../megi-switch-parent-of-MIPI-DSI.patch | 44 - .../files/panic-led-5.12.patch | 12 - .../pinephone-sources/files/panic-led.patch | 12 - .../files/pinetab-accelerometer.patch | 21 - .../files/pinetab-bluetooth.patch | 44 - .../files/remove-v4l2-flash-pp.patch | 26 - .../revert-debug-symbols-rk3399dtsi.patch | 57 - ...he-hang-in-musb_pm_runtime_check_ses.patch | 35 - .../files/wifi-power-saving.patch | 34 - .../pinephone-sources-5.12.17.ebuild | 74 - 194 files changed, 12171 insertions(+), 654487 deletions(-) delete mode 100644 sys-kernel/pinephone-sources/files/0001-arm64-dts-allwinner-pinephone-stop-LEDs-on-suspend.patch create mode 100644 sys-kernel/pinephone-sources/files/0001-base-property-Swap-order-of-search-for-connection-to.patch delete mode 100644 sys-kernel/pinephone-sources/files/0001-drivers-usb-add-reset_resume-callback.patch create mode 100644 sys-kernel/pinephone-sources/files/0001-revert-garbage-collect-fbdev-scrolling-acceleration.patch delete mode 100644 sys-kernel/pinephone-sources/files/0002-Bluetooth-Fix-LL-PRivacy-BLE-device-fails-to-connect.patch delete mode 100644 sys-kernel/pinephone-sources/files/0002-Revert-usb-quirks-Add-USB_QUIRK_RESET-for-Quectel-EG.patch delete mode 100644 sys-kernel/pinephone-sources/files/0002-dts-add-pinetab-dev-old-display-panel.patch rename sys-kernel/pinephone-sources/files/{0001-revert-fbcon-remove-now-unusued-softback_lines-cursor-argument.patch => 0002-revert-fbcon-remove-now-unusued-softback_lines-cursor-argument.patch} (100%) delete mode 100644 sys-kernel/pinephone-sources/files/0002-revert-fbcon-remove-soft-scrollback-code.patch create mode 100644 sys-kernel/pinephone-sources/files/0002-sdhci-arasan-Add-runtime-PM-support.patch delete mode 100644 sys-kernel/pinephone-sources/files/0003-Bluetooth-Fix-attempting-to-set-RPA-timeout-when-unsupported.patch delete mode 100644 sys-kernel/pinephone-sources/files/0003-Bluetooth-btusb.patch create mode 100644 sys-kernel/pinephone-sources/files/0003-clk-rk3399-Export-SCLK_CIF_OUT_SRC-to-device-tree.patch delete mode 100644 sys-kernel/pinephone-sources/files/0003-qmi_wwan-provide-wrapper-for-reset_resume.patch rename sys-kernel/pinephone-sources/files/{0002-revert-fbcon-remove-no-op-fbcon_set_origin.patch => 0003-revert-fbcon-remove-no-op-fbcon_set_origin.patch} (100%) delete mode 100644 sys-kernel/pinephone-sources/files/0004-cdc-wdm-provide-wrapper-for-reset_resume.patch delete mode 100644 sys-kernel/pinephone-sources/files/0004-efivarfs-revert-fix-memory-leak-in-efivarfs_create.patch create mode 100644 sys-kernel/pinephone-sources/files/0004-media-rockchip-rga-Fix-probe-bugs.patch rename sys-kernel/pinephone-sources/files/{0003-revert-fbcon-remove-soft-scrollback-code.patch => 0004-revert-fbcon-remove-soft-scrollback-code.patch} (100%) create mode 100644 sys-kernel/pinephone-sources/files/0005-drm-dw-mipi-dsi-rockchip-Ensure-that-lane-is-properl.patch delete mode 100644 sys-kernel/pinephone-sources/files/0005-net-usb-qmi_wwan-set-the-DTR-when-resuming.patch delete mode 100644 sys-kernel/pinephone-sources/files/0006-cdc-wdm-send-HUP-if-we-are-resetting.patch create mode 100644 sys-kernel/pinephone-sources/files/0006-drm-rockchip-dw-mipi-dsi-Fix-missing-clk_disable_unp.patch create mode 100644 sys-kernel/pinephone-sources/files/0007-drm-bridge-dw-mipi-dsi-Fix-enable-disable-of-dsi-con.patch create mode 100644 sys-kernel/pinephone-sources/files/0008-drm-dw-mipi-dsi-rockchip-Never-allow-lane-bandwidth-.patch create mode 100644 sys-kernel/pinephone-sources/files/0009-drm-rockchip-cdn-dp-Disable-CDN-DP-on-disconnect.patch delete mode 100644 sys-kernel/pinephone-sources/files/0010-dts-pinephone-Add-pine64-pinephone-to-compat-list.patch create mode 100644 sys-kernel/pinephone-sources/files/0010-video-fbdev-Add-events-for-early-fb-event-support.patch delete mode 100644 sys-kernel/pinephone-sources/files/0011-dts-pinetab-hardcode-mmc-numbers.patch create mode 100644 sys-kernel/pinephone-sources/files/0011-power-rk818-Configure-rk808-clkout2-function.patch delete mode 100644 sys-kernel/pinephone-sources/files/0012-pinephone-fix-pogopin-i2c.patch create mode 100644 sys-kernel/pinephone-sources/files/0012-power-rk818-battery-Add-battery-driver-for-RK818.patch create mode 100644 sys-kernel/pinephone-sources/files/0013-power-supply-rk818-battery-Use-a-more-propper-compat.patch create mode 100644 sys-kernel/pinephone-sources/files/0014-power-supply-core-Don-t-ignore-max_current-of-0-when.patch create mode 100644 sys-kernel/pinephone-sources/files/0015-power-supply-rk818-charger-Implement-charger-driver-.patch create mode 100644 sys-kernel/pinephone-sources/files/0016-usb-typec-fusb302-Set-the-current-before-enabling-pu.patch create mode 100644 sys-kernel/pinephone-sources/files/0017-usb-typec-fusb302-Extend-debugging-interface-with-dr.patch create mode 100644 sys-kernel/pinephone-sources/files/0018-usb-typec-fusb302-Retry-reading-of-CC-pins-status-if.patch create mode 100644 sys-kernel/pinephone-sources/files/0019-usb-typec-fusb302-More-useful-of-logging-status-on-i.patch create mode 100644 sys-kernel/pinephone-sources/files/0020-usb-typec-fusb302-Update-VBUS-state-even-if-VBUS-int.patch create mode 100644 sys-kernel/pinephone-sources/files/0021-usb-typec-fusb302-Make-tcpm-fusb302-logs-less-pollut.patch create mode 100644 sys-kernel/pinephone-sources/files/0022-usb-typec-fusb302-Add-OF-extcon-support.patch create mode 100644 sys-kernel/pinephone-sources/files/0023-usb-typec-fusb302-Fix-register-definitions.patch create mode 100644 sys-kernel/pinephone-sources/files/0024-usb-typec-fusb302-Clear-interrupts-before-we-start-t.patch create mode 100644 sys-kernel/pinephone-sources/files/0025-usb-typec-typec-extcon-Add-typec-extcon-bridge-drive.patch create mode 100644 sys-kernel/pinephone-sources/files/0026-phy-rockchip-typec-Make-sure-the-plug-orientation-is.patch create mode 100644 sys-kernel/pinephone-sources/files/0027-media-i2c-imx258-Add-support-for-powerdown-gpio.patch create mode 100644 sys-kernel/pinephone-sources/files/0028-media-i2c-imx258-Don-t-be-too-strict-about-clock-rat.patch create mode 100644 sys-kernel/pinephone-sources/files/0029-media-i2c-imx258-Add-support-for-reset-gpio.patch create mode 100644 sys-kernel/pinephone-sources/files/0030-media-i2c-imx258-Add-support-for-power-supplies.patch create mode 100644 sys-kernel/pinephone-sources/files/0031-media-ov5640-Add-more-framerates-to-the-driver-some-.patch create mode 100644 sys-kernel/pinephone-sources/files/0032-media-ov5640-Experiment-Try-to-disable-denoising-sha.patch create mode 100644 sys-kernel/pinephone-sources/files/0033-media-ov5640-Sleep-after-poweroff-to-ensure-next-pow.patch create mode 100644 sys-kernel/pinephone-sources/files/0034-media-ov5640-Don-t-powerup-the-sensor-during-driver-.patch rename sys-kernel/pinephone-sources/files/{media-ov5640-Implement-autofocus.patch => 0035-media-ov5640-Implement-autofocus.patch} (91%) create mode 100644 sys-kernel/pinephone-sources/files/0036-media-ov5640-set-default-ae-target-lower.patch create mode 100644 sys-kernel/pinephone-sources/files/0037-drm-panel-hx8394-Add-driver-for-HX8394-based-HannSta.patch create mode 100644 sys-kernel/pinephone-sources/files/0038-drm-panel-hx8394-Improve-the-panel-driver-make-it-wo.patch create mode 100644 sys-kernel/pinephone-sources/files/0039-drm-panel-hx8394-Fix-mode-clock-for-the-pinephone-pr.patch create mode 100644 sys-kernel/pinephone-sources/files/0040-input-goodix-Add-option-to-power-off-the-controller-.patch create mode 100644 sys-kernel/pinephone-sources/files/0041-input-goodix-Don-t-disable-regulators-during-suspend.patch create mode 100644 sys-kernel/pinephone-sources/files/0042-input-touchscreen-goodix-Respect-IRQ-flags-from-DT-w.patch create mode 100644 sys-kernel/pinephone-sources/files/0043-input-touchscreen-goodix-Add-support-for-GT1158.patch create mode 100644 sys-kernel/pinephone-sources/files/0044-arm64-dts-rk3399-pinephone-pro-Add-support-for-Pinep.patch create mode 100644 sys-kernel/pinephone-sources/files/0045-arm64-dts-rk3399-pinephone-pro-Fixup-DT-validation-i.patch create mode 100644 sys-kernel/pinephone-sources/files/0046-arm64-dts-rk3399-pinephone-pro-Make-charging-and-per.patch create mode 100644 sys-kernel/pinephone-sources/files/0047-arm64-dts-rk3399-pinephone-pro-Fix-goodix-toucscreen.patch create mode 100644 sys-kernel/pinephone-sources/files/0048-arm64-dts-rk3399-pinephone-pro-Correct-the-pmu1830-i.patch create mode 100644 sys-kernel/pinephone-sources/files/0049-arm64-dts-rk3399-pinephone-pro-Power-off-goodix-touc.patch create mode 100644 sys-kernel/pinephone-sources/files/0050-arm64-dts-rk3399-pinephone-pro-Add-support-for-both-.patch create mode 100644 sys-kernel/pinephone-sources/files/0051-arm64-dts-rk3399-pinephone-pro-Fix-SD-card-power-sup.patch create mode 100644 sys-kernel/pinephone-sources/files/0052-arm64-dts-rk3399-pinephone-pro-Correct-the-battery-s.patch create mode 100644 sys-kernel/pinephone-sources/files/0053-arm64-dts-rk3399-pinephone-pro-Cleanup-some-USB-node.patch create mode 100644 sys-kernel/pinephone-sources/files/0054-arm64-dts-rk3399-pinephone-pro-Fix-PDOs-to-be-more-r.patch create mode 100644 sys-kernel/pinephone-sources/files/0055-arm64-dts-rk3399-pinephone-pro-Add-chassis-type-hand.patch create mode 100644 sys-kernel/pinephone-sources/files/0056-arm64-dts-rk3399-pinephone-pro-Add-mmc-aliases-to-ge.patch create mode 100644 sys-kernel/pinephone-sources/files/0057-arm64-dts-rk3399-pinephone-pro-Use-a-new-rk818-batte.patch create mode 100644 sys-kernel/pinephone-sources/files/0058-arm64-dts-rk3399-pinephone-pro-Full-support-for-Type.patch create mode 100644 sys-kernel/pinephone-sources/files/0059-arm64-dts-rk3399-pinephone-pro-Use-DCLK_VOP-_FRAC-to.patch create mode 100644 sys-kernel/pinephone-sources/files/0060-arm64-dts-rk3399-pinephone-pro-Add-support-for-power.patch create mode 100644 sys-kernel/pinephone-sources/files/0061-arm64-dts-rk3399-pinephone-pro-Add-audio-support.patch create mode 100644 sys-kernel/pinephone-sources/files/0062-arm64-dts-rk3399-pinephone-pro-Add-flash-and-fix-led.patch create mode 100644 sys-kernel/pinephone-sources/files/0063-arm64-dts-rk3399-pinephone-pro-add-modem-RI-pin.patch create mode 100644 sys-kernel/pinephone-sources/files/0064-arm64-dts-rk3399-pinephone-pro-improve-sound-device.patch delete mode 100644 sys-kernel/pinephone-sources/files/0107-quirk-kernel-org-bug-210681-firmware_rome_error.patch delete mode 100644 sys-kernel/pinephone-sources/files/0177-leds-gpio-make-max_brightness-configurable.patch delete mode 100644 sys-kernel/pinephone-sources/files/0178-sun8i-codec-fix-headphone-jack-pin-name.patch delete mode 100644 sys-kernel/pinephone-sources/files/0179-arm64-dts-allwinner-pinephone-improve-device-tree-5.12.patch delete mode 100644 sys-kernel/pinephone-sources/files/0179-arm64-dts-allwinner-pinephone-improve-device-tree.patch delete mode 100644 sys-kernel/pinephone-sources/files/5.11.3.patch delete mode 100644 sys-kernel/pinephone-sources/files/5.11.5.patch delete mode 100644 sys-kernel/pinephone-sources/files/5.15.1.patch delete mode 100644 sys-kernel/pinephone-sources/files/90-linux.hook delete mode 100644 sys-kernel/pinephone-sources/files/Multigenerational-LRU-Framework.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-0-4-HID-add-Apple-Magic-Mouse-2-support.txt delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-1-4-HID-magicmouse-add-Apple-Magic-Mouse-2-support.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-2-4-HID-magicmouse-fix-3-button-emulation-of-Mouse-2.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-3-4-HID-magicmouse-fix-reconnection-of-Magic-Mouse-2.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-4-4-HID-input-map-battery-capacity-00850065.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v1-00-14-Multigenerational-LRU.txt delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v1-01-14-include-linux-memcontrol.h-do-not-warn-in-page_memcg_rcu-if-CONFIG_MEMCG.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v1-02-14-include-linux-nodemask.h-define-next_memory_node-if-CONFIG_NUMA.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v1-03-14-include-linux-huge_mm.h-define-is_huge_zero_pmd-if-CONFIG_TRANSPARENT_HUGEPAGE.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v1-04-14-include-linux-cgroup.h-export-cgroup_mutex.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v1-05-14-mm-swap.c-export-activate_page.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v1-06-14-mm-x86-support-the-access-bit-on-non-leaf-PMD-entries.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v1-07-14-mm-pagewalk.c-add-pud_entry_post-for-post-order-traversals.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v1-08-14-mm-vmscan.c-refactor-shrink_node.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v1-09-14-mm-multigenerational-lru-mm_struct-list.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v1-10-14-mm-multigenerational-lru-core.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v1-11-14-mm-multigenerational-lru-page-activation.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v1-12-14-mm-multigenerational-lru-user-space-interface.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v1-13-14-mm-multigenerational-lru-Kconfig.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v1-14-14-mm-multigenerational-lru-documentation.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v2-01-16-include-linux-memcontrol.h-do-not-warn-in-page_memcg_rcu-if-CONFIG_MEMCG.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v2-02-16-include-linux-nodemask.h-define-next_memory_node-if-CONFIG_NUMA.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v2-03-16-include-linux-huge_mm.h-define-is_huge_zero_pmd-if-CONFIG_TRANSPARENT_HUGEPAGE.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v2-04-16-include-linux-cgroup.h-export-cgroup_mutex.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v2-05-16-mm-swap.c-export-activate_page.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v2-06-16-mm-x86-support-the-access-bit-on-non-leaf-PMD-entries.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v2-07-16-mm-vmscan.c-refactor-shrink_node.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v2-08-16-mm-multigenerational-lru-groundwork.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v2-09-16-mm-multigenerational-lru-activation.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v2-10-16-mm-multigenerational-lru-mm_struct-list.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v2-11-16-mm-multigenerational-lru-aging.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v2-12-16-mm-multigenerational-lru-eviction.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v2-13-16-mm-multigenerational-lru-page-reclaim.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v2-14-16-mm-multigenerational-lru-user-interface.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v2-15-16-mm-multigenerational-lru-Kconfig.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v2-16-16-mm-multigenerational-lru-documentation.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v3-00-14-Multigenerational-LRU-Framework.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v3-01-14-include-linux-memcontrol.h-do-not-warn-in-page_memcg_rcu-if-CONFIG_MEMCG.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v3-02-14-include-linux-nodemask.h-define-next_memory_node-if-CONFIG_NUMA.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v3-03-14-include-linux-cgroup.h-export-cgroup_mutex.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v3-04-14-mm-x86-support-the-access-bit-on-non-leaf-PMD-entries.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v3-05-14-mm-vmscan.c-refactor-shrink_node.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v3-06-14-mm-workingset.c-refactor-pack_shadow-and-unpack_shadow.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v3-07-14-mm-multigenerational-lru-groundwork.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v3-08-14-mm-multigenerational-lru-activation.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v3-09-14-mm-multigenerational-lru-mm_struct-list.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v3-10-14-mm-multigenerational-lru-aging.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v3-11-14-mm-multigenerational-lru-eviction.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v3-12-14-mm-multigenerational-lru-user-interface.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v3-13-14-mm-multigenerational-lru-Kconfig.patch delete mode 100644 sys-kernel/pinephone-sources/files/PATCH-v3-14-14-mm-multigenerational-lru-documentation.patch delete mode 100644 sys-kernel/pinephone-sources/files/all-5.11.4.patch delete mode 100644 sys-kernel/pinephone-sources/files/camera-added-bggr-bayer-mode.patch delete mode 100644 sys-kernel/pinephone-sources/files/camera-autofocus.patch delete mode 100644 sys-kernel/pinephone-sources/files/ccu-sun50i-a64-reparent-clocks-to-lower-speed-oscillator.patch delete mode 100644 sys-kernel/pinephone-sources/files/config-5.10.14 delete mode 100644 sys-kernel/pinephone-sources/files/config-5.10.15 delete mode 100644 sys-kernel/pinephone-sources/files/config-5.11.0 delete mode 100644 sys-kernel/pinephone-sources/files/config-5.11.6 delete mode 100644 sys-kernel/pinephone-sources/files/config-5.12 delete mode 100644 sys-kernel/pinephone-sources/files/config-5.12.17 delete mode 100644 sys-kernel/pinephone-sources/files/config-5.13 delete mode 100644 sys-kernel/pinephone-sources/files/config-5.13.13 delete mode 100644 sys-kernel/pinephone-sources/files/config-5.13.2 delete mode 100644 sys-kernel/pinephone-sources/files/config-5.13.4 delete mode 100644 sys-kernel/pinephone-sources/files/config-5.13.5 delete mode 100644 sys-kernel/pinephone-sources/files/config-5.13.5-2 delete mode 100644 sys-kernel/pinephone-sources/files/config-5.13.7 delete mode 100644 sys-kernel/pinephone-sources/files/config-5.13.8 delete mode 100644 sys-kernel/pinephone-sources/files/config-5.14.17 delete mode 100644 sys-kernel/pinephone-sources/files/config-5.14.2 delete mode 100644 sys-kernel/pinephone-sources/files/config-5.14.9 rename sys-kernel/pinephone-sources/files/{config => config-5.16} (92%) delete mode 100644 sys-kernel/pinephone-sources/files/config-5.9.11 delete mode 100644 sys-kernel/pinephone-sources/files/config-postmarketos-allwinner.aarch64 delete mode 100644 sys-kernel/pinephone-sources/files/drm-lima-add-LIMA_BO_FLAG_FORCE_VA.patch delete mode 100644 sys-kernel/pinephone-sources/files/dts-headphone-jack-detection.patch delete mode 100644 sys-kernel/pinephone-sources/files/dts-pinephone-drop-modem-power-node.patch delete mode 100644 sys-kernel/pinephone-sources/files/enable-hdmi-output-pinetab.patch delete mode 100644 sys-kernel/pinephone-sources/files/enable-jack-detection-pinetab.patch delete mode 100644 sys-kernel/pinephone-sources/files/improve-brightness.patch delete mode 100644 sys-kernel/pinephone-sources/files/improve-jack-button-handling-and-mic.patch delete mode 100644 sys-kernel/pinephone-sources/files/improve-pinephone-brightness.patch delete mode 100644 sys-kernel/pinephone-sources/files/linux-pinephone.install delete mode 100644 sys-kernel/pinephone-sources/files/linux.preset delete mode 100644 sys-kernel/pinephone-sources/files/media-ov5640-dont-break-when-firmware-for-autofocus-isnt-loaded.patch delete mode 100644 sys-kernel/pinephone-sources/files/megi-fix-mipi-dsi-panel-framerate.patch delete mode 100644 sys-kernel/pinephone-sources/files/megi-fix-xbd599-timings.patch delete mode 100644 sys-kernel/pinephone-sources/files/megi-recover-from-occasional-HW-failures.patch delete mode 100644 sys-kernel/pinephone-sources/files/megi-switch-parent-of-MIPI-DSI.patch delete mode 100644 sys-kernel/pinephone-sources/files/panic-led-5.12.patch delete mode 100644 sys-kernel/pinephone-sources/files/panic-led.patch delete mode 100644 sys-kernel/pinephone-sources/files/pinetab-accelerometer.patch delete mode 100644 sys-kernel/pinephone-sources/files/pinetab-bluetooth.patch delete mode 100644 sys-kernel/pinephone-sources/files/remove-v4l2-flash-pp.patch delete mode 100644 sys-kernel/pinephone-sources/files/revert-debug-symbols-rk3399dtsi.patch delete mode 100644 sys-kernel/pinephone-sources/files/usb-musb-avoid-the-hang-in-musb_pm_runtime_check_ses.patch delete mode 100644 sys-kernel/pinephone-sources/files/wifi-power-saving.patch delete mode 100644 sys-kernel/pinephone-sources/pinephone-sources-5.12.17.ebuild diff --git a/sys-kernel/pinephone-sources/Manifest b/sys-kernel/pinephone-sources/Manifest index d03d7aa..7adbd36 100644 --- a/sys-kernel/pinephone-sources/Manifest +++ b/sys-kernel/pinephone-sources/Manifest @@ -1,6 +1,3 @@ -DIST all-5.12.17.patch 15073524 BLAKE2B bd085b619a3bc1190cbfdc6bfd74cd6606f67f55106f89e0522619aaf69b672d68a7e93d843d7a4680529532e654d71becf4a7378d453fcc72978ee24d415b57 SHA512 e4bae86407767b685b3ec47f383d2db44281fe6a58914e4a117449c2ff96fe3c6341a7b59d1a92790c1565ecc3c14dacbbdc96fbc1b718ec7402c27c2dc37d0d DIST all-5.15.6.patch 15465054 BLAKE2B e60ee46d00f0d2a82c491431366e96650b08050e64bc5a751961a7f7d465735250bdc2b26832fc1f0a6a21da1d7c5e87d119359a02c9e175201af5750f8e50f4 SHA512 6499b9f8b5575ca3ab21a28ec5a9c9bd0bcecfec54b90cf7dac1f2c9e818f0a5804a7389f37e6220661210f7ecc80da3cfd3b2c218d29c0d10bb9066c68aa802 -DIST linux-5.12.tar.xz 118112412 BLAKE2B 842d921b9a73d2aaade763dbd2ec67bdfe0275baa6d628b775f5c87574ad7dc86f0419afcd48c10c1235f4bffa16084243f2cf4556e6afcd391e975fe8ba530b SHA512 be03b6fee1d1ea8087b09874d27c0a602c0b04fd90ad38b975bd2c8455a07e83c29b56814aaf1389e82305fae0e4c2d1701075a7f0a7295dd28149f967ec5b3d DIST linux-5.15.tar.xz 121913744 BLAKE2B 3921274b23f7938abdf3ed9334534b4581e13d7484303d3a5280eddb038999aaa8b836666a487472d9c4a219af0f06b9fecccaf348fb5510ab8762f4ef4b7e83 SHA512 d25ad40b5bcd6a4c6042fd0fd84e196e7a58024734c3e9a484fd0d5d54a0c1d87db8a3c784eff55e43b6f021709dc685eb0efa18d2aec327e4f88a79f405705a -DIST patch-5.12.17.xz 889940 BLAKE2B 78c10887c9c28261f5a99c3ed19c9390d8af92ee49dd4dd13092493e71c446fa58b9025eecb3ea8f605f1c3644c5ef85a25c056a4fbe143eb91f49eb9b2b866b SHA512 1c42b4efa3d26fb4bdc96c422a32646b2d9018cb84d231ccae4c64e1cdd342ed43828c6da3eb40cbe80550734254b8e824cec98cb1635d47baef75bae37bfd31 DIST patch-5.15.6.xz 430232 BLAKE2B ed3eb640ae8617ae726b9d98f3b17ab1602f733e228970826e4a34cb7e466b61ead5371f51e6bc1fc75bb9e468cfeca66c6bca21731f25b53decc52eb59ecb3e SHA512 926403406c82f4589bb872cb74ff21dc4729184a3dd5293ba0cc201c13a5c3917b231721be551a6f19fb1d93e443b8ebac557df2a9cc265967a693b4031cedb3 diff --git a/sys-kernel/pinephone-sources/files/0001-arm64-dts-allwinner-pinephone-stop-LEDs-on-suspend.patch b/sys-kernel/pinephone-sources/files/0001-arm64-dts-allwinner-pinephone-stop-LEDs-on-suspend.patch deleted file mode 100644 index 331ca3e..0000000 --- a/sys-kernel/pinephone-sources/files/0001-arm64-dts-allwinner-pinephone-stop-LEDs-on-suspend.patch +++ /dev/null @@ -1,40 +0,0 @@ -From 7045054c96224ead00aae09246f475dfe6202def Mon Sep 17 00:00:00 2001 -From: Danct12 -Date: Tue, 19 Jan 2021 10:09:01 +0700 -Subject: [PATCH] arm64: dts: allwinner: pinephone: stop LEDs on suspend - -Signed-off-by: Danct12 ---- - arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi | 3 --- - 1 file changed, 3 deletions(-) - -diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi -index 02d82980c..00ed866ae 100644 ---- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi -+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi -@@ -218,14 +218,12 @@ - function = LED_FUNCTION_INDICATOR; - color = ; - gpios = <&pio 3 20 GPIO_ACTIVE_HIGH>; /* PD20 */ -- retain-state-suspended; - }; - - green { - function = LED_FUNCTION_INDICATOR; - color = ; - gpios = <&pio 3 18 GPIO_ACTIVE_HIGH>; /* PD18 */ -- retain-state-suspended; - }; - - red { -@@ -233,7 +231,6 @@ - function = LED_FUNCTION_INDICATOR; - color = ; - gpios = <&pio 3 19 GPIO_ACTIVE_HIGH>; /* PD19 */ -- retain-state-suspended; - }; - }; - --- -2.30.0 - diff --git a/sys-kernel/pinephone-sources/files/0001-base-property-Swap-order-of-search-for-connection-to.patch b/sys-kernel/pinephone-sources/files/0001-base-property-Swap-order-of-search-for-connection-to.patch new file mode 100644 index 0000000..8a3b62c --- /dev/null +++ b/sys-kernel/pinephone-sources/files/0001-base-property-Swap-order-of-search-for-connection-to.patch @@ -0,0 +1,35 @@ +From: Ondrej Jirman +Date: Mon, 15 Nov 2021 04:09:50 +0100 +Subject: [PATCH 01/36] base: property: Swap order of search for connection to + "devcon, graph" + +This avoids confusing error in kernel log, when using devcon matching +in DT. Example: + +"OF: graph: no port node found in /i2c@ff3d0000/typec-portc@22" + +This suggest there's some error because graph based search failed, +but there is no error, because devcon based matching succeeds. + +Signed-off-by: Ondrej Jirman +--- + drivers/base/property.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/base/property.c b/drivers/base/property.c +index f1f35b4..68cb945 100644 +--- a/drivers/base/property.c ++++ b/drivers/base/property.c +@@ -1261,10 +1261,10 @@ void *fwnode_connection_find_match(struct fwnode_handle *fwnode, + if (!fwnode || !match) + return NULL; + +- ret = fwnode_graph_devcon_match(fwnode, con_id, data, match); ++ ret = fwnode_devcon_match(fwnode, con_id, data, match); + if (ret) + return ret; + +- return fwnode_devcon_match(fwnode, con_id, data, match); ++ return fwnode_graph_devcon_match(fwnode, con_id, data, match); + } + EXPORT_SYMBOL_GPL(fwnode_connection_find_match); diff --git a/sys-kernel/pinephone-sources/files/0001-drivers-usb-add-reset_resume-callback.patch b/sys-kernel/pinephone-sources/files/0001-drivers-usb-add-reset_resume-callback.patch deleted file mode 100644 index c232f58..0000000 --- a/sys-kernel/pinephone-sources/files/0001-drivers-usb-add-reset_resume-callback.patch +++ /dev/null @@ -1,24 +0,0 @@ -From 22008251d617054271a65f29178e2df74dd3e33c Mon Sep 17 00:00:00 2001 -From: Bhushan Shah -Date: Fri, 9 Apr 2021 16:22:49 +0530 -Subject: [PATCH 1/5] drivers/usb: add reset_resume callback - ---- - 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 c6969ca728390..21aeb7dc8f6ee 100644 ---- a/drivers/usb/serial/option.c -+++ b/drivers/usb/serial/option.c -@@ -2105,6 +2105,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.31.1 - diff --git a/sys-kernel/pinephone-sources/files/0001-revert-garbage-collect-fbdev-scrolling-acceleration.patch b/sys-kernel/pinephone-sources/files/0001-revert-garbage-collect-fbdev-scrolling-acceleration.patch new file mode 100644 index 0000000..280ed96 --- /dev/null +++ b/sys-kernel/pinephone-sources/files/0001-revert-garbage-collect-fbdev-scrolling-acceleration.patch @@ -0,0 +1,1038 @@ +--- b/Documentation/gpu/todo.rst ++++ a/Documentation/gpu/todo.rst +@@ -314,19 +314,16 @@ + Garbage collect fbdev scrolling acceleration + -------------------------------------------- + ++Scroll acceleration is disabled in fbcon by hard-wiring p->scrollmode = ++SCROLL_REDRAW. There's a ton of code this will allow us to remove: +-Scroll acceleration has been disabled in fbcon. Now it works as the old +-SCROLL_REDRAW mode. A ton of code was removed in fbcon.c and the hook bmove was +-removed from fbcon_ops. +-Remaining tasks: + ++- lots of code in fbcon.c ++ ++- a bunch of the hooks in fbcon_ops, maybe the remaining hooks could be called +-- a bunch of the hooks in fbcon_ops could be removed or simplified by calling + directly instead of the function table (with a switch on p->rotate) + + - fb_copyarea is unused after this, and can be deleted from all drivers + +-- after that, fb_copyarea can be deleted from fb_ops in include/linux/fb.h as +- well as cfb_copyarea +- + Note that not all acceleration code can be deleted, since clearing and cursor + support is still accelerated, which might be good candidates for further + deletion projects. +--- b/drivers/video/fbdev/core/bitblit.c ++++ a/drivers/video/fbdev/core/bitblit.c +@@ -43,6 +43,21 @@ + } + } + ++static void bit_bmove(struct vc_data *vc, struct fb_info *info, int sy, ++ int sx, int dy, int dx, int height, int width) ++{ ++ struct fb_copyarea area; ++ ++ area.sx = sx * vc->vc_font.width; ++ area.sy = sy * vc->vc_font.height; ++ area.dx = dx * vc->vc_font.width; ++ area.dy = dy * vc->vc_font.height; ++ area.height = height * vc->vc_font.height; ++ area.width = width * vc->vc_font.width; ++ ++ info->fbops->fb_copyarea(info, &area); ++} ++ + static void bit_clear(struct vc_data *vc, struct fb_info *info, int sy, + int sx, int height, int width) + { +@@ -378,6 +393,7 @@ + + void fbcon_set_bitops(struct fbcon_ops *ops) + { ++ ops->bmove = bit_bmove; + ops->clear = bit_clear; + ops->putcs = bit_putcs; + ops->clear_margins = bit_clear_margins; +--- b/drivers/video/fbdev/core/fbcon.c ++++ a/drivers/video/fbdev/core/fbcon.c +@@ -173,6 +173,8 @@ + int count, int ypos, int xpos); + static void fbcon_clear_margins(struct vc_data *vc, int bottom_only); + static void fbcon_cursor(struct vc_data *vc, int mode); ++static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx, ++ int height, int width); + static int fbcon_switch(struct vc_data *vc); + static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch); + static void fbcon_set_palette(struct vc_data *vc, const unsigned char *table); +@@ -180,8 +182,16 @@ + /* + * Internal routines + */ ++static __inline__ void ywrap_up(struct vc_data *vc, int count); ++static __inline__ void ywrap_down(struct vc_data *vc, int count); ++static __inline__ void ypan_up(struct vc_data *vc, int count); ++static __inline__ void ypan_down(struct vc_data *vc, int count); ++static void fbcon_bmove_rec(struct vc_data *vc, struct fbcon_display *p, int sy, int sx, ++ int dy, int dx, int height, int width, u_int y_break); + static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var, + int unit); ++static void fbcon_redraw_move(struct vc_data *vc, struct fbcon_display *p, ++ int line, int count, int dy); + static void fbcon_modechanged(struct fb_info *info); + static void fbcon_set_all_vcs(struct fb_info *info); + static void fbcon_start(void); +@@ -1125,6 +1135,14 @@ + + ops->graphics = 0; + ++ /* ++ * No more hw acceleration for fbcon. ++ * ++ * FIXME: Garbage collect all the now dead code after sufficient time ++ * has passed. ++ */ ++ p->scrollmode = SCROLL_REDRAW; ++ + /* + * ++guenther: console.c:vc_allocate() relies on initializing + * vc_{cols,rows}, but we must not set those if we are only +@@ -1211,13 +1229,14 @@ + * This system is now divided into two levels because of complications + * caused by hardware scrolling. Top level functions: + * ++ * fbcon_bmove(), fbcon_clear(), fbcon_putc(), fbcon_clear_margins() +- * fbcon_clear(), fbcon_putc(), fbcon_clear_margins() + * + * handles y values in range [0, scr_height-1] that correspond to real + * screen positions. y_wrap shift means that first line of bitmap may be + * anywhere on this display. These functions convert lineoffsets to + * bitmap offsets and deal with the wrap-around case by splitting blits. + * ++ * fbcon_bmove_physical_8() -- These functions fast implementations + * fbcon_clear_physical_8() -- of original fbcon_XXX fns. + * fbcon_putc_physical_8() -- (font width != 8) may be added later + * +@@ -1390,6 +1409,224 @@ + } + } + ++static __inline__ void ywrap_up(struct vc_data *vc, int count) ++{ ++ struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; ++ struct fbcon_ops *ops = info->fbcon_par; ++ struct fbcon_display *p = &fb_display[vc->vc_num]; ++ ++ p->yscroll += count; ++ if (p->yscroll >= p->vrows) /* Deal with wrap */ ++ p->yscroll -= p->vrows; ++ ops->var.xoffset = 0; ++ ops->var.yoffset = p->yscroll * vc->vc_font.height; ++ ops->var.vmode |= FB_VMODE_YWRAP; ++ ops->update_start(info); ++ scrollback_max += count; ++ if (scrollback_max > scrollback_phys_max) ++ scrollback_max = scrollback_phys_max; ++ scrollback_current = 0; ++} ++ ++static __inline__ void ywrap_down(struct vc_data *vc, int count) ++{ ++ struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; ++ struct fbcon_ops *ops = info->fbcon_par; ++ struct fbcon_display *p = &fb_display[vc->vc_num]; ++ ++ p->yscroll -= count; ++ if (p->yscroll < 0) /* Deal with wrap */ ++ p->yscroll += p->vrows; ++ ops->var.xoffset = 0; ++ ops->var.yoffset = p->yscroll * vc->vc_font.height; ++ ops->var.vmode |= FB_VMODE_YWRAP; ++ ops->update_start(info); ++ scrollback_max -= count; ++ if (scrollback_max < 0) ++ scrollback_max = 0; ++ scrollback_current = 0; ++} ++ ++static __inline__ void ypan_up(struct vc_data *vc, int count) ++{ ++ struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; ++ struct fbcon_display *p = &fb_display[vc->vc_num]; ++ struct fbcon_ops *ops = info->fbcon_par; ++ ++ p->yscroll += count; ++ if (p->yscroll > p->vrows - vc->vc_rows) { ++ ops->bmove(vc, info, p->vrows - vc->vc_rows, ++ 0, 0, 0, vc->vc_rows, vc->vc_cols); ++ p->yscroll -= p->vrows - vc->vc_rows; ++ } ++ ++ ops->var.xoffset = 0; ++ ops->var.yoffset = p->yscroll * vc->vc_font.height; ++ ops->var.vmode &= ~FB_VMODE_YWRAP; ++ ops->update_start(info); ++ fbcon_clear_margins(vc, 1); ++ scrollback_max += count; ++ if (scrollback_max > scrollback_phys_max) ++ scrollback_max = scrollback_phys_max; ++ scrollback_current = 0; ++} ++ ++static __inline__ void ypan_up_redraw(struct vc_data *vc, int t, int count) ++{ ++ struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; ++ struct fbcon_ops *ops = info->fbcon_par; ++ struct fbcon_display *p = &fb_display[vc->vc_num]; ++ ++ p->yscroll += count; ++ ++ if (p->yscroll > p->vrows - vc->vc_rows) { ++ p->yscroll -= p->vrows - vc->vc_rows; ++ fbcon_redraw_move(vc, p, t + count, vc->vc_rows - count, t); ++ } ++ ++ ops->var.xoffset = 0; ++ ops->var.yoffset = p->yscroll * vc->vc_font.height; ++ ops->var.vmode &= ~FB_VMODE_YWRAP; ++ ops->update_start(info); ++ fbcon_clear_margins(vc, 1); ++ scrollback_max += count; ++ if (scrollback_max > scrollback_phys_max) ++ scrollback_max = scrollback_phys_max; ++ scrollback_current = 0; ++} ++ ++static __inline__ void ypan_down(struct vc_data *vc, int count) ++{ ++ struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; ++ struct fbcon_display *p = &fb_display[vc->vc_num]; ++ struct fbcon_ops *ops = info->fbcon_par; ++ ++ p->yscroll -= count; ++ if (p->yscroll < 0) { ++ ops->bmove(vc, info, 0, 0, p->vrows - vc->vc_rows, ++ 0, vc->vc_rows, vc->vc_cols); ++ p->yscroll += p->vrows - vc->vc_rows; ++ } ++ ++ ops->var.xoffset = 0; ++ ops->var.yoffset = p->yscroll * vc->vc_font.height; ++ ops->var.vmode &= ~FB_VMODE_YWRAP; ++ ops->update_start(info); ++ fbcon_clear_margins(vc, 1); ++ scrollback_max -= count; ++ if (scrollback_max < 0) ++ scrollback_max = 0; ++ scrollback_current = 0; ++} ++ ++static __inline__ void ypan_down_redraw(struct vc_data *vc, int t, int count) ++{ ++ struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; ++ struct fbcon_ops *ops = info->fbcon_par; ++ struct fbcon_display *p = &fb_display[vc->vc_num]; ++ ++ p->yscroll -= count; ++ ++ if (p->yscroll < 0) { ++ p->yscroll += p->vrows - vc->vc_rows; ++ fbcon_redraw_move(vc, p, t, vc->vc_rows - count, t + count); ++ } ++ ++ ops->var.xoffset = 0; ++ ops->var.yoffset = p->yscroll * vc->vc_font.height; ++ ops->var.vmode &= ~FB_VMODE_YWRAP; ++ ops->update_start(info); ++ fbcon_clear_margins(vc, 1); ++ scrollback_max -= count; ++ if (scrollback_max < 0) ++ scrollback_max = 0; ++ scrollback_current = 0; ++} ++ ++static void fbcon_redraw_move(struct vc_data *vc, struct fbcon_display *p, ++ int line, int count, int dy) ++{ ++ unsigned short *s = (unsigned short *) ++ (vc->vc_origin + vc->vc_size_row * line); ++ ++ while (count--) { ++ unsigned short *start = s; ++ unsigned short *le = advance_row(s, 1); ++ unsigned short c; ++ int x = 0; ++ unsigned short attr = 1; ++ ++ do { ++ c = scr_readw(s); ++ if (attr != (c & 0xff00)) { ++ attr = c & 0xff00; ++ if (s > start) { ++ fbcon_putcs(vc, start, s - start, ++ dy, x); ++ x += s - start; ++ start = s; ++ } ++ } ++ console_conditional_schedule(); ++ s++; ++ } while (s < le); ++ if (s > start) ++ fbcon_putcs(vc, start, s - start, dy, x); ++ console_conditional_schedule(); ++ dy++; ++ } ++} ++ ++static void fbcon_redraw_blit(struct vc_data *vc, struct fb_info *info, ++ struct fbcon_display *p, int line, int count, int ycount) ++{ ++ int offset = ycount * vc->vc_cols; ++ unsigned short *d = (unsigned short *) ++ (vc->vc_origin + vc->vc_size_row * line); ++ unsigned short *s = d + offset; ++ struct fbcon_ops *ops = info->fbcon_par; ++ ++ while (count--) { ++ unsigned short *start = s; ++ unsigned short *le = advance_row(s, 1); ++ unsigned short c; ++ int x = 0; ++ ++ do { ++ c = scr_readw(s); ++ ++ if (c == scr_readw(d)) { ++ if (s > start) { ++ ops->bmove(vc, info, line + ycount, x, ++ line, x, 1, s-start); ++ x += s - start + 1; ++ start = s + 1; ++ } else { ++ x++; ++ start++; ++ } ++ } ++ ++ scr_writew(c, d); ++ console_conditional_schedule(); ++ s++; ++ d++; ++ } while (s < le); ++ if (s > start) ++ ops->bmove(vc, info, line + ycount, x, line, x, 1, ++ s-start); ++ console_conditional_schedule(); ++ if (ycount > 0) ++ line++; ++ else { ++ line--; ++ /* NOTE: We subtract two lines from these pointers */ ++ s -= vc->vc_size_row; ++ d -= vc->vc_size_row; ++ } ++ } ++} ++ + static void fbcon_redraw(struct vc_data *vc, struct fbcon_display *p, + int line, int count, int offset) + { +@@ -1450,6 +1687,7 @@ + { + struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; + struct fbcon_display *p = &fb_display[vc->vc_num]; ++ int scroll_partial = info->flags & FBINFO_PARTIAL_PAN_OK; + + if (fbcon_is_inactive(vc, info)) + return true; +@@ -1466,32 +1704,249 @@ + case SM_UP: + if (count > vc->vc_rows) /* Maximum realistic size */ + count = vc->vc_rows; ++ if (logo_shown >= 0) ++ goto redraw_up; ++ switch (p->scrollmode) { ++ case SCROLL_MOVE: ++ fbcon_redraw_blit(vc, info, p, t, b - t - count, ++ count); ++ fbcon_clear(vc, b - count, 0, count, vc->vc_cols); ++ scr_memsetw((unsigned short *) (vc->vc_origin + ++ vc->vc_size_row * ++ (b - count)), ++ vc->vc_video_erase_char, ++ vc->vc_size_row * count); ++ return true; ++ ++ case SCROLL_WRAP_MOVE: ++ if (b - t - count > 3 * vc->vc_rows >> 2) { ++ if (t > 0) ++ fbcon_bmove(vc, 0, 0, count, 0, t, ++ vc->vc_cols); ++ ywrap_up(vc, count); ++ if (vc->vc_rows - b > 0) ++ fbcon_bmove(vc, b - count, 0, b, 0, ++ vc->vc_rows - b, ++ vc->vc_cols); ++ } else if (info->flags & FBINFO_READS_FAST) ++ fbcon_bmove(vc, t + count, 0, t, 0, ++ b - t - count, vc->vc_cols); ++ else ++ goto redraw_up; ++ fbcon_clear(vc, b - count, 0, count, vc->vc_cols); ++ break; ++ ++ case SCROLL_PAN_REDRAW: ++ if ((p->yscroll + count <= ++ 2 * (p->vrows - vc->vc_rows)) ++ && ((!scroll_partial && (b - t == vc->vc_rows)) ++ || (scroll_partial ++ && (b - t - count > ++ 3 * vc->vc_rows >> 2)))) { ++ if (t > 0) ++ fbcon_redraw_move(vc, p, 0, t, count); ++ ypan_up_redraw(vc, t, count); ++ if (vc->vc_rows - b > 0) ++ fbcon_redraw_move(vc, p, b, ++ vc->vc_rows - b, b); ++ } else ++ fbcon_redraw_move(vc, p, t + count, b - t - count, t); ++ fbcon_clear(vc, b - count, 0, count, vc->vc_cols); ++ break; ++ ++ case SCROLL_PAN_MOVE: ++ if ((p->yscroll + count <= ++ 2 * (p->vrows - vc->vc_rows)) ++ && ((!scroll_partial && (b - t == vc->vc_rows)) ++ || (scroll_partial ++ && (b - t - count > ++ 3 * vc->vc_rows >> 2)))) { ++ if (t > 0) ++ fbcon_bmove(vc, 0, 0, count, 0, t, ++ vc->vc_cols); ++ ypan_up(vc, count); ++ if (vc->vc_rows - b > 0) ++ fbcon_bmove(vc, b - count, 0, b, 0, ++ vc->vc_rows - b, ++ vc->vc_cols); ++ } else if (info->flags & FBINFO_READS_FAST) ++ fbcon_bmove(vc, t + count, 0, t, 0, ++ b - t - count, vc->vc_cols); ++ else ++ goto redraw_up; ++ fbcon_clear(vc, b - count, 0, count, vc->vc_cols); ++ break; ++ ++ case SCROLL_REDRAW: ++ redraw_up: ++ fbcon_redraw(vc, p, t, b - t - count, ++ count * vc->vc_cols); ++ fbcon_clear(vc, b - count, 0, count, vc->vc_cols); ++ scr_memsetw((unsigned short *) (vc->vc_origin + ++ vc->vc_size_row * ++ (b - count)), ++ vc->vc_video_erase_char, ++ vc->vc_size_row * count); ++ return true; ++ } ++ break; +- fbcon_redraw(vc, p, t, b - t - count, +- count * vc->vc_cols); +- fbcon_clear(vc, b - count, 0, count, vc->vc_cols); +- scr_memsetw((unsigned short *) (vc->vc_origin + +- vc->vc_size_row * +- (b - count)), +- vc->vc_video_erase_char, +- vc->vc_size_row * count); +- return true; + + case SM_DOWN: + if (count > vc->vc_rows) /* Maximum realistic size */ + count = vc->vc_rows; ++ if (logo_shown >= 0) ++ goto redraw_down; ++ switch (p->scrollmode) { ++ case SCROLL_MOVE: ++ fbcon_redraw_blit(vc, info, p, b - 1, b - t - count, ++ -count); ++ fbcon_clear(vc, t, 0, count, vc->vc_cols); ++ scr_memsetw((unsigned short *) (vc->vc_origin + ++ vc->vc_size_row * ++ t), ++ vc->vc_video_erase_char, ++ vc->vc_size_row * count); ++ return true; ++ ++ case SCROLL_WRAP_MOVE: ++ if (b - t - count > 3 * vc->vc_rows >> 2) { ++ if (vc->vc_rows - b > 0) ++ fbcon_bmove(vc, b, 0, b - count, 0, ++ vc->vc_rows - b, ++ vc->vc_cols); ++ ywrap_down(vc, count); ++ if (t > 0) ++ fbcon_bmove(vc, count, 0, 0, 0, t, ++ vc->vc_cols); ++ } else if (info->flags & FBINFO_READS_FAST) ++ fbcon_bmove(vc, t, 0, t + count, 0, ++ b - t - count, vc->vc_cols); ++ else ++ goto redraw_down; ++ fbcon_clear(vc, t, 0, count, vc->vc_cols); ++ break; ++ ++ case SCROLL_PAN_MOVE: ++ if ((count - p->yscroll <= p->vrows - vc->vc_rows) ++ && ((!scroll_partial && (b - t == vc->vc_rows)) ++ || (scroll_partial ++ && (b - t - count > ++ 3 * vc->vc_rows >> 2)))) { ++ if (vc->vc_rows - b > 0) ++ fbcon_bmove(vc, b, 0, b - count, 0, ++ vc->vc_rows - b, ++ vc->vc_cols); ++ ypan_down(vc, count); ++ if (t > 0) ++ fbcon_bmove(vc, count, 0, 0, 0, t, ++ vc->vc_cols); ++ } else if (info->flags & FBINFO_READS_FAST) ++ fbcon_bmove(vc, t, 0, t + count, 0, ++ b - t - count, vc->vc_cols); ++ else ++ goto redraw_down; ++ fbcon_clear(vc, t, 0, count, vc->vc_cols); ++ break; ++ ++ case SCROLL_PAN_REDRAW: ++ if ((count - p->yscroll <= p->vrows - vc->vc_rows) ++ && ((!scroll_partial && (b - t == vc->vc_rows)) ++ || (scroll_partial ++ && (b - t - count > ++ 3 * vc->vc_rows >> 2)))) { ++ if (vc->vc_rows - b > 0) ++ fbcon_redraw_move(vc, p, b, vc->vc_rows - b, ++ b - count); ++ ypan_down_redraw(vc, t, count); ++ if (t > 0) ++ fbcon_redraw_move(vc, p, count, t, 0); ++ } else ++ fbcon_redraw_move(vc, p, t, b - t - count, t + count); ++ fbcon_clear(vc, t, 0, count, vc->vc_cols); ++ break; ++ ++ case SCROLL_REDRAW: ++ redraw_down: ++ fbcon_redraw(vc, p, b - 1, b - t - count, ++ -count * vc->vc_cols); ++ fbcon_clear(vc, t, 0, count, vc->vc_cols); ++ scr_memsetw((unsigned short *) (vc->vc_origin + ++ vc->vc_size_row * ++ t), ++ vc->vc_video_erase_char, ++ vc->vc_size_row * count); ++ return true; ++ } +- fbcon_redraw(vc, p, b - 1, b - t - count, +- -count * vc->vc_cols); +- fbcon_clear(vc, t, 0, count, vc->vc_cols); +- scr_memsetw((unsigned short *) (vc->vc_origin + +- vc->vc_size_row * +- t), +- vc->vc_video_erase_char, +- vc->vc_size_row * count); +- return true; + } + return false; + } + ++ ++static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx, ++ int height, int width) ++{ ++ struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; ++ struct fbcon_display *p = &fb_display[vc->vc_num]; ++ ++ if (fbcon_is_inactive(vc, info)) ++ return; ++ ++ if (!width || !height) ++ return; ++ ++ /* Split blits that cross physical y_wrap case. ++ * Pathological case involves 4 blits, better to use recursive ++ * code rather than unrolled case ++ * ++ * Recursive invocations don't need to erase the cursor over and ++ * over again, so we use fbcon_bmove_rec() ++ */ ++ fbcon_bmove_rec(vc, p, sy, sx, dy, dx, height, width, ++ p->vrows - p->yscroll); ++} ++ ++static void fbcon_bmove_rec(struct vc_data *vc, struct fbcon_display *p, int sy, int sx, ++ int dy, int dx, int height, int width, u_int y_break) ++{ ++ struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; ++ struct fbcon_ops *ops = info->fbcon_par; ++ u_int b; ++ ++ if (sy < y_break && sy + height > y_break) { ++ b = y_break - sy; ++ if (dy < sy) { /* Avoid trashing self */ ++ fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width, ++ y_break); ++ fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx, ++ height - b, width, y_break); ++ } else { ++ fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx, ++ height - b, width, y_break); ++ fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width, ++ y_break); ++ } ++ return; ++ } ++ ++ if (dy < y_break && dy + height > y_break) { ++ b = y_break - dy; ++ if (dy < sy) { /* Avoid trashing self */ ++ fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width, ++ y_break); ++ fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx, ++ height - b, width, y_break); ++ } else { ++ fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx, ++ height - b, width, y_break); ++ fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width, ++ y_break); ++ } ++ return; ++ } ++ ops->bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx, ++ height, width); ++} ++ + static void updatescrollmode(struct fbcon_display *p, + struct fb_info *info, + struct vc_data *vc) +@@ -1664,7 +2119,21 @@ + + updatescrollmode(p, info, vc); + ++ switch (p->scrollmode) { ++ case SCROLL_WRAP_MOVE: ++ scrollback_phys_max = p->vrows - vc->vc_rows; ++ break; ++ case SCROLL_PAN_MOVE: ++ case SCROLL_PAN_REDRAW: ++ scrollback_phys_max = p->vrows - 2 * vc->vc_rows; ++ if (scrollback_phys_max < 0) ++ scrollback_phys_max = 0; ++ break; ++ default: ++ scrollback_phys_max = 0; ++ break; ++ } ++ +- scrollback_phys_max = 0; + scrollback_max = 0; + scrollback_current = 0; + +--- b/drivers/video/fbdev/core/fbcon.h ++++ a/drivers/video/fbdev/core/fbcon.h +@@ -29,6 +29,7 @@ + /* Filled in by the low-level console driver */ + const u_char *fontdata; + int userfont; /* != 0 if fontdata kmalloc()ed */ ++ u_short scrollmode; /* Scroll Method */ + u_short inverse; /* != 0 text black on white as default */ + short yscroll; /* Hardware scrolling */ + int vrows; /* number of virtual rows */ +@@ -51,6 +52,8 @@ + }; + + struct fbcon_ops { ++ void (*bmove)(struct vc_data *vc, struct fb_info *info, int sy, ++ int sx, int dy, int dx, int height, int width); + void (*clear)(struct vc_data *vc, struct fb_info *info, int sy, + int sx, int height, int width); + void (*putcs)(struct vc_data *vc, struct fb_info *info, +@@ -149,6 +152,62 @@ + #define attr_bgcol_ec(bgshift, vc, info) attr_col_ec(bgshift, vc, info, 0) + #define attr_fgcol_ec(fgshift, vc, info) attr_col_ec(fgshift, vc, info, 1) + ++ /* ++ * Scroll Method ++ */ ++ ++/* There are several methods fbcon can use to move text around the screen: ++ * ++ * Operation Pan Wrap ++ *--------------------------------------------- ++ * SCROLL_MOVE copyarea No No ++ * SCROLL_PAN_MOVE copyarea Yes No ++ * SCROLL_WRAP_MOVE copyarea No Yes ++ * SCROLL_REDRAW imageblit No No ++ * SCROLL_PAN_REDRAW imageblit Yes No ++ * SCROLL_WRAP_REDRAW imageblit No Yes ++ * ++ * (SCROLL_WRAP_REDRAW is not implemented yet) ++ * ++ * In general, fbcon will choose the best scrolling ++ * method based on the rule below: ++ * ++ * Pan/Wrap > accel imageblit > accel copyarea > ++ * soft imageblit > (soft copyarea) ++ * ++ * Exception to the rule: Pan + accel copyarea is ++ * preferred over Pan + accel imageblit. ++ * ++ * The above is typical for PCI/AGP cards. Unless ++ * overridden, fbcon will never use soft copyarea. ++ * ++ * If you need to override the above rule, set the ++ * appropriate flags in fb_info->flags. For example, ++ * to prefer copyarea over imageblit, set ++ * FBINFO_READS_FAST. ++ * ++ * Other notes: ++ * + use the hardware engine to move the text ++ * (hw-accelerated copyarea() and fillrect()) ++ * + use hardware-supported panning on a large virtual screen ++ * + amifb can not only pan, but also wrap the display by N lines ++ * (i.e. visible line i = physical line (i+N) % yres). ++ * + read what's already rendered on the screen and ++ * write it in a different place (this is cfb_copyarea()) ++ * + re-render the text to the screen ++ * ++ * Whether to use wrapping or panning can only be figured out at ++ * runtime (when we know whether our font height is a multiple ++ * of the pan/wrap step) ++ * ++ */ ++ ++#define SCROLL_MOVE 0x001 ++#define SCROLL_PAN_MOVE 0x002 ++#define SCROLL_WRAP_MOVE 0x003 ++#define SCROLL_REDRAW 0x004 ++#define SCROLL_PAN_REDRAW 0x005 ++ + #ifdef CONFIG_FB_TILEBLITTING + extern void fbcon_set_tileops(struct vc_data *vc, struct fb_info *info); + #endif +--- b/drivers/video/fbdev/core/fbcon_ccw.c ++++ a/drivers/video/fbdev/core/fbcon_ccw.c +@@ -59,12 +59,31 @@ + } + } + ++ ++static void ccw_bmove(struct vc_data *vc, struct fb_info *info, int sy, ++ int sx, int dy, int dx, int height, int width) ++{ ++ struct fbcon_ops *ops = info->fbcon_par; ++ struct fb_copyarea area; ++ u32 vyres = GETVYRES(ops->p->scrollmode, info); ++ ++ area.sx = sy * vc->vc_font.height; ++ area.sy = vyres - ((sx + width) * vc->vc_font.width); ++ area.dx = dy * vc->vc_font.height; ++ area.dy = vyres - ((dx + width) * vc->vc_font.width); ++ area.width = height * vc->vc_font.height; ++ area.height = width * vc->vc_font.width; ++ ++ info->fbops->fb_copyarea(info, &area); ++} ++ + static void ccw_clear(struct vc_data *vc, struct fb_info *info, int sy, + int sx, int height, int width) + { ++ struct fbcon_ops *ops = info->fbcon_par; + struct fb_fillrect region; + int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; ++ u32 vyres = GETVYRES(ops->p->scrollmode, info); +- u32 vyres = info->var.yres; + + region.color = attr_bgcol_ec(bgshift,vc,info); + region.dx = sy * vc->vc_font.height; +@@ -121,7 +140,7 @@ + u32 cnt, pitch, size; + u32 attribute = get_attribute(info, scr_readw(s)); + u8 *dst, *buf = NULL; ++ u32 vyres = GETVYRES(ops->p->scrollmode, info); +- u32 vyres = info->var.yres; + + if (!ops->fontbuffer) + return; +@@ -210,7 +229,7 @@ + int attribute, use_sw = vc->vc_cursor_type & CUR_SW; + int err = 1, dx, dy; + char *src; ++ u32 vyres = GETVYRES(ops->p->scrollmode, info); +- u32 vyres = info->var.yres; + + if (!ops->fontbuffer) + return; +@@ -368,7 +387,7 @@ + { + struct fbcon_ops *ops = info->fbcon_par; + u32 yoffset; ++ u32 vyres = GETVYRES(ops->p->scrollmode, info); +- u32 vyres = info->var.yres; + int err; + + yoffset = (vyres - info->var.yres) - ops->var.xoffset; +@@ -383,6 +402,7 @@ + + void fbcon_rotate_ccw(struct fbcon_ops *ops) + { ++ ops->bmove = ccw_bmove; + ops->clear = ccw_clear; + ops->putcs = ccw_putcs; + ops->clear_margins = ccw_clear_margins; +--- b/drivers/video/fbdev/core/fbcon_cw.c ++++ a/drivers/video/fbdev/core/fbcon_cw.c +@@ -44,12 +44,31 @@ + } + } + ++ ++static void cw_bmove(struct vc_data *vc, struct fb_info *info, int sy, ++ int sx, int dy, int dx, int height, int width) ++{ ++ struct fbcon_ops *ops = info->fbcon_par; ++ struct fb_copyarea area; ++ u32 vxres = GETVXRES(ops->p->scrollmode, info); ++ ++ area.sx = vxres - ((sy + height) * vc->vc_font.height); ++ area.sy = sx * vc->vc_font.width; ++ area.dx = vxres - ((dy + height) * vc->vc_font.height); ++ area.dy = dx * vc->vc_font.width; ++ area.width = height * vc->vc_font.height; ++ area.height = width * vc->vc_font.width; ++ ++ info->fbops->fb_copyarea(info, &area); ++} ++ + static void cw_clear(struct vc_data *vc, struct fb_info *info, int sy, + int sx, int height, int width) + { ++ struct fbcon_ops *ops = info->fbcon_par; + struct fb_fillrect region; + int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; ++ u32 vxres = GETVXRES(ops->p->scrollmode, info); +- u32 vxres = info->var.xres; + + region.color = attr_bgcol_ec(bgshift,vc,info); + region.dx = vxres - ((sy + height) * vc->vc_font.height); +@@ -106,7 +125,7 @@ + u32 cnt, pitch, size; + u32 attribute = get_attribute(info, scr_readw(s)); + u8 *dst, *buf = NULL; ++ u32 vxres = GETVXRES(ops->p->scrollmode, info); +- u32 vxres = info->var.xres; + + if (!ops->fontbuffer) + return; +@@ -193,7 +212,7 @@ + int attribute, use_sw = vc->vc_cursor_type & CUR_SW; + int err = 1, dx, dy; + char *src; ++ u32 vxres = GETVXRES(ops->p->scrollmode, info); +- u32 vxres = info->var.xres; + + if (!ops->fontbuffer) + return; +@@ -350,7 +369,7 @@ + static int cw_update_start(struct fb_info *info) + { + struct fbcon_ops *ops = info->fbcon_par; ++ u32 vxres = GETVXRES(ops->p->scrollmode, info); +- u32 vxres = info->var.xres; + u32 xoffset; + int err; + +@@ -366,6 +385,7 @@ + + void fbcon_rotate_cw(struct fbcon_ops *ops) + { ++ ops->bmove = cw_bmove; + ops->clear = cw_clear; + ops->putcs = cw_putcs; + ops->clear_margins = cw_clear_margins; +--- b/drivers/video/fbdev/core/fbcon_rotate.h ++++ a/drivers/video/fbdev/core/fbcon_rotate.h +@@ -11,6 +11,15 @@ + #ifndef _FBCON_ROTATE_H + #define _FBCON_ROTATE_H + ++#define GETVYRES(s,i) ({ \ ++ (s == SCROLL_REDRAW || s == SCROLL_MOVE) ? \ ++ (i)->var.yres : (i)->var.yres_virtual; }) ++ ++#define GETVXRES(s,i) ({ \ ++ (s == SCROLL_REDRAW || s == SCROLL_MOVE || !(i)->fix.xpanstep) ? \ ++ (i)->var.xres : (i)->var.xres_virtual; }) ++ ++ + static inline int pattern_test_bit(u32 x, u32 y, u32 pitch, const char *pat) + { + u32 tmp = (y * pitch) + x, index = tmp / 8, bit = tmp % 8; +--- b/drivers/video/fbdev/core/fbcon_ud.c ++++ a/drivers/video/fbdev/core/fbcon_ud.c +@@ -44,13 +44,33 @@ + } + } + ++ ++static void ud_bmove(struct vc_data *vc, struct fb_info *info, int sy, ++ int sx, int dy, int dx, int height, int width) ++{ ++ struct fbcon_ops *ops = info->fbcon_par; ++ struct fb_copyarea area; ++ u32 vyres = GETVYRES(ops->p->scrollmode, info); ++ u32 vxres = GETVXRES(ops->p->scrollmode, info); ++ ++ area.sy = vyres - ((sy + height) * vc->vc_font.height); ++ area.sx = vxres - ((sx + width) * vc->vc_font.width); ++ area.dy = vyres - ((dy + height) * vc->vc_font.height); ++ area.dx = vxres - ((dx + width) * vc->vc_font.width); ++ area.height = height * vc->vc_font.height; ++ area.width = width * vc->vc_font.width; ++ ++ info->fbops->fb_copyarea(info, &area); ++} ++ + static void ud_clear(struct vc_data *vc, struct fb_info *info, int sy, + int sx, int height, int width) + { ++ struct fbcon_ops *ops = info->fbcon_par; + struct fb_fillrect region; + int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; ++ u32 vyres = GETVYRES(ops->p->scrollmode, info); ++ u32 vxres = GETVXRES(ops->p->scrollmode, info); +- u32 vyres = info->var.yres; +- u32 vxres = info->var.xres; + + region.color = attr_bgcol_ec(bgshift,vc,info); + region.dy = vyres - ((sy + height) * vc->vc_font.height); +@@ -142,8 +162,8 @@ + u32 mod = vc->vc_font.width % 8, cnt, pitch, size; + u32 attribute = get_attribute(info, scr_readw(s)); + u8 *dst, *buf = NULL; ++ u32 vyres = GETVYRES(ops->p->scrollmode, info); ++ u32 vxres = GETVXRES(ops->p->scrollmode, info); +- u32 vyres = info->var.yres; +- u32 vxres = info->var.xres; + + if (!ops->fontbuffer) + return; +@@ -239,8 +259,8 @@ + int attribute, use_sw = vc->vc_cursor_type & CUR_SW; + int err = 1, dx, dy; + char *src; ++ u32 vyres = GETVYRES(ops->p->scrollmode, info); ++ u32 vxres = GETVXRES(ops->p->scrollmode, info); +- u32 vyres = info->var.yres; +- u32 vxres = info->var.xres; + + if (!ops->fontbuffer) + return; +@@ -390,8 +410,8 @@ + { + struct fbcon_ops *ops = info->fbcon_par; + int xoffset, yoffset; ++ u32 vyres = GETVYRES(ops->p->scrollmode, info); ++ u32 vxres = GETVXRES(ops->p->scrollmode, info); +- u32 vyres = info->var.yres; +- u32 vxres = info->var.xres; + int err; + + xoffset = vxres - info->var.xres - ops->var.xoffset; +@@ -409,6 +429,7 @@ + + void fbcon_rotate_ud(struct fbcon_ops *ops) + { ++ ops->bmove = ud_bmove; + ops->clear = ud_clear; + ops->putcs = ud_putcs; + ops->clear_margins = ud_clear_margins; +--- b/drivers/video/fbdev/core/tileblit.c ++++ a/drivers/video/fbdev/core/tileblit.c +@@ -16,6 +16,21 @@ + #include + #include "fbcon.h" + ++static void tile_bmove(struct vc_data *vc, struct fb_info *info, int sy, ++ int sx, int dy, int dx, int height, int width) ++{ ++ struct fb_tilearea area; ++ ++ area.sx = sx; ++ area.sy = sy; ++ area.dx = dx; ++ area.dy = dy; ++ area.height = height; ++ area.width = width; ++ ++ info->tileops->fb_tilecopy(info, &area); ++} ++ + static void tile_clear(struct vc_data *vc, struct fb_info *info, int sy, + int sx, int height, int width) + { +@@ -118,6 +133,7 @@ + struct fb_tilemap map; + struct fbcon_ops *ops = info->fbcon_par; + ++ ops->bmove = tile_bmove; + ops->clear = tile_clear; + ops->putcs = tile_putcs; + ops->clear_margins = tile_clear_margins; +--- b/drivers/video/fbdev/skeletonfb.c ++++ a/drivers/video/fbdev/skeletonfb.c +@@ -505,15 +505,15 @@ + } + + /** ++ * xxxfb_copyarea - REQUIRED function. Can use generic routines if ++ * non acclerated hardware and packed pixel based. +- * xxxfb_copyarea - OBSOLETE function. + * Copies one area of the screen to another area. +- * Will be deleted in a future version + * + * @info: frame buffer structure that represents a single frame buffer + * @area: Structure providing the data to copy the framebuffer contents + * from one region to another. + * ++ * This drawing operation copies a rectangular area from one area of the +- * This drawing operation copied a rectangular area from one area of the + * screen to another area. + */ + void xxxfb_copyarea(struct fb_info *p, const struct fb_copyarea *area) +@@ -645,9 +645,9 @@ + .fb_setcolreg = xxxfb_setcolreg, + .fb_blank = xxxfb_blank, + .fb_pan_display = xxxfb_pan_display, ++ .fb_fillrect = xxxfb_fillrect, /* Needed !!! */ ++ .fb_copyarea = xxxfb_copyarea, /* Needed !!! */ ++ .fb_imageblit = xxxfb_imageblit, /* Needed !!! */ +- .fb_fillrect = xxxfb_fillrect, /* Needed !!! */ +- .fb_copyarea = xxxfb_copyarea, /* Obsolete */ +- .fb_imageblit = xxxfb_imageblit, /* Needed !!! */ + .fb_cursor = xxxfb_cursor, /* Optional !!! */ + .fb_sync = xxxfb_sync, + .fb_ioctl = xxxfb_ioctl, +--- b/include/linux/fb.h ++++ a/include/linux/fb.h +@@ -262,7 +262,7 @@ + + /* Draws a rectangle */ + void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect); ++ /* Copy data from area to another */ +- /* Copy data from area to another. Obsolete. */ + void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region); + /* Draws a image to the display */ + void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image); diff --git a/sys-kernel/pinephone-sources/files/0002-Bluetooth-Fix-LL-PRivacy-BLE-device-fails-to-connect.patch b/sys-kernel/pinephone-sources/files/0002-Bluetooth-Fix-LL-PRivacy-BLE-device-fails-to-connect.patch deleted file mode 100644 index 8707046..0000000 --- a/sys-kernel/pinephone-sources/files/0002-Bluetooth-Fix-LL-PRivacy-BLE-device-fails-to-connect.patch +++ /dev/null @@ -1,67 +0,0 @@ -From 27061f0b322a585c30db111719f89c23c15a88b4 Mon Sep 17 00:00:00 2001 -From: Sathish Narasimman -Date: Thu, 29 Oct 2020 13:18:21 +0530 -Subject: Bluetooth: Fix: LL PRivacy BLE device fails to connect - -When adding device to white list the device is added to resolving list -also. It has to be added only when HCI_ENABLE_LL_PRIVACY flag is set. -HCI_ENABLE_LL_PRIVACY flag has to be tested before adding/deleting devices -to resolving list. use_ll_privacy macro is used only to check if controller -supports LL_Privacy. - -https://bugzilla.kernel.org/show_bug.cgi?id=209745 - -Fixes: 0eee35bdfa3b ("Bluetooth: Update resolving list when updating whitelist") -Signed-off-by: Sathish Narasimman -Signed-off-by: Marcel Holtmann ---- - net/bluetooth/hci_request.c | 12 ++++++++---- - 1 file changed, 8 insertions(+), 4 deletions(-) - -diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c -index e0269192f2e5..a565c91b8599 100644 ---- a/net/bluetooth/hci_request.c -+++ b/net/bluetooth/hci_request.c -@@ -698,7 +698,8 @@ static void del_from_white_list(struct hci_request *req, bdaddr_t *bdaddr, - cp.bdaddr_type); - hci_req_add(req, HCI_OP_LE_DEL_FROM_WHITE_LIST, sizeof(cp), &cp); - -- if (use_ll_privacy(req->hdev)) { -+ if (use_ll_privacy(req->hdev) && -+ hci_dev_test_flag(req->hdev, HCI_ENABLE_LL_PRIVACY)) { - struct smp_irk *irk; - - irk = hci_find_irk_by_addr(req->hdev, bdaddr, bdaddr_type); -@@ -732,7 +733,8 @@ static int add_to_white_list(struct hci_request *req, - return -1; - - /* White list can not be used with RPAs */ -- if (!allow_rpa && !use_ll_privacy(hdev) && -+ if (!allow_rpa && -+ !hci_dev_test_flag(hdev, HCI_ENABLE_LL_PRIVACY) && - hci_find_irk_by_addr(hdev, ¶ms->addr, params->addr_type)) { - return -1; - } -@@ -750,7 +752,8 @@ static int add_to_white_list(struct hci_request *req, - cp.bdaddr_type); - hci_req_add(req, HCI_OP_LE_ADD_TO_WHITE_LIST, sizeof(cp), &cp); - -- if (use_ll_privacy(hdev)) { -+ if (use_ll_privacy(hdev) && -+ hci_dev_test_flag(hdev, HCI_ENABLE_LL_PRIVACY)) { - struct smp_irk *irk; - - irk = hci_find_irk_by_addr(hdev, ¶ms->addr, -@@ -812,7 +815,8 @@ static u8 update_white_list(struct hci_request *req) - } - - /* White list can not be used with RPAs */ -- if (!allow_rpa && !use_ll_privacy(hdev) && -+ if (!allow_rpa && -+ !hci_dev_test_flag(hdev, HCI_ENABLE_LL_PRIVACY) && - hci_find_irk_by_addr(hdev, &b->bdaddr, b->bdaddr_type)) { - return 0x00; - } --- -cgit v1.2.3-1-gf6bb5 - diff --git a/sys-kernel/pinephone-sources/files/0002-Revert-usb-quirks-Add-USB_QUIRK_RESET-for-Quectel-EG.patch b/sys-kernel/pinephone-sources/files/0002-Revert-usb-quirks-Add-USB_QUIRK_RESET-for-Quectel-EG.patch deleted file mode 100644 index f7f97f0..0000000 --- a/sys-kernel/pinephone-sources/files/0002-Revert-usb-quirks-Add-USB_QUIRK_RESET-for-Quectel-EG.patch +++ /dev/null @@ -1,28 +0,0 @@ -From 9d662fb865ae496a7eb51d2bdddefd2427d9a30e Mon Sep 17 00:00:00 2001 -From: Bhushan Shah -Date: Fri, 9 Apr 2021 16:25:25 +0530 -Subject: [PATCH 2/5] Revert "usb: quirks: Add USB_QUIRK_RESET for Quectel - EG25G Modem" - -Reverts 8cc2a406ecc711f5 ---- - drivers/usb/core/quirks.c | 3 --- - 1 file changed, 3 deletions(-) - -diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c -index cb556617aa34f..6ade3daf78584 100644 ---- a/drivers/usb/core/quirks.c -+++ b/drivers/usb/core/quirks.c -@@ -501,9 +501,6 @@ static const struct usb_device_id usb_quirk_list[] = { - /* INTEL VALUE SSD */ - { USB_DEVICE(0x8086, 0xf1a5), .driver_info = USB_QUIRK_RESET_RESUME }, - -- /* Quectel EG25G Modem */ -- { USB_DEVICE(0x2c7c, 0x0125), .driver_info = USB_QUIRK_RESET }, -- - { } /* terminating entry must be last */ - }; - --- -2.31.1 - diff --git a/sys-kernel/pinephone-sources/files/0002-dts-add-pinetab-dev-old-display-panel.patch b/sys-kernel/pinephone-sources/files/0002-dts-add-pinetab-dev-old-display-panel.patch deleted file mode 100644 index 520d52d..0000000 --- a/sys-kernel/pinephone-sources/files/0002-dts-add-pinetab-dev-old-display-panel.patch +++ /dev/null @@ -1,61 +0,0 @@ -From 33212e529708fd480eaf9cc76579f8e7044c0505 Mon Sep 17 00:00:00 2001 -From: Martijn Braam -Date: Tue, 20 Oct 2020 14:42:01 +0200 -Subject: [PATCH] dts: add pinetab-dev (old display panel) - ---- - arch/arm64/boot/dts/allwinner/Makefile | 1 + - .../dts/allwinner/sun50i-a64-pinetab-dev.dts | 29 +++++++++++++++++++ - 2 files changed, 30 insertions(+) - create mode 100644 arch/arm64/boot/dts/allwinner/sun50i-a64-pinetab-dev.dts - -diff --git a/arch/arm64/boot/dts/allwinner/Makefile b/arch/arm64/boot/dts/allwinner/Makefile -index a21cfdd8924d..2936092002b5 100644 ---- a/arch/arm64/boot/dts/allwinner/Makefile -+++ b/arch/arm64/boot/dts/allwinner/Makefile -@@ -15,6 +15,7 @@ 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-pinetab.dtb -+dtb-$(CONFIG_ARCH_SUNXI) += sun50i-a64-pinetab-dev.dtb - dtb-$(CONFIG_ARCH_SUNXI) += sun50i-a64-sopine-baseboard.dtb - dtb-$(CONFIG_ARCH_SUNXI) += sun50i-a64-teres-i.dtb - dtb-$(CONFIG_ARCH_SUNXI) += sun50i-h5-bananapi-m2-plus.dtb -diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinetab-dev.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinetab-dev.dts -new file mode 100644 -index 000000000000..1e287f2fb9f3 ---- /dev/null -+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinetab-dev.dts -@@ -0,0 +1,29 @@ -+// SPDX-License-Identifier: (GPL-2.0+ OR MIT) -+/* -+ * Copyright (C) 2019 Icenowy Zheng -+ * -+ */ -+ -+/dts-v1/; -+ -+#include "sun50i-a64-pinetab.dts" -+ -+/ { -+ model = "PineTab"; -+ compatible = "pine64,pinetab", "allwinner,sun50i-a64"; -+}; -+ -+&dsi { -+ vcc-dsi-supply = <®_dldo1>; -+ status = "okay"; -+ -+ panel@0 { -+ compatible = "feixin,k101-im2ba02"; -+ reg = <0>; -+ avdd-supply = <®_dc1sw>; -+ dvdd-supply = <®_dc1sw>; -+ cvdd-supply = <®_ldo_io1>; -+ reset-gpios = <&pio 3 24 GPIO_ACTIVE_HIGH>; /* PD24 */ -+ backlight = <&backlight>; -+ }; -+}; --- -2.25.4 - diff --git a/sys-kernel/pinephone-sources/files/0001-revert-fbcon-remove-now-unusued-softback_lines-cursor-argument.patch b/sys-kernel/pinephone-sources/files/0002-revert-fbcon-remove-now-unusued-softback_lines-cursor-argument.patch similarity index 100% rename from sys-kernel/pinephone-sources/files/0001-revert-fbcon-remove-now-unusued-softback_lines-cursor-argument.patch rename to sys-kernel/pinephone-sources/files/0002-revert-fbcon-remove-now-unusued-softback_lines-cursor-argument.patch diff --git a/sys-kernel/pinephone-sources/files/0002-revert-fbcon-remove-soft-scrollback-code.patch b/sys-kernel/pinephone-sources/files/0002-revert-fbcon-remove-soft-scrollback-code.patch deleted file mode 100644 index 6d15dfd..0000000 --- a/sys-kernel/pinephone-sources/files/0002-revert-fbcon-remove-soft-scrollback-code.patch +++ /dev/null @@ -1,497 +0,0 @@ ---- b/drivers/video/fbdev/core/fbcon.c -+++ a/drivers/video/fbdev/core/fbcon.c -@@ -122,6 +122,12 @@ - /* logo_shown is an index to vc_cons when >= 0; otherwise follows FBCON_LOGO - enums. */ - static int logo_shown = FBCON_LOGO_CANSHOW; -+/* Software scrollback */ -+static int fbcon_softback_size = 32768; -+static unsigned long softback_buf, softback_curr; -+static unsigned long softback_in; -+static unsigned long softback_top, softback_end; -+static int softback_lines; - /* console mappings */ - static int first_fb_vc; - static int last_fb_vc = MAX_NR_CONSOLES - 1; -@@ -161,6 +167,8 @@ - - static const struct consw fb_con; - -+#define CM_SOFTBACK (8) -+ - #define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * vc->vc_size_row) - - static int fbcon_set_origin(struct vc_data *); -@@ -365,6 +373,18 @@ - return color; - } - -+static void fbcon_update_softback(struct vc_data *vc) -+{ -+ int l = fbcon_softback_size / vc->vc_size_row; -+ -+ if (l > 5) -+ softback_end = softback_buf + l * vc->vc_size_row; -+ else -+ /* Smaller scrollback makes no sense, and 0 would screw -+ the operation totally */ -+ softback_top = 0; -+} -+ - static void fb_flashcursor(struct work_struct *work) - { - struct fb_info *info = container_of(work, struct fb_info, queue); -@@ -394,7 +414,7 @@ - c = scr_readw((u16 *) vc->vc_pos); - mode = (!ops->cursor_flash || ops->cursor_state.enable) ? - CM_ERASE : CM_DRAW; -+ ops->cursor(vc, info, mode, softback_lines, get_color(vc, info, c, 1), -- ops->cursor(vc, info, mode, 0, get_color(vc, info, c, 1), - get_color(vc, info, c, 0)); - console_unlock(); - } -@@ -451,7 +471,13 @@ - } - - if (!strncmp(options, "scrollback:", 11)) { -+ options += 11; -+ if (*options) { -+ fbcon_softback_size = simple_strtoul(options, &options, 0); -+ if (*options == 'k' || *options == 'K') { -+ fbcon_softback_size *= 1024; -+ } -+ } -- pr_warn("Ignoring scrollback size option\n"); - continue; - } - -@@ -996,6 +1022,31 @@ - - set_blitting_type(vc, info); - -+ if (info->fix.type != FB_TYPE_TEXT) { -+ if (fbcon_softback_size) { -+ if (!softback_buf) { -+ softback_buf = -+ (unsigned long) -+ kvmalloc(fbcon_softback_size, -+ GFP_KERNEL); -+ if (!softback_buf) { -+ fbcon_softback_size = 0; -+ softback_top = 0; -+ } -+ } -+ } else { -+ if (softback_buf) { -+ kvfree((void *) softback_buf); -+ softback_buf = 0; -+ softback_top = 0; -+ } -+ } -+ if (softback_buf) -+ softback_in = softback_top = softback_curr = -+ softback_buf; -+ softback_lines = 0; -+ } -+ - /* Setup default font */ - if (!p->fontdata && !vc->vc_font.data) { - if (!fontname[0] || !(font = find_font(fontname))) -@@ -1169,6 +1220,9 @@ - if (logo) - fbcon_prepare_logo(vc, info, cols, rows, new_cols, new_rows); - -+ if (vc == svc && softback_buf) -+ fbcon_update_softback(vc); -+ - if (ops->rotate_font && ops->rotate_font(info, vc)) { - ops->rotate = FB_ROTATE_UR; - set_blitting_type(vc, info); -@@ -1331,6 +1385,7 @@ - { - struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; - struct fbcon_ops *ops = info->fbcon_par; -+ int y; - int c = scr_readw((u16 *) vc->vc_pos); - - ops->cur_blink_jiffies = msecs_to_jiffies(vc->vc_cur_blink_ms); -@@ -1344,8 +1399,16 @@ - fbcon_add_cursor_timer(info); - - ops->cursor_flash = (mode == CM_ERASE) ? 0 : 1; -+ if (mode & CM_SOFTBACK) { -+ mode &= ~CM_SOFTBACK; -+ y = softback_lines; -+ } else { -+ if (softback_lines) -+ fbcon_set_origin(vc); -+ y = 0; -+ } - -+ ops->cursor(vc, info, mode, y, get_color(vc, info, c, 1), -- ops->cursor(vc, info, mode, 0, get_color(vc, info, c, 1), - get_color(vc, info, c, 0)); - } - -@@ -1416,6 +1479,8 @@ - - if (con_is_visible(vc)) { - update_screen(vc); -+ if (softback_buf) -+ fbcon_update_softback(vc); - } - } - -@@ -1553,6 +1618,99 @@ - scrollback_current = 0; - } - -+static void fbcon_redraw_softback(struct vc_data *vc, struct fbcon_display *p, -+ long delta) -+{ -+ int count = vc->vc_rows; -+ unsigned short *d, *s; -+ unsigned long n; -+ int line = 0; -+ -+ d = (u16 *) softback_curr; -+ if (d == (u16 *) softback_in) -+ d = (u16 *) vc->vc_origin; -+ n = softback_curr + delta * vc->vc_size_row; -+ softback_lines -= delta; -+ if (delta < 0) { -+ if (softback_curr < softback_top && n < softback_buf) { -+ n += softback_end - softback_buf; -+ if (n < softback_top) { -+ softback_lines -= -+ (softback_top - n) / vc->vc_size_row; -+ n = softback_top; -+ } -+ } else if (softback_curr >= softback_top -+ && n < softback_top) { -+ softback_lines -= -+ (softback_top - n) / vc->vc_size_row; -+ n = softback_top; -+ } -+ } else { -+ if (softback_curr > softback_in && n >= softback_end) { -+ n += softback_buf - softback_end; -+ if (n > softback_in) { -+ n = softback_in; -+ softback_lines = 0; -+ } -+ } else if (softback_curr <= softback_in && n > softback_in) { -+ n = softback_in; -+ softback_lines = 0; -+ } -+ } -+ if (n == softback_curr) -+ return; -+ softback_curr = n; -+ s = (u16 *) softback_curr; -+ if (s == (u16 *) softback_in) -+ s = (u16 *) vc->vc_origin; -+ while (count--) { -+ unsigned short *start; -+ unsigned short *le; -+ unsigned short c; -+ int x = 0; -+ unsigned short attr = 1; -+ -+ start = s; -+ le = advance_row(s, 1); -+ do { -+ c = scr_readw(s); -+ if (attr != (c & 0xff00)) { -+ attr = c & 0xff00; -+ if (s > start) { -+ fbcon_putcs(vc, start, s - start, -+ line, x); -+ x += s - start; -+ start = s; -+ } -+ } -+ if (c == scr_readw(d)) { -+ if (s > start) { -+ fbcon_putcs(vc, start, s - start, -+ line, x); -+ x += s - start + 1; -+ start = s + 1; -+ } else { -+ x++; -+ start++; -+ } -+ } -+ s++; -+ d++; -+ } while (s < le); -+ if (s > start) -+ fbcon_putcs(vc, start, s - start, line, x); -+ line++; -+ if (d == (u16 *) softback_end) -+ d = (u16 *) softback_buf; -+ if (d == (u16 *) softback_in) -+ d = (u16 *) vc->vc_origin; -+ if (s == (u16 *) softback_end) -+ s = (u16 *) softback_buf; -+ if (s == (u16 *) softback_in) -+ s = (u16 *) vc->vc_origin; -+ } -+} -+ - static void fbcon_redraw_move(struct vc_data *vc, struct fbcon_display *p, - int line, int count, int dy) - { -@@ -1692,6 +1850,31 @@ - } - } - -+static inline void fbcon_softback_note(struct vc_data *vc, int t, -+ int count) -+{ -+ unsigned short *p; -+ -+ if (vc->vc_num != fg_console) -+ return; -+ p = (unsigned short *) (vc->vc_origin + t * vc->vc_size_row); -+ -+ while (count) { -+ scr_memcpyw((u16 *) softback_in, p, vc->vc_size_row); -+ count--; -+ p = advance_row(p, 1); -+ softback_in += vc->vc_size_row; -+ if (softback_in == softback_end) -+ softback_in = softback_buf; -+ if (softback_in == softback_top) { -+ softback_top += vc->vc_size_row; -+ if (softback_top == softback_end) -+ softback_top = softback_buf; -+ } -+ } -+ softback_curr = softback_in; -+} -+ - static bool fbcon_scroll(struct vc_data *vc, unsigned int t, unsigned int b, - enum con_scroll dir, unsigned int count) - { -@@ -1714,6 +1897,8 @@ - case SM_UP: - if (count > vc->vc_rows) /* Maximum realistic size */ - count = vc->vc_rows; -+ if (softback_top) -+ fbcon_softback_note(vc, t, count); - if (logo_shown >= 0) - goto redraw_up; - switch (p->scrollmode) { -@@ -2084,6 +2269,14 @@ - info = registered_fb[con2fb_map[vc->vc_num]]; - ops = info->fbcon_par; - -+ if (softback_top) { -+ if (softback_lines) -+ fbcon_set_origin(vc); -+ softback_top = softback_curr = softback_in = softback_buf; -+ softback_lines = 0; -+ fbcon_update_softback(vc); -+ } -+ - if (logo_shown >= 0) { - struct vc_data *conp2 = vc_cons[logo_shown].d; - -@@ -2407,6 +2600,9 @@ - int cnt; - char *old_data = NULL; - -+ if (con_is_visible(vc) && softback_lines) -+ fbcon_set_origin(vc); -+ - resize = (w != vc->vc_font.width) || (h != vc->vc_font.height); - if (p->userfont) - old_data = vc->vc_font.data; -@@ -2432,6 +2628,8 @@ - cols /= w; - rows /= h; - vc_resize(vc, cols, rows); -+ if (con_is_visible(vc) && softback_buf) -+ fbcon_update_softback(vc); - } else if (con_is_visible(vc) - && vc->vc_mode == KD_TEXT) { - fbcon_clear_margins(vc, 0); -@@ -2590,7 +2788,19 @@ - - static u16 *fbcon_screen_pos(struct vc_data *vc, int offset) - { -+ unsigned long p; -+ int line; -+ -+ if (vc->vc_num != fg_console || !softback_lines) -+ return (u16 *) (vc->vc_origin + offset); -+ line = offset / vc->vc_size_row; -+ if (line >= softback_lines) -+ return (u16 *) (vc->vc_origin + offset - -+ softback_lines * vc->vc_size_row); -+ p = softback_curr + offset; -+ if (p >= softback_end) -+ p += softback_buf - softback_end; -+ return (u16 *) p; -- return (u16 *) (vc->vc_origin + offset); - } - - static unsigned long fbcon_getxy(struct vc_data *vc, unsigned long pos, -@@ -2604,7 +2814,22 @@ - - x = offset % vc->vc_cols; - y = offset / vc->vc_cols; -+ if (vc->vc_num == fg_console) -+ y += softback_lines; - ret = pos + (vc->vc_cols - x) * 2; -+ } else if (vc->vc_num == fg_console && softback_lines) { -+ unsigned long offset = pos - softback_curr; -+ -+ if (pos < softback_curr) -+ offset += softback_end - softback_buf; -+ offset /= 2; -+ x = offset % vc->vc_cols; -+ y = offset / vc->vc_cols; -+ ret = pos + (vc->vc_cols - x) * 2; -+ if (ret == softback_end) -+ ret = softback_buf; -+ if (ret == softback_in) -+ ret = vc->vc_origin; - } else { - /* Should not happen */ - x = y = 0; -@@ -2632,11 +2857,106 @@ - a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) | - (((a) & 0x0700) << 4); - scr_writew(a, p++); -+ if (p == (u16 *) softback_end) -+ p = (u16 *) softback_buf; -+ if (p == (u16 *) softback_in) -+ p = (u16 *) vc->vc_origin; -+ } -+} -+ -+static void fbcon_scrolldelta(struct vc_data *vc, int lines) -+{ -+ struct fb_info *info = registered_fb[con2fb_map[fg_console]]; -+ struct fbcon_ops *ops = info->fbcon_par; -+ struct fbcon_display *disp = &fb_display[fg_console]; -+ int offset, limit, scrollback_old; -+ -+ if (softback_top) { -+ if (vc->vc_num != fg_console) -+ return; -+ if (vc->vc_mode != KD_TEXT || !lines) -+ return; -+ if (logo_shown >= 0) { -+ struct vc_data *conp2 = vc_cons[logo_shown].d; -+ -+ if (conp2->vc_top == logo_lines -+ && conp2->vc_bottom == conp2->vc_rows) -+ conp2->vc_top = 0; -+ if (logo_shown == vc->vc_num) { -+ unsigned long p, q; -+ int i; -+ -+ p = softback_in; -+ q = vc->vc_origin + -+ logo_lines * vc->vc_size_row; -+ for (i = 0; i < logo_lines; i++) { -+ if (p == softback_top) -+ break; -+ if (p == softback_buf) -+ p = softback_end; -+ p -= vc->vc_size_row; -+ q -= vc->vc_size_row; -+ scr_memcpyw((u16 *) q, (u16 *) p, -+ vc->vc_size_row); -+ } -+ softback_in = softback_curr = p; -+ update_region(vc, vc->vc_origin, -+ logo_lines * vc->vc_cols); -+ } -+ logo_shown = FBCON_LOGO_CANSHOW; -+ } -+ fbcon_cursor(vc, CM_ERASE | CM_SOFTBACK); -+ fbcon_redraw_softback(vc, disp, lines); -+ fbcon_cursor(vc, CM_DRAW | CM_SOFTBACK); -+ return; - } -+ -+ if (!scrollback_phys_max) -+ return; -+ -+ scrollback_old = scrollback_current; -+ scrollback_current -= lines; -+ if (scrollback_current < 0) -+ scrollback_current = 0; -+ else if (scrollback_current > scrollback_max) -+ scrollback_current = scrollback_max; -+ if (scrollback_current == scrollback_old) -+ return; -+ -+ if (fbcon_is_inactive(vc, info)) -+ return; -+ -+ fbcon_cursor(vc, CM_ERASE); -+ -+ offset = disp->yscroll - scrollback_current; -+ limit = disp->vrows; -+ switch (disp->scrollmode) { -+ case SCROLL_WRAP_MOVE: -+ info->var.vmode |= FB_VMODE_YWRAP; -+ break; -+ case SCROLL_PAN_MOVE: -+ case SCROLL_PAN_REDRAW: -+ limit -= vc->vc_rows; -+ info->var.vmode &= ~FB_VMODE_YWRAP; -+ break; -+ } -+ if (offset < 0) -+ offset += limit; -+ else if (offset >= limit) -+ offset -= limit; -+ -+ ops->var.xoffset = 0; -+ ops->var.yoffset = offset * vc->vc_font.height; -+ ops->update_start(info); -+ -+ if (!scrollback_current) -+ fbcon_cursor(vc, CM_DRAW); - } - - static int fbcon_set_origin(struct vc_data *vc) - { -+ if (softback_lines) -+ fbcon_scrolldelta(vc, softback_lines); - return 0; - } - -@@ -2700,6 +3020,8 @@ - - fbcon_set_palette(vc, color_table); - update_screen(vc); -+ if (softback_buf) -+ fbcon_update_softback(vc); - } - } - -@@ -3110,6 +3432,7 @@ - .con_font_default = fbcon_set_def_font, - .con_font_copy = fbcon_copy_font, - .con_set_palette = fbcon_set_palette, -+ .con_scrolldelta = fbcon_scrolldelta, - .con_set_origin = fbcon_set_origin, - .con_invert_region = fbcon_invert_region, - .con_screen_pos = fbcon_screen_pos, -@@ -3344,6 +3667,9 @@ - } - #endif - -+ kvfree((void *)softback_buf); -+ softback_buf = 0UL; -+ - for_each_registered_fb(i) { - int pending = 0; - diff --git a/sys-kernel/pinephone-sources/files/0002-sdhci-arasan-Add-runtime-PM-support.patch b/sys-kernel/pinephone-sources/files/0002-sdhci-arasan-Add-runtime-PM-support.patch new file mode 100644 index 0000000..0ceed66 --- /dev/null +++ b/sys-kernel/pinephone-sources/files/0002-sdhci-arasan-Add-runtime-PM-support.patch @@ -0,0 +1,152 @@ +From: Manish Narani +Date: Tue, 18 Sep 2018 20:34:06 +0530 +Subject: [PATCH 16/36] sdhci: arasan: Add runtime PM support + +Add runtime PM support in Arasan SDHCI driver. + +Signed-off-by: Manish Narani +--- + drivers/mmc/host/sdhci-of-arasan.c | 88 +++++++++++++++++++++++++++++++++++++- + 1 file changed, 86 insertions(+), 2 deletions(-) + +diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c +index 6a2e5a4..e08d6ef 100644 +--- a/drivers/mmc/host/sdhci-of-arasan.c ++++ b/drivers/mmc/host/sdhci-of-arasan.c +@@ -19,6 +19,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -27,6 +28,7 @@ + #include "cqhci.h" + #include "sdhci-pltfm.h" + ++#define SDHCI_ARASAN_AUTOSUSPEND_DELAY 2000 /* ms */ + #define SDHCI_ARASAN_VENDOR_REGISTER 0x78 + + #define SDHCI_ARASAN_ITAPDLY_REGISTER 0xF0F8 +@@ -472,6 +474,70 @@ static const struct sdhci_pltfm_data sdhci_arasan_thunderbay_pdata = { + SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400, + }; + ++#ifdef CONFIG_PM ++static int sdhci_arasan_runtime_suspend(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct sdhci_host *host = platform_get_drvdata(pdev); ++ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); ++ struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); ++ int ret; ++ ++ if (sdhci_arasan->has_cqe) { ++ ret = cqhci_suspend(host->mmc); ++ if (ret) ++ return ret; ++ } ++ ++ ret = sdhci_runtime_suspend_host(host); ++ if (ret) ++ return ret; ++ ++ if (host->tuning_mode != SDHCI_TUNING_MODE_3) ++ mmc_retune_needed(host->mmc); ++ ++ clk_disable(pltfm_host->clk); ++ clk_disable(sdhci_arasan->clk_ahb); ++ ++ return 0; ++} ++ ++static int sdhci_arasan_runtime_resume(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct sdhci_host *host = platform_get_drvdata(pdev); ++ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); ++ struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); ++ int ret; ++ ++ ret = clk_enable(sdhci_arasan->clk_ahb); ++ if (ret) { ++ dev_err(dev, "Cannot enable AHB clock.\n"); ++ return ret; ++ } ++ ++ ret = clk_enable(pltfm_host->clk); ++ if (ret) { ++ dev_err(dev, "Cannot enable SD clock.\n"); ++ return ret; ++ } ++ ++ ret = sdhci_runtime_resume_host(host, 0); ++ if (ret) ++ goto out; ++ ++ if (sdhci_arasan->has_cqe) ++ return cqhci_resume(host->mmc); ++ ++ return 0; ++out: ++ clk_disable(pltfm_host->clk); ++ clk_disable(sdhci_arasan->clk_ahb); ++ ++ return ret; ++} ++#endif /* ! CONFIG_PM */ ++ + #ifdef CONFIG_PM_SLEEP + /** + * sdhci_arasan_suspend - Suspend method for the driver +@@ -568,8 +634,10 @@ static int sdhci_arasan_resume(struct device *dev) + } + #endif /* ! CONFIG_PM_SLEEP */ + +-static SIMPLE_DEV_PM_OPS(sdhci_arasan_dev_pm_ops, sdhci_arasan_suspend, +- sdhci_arasan_resume); ++static const struct dev_pm_ops sdhci_arasan_dev_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(sdhci_arasan_suspend, sdhci_arasan_resume) ++ SET_RUNTIME_PM_OPS(sdhci_arasan_runtime_suspend, ++ sdhci_arasan_runtime_resume, NULL) }; + + /** + * sdhci_arasan_sdcardclk_recalc_rate - Return the card clock rate +@@ -1708,13 +1776,25 @@ static int sdhci_arasan_probe(struct platform_device *pdev) + host->mmc->caps2 |= MMC_CAP2_CQE_DCMD; + } + ++ pm_runtime_get_noresume(&pdev->dev); ++ pm_runtime_set_active(&pdev->dev); ++ pm_runtime_enable(&pdev->dev); ++ pm_runtime_set_autosuspend_delay(&pdev->dev, ++ SDHCI_ARASAN_AUTOSUSPEND_DELAY); ++ pm_runtime_use_autosuspend(&pdev->dev); ++ + ret = sdhci_arasan_add_host(sdhci_arasan); + if (ret) + goto err_add_host; + ++ pm_runtime_put_autosuspend(&pdev->dev); ++ + return 0; + + err_add_host: ++ pm_runtime_disable(&pdev->dev); ++ pm_runtime_set_suspended(&pdev->dev); ++ pm_runtime_put_noidle(&pdev->dev); + if (!IS_ERR(sdhci_arasan->phy)) + phy_exit(sdhci_arasan->phy); + unreg_clk: +@@ -1742,6 +1822,10 @@ static int sdhci_arasan_remove(struct platform_device *pdev) + phy_exit(sdhci_arasan->phy); + } + ++ pm_runtime_get_sync(&pdev->dev); ++ pm_runtime_disable(&pdev->dev); ++ pm_runtime_put_noidle(&pdev->dev); ++ + sdhci_arasan_unregister_sdclk(&pdev->dev); + + ret = sdhci_pltfm_unregister(pdev); diff --git a/sys-kernel/pinephone-sources/files/0003-Bluetooth-Fix-attempting-to-set-RPA-timeout-when-unsupported.patch b/sys-kernel/pinephone-sources/files/0003-Bluetooth-Fix-attempting-to-set-RPA-timeout-when-unsupported.patch deleted file mode 100644 index f6bebdd..0000000 --- a/sys-kernel/pinephone-sources/files/0003-Bluetooth-Fix-attempting-to-set-RPA-timeout-when-unsupported.patch +++ /dev/null @@ -1,48 +0,0 @@ -From 4264c74c96e7907b60ee6ed82670317d19ed7ebe Mon Sep 17 00:00:00 2001 -From: Edward Vear -Date: Tue, 27 Oct 2020 00:02:03 -0700 -Subject: Bluetooth: Fix attempting to set RPA timeout when unsupported - -During controller initialization, an LE Set RPA Timeout command is sent -to the controller if supported. However, the value checked to determine -if the command is supported is incorrect. Page 1921 of the Bluetooth -Core Spec v5.2 shows that bit 2 of octet 35 of the Supported_Commands -field corresponds to the LE Set RPA Timeout command, but currently -bit 6 of octet 35 is checked. This patch checks the correct value -instead. - -This issue led to the error seen in the following btmon output during -initialization of an adapter (rtl8761b) and prevented initialization -from completing. - -< HCI Command: LE Set Resolvable Private Address Timeout (0x08|0x002e) plen 2 - Timeout: 900 seconds -> HCI Event: Command Complete (0x0e) plen 4 - LE Set Resolvable Private Address Timeout (0x08|0x002e) ncmd 2 - Status: Unsupported Remote Feature / Unsupported LMP Feature (0x1a) -= Close Index: 00:E0:4C:6B:E5:03 - -The error did not appear when running with this patch. - -Signed-off-by: Edward Vear -Signed-off-by: Marcel Holtmann ---- - net/bluetooth/hci_core.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c -index be9cdf5dabe5..30a5267af490 100644 ---- a/net/bluetooth/hci_core.c -+++ b/net/bluetooth/hci_core.c -@@ -763,7 +763,7 @@ static int hci_init3_req(struct hci_request *req, unsigned long opt) - hci_req_add(req, HCI_OP_LE_CLEAR_RESOLV_LIST, 0, NULL); - } - -- if (hdev->commands[35] & 0x40) { -+ if (hdev->commands[35] & 0x04) { - __le16 rpa_timeout = cpu_to_le16(hdev->rpa_timeout); - - /* Set RPA timeout */ --- -cgit v1.2.3-1-gf6bb5 - diff --git a/sys-kernel/pinephone-sources/files/0003-Bluetooth-btusb.patch b/sys-kernel/pinephone-sources/files/0003-Bluetooth-btusb.patch deleted file mode 100644 index 14375ec..0000000 --- a/sys-kernel/pinephone-sources/files/0003-Bluetooth-btusb.patch +++ /dev/null @@ -1,40 +0,0 @@ -Bluetooth: btusb: Some Qualcomm Bluetooth adapters stop working -This issue starts from linux-5.10-rc1, I reproduced this issue on my -Dell Inspiron 7447 with BT adapter 0cf3:e005, the kernel will print -out: "Bluetooth: hci0: don't support firmware rome 0x31010000", and -someone else also reported the similar issue to bugzilla #211571. - -I found this is a regression introduced by 'commit b40f58b97386 -("Bluetooth: btusb: Add Qualcomm Bluetooth SoC WCN6855 support"), the -patch assumed that if high ROM version is not zero, it is an adapter -on WCN6855, but many old adapters don't need to load rampatch or nvm, -and they have non-zero high ROM version. - -To fix it, let the driver match the rom_version in the -qca_devices_table first, if there is no entry matched, check the -high ROM version, if it is not zero, we assume this adapter is ready -to work and no need to load rampatch and nvm like previously. - -BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=211571 -Fixes: b40f58b97386 ("Bluetooth: btusb: Add Qualcomm Bluetooth SoC WCN6855 support") -Signed-off-by: Hui Wang -Signed-off-by: Marcel Holtmann - -diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c -index 03b83aa91277..32161dd40ed6 100644 ---- a/drivers/bluetooth/btusb.c -+++ b/drivers/bluetooth/btusb.c -@@ -4069,6 +4069,13 @@ static int btusb_setup_qca(struct hci_dev *hdev) - info = &qca_devices_table[i]; - } - if (!info) { -+ /* If the rom_version is not matched in the qca_devices_table -+ * and the high ROM version is not zero, we assume this chip no -+ * need to load the rampatch and nvm. -+ */ -+ if (ver_rom & ~0xffffU) -+ return 0; -+ - bt_dev_err(hdev, "don't support firmware rome 0x%x", ver_rom); - return -ENODEV; - } diff --git a/sys-kernel/pinephone-sources/files/0003-clk-rk3399-Export-SCLK_CIF_OUT_SRC-to-device-tree.patch b/sys-kernel/pinephone-sources/files/0003-clk-rk3399-Export-SCLK_CIF_OUT_SRC-to-device-tree.patch new file mode 100644 index 0000000..fd8585a --- /dev/null +++ b/sys-kernel/pinephone-sources/files/0003-clk-rk3399-Export-SCLK_CIF_OUT_SRC-to-device-tree.patch @@ -0,0 +1,37 @@ +From: Ondrej Jirman +Date: Fri, 22 Oct 2021 18:09:09 +0200 +Subject: [PATCH 19/36] clk: rk3399: Export SCLK_CIF_OUT_SRC to device tree + +So that it can be used in assigned-clock-parents. + +Signed-off-by: Ondrej Jirman +--- + drivers/clk/rockchip/clk-rk3399.c | 2 +- + include/dt-bindings/clock/rk3399-cru.h | 1 + + 2 files changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/clk/rockchip/clk-rk3399.c b/drivers/clk/rockchip/clk-rk3399.c +index 306910a..897581e 100644 +--- a/drivers/clk/rockchip/clk-rk3399.c ++++ b/drivers/clk/rockchip/clk-rk3399.c +@@ -1259,7 +1259,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = { + RK3399_CLKGATE_CON(27), 6, GFLAGS), + + /* cif */ +- COMPOSITE_NODIV(0, "clk_cifout_src", mux_pll_src_cpll_gpll_npll_p, 0, ++ COMPOSITE_NODIV(SCLK_CIF_OUT_SRC, "clk_cifout_src", mux_pll_src_cpll_gpll_npll_p, 0, + RK3399_CLKSEL_CON(56), 6, 2, MFLAGS, + RK3399_CLKGATE_CON(10), 7, GFLAGS), + +diff --git a/include/dt-bindings/clock/rk3399-cru.h b/include/dt-bindings/clock/rk3399-cru.h +index 44e0a31..e83b3fb 100644 +--- a/include/dt-bindings/clock/rk3399-cru.h ++++ b/include/dt-bindings/clock/rk3399-cru.h +@@ -125,6 +125,7 @@ + #define SCLK_DDRC 168 + #define SCLK_TESTCLKOUT1 169 + #define SCLK_TESTCLKOUT2 170 ++#define SCLK_CIF_OUT_SRC 171 + + #define DCLK_VOP0 180 + #define DCLK_VOP1 181 diff --git a/sys-kernel/pinephone-sources/files/0003-qmi_wwan-provide-wrapper-for-reset_resume.patch b/sys-kernel/pinephone-sources/files/0003-qmi_wwan-provide-wrapper-for-reset_resume.patch deleted file mode 100644 index ae9a456..0000000 --- a/sys-kernel/pinephone-sources/files/0003-qmi_wwan-provide-wrapper-for-reset_resume.patch +++ /dev/null @@ -1,51 +0,0 @@ -From 562a6c114ce736db51e41b8c06c408104b79b126 Mon Sep 17 00:00:00 2001 -From: Bhushan Shah -Date: Wed, 14 Apr 2021 10:29:39 +0530 -Subject: [PATCH 3/5] qmi_wwan: provide wrapper for reset_resume - ---- - drivers/net/usb/qmi_wwan.c | 21 ++++++++++++++++++++- - 1 file changed, 20 insertions(+), 1 deletion(-) - -diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c -index e18ded349d840..cd6ae9696b56a 100644 ---- a/drivers/net/usb/qmi_wwan.c -+++ b/drivers/net/usb/qmi_wwan.c -@@ -840,6 +840,25 @@ static int qmi_wwan_resume(struct usb_interface *intf) - return ret; - } - -+static int qmi_wwan_reset_resume(struct usb_interface *intf) -+{ -+ struct usbnet *dev = usb_get_intfdata(intf); -+ struct qmi_wwan_state *info = (void *)&dev->data; -+ int ret = 0; -+ bool callsub = (intf == info->control && info->subdriver && -+ info->subdriver->reset_resume); -+ -+ if (callsub) -+ ret = info->subdriver->reset_resume(intf); -+ if (ret < 0) -+ goto err; -+ ret = usbnet_resume(intf); -+ if (ret < 0 && callsub) -+ info->subdriver->suspend(intf, PMSG_SUSPEND); -+err: -+ return ret; -+} -+ - static const struct driver_info qmi_wwan_info = { - .description = "WWAN/QMI device", - .flags = FLAG_WWAN | FLAG_SEND_ZLP, -@@ -1478,7 +1497,7 @@ static struct usb_driver qmi_wwan_driver = { - .disconnect = qmi_wwan_disconnect, - .suspend = qmi_wwan_suspend, - .resume = qmi_wwan_resume, -- .reset_resume = qmi_wwan_resume, -+ .reset_resume = qmi_wwan_reset_resume, - .supports_autosuspend = 1, - .disable_hub_initiated_lpm = 1, - }; --- -2.31.1 - diff --git a/sys-kernel/pinephone-sources/files/0002-revert-fbcon-remove-no-op-fbcon_set_origin.patch b/sys-kernel/pinephone-sources/files/0003-revert-fbcon-remove-no-op-fbcon_set_origin.patch similarity index 100% rename from sys-kernel/pinephone-sources/files/0002-revert-fbcon-remove-no-op-fbcon_set_origin.patch rename to sys-kernel/pinephone-sources/files/0003-revert-fbcon-remove-no-op-fbcon_set_origin.patch diff --git a/sys-kernel/pinephone-sources/files/0004-cdc-wdm-provide-wrapper-for-reset_resume.patch b/sys-kernel/pinephone-sources/files/0004-cdc-wdm-provide-wrapper-for-reset_resume.patch deleted file mode 100644 index b99092f..0000000 --- a/sys-kernel/pinephone-sources/files/0004-cdc-wdm-provide-wrapper-for-reset_resume.patch +++ /dev/null @@ -1,65 +0,0 @@ -From 3af7a8b44f265a482c8297b420085cfb53725136 Mon Sep 17 00:00:00 2001 -From: Bhushan Shah -Date: Wed, 14 Apr 2021 10:29:57 +0530 -Subject: [PATCH 4/5] cdc-wdm: provide wrapper for reset_resume - ---- - drivers/usb/class/cdc-wdm.c | 35 ++++++++++++++++++++++++++++++++++- - 1 file changed, 34 insertions(+), 1 deletion(-) - -diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c -index 508b1c3f8b731..2b9355ed4a2ad 100644 ---- a/drivers/usb/class/cdc-wdm.c -+++ b/drivers/usb/class/cdc-wdm.c -@@ -1119,6 +1119,39 @@ static int wdm_resume(struct usb_interface *intf) - - return rv; - } -+ -+static int wdm_reset_resume(struct usb_interface *intf) -+{ -+ struct wdm_device *desc = wdm_find_device(intf); -+ int rv; -+ -+ dev_dbg(&desc->intf->dev, "wdm%d_reset_resume\n", intf->minor); -+ -+ spin_lock_irq(&desc->iuspin); -+ set_bit(WDM_RESETTING, &desc->flags); -+ set_bit(WDM_READ, &desc->flags); -+ clear_bit(WDM_IN_USE, &desc->flags); -+ -+ desc->rerr = -EINTR; -+ -+ spin_unlock_irq(&desc->iuspin); -+ wake_up_all(&desc->wait); -+ mutex_lock(&desc->rlock); -+ mutex_lock(&desc->wlock); -+ poison_urbs(desc); -+ cancel_work_sync(&desc->rxwork); -+ cancel_work_sync(&desc->service_outs_intr); -+ -+ clear_bit(WDM_SUSPENDING, &desc->flags); -+ clear_bit(WDM_OVERFLOW, &desc->flags); -+ clear_bit(WDM_RESETTING, &desc->flags); -+ -+ rv = recover_from_urb_loss(desc); -+ mutex_unlock(&desc->wlock); -+ mutex_unlock(&desc->rlock); -+ -+ return rv; -+} - #endif - - static int wdm_pre_reset(struct usb_interface *intf) -@@ -1166,7 +1199,7 @@ static struct usb_driver wdm_driver = { - #ifdef CONFIG_PM - .suspend = wdm_suspend, - .resume = wdm_resume, -- .reset_resume = wdm_resume, -+ .reset_resume = wdm_reset_resume, - #endif - .pre_reset = wdm_pre_reset, - .post_reset = wdm_post_reset, --- -2.31.1 - diff --git a/sys-kernel/pinephone-sources/files/0004-efivarfs-revert-fix-memory-leak-in-efivarfs_create.patch b/sys-kernel/pinephone-sources/files/0004-efivarfs-revert-fix-memory-leak-in-efivarfs_create.patch deleted file mode 100644 index 6589b85..0000000 --- a/sys-kernel/pinephone-sources/files/0004-efivarfs-revert-fix-memory-leak-in-efivarfs_create.patch +++ /dev/null @@ -1,58 +0,0 @@ -From a163474e9b86c2c25f20733385d8b1d6de492a7f Mon Sep 17 00:00:00 2001 -From: Ard Biesheuvel -Date: Wed, 25 Nov 2020 08:45:55 +0100 -Subject: efivarfs: revert "fix memory leak in efivarfs_create()" - -The memory leak addressed by commit fe5186cf12e3 is a false positive: -all allocations are recorded in a linked list, and freed when the -filesystem is unmounted. This leads to double frees, and as reported -by David, leads to crashes if SLUB is configured to self destruct when -double frees occur. - -So drop the redundant kfree() again, and instead, mark the offending -pointer variable so the allocation is ignored by kmemleak. - -Cc: Vamshi K Sthambamkadi -Fixes: fe5186cf12e3 ("efivarfs: fix memory leak in efivarfs_create()") -Reported-by: David Laight -Signed-off-by: Ard Biesheuvel ---- - fs/efivarfs/inode.c | 2 ++ - fs/efivarfs/super.c | 1 - - 2 files changed, 2 insertions(+), 1 deletion(-) - -diff --git a/fs/efivarfs/inode.c b/fs/efivarfs/inode.c -index 96c0c86f3fff..0297ad95eb5c 100644 ---- a/fs/efivarfs/inode.c -+++ b/fs/efivarfs/inode.c -@@ -7,6 +7,7 @@ - #include - #include - #include -+#include - #include - #include - -@@ -103,6 +104,7 @@ static int efivarfs_create(struct inode *dir, struct dentry *dentry, - var->var.VariableName[i] = '\0'; - - inode->i_private = var; -+ kmemleak_ignore(var); - - err = efivar_entry_add(var, &efivarfs_list); - if (err) -diff --git a/fs/efivarfs/super.c b/fs/efivarfs/super.c -index f943fd0b0699..15880a68faad 100644 ---- a/fs/efivarfs/super.c -+++ b/fs/efivarfs/super.c -@@ -21,7 +21,6 @@ LIST_HEAD(efivarfs_list); - static void efivarfs_evict_inode(struct inode *inode) - { - clear_inode(inode); -- kfree(inode->i_private); - } - - static const struct super_operations efivarfs_ops = { --- -cgit v1.2.3-1-gf6bb5 - diff --git a/sys-kernel/pinephone-sources/files/0004-media-rockchip-rga-Fix-probe-bugs.patch b/sys-kernel/pinephone-sources/files/0004-media-rockchip-rga-Fix-probe-bugs.patch new file mode 100644 index 0000000..a0e574f --- /dev/null +++ b/sys-kernel/pinephone-sources/files/0004-media-rockchip-rga-Fix-probe-bugs.patch @@ -0,0 +1,39 @@ +From: Ondrej Jirman +Date: Sun, 21 Nov 2021 17:00:09 +0100 +Subject: [PATCH 24/36] media: rockchip: rga: Fix probe bugs + +The check for dst_mmu_pages allocation failure was inverted. + +rga_parse_dt is missing a error return, so if some of the resources +return DEFER_PROBE, probe will succeed without these resources. + +Signed-off-by: Ondrej Jirman +--- + drivers/media/platform/rockchip/rga/rga.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c +index 4de5e8d..82cc0c0 100644 +--- a/drivers/media/platform/rockchip/rga/rga.c ++++ b/drivers/media/platform/rockchip/rga/rga.c +@@ -815,8 +815,10 @@ static int rga_probe(struct platform_device *pdev) + mutex_init(&rga->mutex); + + ret = rga_parse_dt(rga); +- if (ret) ++ if (ret) { + dev_err(&pdev->dev, "Unable to parse OF data\n"); ++ return ret; ++ } + + pm_runtime_enable(rga->dev); + +@@ -892,7 +894,7 @@ static int rga_probe(struct platform_device *pdev) + } + rga->dst_mmu_pages = + (unsigned int *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 3); +- if (rga->dst_mmu_pages) { ++ if (!rga->dst_mmu_pages) { + ret = -ENOMEM; + goto free_src_pages; + } diff --git a/sys-kernel/pinephone-sources/files/0003-revert-fbcon-remove-soft-scrollback-code.patch b/sys-kernel/pinephone-sources/files/0004-revert-fbcon-remove-soft-scrollback-code.patch similarity index 100% rename from sys-kernel/pinephone-sources/files/0003-revert-fbcon-remove-soft-scrollback-code.patch rename to sys-kernel/pinephone-sources/files/0004-revert-fbcon-remove-soft-scrollback-code.patch diff --git a/sys-kernel/pinephone-sources/files/0005-drm-dw-mipi-dsi-rockchip-Ensure-that-lane-is-properl.patch b/sys-kernel/pinephone-sources/files/0005-drm-dw-mipi-dsi-rockchip-Ensure-that-lane-is-properl.patch new file mode 100644 index 0000000..686dc3e --- /dev/null +++ b/sys-kernel/pinephone-sources/files/0005-drm-dw-mipi-dsi-rockchip-Ensure-that-lane-is-properl.patch @@ -0,0 +1,34 @@ +From: =?utf-8?q?Kamil_Trzci=C5=84ski?= +Date: Fri, 8 Jan 2021 00:19:23 +0100 +Subject: [PATCH 02/36] drm: dw-mipi-dsi-rockchip: Ensure that lane is + properly configured +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit + +??? + +Signed-of-by: Kamil TrzciÅ„ski +--- + 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 a9acbcc..53c8b40 100644 +--- a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c ++++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c +@@ -406,6 +406,14 @@ static int dw_mipi_dsi_phy_init(void *priv_data) + */ + vco = (dsi->lane_mbps < 200) ? 0 : (dsi->lane_mbps + 100) / 200; + ++ if (dsi->cdata->lanecfg1_grf_reg) { ++ regmap_write(dsi->grf_regmap, dsi->cdata->lanecfg1_grf_reg, ++ dsi->cdata->lanecfg1); ++ ++ dev_info(dsi->dev, "dw_mipi_dsi_phy_init / dw_mipi_dsi_rockchip_config: %08x => set=%08x\n", ++ dsi->cdata->lanecfg1_grf_reg, dsi->cdata->lanecfg1); ++ } ++ + i = max_mbps_to_parameter(dsi->lane_mbps); + if (i < 0) { + DRM_DEV_ERROR(dsi->dev, diff --git a/sys-kernel/pinephone-sources/files/0005-net-usb-qmi_wwan-set-the-DTR-when-resuming.patch b/sys-kernel/pinephone-sources/files/0005-net-usb-qmi_wwan-set-the-DTR-when-resuming.patch deleted file mode 100644 index 6fe7e32..0000000 --- a/sys-kernel/pinephone-sources/files/0005-net-usb-qmi_wwan-set-the-DTR-when-resuming.patch +++ /dev/null @@ -1,34 +0,0 @@ -From ed73c96e313c549f710df58c8fbe47200ee13df1 Mon Sep 17 00:00:00 2001 -From: Bhushan Shah -Date: Sat, 10 Apr 2021 08:52:05 +0530 -Subject: [PATCH 5/5] net: usb: qmi_wwan: set the DTR when resuming - -If usb device does reset_resume instead of unbind/bind, we need to -re-enable the DTR quirk, that way after resuming connection QMI -communication between host and modem is possible again. - -Signed-off-by: Bhushan Shah -Tested-by: Dalton Durst ---- - drivers/net/usb/qmi_wwan.c | 5 +++++ - 1 file changed, 5 insertions(+) - -diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c -index cd6ae9696b56a..ada94a3242146 100644 ---- a/drivers/net/usb/qmi_wwan.c -+++ b/drivers/net/usb/qmi_wwan.c -@@ -855,6 +855,11 @@ static int qmi_wwan_reset_resume(struct usb_interface *intf) - ret = usbnet_resume(intf); - if (ret < 0 && callsub) - info->subdriver->suspend(intf, PMSG_SUSPEND); -+ -+ if (dev->driver_info->data & QMI_WWAN_QUIRK_DTR || -+ le16_to_cpu(dev->udev->descriptor.bcdUSB) >= 0x0201) { -+ qmi_wwan_change_dtr(dev, true); -+ } - err: - return ret; - } --- -2.31.1 - diff --git a/sys-kernel/pinephone-sources/files/0006-cdc-wdm-send-HUP-if-we-are-resetting.patch b/sys-kernel/pinephone-sources/files/0006-cdc-wdm-send-HUP-if-we-are-resetting.patch deleted file mode 100644 index 072bda0..0000000 --- a/sys-kernel/pinephone-sources/files/0006-cdc-wdm-send-HUP-if-we-are-resetting.patch +++ /dev/null @@ -1,27 +0,0 @@ -From 1671ef2de0f3f698622bed7ba0e9a605fdd260fc Mon Sep 17 00:00:00 2001 -From: Bhushan Shah -Date: Wed, 14 Apr 2021 18:58:41 +0530 -Subject: [PATCH 6/6] cdc-wdm: send HUP if we are resetting - -If userspace is polling the cdc-wdm socket, and device resets then we -should notify userspace/client about reset. ---- - drivers/usb/class/cdc-wdm.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c -index 2b9355ed4a2ad..93d9bacc18384 100644 ---- a/drivers/usb/class/cdc-wdm.c -+++ b/drivers/usb/class/cdc-wdm.c -@@ -666,6 +666,8 @@ static __poll_t wdm_poll(struct file *file, struct poll_table_struct *wait) - spin_unlock_irqrestore(&desc->iuspin, flags); - goto desc_out; - } -+ if (test_bit(WDM_RESETTING, &desc->flags)) -+ mask = EPOLLHUP; - if (test_bit(WDM_READ, &desc->flags)) - mask = EPOLLIN | EPOLLRDNORM; - if (desc->rerr || desc->werr) --- -2.31.1 - diff --git a/sys-kernel/pinephone-sources/files/0006-drm-rockchip-dw-mipi-dsi-Fix-missing-clk_disable_unp.patch b/sys-kernel/pinephone-sources/files/0006-drm-rockchip-dw-mipi-dsi-Fix-missing-clk_disable_unp.patch new file mode 100644 index 0000000..425c986 --- /dev/null +++ b/sys-kernel/pinephone-sources/files/0006-drm-rockchip-dw-mipi-dsi-Fix-missing-clk_disable_unp.patch @@ -0,0 +1,48 @@ +From: Ondrej Jirman +Date: Sun, 17 Oct 2021 18:04:21 +0200 +Subject: [PATCH 03/36] drm: rockchip: dw-mipi-dsi: Fix missing + clk_disable_unprepare for pllref_clk + +In some error paths, clk_disable_unprepare function was not called. + +Signed-off-by: Ondrej Jirman +--- + drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c +index 53c8b40..095d0f1 100644 +--- a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c ++++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c +@@ -960,7 +960,7 @@ static int dw_mipi_dsi_rockchip_bind(struct device *dev, + ret = clk_prepare_enable(dsi->grf_clk); + if (ret) { + DRM_DEV_ERROR(dsi->dev, "Failed to enable grf_clk: %d\n", ret); +- return ret; ++ goto err_pllref_disable; + } + + dw_mipi_dsi_rockchip_config(dsi); +@@ -972,16 +972,20 @@ static int dw_mipi_dsi_rockchip_bind(struct device *dev, + ret = rockchip_dsi_drm_create_encoder(dsi, drm_dev); + if (ret) { + DRM_DEV_ERROR(dev, "Failed to create drm encoder\n"); +- return ret; ++ goto err_pllref_disable; + } + + ret = dw_mipi_dsi_bind(dsi->dmd, &dsi->encoder); + if (ret) { + DRM_DEV_ERROR(dev, "Failed to bind: %d\n", ret); +- return ret; ++ goto err_pllref_disable; + } + + return 0; ++ ++err_pllref_disable: ++ clk_disable_unprepare(dsi->pllref_clk); ++ return ret; + } + + static void dw_mipi_dsi_rockchip_unbind(struct device *dev, diff --git a/sys-kernel/pinephone-sources/files/0007-drm-bridge-dw-mipi-dsi-Fix-enable-disable-of-dsi-con.patch b/sys-kernel/pinephone-sources/files/0007-drm-bridge-dw-mipi-dsi-Fix-enable-disable-of-dsi-con.patch new file mode 100644 index 0000000..e2eddd2 --- /dev/null +++ b/sys-kernel/pinephone-sources/files/0007-drm-bridge-dw-mipi-dsi-Fix-enable-disable-of-dsi-con.patch @@ -0,0 +1,145 @@ +From: Ondrej Jirman +Date: Sun, 17 Oct 2021 20:14:25 +0200 +Subject: [PATCH 04/36] drm: bridge: dw-mipi-dsi: Fix enable/disable of dsi + controller + +The driver had it all wrong. mode_set is not for enabling the +DSI controller, that should be done in pre_enable so that +panel driver can initialize the panel (working dsi controller +is needed for that). Having dsi powerup in mode_set led to +all kind of fun, because disable would be called more often +than mode_set. + +The whole panel/dsi enable/disable dance is such (for future +reference): + +- dsi: mode set +- panel: prepare (we turn on panel power) +- dsi: pre enable (we enable and setup DSI) +- dsi: enable (we enable video data) +- panel: enable (we configure and turn on the display) + +For disable: + +- panel: disable (we turn off the display nicely) +- dsi: disable (we disable DSI) +- dsi: post disable +- panel: unprepare (we power off display power, panel should + be safely sleeping now) + +Signed-off-by: Ondrej Jirman +--- + drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c | 45 +++++++++++++++++++-------- + 1 file changed, 32 insertions(+), 13 deletions(-) + +diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c +index e44e18a..9cce2ab 100644 +--- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c ++++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c +@@ -266,6 +266,7 @@ struct dw_mipi_dsi { + struct dw_mipi_dsi *master; /* dual-dsi master ptr */ + struct dw_mipi_dsi *slave; /* dual-dsi slave ptr */ + ++ struct drm_display_mode mode; + const struct dw_mipi_dsi_plat_data *plat_data; + }; + +@@ -597,6 +598,8 @@ static void dw_mipi_dsi_set_mode(struct dw_mipi_dsi *dsi, + { + u32 val; + ++ dev_info(dsi->dev, "mode %d\n", (int)mode_flags); ++ + dsi_write(dsi, DSI_PWR_UP, RESET); + + if (mode_flags & MIPI_DSI_MODE_VIDEO) { +@@ -871,11 +874,20 @@ static void dw_mipi_dsi_clear_err(struct dw_mipi_dsi *dsi) + dsi_write(dsi, DSI_INT_MSK1, 0); + } + ++static void dw_mipi_dsi_bridge_disable(struct drm_bridge *bridge) ++{ ++ struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge); ++ ++ dev_info(dsi->dev, "disable\n"); ++} ++ + static void dw_mipi_dsi_bridge_post_disable(struct drm_bridge *bridge) + { + struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge); + const struct dw_mipi_dsi_phy_ops *phy_ops = dsi->plat_data->phy_ops; + ++ dev_info(dsi->dev, "post disable\n"); ++ + /* + * Switch to command mode before panel-bridge post_disable & + * panel unprepare. +@@ -884,15 +896,6 @@ static void dw_mipi_dsi_bridge_post_disable(struct drm_bridge *bridge) + */ + dw_mipi_dsi_set_mode(dsi, 0); + +- /* +- * TODO Only way found to call panel-bridge post_disable & +- * panel unprepare before the dsi "final" disable... +- * This needs to be fixed in the drm_bridge framework and the API +- * needs to be updated to manage our own call chains... +- */ +- if (dsi->panel_bridge->funcs->post_disable) +- dsi->panel_bridge->funcs->post_disable(dsi->panel_bridge); +- + if (phy_ops->power_off) + phy_ops->power_off(dsi->plat_data->priv_data); + +@@ -921,7 +924,7 @@ static unsigned int dw_mipi_dsi_get_lanes(struct dw_mipi_dsi *dsi) + return dsi->lanes; + } + +-static void dw_mipi_dsi_mode_set(struct dw_mipi_dsi *dsi, ++static void dw_mipi_dsi_enable(struct dw_mipi_dsi *dsi, + const struct drm_display_mode *adjusted_mode) + { + const struct dw_mipi_dsi_phy_ops *phy_ops = dsi->plat_data->phy_ops; +@@ -973,16 +976,30 @@ static void dw_mipi_dsi_bridge_mode_set(struct drm_bridge *bridge, + { + struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge); + +- dw_mipi_dsi_mode_set(dsi, adjusted_mode); ++ dev_info(dsi->dev, "mode set\n"); ++ ++ /* Store the display mode for plugin/DKMS poweron events */ ++ memcpy(&dsi->mode, mode, sizeof(dsi->mode)); ++} ++ ++static void dw_mipi_dsi_bridge_pre_enable(struct drm_bridge *bridge) ++{ ++ struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge); ++ ++ dev_info(dsi->dev, "pre enable\n"); ++ ++ /* power up the dsi ctl into a command mode */ ++ dw_mipi_dsi_enable(dsi, &dsi->mode); + if (dsi->slave) +- dw_mipi_dsi_mode_set(dsi->slave, adjusted_mode); ++ dw_mipi_dsi_enable(dsi->slave, &dsi->mode); + } + + static void dw_mipi_dsi_bridge_enable(struct drm_bridge *bridge) + { + struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge); + +- /* Switch to video mode for panel-bridge enable & panel enable */ ++ dev_info(dsi->dev, "enable\n"); ++ + dw_mipi_dsi_set_mode(dsi, MIPI_DSI_MODE_VIDEO); + if (dsi->slave) + dw_mipi_dsi_set_mode(dsi->slave, MIPI_DSI_MODE_VIDEO); +@@ -1033,7 +1050,9 @@ static int dw_mipi_dsi_bridge_attach(struct drm_bridge *bridge, + + static const struct drm_bridge_funcs dw_mipi_dsi_bridge_funcs = { + .mode_set = dw_mipi_dsi_bridge_mode_set, ++ .pre_enable = dw_mipi_dsi_bridge_pre_enable, + .enable = dw_mipi_dsi_bridge_enable, ++ .disable = dw_mipi_dsi_bridge_disable, + .post_disable = dw_mipi_dsi_bridge_post_disable, + .mode_valid = dw_mipi_dsi_bridge_mode_valid, + .attach = dw_mipi_dsi_bridge_attach, diff --git a/sys-kernel/pinephone-sources/files/0008-drm-dw-mipi-dsi-rockchip-Never-allow-lane-bandwidth-.patch b/sys-kernel/pinephone-sources/files/0008-drm-dw-mipi-dsi-rockchip-Never-allow-lane-bandwidth-.patch new file mode 100644 index 0000000..003fd7c --- /dev/null +++ b/sys-kernel/pinephone-sources/files/0008-drm-dw-mipi-dsi-rockchip-Never-allow-lane-bandwidth-.patch @@ -0,0 +1,28 @@ +From: Ondrej Jirman +Date: Tue, 16 Nov 2021 21:16:26 +0100 +Subject: [PATCH 07/36] drm: dw-mipi-dsi-rockchip: Never allow lane bandwidth + to be less than requested + +Bandwidth can be less than requested in some cases, because the search +for best values only checked for absolute difference from ideal value. + +This is likely not intentional. + +Signed-off-by: Ondrej Jirman +--- + drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c +index 095d0f1..bd488a8 100644 +--- a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c ++++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c +@@ -612,7 +612,7 @@ dw_mipi_dsi_get_lane_mbps(void *priv_data, const struct drm_display_mode *mode, + continue; + + delta = abs(fout - tmp); +- if (delta < min_delta) { ++ if (delta < min_delta && fout < tmp) { + best_prediv = _prediv; + best_fbdiv = _fbdiv; + min_delta = delta; diff --git a/sys-kernel/pinephone-sources/files/0009-drm-rockchip-cdn-dp-Disable-CDN-DP-on-disconnect.patch b/sys-kernel/pinephone-sources/files/0009-drm-rockchip-cdn-dp-Disable-CDN-DP-on-disconnect.patch new file mode 100644 index 0000000..187e5e2 --- /dev/null +++ b/sys-kernel/pinephone-sources/files/0009-drm-rockchip-cdn-dp-Disable-CDN-DP-on-disconnect.patch @@ -0,0 +1,23 @@ +From: Ondrej Jirman +Date: Sat, 20 Nov 2021 14:35:52 +0100 +Subject: [PATCH 09/36] drm: rockchip: cdn-dp: Disable CDN DP on disconnect + +Why not? + +Signed-off-by: Ondrej Jirman +--- + drivers/gpu/drm/rockchip/cdn-dp-core.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c +index 16497c3..f4dcd9e 100644 +--- a/drivers/gpu/drm/rockchip/cdn-dp-core.c ++++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c +@@ -934,6 +934,7 @@ static void cdn_dp_pd_event_work(struct work_struct *work) + DRM_DEV_INFO(dp->dev, "Not connected. Disabling cdn\n"); + dp->connected = false; + ++ cdn_dp_disable(dp); + /* Connected but not enabled, enable the block */ + } else if (!dp->active) { + DRM_DEV_INFO(dp->dev, "Connected, not enabled. Enabling cdn\n"); diff --git a/sys-kernel/pinephone-sources/files/0010-dts-pinephone-Add-pine64-pinephone-to-compat-list.patch b/sys-kernel/pinephone-sources/files/0010-dts-pinephone-Add-pine64-pinephone-to-compat-list.patch deleted file mode 100644 index 156e837..0000000 --- a/sys-kernel/pinephone-sources/files/0010-dts-pinephone-Add-pine64-pinephone-to-compat-list.patch +++ /dev/null @@ -1,56 +0,0 @@ -From 05044b9e4e4ae03f66e1c504d6fef57a1d135897 Mon Sep 17 00:00:00 2001 -From: Dylan Van Assche -Date: Thu, 24 Dec 2020 19:57:12 +0100 -Subject: [PATCH] dts: pinephone: Add 'pine64,pinephone' to compat list - -Indicates that all PinePhone models share most of the hardware with each other. -Used for feedbackd configuration when retrieving a device specific config for -haptic feedbackd. ---- - arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.0.dts | 2 +- - arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.1.dts | 2 +- - arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.2.dts | 2 +- - 3 files changed, 3 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 0f6faa44c..2e0892b32 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 -@@ -7,7 +7,7 @@ - - / { - model = "Pine64 PinePhone Developer Batch (1.0)"; -- compatible = "pine64,pinephone-1.0", "allwinner,sun50i-a64"; -+ compatible = "pine64,pinephone-1.0", "pine64,pinephone", "allwinner,sun50i-a64"; - - reg_vbus: usb0-vbus { - compatible = "regulator-fixed"; -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 95a880fdc..d6bad0838 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 -@@ -7,7 +7,7 @@ - - / { - model = "Pine64 PinePhone Braveheart (1.1)"; -- compatible = "pine64,pinephone-1.1", "allwinner,sun50i-a64"; -+ compatible = "pine64,pinephone-1.1", "pine64,pinephone", "allwinner,sun50i-a64"; - - reg_vbus: usb0-vbus { - compatible = "regulator-fixed"; -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 23ba72508..710493186 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 -@@ -7,7 +7,7 @@ - - / { - model = "Pine64 PinePhone (1.2)"; -- compatible = "pine64,pinephone-1.2", "allwinner,sun50i-a64"; -+ compatible = "pine64,pinephone-1.2", "pine64,pinephone", "allwinner,sun50i-a64"; - - wifi_pwrseq: wifi-pwrseq { - compatible = "mmc-pwrseq-simple"; --- -2.26.2 - diff --git a/sys-kernel/pinephone-sources/files/0010-video-fbdev-Add-events-for-early-fb-event-support.patch b/sys-kernel/pinephone-sources/files/0010-video-fbdev-Add-events-for-early-fb-event-support.patch new file mode 100644 index 0000000..d424c70 --- /dev/null +++ b/sys-kernel/pinephone-sources/files/0010-video-fbdev-Add-events-for-early-fb-event-support.patch @@ -0,0 +1,69 @@ +From: Ondrej Jirman +Date: Sun, 17 Oct 2021 12:46:01 +0200 +Subject: [PATCH 10/36] video: fbdev: Add events for early fb event support + +This patch adds FB_EARLY_EVENT_BLANK and FB_R_EARLY_EVENT_BLANK +event mode supports. first, fb_notifier_call_chain() is called with +FB_EARLY_EVENT_BLANK and fb_blank() of specific fb driver is called +and then fb_notifier_call_chain() is called with FB_EVENT_BLANK again +at fb_blank(). and if fb_blank() was failed then fb_nitifier_call_chain() +would be called with FB_R_EARLY_EVENT_BLANK to revert the previous effects. + +Signed-off-by: Inki Dae +Signed-off-by: Kyungmin Park +--- + drivers/video/fbdev/core/fbmem.c | 12 +++++++++++- + include/linux/fb.h | 5 +++++ + 2 files changed, 16 insertions(+), 1 deletion(-) + +diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c +index 826175a..fb1651a 100644 +--- a/drivers/video/fbdev/core/fbmem.c ++++ b/drivers/video/fbdev/core/fbmem.c +@@ -1067,7 +1067,7 @@ int + fb_blank(struct fb_info *info, int blank) + { + struct fb_event event; +- int ret = -EINVAL; ++ int ret = -EINVAL, early_ret; + + if (blank > FB_BLANK_POWERDOWN) + blank = FB_BLANK_POWERDOWN; +@@ -1075,11 +1075,21 @@ fb_blank(struct fb_info *info, int blank) + event.info = info; + event.data = ␣ + ++ early_ret = fb_notifier_call_chain(FB_EARLY_EVENT_BLANK, &event); ++ + if (info->fbops->fb_blank) + ret = info->fbops->fb_blank(blank, info); + + if (!ret) + fb_notifier_call_chain(FB_EVENT_BLANK, &event); ++ else { ++ /* ++ * if fb_blank is failed then revert effects of ++ * the early blank event. ++ */ ++ if (!early_ret) ++ fb_notifier_call_chain(FB_R_EARLY_EVENT_BLANK, &event); ++ } + + return ret; + } +diff --git a/include/linux/fb.h b/include/linux/fb.h +index 6f3db99..ffef502a 100644 +--- a/include/linux/fb.h ++++ b/include/linux/fb.h +@@ -137,6 +137,11 @@ struct fb_cursor_user { + /* A display blank is requested */ + #define FB_EVENT_BLANK 0x09 + ++/* A hardware display blank early change occured */ ++#define FB_EARLY_EVENT_BLANK 0x10 ++/* A hardware display blank revert early change occured */ ++#define FB_R_EARLY_EVENT_BLANK 0x11 ++ + struct fb_event { + struct fb_info *info; + void *data; diff --git a/sys-kernel/pinephone-sources/files/0011-dts-pinetab-hardcode-mmc-numbers.patch b/sys-kernel/pinephone-sources/files/0011-dts-pinetab-hardcode-mmc-numbers.patch deleted file mode 100644 index 0d35af8..0000000 --- a/sys-kernel/pinephone-sources/files/0011-dts-pinetab-hardcode-mmc-numbers.patch +++ /dev/null @@ -1,29 +0,0 @@ -From c965cb8a004c6cc370b4bf297c61fe5ac8ab0583 Mon Sep 17 00:00:00 2001 -From: Martijn Braam -Date: Wed, 6 Jan 2021 03:11:17 +0100 -Subject: [PATCH] arm64: dts: sunxi: Add mmc aliases for the PineTab - -The order for the mmc devices changed in the kernel without this change. - -Signed-off-by: Martijn Braam ---- - arch/arm64/boot/dts/allwinner/sun50i-a64-pinetab.dts | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinetab.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinetab.dts -index a87790df94b3..1cf3c3a9ad7f 100644 ---- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinetab.dts -+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinetab.dts -@@ -18,6 +18,9 @@ / { - compatible = "pine64,pinetab", "allwinner,sun50i-a64"; - - aliases { -+ mmc0 = &mmc0; -+ mmc1 = &mmc1; -+ mmc2 = &mmc2; - serial0 = &uart0; - ethernet0 = &rtl8723cs; - }; --- -2.29.2 - diff --git a/sys-kernel/pinephone-sources/files/0011-power-rk818-Configure-rk808-clkout2-function.patch b/sys-kernel/pinephone-sources/files/0011-power-rk818-Configure-rk808-clkout2-function.patch new file mode 100644 index 0000000..8ff73f2 --- /dev/null +++ b/sys-kernel/pinephone-sources/files/0011-power-rk818-Configure-rk808-clkout2-function.patch @@ -0,0 +1,40 @@ +From: =?utf-8?q?Kamil_Trzci=C5=84ski?= +Date: Mon, 4 Jan 2021 17:57:49 +0100 +Subject: [PATCH 11/36] power: rk818: Configure `rk808-clkout2` function +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit + +??? + +Signed-of-by: Kamil TrzciÅ„ski +--- + drivers/mfd/rk808.c | 1 + + include/linux/mfd/rk808.h | 2 ++ + 2 files changed, 3 insertions(+) + +diff --git a/drivers/mfd/rk808.c b/drivers/mfd/rk808.c +index b181fe4..1a6857e 100644 +--- a/drivers/mfd/rk808.c ++++ b/drivers/mfd/rk808.c +@@ -303,6 +303,7 @@ static const struct rk808_reg_data rk818_pre_init_reg[] = { + { RK818_H5V_EN_REG, BIT(0), RK818_H5V_EN }, + { RK808_VB_MON_REG, MASK_ALL, VB_LO_ACT | + VB_LO_SEL_3500MV }, ++ { RK808_CLK32OUT_REG, CLK32KOUT2_FUNC_MASK, CLK32KOUT2_FUNC }, + }; + + static const struct regmap_irq rk805_irqs[] = { +diff --git a/include/linux/mfd/rk808.h b/include/linux/mfd/rk808.h +index a96e6d4..2ec0520 100644 +--- a/include/linux/mfd/rk808.h ++++ b/include/linux/mfd/rk808.h +@@ -381,6 +381,8 @@ enum rk805_reg { + + #define VOUT_LO_INT BIT(0) + #define CLK32KOUT2_EN BIT(0) ++#define CLK32KOUT2_FUNC (0 << 1) ++#define CLK32KOUT2_FUNC_MASK BIT(1) + + #define TEMP115C 0x0c + #define TEMP_HOTDIE_MSK 0x0c diff --git a/sys-kernel/pinephone-sources/files/0012-pinephone-fix-pogopin-i2c.patch b/sys-kernel/pinephone-sources/files/0012-pinephone-fix-pogopin-i2c.patch deleted file mode 100644 index 86a260b..0000000 --- a/sys-kernel/pinephone-sources/files/0012-pinephone-fix-pogopin-i2c.patch +++ /dev/null @@ -1,29 +0,0 @@ -From d753557c64f6e85f63cffab53496d6271d724074 Mon Sep 17 00:00:00 2001 -From: Martijn Braam -Date: Mon, 15 Feb 2021 13:10:37 -0800 -Subject: [PATCH] pinephone: fix pogopin i2c - ---- - arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi | 6 +----- - 1 file changed, 1 insertion(+), 5 deletions(-) - -diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi -index 39fdf96fe95d..7d0dd52e2f9d 100644 ---- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi -+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi -@@ -635,11 +635,7 @@ &pio { - vcc-pb-supply = <®_dcdc1>; - vcc-pc-supply = <®_dcdc1>; - vcc-pd-supply = <®_dcdc1>; -- /* pinctrl would enable this even if no camera is powered, -- * which is wrong/not necessary -- * -- * vcc-pe-supply = <®_aldo1>; (also used by pogo pins i2c) -- */ -+ vcc-pe-supply = <®_aldo1>; /* (also used by pogo pins i2c) */ - vcc-pf-supply = <®_dcdc1>; - vcc-pg-supply = <®_dldo4>; - vcc-ph-supply = <®_dcdc1>; --- -2.30.1 - diff --git a/sys-kernel/pinephone-sources/files/0012-power-rk818-battery-Add-battery-driver-for-RK818.patch b/sys-kernel/pinephone-sources/files/0012-power-rk818-battery-Add-battery-driver-for-RK818.patch new file mode 100644 index 0000000..fd0eb7e --- /dev/null +++ b/sys-kernel/pinephone-sources/files/0012-power-rk818-battery-Add-battery-driver-for-RK818.patch @@ -0,0 +1,3964 @@ +From: =?utf-8?q?Kamil_Trzci=C5=84ski?= +Date: Sun, 3 Jan 2021 11:43:38 +0100 +Subject: [PATCH 12/36] power: rk818-battery: Add battery driver for RK818 +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit + +This is forward ported driver from Rockchip BSP. + +Signed-of-by: Kamil TrzciÅ„ski +--- + drivers/mfd/rk808.c | 40 +- + drivers/power/supply/Kconfig | 8 + + drivers/power/supply/Makefile | 1 + + drivers/power/supply/rk818_battery.c | 3568 ++++++++++++++++++++++++++++++++++ + drivers/power/supply/rk818_battery.h | 168 ++ + include/linux/mfd/rk808.h | 81 +- + 6 files changed, 3863 insertions(+), 3 deletions(-) + create mode 100644 drivers/power/supply/rk818_battery.c + create mode 100644 drivers/power/supply/rk818_battery.h + +diff --git a/drivers/mfd/rk808.c b/drivers/mfd/rk808.c +index 1a6857e..7d1f000 100644 +--- a/drivers/mfd/rk808.c ++++ b/drivers/mfd/rk808.c +@@ -76,12 +76,47 @@ static bool rk817_is_volatile_reg(struct device *dev, unsigned int reg) + return true; + } + ++static bool rk818_is_volatile_reg(struct device *dev, unsigned int reg) ++{ ++ /* ++ * Notes: ++ * - Technically the ROUND_30s bit makes RTC_CTRL_REG volatile, but ++ * we don't use that feature. It's better to cache. ++ * - It's unlikely we care that RK808_DEVCTRL_REG is volatile since ++ * bits are cleared in case when we shutoff anyway, but better safe. ++ */ ++ ++ switch (reg) { ++ case RK808_SECONDS_REG ... RK808_WEEKS_REG: ++ case RK808_RTC_STATUS_REG: ++ case RK808_VB_MON_REG: ++ case RK808_THERMAL_REG: ++ case RK808_DCDC_EN_REG: ++ case RK808_LDO_EN_REG: ++ case RK808_DCDC_UV_STS_REG: ++ case RK808_LDO_UV_STS_REG: ++ case RK808_DCDC_PG_REG: ++ case RK808_LDO_PG_REG: ++ case RK808_DEVCTRL_REG: ++ case RK808_INT_STS_REG1: ++ case RK808_INT_STS_REG2: ++ case RK808_INT_STS_MSK_REG1: ++ case RK808_INT_STS_MSK_REG2: ++ case RK818_LDO8_ON_VSEL_REG: // TODO(ayufan):?? ++ case RK818_LDO8_SLP_VSEL_REG: // TODO(ayufan):?? ++ case RK818_SUP_STS_REG ... RK818_SAVE_DATA19: ++ return true; ++ } ++ ++ return false; ++} ++ + static const struct regmap_config rk818_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +- .max_register = RK818_USB_CTRL_REG, ++ .max_register = RK818_SAVE_DATA19, + .cache_type = REGCACHE_RBTREE, +- .volatile_reg = rk808_is_volatile_reg, ++ .volatile_reg = rk818_is_volatile_reg, + }; + + static const struct regmap_config rk805_regmap_config = { +@@ -170,6 +205,7 @@ static const struct mfd_cell rk817s[] = { + static const struct mfd_cell rk818s[] = { + { .name = "rk808-clkout", }, + { .name = "rk808-regulator", }, ++ { .name = "rk818-battery", .of_compatible = "rk818-battery", }, + { + .name = "rk808-rtc", + .num_resources = ARRAY_SIZE(rtc_resources), +diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig +index 5cf5bb5..f5d4434 100644 +--- a/drivers/power/supply/Kconfig ++++ b/drivers/power/supply/Kconfig +@@ -854,4 +854,12 @@ config CHARGER_SURFACE + Microsoft Surface devices, i.e. Surface Pro 7, Surface Laptop 3, + Surface Book 3, and Surface Laptop Go. + ++config BATTERY_RK818 ++ bool "RK818 Battery driver" ++ depends on MFD_RK808 ++ default n ++ help ++ If you say yes here you will get support for the battery of RK818 PMIC. ++ This driver can give support for Rk818 Battery Charge Interface. ++ + endif # POWER_SUPPLY +diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile +index 4e55a11aab..1c725ee 100644 +--- a/drivers/power/supply/Makefile ++++ b/drivers/power/supply/Makefile +@@ -104,3 +104,4 @@ obj-$(CONFIG_RN5T618_POWER) += rn5t618_power.o + obj-$(CONFIG_BATTERY_ACER_A500) += acer_a500_battery.o + obj-$(CONFIG_BATTERY_SURFACE) += surface_battery.o + obj-$(CONFIG_CHARGER_SURFACE) += surface_charger.o ++obj-$(CONFIG_BATTERY_RK818) += rk818_battery.o +diff --git a/drivers/power/supply/rk818_battery.c b/drivers/power/supply/rk818_battery.c +new file mode 100644 +index 00000000..f09f456 +--- /dev/null ++++ b/drivers/power/supply/rk818_battery.c +@@ -0,0 +1,3568 @@ ++/* ++ * rk818 battery driver ++ * ++ * Copyright (C) 2016 Rockchip Electronics Co., Ltd ++ * chenjh ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope 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 ++#include ++#include ++#include ++//#include ++#include ++//#include ++#include ++#include ++#include ++//#include ++#include ++#include "rk818_battery.h" ++ ++static int dbg_enable = 0; ++module_param_named(dbg_level, dbg_enable, int, 0644); ++ ++#define DBG(args...) \ ++ do { \ ++ if (dbg_enable) { \ ++ pr_info(args); \ ++ } \ ++ } while (0) ++ ++#define BAT_INFO(fmt, args...) pr_info("rk818-bat: "fmt, ##args) ++ ++/* default param */ ++#define DEFAULT_BAT_RES 135 ++#define DEFAULT_SLP_ENTER_CUR 300 ++#define DEFAULT_SLP_EXIT_CUR 300 ++#define DEFAULT_SLP_FILTER_CUR 100 ++#define DEFAULT_PWROFF_VOL_THRESD 3400 ++#define DEFAULT_MONITOR_SEC 5 ++#define DEFAULT_ALGR_VOL_THRESD1 3850 ++#define DEFAULT_ALGR_VOL_THRESD2 3950 ++#define DEFAULT_MAX_SOC_OFFSET 60 ++#define DEFAULT_FB_TEMP TEMP_105C ++#define DEFAULT_ZERO_RESERVE_DSOC 10 ++#define DEFAULT_POFFSET 42 ++#define DEFAULT_COFFSET 0x832 ++#define DEFAULT_SAMPLE_RES 20 ++#define DEFAULT_ENERGY_MODE 0 ++#define INVALID_COFFSET_MIN 0x780 ++#define INVALID_COFFSET_MAX 0x980 ++#define INVALID_VOL_THRESD 2500 ++ ++/* sample resistor and division */ ++#define SAMPLE_RES_10MR 10 ++#define SAMPLE_RES_20MR 20 ++#define SAMPLE_RES_DIV1 1 ++#define SAMPLE_RES_DIV2 2 ++ ++/* virtual params */ ++#define VIRTUAL_CURRENT 1000 ++#define VIRTUAL_VOLTAGE 3888 ++#define VIRTUAL_SOC 66 ++#define VIRTUAL_PRESET 1 ++#define VIRTUAL_TEMPERATURE 188 ++#define VIRTUAL_STATUS POWER_SUPPLY_STATUS_CHARGING ++ ++/* charge */ ++#define FINISH_CHRG_CUR1 1000 ++#define FINISH_CHRG_CUR2 1500 ++#define FINISH_MAX_SOC_DELAY 20 ++#define TERM_CHRG_DSOC 88 ++#define TERM_CHRG_CURR 600 ++#define TERM_CHRG_K 650 ++#define SIMULATE_CHRG_INTV 8 ++#define SIMULATE_CHRG_CURR 400 ++#define SIMULATE_CHRG_K 1500 ++#define FULL_CHRG_K 400 ++ ++/* zero algorithm */ ++#define PWROFF_THRESD 3400 ++#define MIN_ZERO_DSOC_ACCURACY 10 /*0.01%*/ ++#define MIN_ZERO_OVERCNT 100 ++#define MIN_ACCURACY 1 ++#define DEF_PWRPATH_RES 50 ++#define WAIT_DSOC_DROP_SEC 15 ++#define WAIT_SHTD_DROP_SEC 30 ++#define ZERO_GAP_XSOC1 10 ++#define ZERO_GAP_XSOC2 5 ++#define ZERO_GAP_XSOC3 3 ++#define ZERO_LOAD_LVL1 1400 ++#define ZERO_LOAD_LVL2 600 ++#define ZERO_GAP_CALIB 5 ++ ++#define ADC_CALIB_THRESHOLD 4 ++#define ADC_CALIB_LMT_MIN 3 ++#define ADC_CALIB_CNT 5 ++#define NTC_CALC_FACTOR 7 ++ ++/* time */ ++#define POWER_ON_SEC_BASE 1 ++#define MINUTE(x) ((x) * 60) ++ ++/* sleep */ ++#define SLP_CURR_MAX 40 ++#define SLP_CURR_MIN 6 ++#define DISCHRG_TIME_STEP1 MINUTE(10) ++#define DISCHRG_TIME_STEP2 MINUTE(60) ++#define SLP_DSOC_VOL_THRESD 3600 ++#define REBOOT_PERIOD_SEC 180 ++#define REBOOT_MAX_CNT 80 ++ ++/* fcc */ ++#define MIN_FCC 500 ++ ++/* TS detect battery temperature */ ++#define ADC_CUR_MSK 0x03 ++#define ADC_CUR_20UA 0x00 ++#define ADC_CUR_40UA 0x01 ++#define ADC_CUR_60UA 0x02 ++#define ADC_CUR_80UA 0x03 ++ ++#define NTC_CALC_FACTOR_80UA 7 ++#define NTC_CALC_FACTOR_60UA 9 ++#define NTC_CALC_FACTOR_40UA 13 ++#define NTC_CALC_FACTOR_20UA 27 ++#define NTC_80UA_MAX_MEASURE 27500 ++#define NTC_60UA_MAX_MEASURE 36666 ++#define NTC_40UA_MAX_MEASURE 55000 ++#define NTC_20UA_MAX_MEASURE 110000 ++ ++static const char *bat_status[] = { ++ "charge off", "dead charge", "trickle charge", "cc cv", ++ "finish", "usb over vol", "bat temp error", "timer error", ++}; ++ ++struct rk818_battery { ++ struct platform_device *pdev; ++ struct rk808 *rk818; ++ struct regmap *regmap; ++ struct device *dev; ++ struct power_supply *bat; ++ struct power_supply *usb_psy; ++ struct power_supply *ac_psy; ++ struct battery_platform_data *pdata; ++ struct workqueue_struct *bat_monitor_wq; ++ struct delayed_work bat_delay_work; ++ struct delayed_work calib_delay_work; ++ // struct wake_lock wake_lock; ++ struct notifier_block fb_nb; ++ struct timer_list caltimer; ++ time64_t rtc_base; ++ int bat_res; ++ int chrg_status; ++ bool is_initialized; ++ bool is_first_power_on; ++ u8 res_div; ++ int current_max; ++ int voltage_max; ++ int current_avg; ++ int voltage_avg; ++ int voltage_ocv; ++ int voltage_relax; ++ int voltage_k; ++ int voltage_b; ++ int remain_cap; ++ int design_cap; ++ int nac; ++ int fcc; ++ int qmax; ++ int dsoc; ++ int rsoc; ++ int poffset; ++ int age_ocv_soc; ++ bool age_allow_update; ++ int age_level; ++ int age_ocv_cap; ++ int age_voltage; ++ int age_adjust_cap; ++ unsigned long age_keep_sec; ++ int zero_timeout_cnt; ++ int zero_remain_cap; ++ int zero_dsoc; ++ int zero_linek; ++ u64 zero_drop_sec; ++ u64 shtd_drop_sec; ++ int sm_remain_cap; ++ int sm_linek; ++ int sm_chrg_dsoc; ++ int sm_dischrg_dsoc; ++ int algo_rest_val; ++ int algo_rest_mode; ++ int sleep_sum_cap; ++ int sleep_remain_cap; ++ unsigned long sleep_dischrg_sec; ++ unsigned long sleep_sum_sec; ++ bool sleep_chrg_online; ++ u8 sleep_chrg_status; ++ bool adc_allow_update; ++ int fb_blank; ++ bool s2r; /*suspend to resume*/ ++ u32 work_mode; ++ int temperature; ++ u32 monitor_ms; ++ u32 pwroff_min; ++ u32 adc_calib_cnt; ++ unsigned long finish_base; ++ unsigned long boot_base; ++ unsigned long flat_match_sec; ++ unsigned long plug_in_base; ++ unsigned long plug_out_base; ++ u8 halt_cnt; ++ bool is_halt; ++ bool is_max_soc_offset; ++ bool is_sw_reset; ++ bool is_ocv_calib; ++ bool is_first_on; ++ bool is_force_calib; ++ int last_dsoc; ++ int ocv_pre_dsoc; ++ int ocv_new_dsoc; ++ int max_pre_dsoc; ++ int max_new_dsoc; ++ int force_pre_dsoc; ++ int force_new_dsoc; ++ int dbg_cap_low0; ++ int dbg_pwr_dsoc; ++ int dbg_pwr_rsoc; ++ int dbg_pwr_vol; ++ int dbg_chrg_min[10]; ++ int dbg_meet_soc; ++ int dbg_calc_dsoc; ++ int dbg_calc_rsoc; ++ u8 ac_in; ++ u8 usb_in; ++ int is_charging; ++ unsigned long charge_count; ++}; ++ ++#define DIV(x) ((x) ? (x) : 1) ++ ++static void rk_send_wakeup_key(void) ++{ ++ // TODO: WHAT TO DO HERE? ++} ++ ++static u64 get_boot_sec(void) ++{ ++ struct timespec64 ts; ++ ++ ktime_get_boottime_ts64(&ts); ++ ++ return ts.tv_sec; ++} ++ ++static unsigned long base2sec(unsigned long x) ++{ ++ if (x) ++ return (get_boot_sec() > x) ? (get_boot_sec() - x) : 0; ++ else ++ return 0; ++} ++ ++static unsigned long base2min(unsigned long x) ++{ ++ return base2sec(x) / 60; ++} ++ ++static u32 interpolate(int value, u32 *table, int size) ++{ ++ u8 i; ++ u16 d; ++ ++ for (i = 0; i < size; i++) { ++ if (value < table[i]) ++ break; ++ } ++ ++ if ((i > 0) && (i < size)) { ++ d = (value - table[i - 1]) * (MAX_INTERPOLATE / (size - 1)); ++ d /= table[i] - table[i - 1]; ++ d = d + (i - 1) * (MAX_INTERPOLATE / (size - 1)); ++ } else { ++ d = i * ((MAX_INTERPOLATE + size / 2) / size); ++ } ++ ++ if (d > 1000) ++ d = 1000; ++ ++ return d; ++} ++ ++/* (a*b)/c */ ++static int32_t ab_div_c(u32 a, u32 b, u32 c) ++{ ++ bool sign; ++ u32 ans = MAX_INT; ++ int tmp; ++ ++ sign = ((((a ^ b) ^ c) & 0x80000000) != 0); ++ if (c != 0) { ++ if (sign) ++ c = -c; ++ tmp = (a * b + (c >> 1)) / c; ++ if (tmp < MAX_INT) ++ ans = tmp; ++ } ++ ++ if (sign) ++ ans = -ans; ++ ++ return ans; ++} ++ ++static int rk818_bat_read(struct rk818_battery *di, u8 reg) ++{ ++ int ret, val; ++ ++ ret = regmap_read(di->regmap, reg, &val); ++ if (ret) ++ dev_err(di->dev, "read reg:0x%x failed\n", reg); ++ ++ return val; ++} ++ ++static int rk818_bat_write(struct rk818_battery *di, u8 reg, u8 buf) ++{ ++ int ret; ++ ++ ret = regmap_write(di->regmap, reg, buf); ++ if (ret) ++ dev_err(di->dev, "i2c write reg: 0x%2x error\n", reg); ++ ++ return ret; ++} ++ ++static int rk818_bat_set_bits(struct rk818_battery *di, u8 reg, u8 mask, u8 buf) ++{ ++ int ret; ++ ++ ret = regmap_update_bits(di->regmap, reg, mask, buf); ++ if (ret) ++ dev_err(di->dev, "write reg:0x%x failed\n", reg); ++ ++ return ret; ++} ++ ++static int rk818_bat_clear_bits(struct rk818_battery *di, u8 reg, u8 mask) ++{ ++ int ret; ++ ++ ret = regmap_update_bits(di->regmap, reg, mask, 0); ++ if (ret) ++ dev_err(di->dev, "clr reg:0x%02x failed\n", reg); ++ ++ return ret; ++} ++ ++static void rk818_bat_dump_regs(struct rk818_battery *di, u8 start, u8 end) ++{ ++ int i; ++ ++ if (!dbg_enable) ++ return; ++ ++ DBG("dump regs from: 0x%x-->0x%x\n", start, end); ++ for (i = start; i < end; i++) ++ DBG("0x%x: 0x%0x\n", i, rk818_bat_read(di, i)); ++} ++ ++static bool rk818_bat_chrg_online(struct rk818_battery *di) ++{ ++ u8 buf; ++ ++ buf = rk818_bat_read(di, RK818_VB_MON_REG); ++ ++ return (buf & PLUG_IN_STS) ? true : false; ++} ++ ++static int rk818_bat_get_coulomb_cap(struct rk818_battery *di) ++{ ++ int val = 0; ++ ++ val |= rk818_bat_read(di, RK818_GASCNT3_REG) << 24; ++ val |= rk818_bat_read(di, RK818_GASCNT2_REG) << 16; ++ val |= rk818_bat_read(di, RK818_GASCNT1_REG) << 8; ++ val |= rk818_bat_read(di, RK818_GASCNT0_REG) << 0; ++ ++ return (val / 2390) * di->res_div; ++} ++ ++static int rk818_bat_get_rsoc(struct rk818_battery *di) ++{ ++ int remain_cap; ++ ++ remain_cap = rk818_bat_get_coulomb_cap(di); ++ return (remain_cap + di->fcc / 200) * 100 / DIV(di->fcc); ++} ++ ++static ssize_t bat_info_store(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ char cmd; ++ struct rk818_battery *di = dev_get_drvdata(dev); ++ ++ sscanf(buf, "%c", &cmd); ++ ++ if (cmd == 'n') ++ rk818_bat_set_bits(di, RK818_MISC_MARK_REG, ++ FG_RESET_NOW, FG_RESET_NOW); ++ else if (cmd == 'm') ++ rk818_bat_set_bits(di, RK818_MISC_MARK_REG, ++ FG_RESET_LATE, FG_RESET_LATE); ++ else if (cmd == 'c') ++ rk818_bat_clear_bits(di, RK818_MISC_MARK_REG, ++ FG_RESET_LATE | FG_RESET_NOW); ++ else if (cmd == 'r') ++ BAT_INFO("0x%2x\n", rk818_bat_read(di, RK818_MISC_MARK_REG)); ++ else ++ BAT_INFO("command error\n"); ++ ++ return count; ++} ++ ++static struct device_attribute rk818_bat_attr[] = { ++ __ATTR(bat, 0664, NULL, bat_info_store), ++}; ++ ++static void rk818_bat_enable_gauge(struct rk818_battery *di) ++{ ++ u8 buf; ++ ++ buf = rk818_bat_read(di, RK818_TS_CTRL_REG); ++ buf |= GG_EN; ++ rk818_bat_write(di, RK818_TS_CTRL_REG, buf); ++} ++ ++static void rk818_bat_save_age_level(struct rk818_battery *di, u8 level) ++{ ++ rk818_bat_write(di, RK818_UPDAT_LEVE_REG, level); ++} ++ ++static u8 rk818_bat_get_age_level(struct rk818_battery *di) ++{ ++ return rk818_bat_read(di, RK818_UPDAT_LEVE_REG); ++} ++ ++static int rk818_bat_get_vcalib0(struct rk818_battery *di) ++{ ++ int val = 0; ++ ++ val |= rk818_bat_read(di, RK818_VCALIB0_REGL) << 0; ++ val |= rk818_bat_read(di, RK818_VCALIB0_REGH) << 8; ++ ++ DBG("<%s>. voffset0: 0x%x\n", __func__, val); ++ return val; ++} ++ ++static int rk818_bat_get_vcalib1(struct rk818_battery *di) ++{ ++ int val = 0; ++ ++ val |= rk818_bat_read(di, RK818_VCALIB1_REGL) << 0; ++ val |= rk818_bat_read(di, RK818_VCALIB1_REGH) << 8; ++ ++ DBG("<%s>. voffset1: 0x%x\n", __func__, val); ++ return val; ++} ++ ++static int rk818_bat_get_ioffset(struct rk818_battery *di) ++{ ++ int val = 0; ++ ++ val |= rk818_bat_read(di, RK818_IOFFSET_REGL) << 0; ++ val |= rk818_bat_read(di, RK818_IOFFSET_REGH) << 8; ++ ++ DBG("<%s>. ioffset: 0x%x\n", __func__, val); ++ return val; ++} ++ ++static int rk818_bat_get_coffset(struct rk818_battery *di) ++{ ++ int val = 0; ++ ++ val |= rk818_bat_read(di, RK818_CAL_OFFSET_REGL) << 0; ++ val |= rk818_bat_read(di, RK818_CAL_OFFSET_REGH) << 8; ++ ++ DBG("<%s>. coffset: 0x%x\n", __func__, val); ++ return val; ++} ++ ++static void rk818_bat_set_coffset(struct rk818_battery *di, int val) ++{ ++ u8 buf; ++ ++ if ((val < INVALID_COFFSET_MIN) || (val > INVALID_COFFSET_MAX)) { ++ BAT_INFO("set invalid coffset=0x%x\n", val); ++ return; ++ } ++ ++ buf = (val >> 8) & 0xff; ++ rk818_bat_write(di, RK818_CAL_OFFSET_REGH, buf); ++ buf = (val >> 0) & 0xff; ++ rk818_bat_write(di, RK818_CAL_OFFSET_REGL, buf); ++ DBG("<%s>. coffset: 0x%x\n", __func__, val); ++} ++ ++static void rk818_bat_init_voltage_kb(struct rk818_battery *di) ++{ ++ int vcalib0, vcalib1; ++ ++ vcalib0 = rk818_bat_get_vcalib0(di); ++ vcalib1 = rk818_bat_get_vcalib1(di); ++ di->voltage_k = (4200 - 3000) * 1000 / DIV(vcalib1 - vcalib0); ++ di->voltage_b = 4200 - (di->voltage_k * vcalib1) / 1000; ++ ++ DBG("voltage_k=%d(*1000),voltage_b=%d\n", di->voltage_k, di->voltage_b); ++} ++ ++static int rk818_bat_get_ocv_voltage(struct rk818_battery *di) ++{ ++ int vol, val = 0; ++ ++ val |= rk818_bat_read(di, RK818_BAT_OCV_REGL) << 0; ++ val |= rk818_bat_read(di, RK818_BAT_OCV_REGH) << 8; ++ ++ vol = di->voltage_k * val / 1000 + di->voltage_b; ++ ++ return vol; ++} ++ ++static int rk818_bat_get_avg_voltage(struct rk818_battery *di) ++{ ++ int vol, val = 0; ++ ++ val |= rk818_bat_read(di, RK818_BAT_VOL_REGL) << 0; ++ val |= rk818_bat_read(di, RK818_BAT_VOL_REGH) << 8; ++ ++ vol = di->voltage_k * val / 1000 + di->voltage_b; ++ ++ return vol; ++} ++ ++static bool is_rk818_bat_relax_mode(struct rk818_battery *di) ++{ ++ u8 status; ++ ++ status = rk818_bat_read(di, RK818_GGSTS_REG); ++ if (!(status & RELAX_VOL1_UPD) || !(status & RELAX_VOL2_UPD)) ++ return false; ++ else ++ return true; ++} ++ ++static u16 rk818_bat_get_relax_vol1(struct rk818_battery *di) ++{ ++ u16 vol, val = 0; ++ ++ val |= rk818_bat_read(di, RK818_RELAX_VOL1_REGL) << 0; ++ val |= rk818_bat_read(di, RK818_RELAX_VOL1_REGH) << 8; ++ vol = di->voltage_k * val / 1000 + di->voltage_b; ++ ++ return vol; ++} ++ ++static u16 rk818_bat_get_relax_vol2(struct rk818_battery *di) ++{ ++ u16 vol, val = 0; ++ ++ val |= rk818_bat_read(di, RK818_RELAX_VOL2_REGL) << 0; ++ val |= rk818_bat_read(di, RK818_RELAX_VOL2_REGH) << 8; ++ vol = di->voltage_k * val / 1000 + di->voltage_b; ++ ++ return vol; ++} ++ ++static u16 rk818_bat_get_relax_voltage(struct rk818_battery *di) ++{ ++ u16 relax_vol1, relax_vol2; ++ ++ if (!is_rk818_bat_relax_mode(di)) ++ return 0; ++ ++ relax_vol1 = rk818_bat_get_relax_vol1(di); ++ relax_vol2 = rk818_bat_get_relax_vol2(di); ++ ++ return relax_vol1 > relax_vol2 ? relax_vol1 : relax_vol2; ++} ++ ++static int rk818_bat_get_avg_current(struct rk818_battery *di) ++{ ++ int cur, val = 0; ++ ++ val |= rk818_bat_read(di, RK818_BAT_CUR_AVG_REGL) << 0; ++ val |= rk818_bat_read(di, RK818_BAT_CUR_AVG_REGH) << 8; ++ ++ if (val & 0x800) ++ val -= 4096; ++ cur = val * di->res_div * 1506 / 1000; ++ ++ return cur; ++} ++ ++static int rk818_bat_vol_to_ocvsoc(struct rk818_battery *di, int voltage) ++{ ++ u32 *ocv_table, temp; ++ int ocv_size, ocv_soc; ++ ++ ocv_table = di->pdata->ocv_table; ++ ocv_size = di->pdata->ocv_size; ++ temp = interpolate(voltage, ocv_table, ocv_size); ++ ocv_soc = ab_div_c(temp, MAX_PERCENTAGE, MAX_INTERPOLATE); ++ ++ return ocv_soc; ++} ++ ++static int rk818_bat_vol_to_ocvcap(struct rk818_battery *di, int voltage) ++{ ++ u32 *ocv_table, temp; ++ int ocv_size, cap; ++ ++ ocv_table = di->pdata->ocv_table; ++ ocv_size = di->pdata->ocv_size; ++ temp = interpolate(voltage, ocv_table, ocv_size); ++ cap = ab_div_c(temp, di->fcc, MAX_INTERPOLATE); ++ ++ return cap; ++} ++ ++static int rk818_bat_vol_to_zerosoc(struct rk818_battery *di, int voltage) ++{ ++ u32 *ocv_table, temp; ++ int ocv_size, ocv_soc; ++ ++ ocv_table = di->pdata->zero_table; ++ ocv_size = di->pdata->ocv_size; ++ temp = interpolate(voltage, ocv_table, ocv_size); ++ ocv_soc = ab_div_c(temp, MAX_PERCENTAGE, MAX_INTERPOLATE); ++ ++ return ocv_soc; ++} ++ ++static int rk818_bat_vol_to_zerocap(struct rk818_battery *di, int voltage) ++{ ++ u32 *ocv_table, temp; ++ int ocv_size, cap; ++ ++ ocv_table = di->pdata->zero_table; ++ ocv_size = di->pdata->ocv_size; ++ temp = interpolate(voltage, ocv_table, ocv_size); ++ cap = ab_div_c(temp, di->fcc, MAX_INTERPOLATE); ++ ++ return cap; ++} ++ ++static int rk818_bat_get_iadc(struct rk818_battery *di) ++{ ++ int val = 0; ++ ++ val |= rk818_bat_read(di, RK818_BAT_CUR_AVG_REGL) << 0; ++ val |= rk818_bat_read(di, RK818_BAT_CUR_AVG_REGH) << 8; ++ if (val > 2047) ++ val -= 4096; ++ ++ return val; ++} ++ ++static bool rk818_bat_adc_calib(struct rk818_battery *di) ++{ ++ int i, ioffset, coffset, adc, save_coffset; ++ ++ if ((di->chrg_status != CHARGE_FINISH) || ++ (di->adc_calib_cnt > ADC_CALIB_CNT) || ++ (base2min(di->boot_base) < ADC_CALIB_LMT_MIN) || ++ (abs(di->current_avg) < ADC_CALIB_THRESHOLD)) ++ return false; ++ ++ di->adc_calib_cnt++; ++ save_coffset = rk818_bat_get_coffset(di); ++ for (i = 0; i < 5; i++) { ++ adc = rk818_bat_get_iadc(di); ++ if (!rk818_bat_chrg_online(di)) { ++ rk818_bat_set_coffset(di, save_coffset); ++ BAT_INFO("quit, charger plugout when calib adc\n"); ++ return false; ++ } ++ coffset = rk818_bat_get_coffset(di); ++ rk818_bat_set_coffset(di, coffset + adc); ++ msleep(2000); ++ adc = rk818_bat_get_iadc(di); ++ if (abs(adc) < ADC_CALIB_THRESHOLD) { ++ coffset = rk818_bat_get_coffset(di); ++ ioffset = rk818_bat_get_ioffset(di); ++ di->poffset = coffset - ioffset; ++ rk818_bat_write(di, RK818_POFFSET_REG, di->poffset); ++ BAT_INFO("new offset:c=0x%x, i=0x%x, p=0x%x\n", ++ coffset, ioffset, di->poffset); ++ return true; ++ } else { ++ BAT_INFO("coffset calib again %d.., max_cnt=%d\n", ++ i, di->adc_calib_cnt); ++ rk818_bat_set_coffset(di, coffset); ++ msleep(2000); ++ } ++ } ++ ++ rk818_bat_set_coffset(di, save_coffset); ++ ++ return false; ++} ++ ++static void rk818_bat_set_ioffset_sample(struct rk818_battery *di) ++{ ++ u8 ggcon; ++ ++ ggcon = rk818_bat_read(di, RK818_GGCON_REG); ++ ggcon &= ~ADC_CAL_MIN_MSK; ++ ggcon |= ADC_CAL_8MIN; ++ rk818_bat_write(di, RK818_GGCON_REG, ggcon); ++} ++ ++static void rk818_bat_set_ocv_sample(struct rk818_battery *di) ++{ ++ u8 ggcon; ++ ++ ggcon = rk818_bat_read(di, RK818_GGCON_REG); ++ ggcon &= ~OCV_SAMP_MIN_MSK; ++ ggcon |= OCV_SAMP_8MIN; ++ rk818_bat_write(di, RK818_GGCON_REG, ggcon); ++} ++ ++static void rk818_bat_restart_relax(struct rk818_battery *di) ++{ ++ u8 ggsts; ++ ++ ggsts = rk818_bat_read(di, RK818_GGSTS_REG); ++ ggsts &= ~RELAX_VOL12_UPD_MSK; ++ rk818_bat_write(di, RK818_GGSTS_REG, ggsts); ++} ++ ++static void rk818_bat_set_relax_sample(struct rk818_battery *di) ++{ ++ u8 buf; ++ int enter_thres, exit_thres; ++ struct battery_platform_data *pdata = di->pdata; ++ ++ enter_thres = pdata->sleep_enter_current * 1000 / 1506 / DIV(di->res_div); ++ exit_thres = pdata->sleep_exit_current * 1000 / 1506 / DIV(di->res_div); ++ ++ /* set relax enter and exit threshold */ ++ buf = enter_thres & 0xff; ++ rk818_bat_write(di, RK818_RELAX_ENTRY_THRES_REGL, buf); ++ buf = (enter_thres >> 8) & 0xff; ++ rk818_bat_write(di, RK818_RELAX_ENTRY_THRES_REGH, buf); ++ ++ buf = exit_thres & 0xff; ++ rk818_bat_write(di, RK818_RELAX_EXIT_THRES_REGL, buf); ++ buf = (exit_thres >> 8) & 0xff; ++ rk818_bat_write(di, RK818_RELAX_EXIT_THRES_REGH, buf); ++ ++ /* reset relax update state */ ++ rk818_bat_restart_relax(di); ++ DBG("<%s>. sleep_enter_current = %d, sleep_exit_current = %d\n", ++ __func__, pdata->sleep_enter_current, pdata->sleep_exit_current); ++} ++ ++static bool is_rk818_bat_exist(struct rk818_battery *di) ++{ ++ return (rk818_bat_read(di, RK818_SUP_STS_REG) & BAT_EXS) ? true : false; ++} ++ ++static bool is_rk818_bat_first_pwron(struct rk818_battery *di) ++{ ++ u8 buf; ++ ++ buf = rk818_bat_read(di, RK818_GGSTS_REG); ++ if (buf & BAT_CON) { ++ buf &= ~BAT_CON; ++ rk818_bat_write(di, RK818_GGSTS_REG, buf); ++ return true; ++ } ++ ++ return false; ++} ++ ++static u8 rk818_bat_get_pwroff_min(struct rk818_battery *di) ++{ ++ u8 cur, last; ++ ++ cur = rk818_bat_read(di, RK818_NON_ACT_TIMER_CNT_REG); ++ last = rk818_bat_read(di, RK818_NON_ACT_TIMER_CNT_SAVE_REG); ++ rk818_bat_write(di, RK818_NON_ACT_TIMER_CNT_SAVE_REG, cur); ++ ++ return (cur != last) ? cur : 0; ++} ++ ++static u8 is_rk818_bat_initialized(struct rk818_battery *di) ++{ ++ u8 val = rk818_bat_read(di, RK818_MISC_MARK_REG); ++ ++ if (val & FG_INIT) { ++ val &= ~FG_INIT; ++ rk818_bat_write(di, RK818_MISC_MARK_REG, val); ++ return true; ++ } else { ++ return false; ++ } ++} ++ ++static bool is_rk818_bat_ocv_valid(struct rk818_battery *di) ++{ ++ return (!di->is_initialized && di->pwroff_min >= 30) ? true : false; ++} ++ ++static void rk818_bat_init_age_algorithm(struct rk818_battery *di) ++{ ++ int age_level, ocv_soc, ocv_cap, ocv_vol; ++ ++ if (di->is_first_power_on || is_rk818_bat_ocv_valid(di)) { ++ DBG("<%s> enter.\n", __func__); ++ ocv_vol = rk818_bat_get_ocv_voltage(di); ++ ocv_soc = rk818_bat_vol_to_ocvsoc(di, ocv_vol); ++ ocv_cap = rk818_bat_vol_to_ocvcap(di, ocv_vol); ++ if (ocv_soc < 20) { ++ di->age_voltage = ocv_vol; ++ di->age_ocv_cap = ocv_cap; ++ di->age_ocv_soc = ocv_soc; ++ di->age_adjust_cap = 0; ++ ++ if (ocv_soc <= 0) ++ di->age_level = 100; ++ else if (ocv_soc < 5) ++ di->age_level = 95; ++ else if (ocv_soc < 10) ++ di->age_level = 90; ++ else ++ di->age_level = 80; ++ ++ age_level = rk818_bat_get_age_level(di); ++ if (age_level > di->age_level) { ++ di->age_allow_update = false; ++ age_level -= 5; ++ if (age_level <= 80) ++ age_level = 80; ++ rk818_bat_save_age_level(di, age_level); ++ } else { ++ di->age_allow_update = true; ++ di->age_keep_sec = get_boot_sec(); ++ } ++ ++ BAT_INFO("init_age_algorithm: " ++ "age_vol:%d, age_ocv_cap:%d, " ++ "age_ocv_soc:%d, old_age_level:%d, " ++ "age_allow_update:%d, new_age_level:%d\n", ++ di->age_voltage, di->age_ocv_cap, ++ ocv_soc, age_level, di->age_allow_update, ++ di->age_level); ++ } ++ } ++} ++ ++static enum power_supply_property rk818_bat_props[] = { ++ POWER_SUPPLY_PROP_CURRENT_NOW, ++ POWER_SUPPLY_PROP_VOLTAGE_NOW, ++ POWER_SUPPLY_PROP_PRESENT, ++ POWER_SUPPLY_PROP_HEALTH, ++ POWER_SUPPLY_PROP_CAPACITY, ++ POWER_SUPPLY_PROP_TEMP, ++ POWER_SUPPLY_PROP_STATUS, ++ POWER_SUPPLY_PROP_CHARGE_COUNTER, ++ POWER_SUPPLY_PROP_CHARGE_FULL, ++ POWER_SUPPLY_PROP_VOLTAGE_MAX, ++ POWER_SUPPLY_PROP_CURRENT_MAX, ++}; ++ ++static int rk818_bat_get_usb_psy(struct device *dev, void *data) ++{ ++ struct rk818_battery *di = data; ++ struct power_supply *psy = dev_get_drvdata(dev); ++ ++ if (psy->desc->type == POWER_SUPPLY_TYPE_USB) { ++ di->usb_psy = psy; ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static int rk818_bat_get_ac_psy(struct device *dev, void *data) ++{ ++ struct rk818_battery *di = data; ++ struct power_supply *psy = dev_get_drvdata(dev); ++ ++ if (psy->desc->type == POWER_SUPPLY_TYPE_MAINS) { ++ di->ac_psy = psy; ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static void rk818_bat_get_chrg_psy(struct rk818_battery *di) ++{ ++ if (!di->usb_psy) ++ class_for_each_device(power_supply_class, NULL, (void *)di, ++ rk818_bat_get_usb_psy); ++ if (!di->ac_psy) ++ class_for_each_device(power_supply_class, NULL, (void *)di, ++ rk818_bat_get_ac_psy); ++} ++ ++static int rk818_bat_get_charge_state(struct rk818_battery *di) ++{ ++ union power_supply_propval val; ++ int ret; ++ ++ if (!di->usb_psy || !di->ac_psy) ++ rk818_bat_get_chrg_psy(di); ++ ++ if (di->usb_psy) { ++ ret = di->usb_psy->desc->get_property(di->usb_psy, ++ POWER_SUPPLY_PROP_ONLINE, ++ &val); ++ if (!ret) ++ di->usb_in = val.intval; ++ } ++ ++ if (di->ac_psy) { ++ ret = di->ac_psy->desc->get_property(di->ac_psy, ++ POWER_SUPPLY_PROP_ONLINE, ++ &val); ++ if (!ret) ++ di->ac_in = val.intval; ++ } ++ ++ DBG("%s: ac_online=%d, usb_online=%d\n", ++ __func__, di->ac_in, di->usb_in); ++ ++ return (di->usb_in || di->ac_in); ++} ++ ++static int rk818_battery_get_property(struct power_supply *psy, ++ enum power_supply_property psp, ++ union power_supply_propval *val) ++{ ++ struct rk818_battery *di = power_supply_get_drvdata(psy); ++ ++ switch (psp) { ++ case POWER_SUPPLY_PROP_CURRENT_NOW: ++ val->intval = di->current_avg * 1000;/*uA*/ ++ if (di->pdata->bat_mode == MODE_VIRTUAL) ++ val->intval = VIRTUAL_CURRENT * 1000; ++ break; ++ case POWER_SUPPLY_PROP_VOLTAGE_NOW: ++ val->intval = di->voltage_avg * 1000;/*uV*/ ++ if (di->pdata->bat_mode == MODE_VIRTUAL) ++ val->intval = VIRTUAL_VOLTAGE * 1000; ++ break; ++ case POWER_SUPPLY_PROP_PRESENT: ++ val->intval = is_rk818_bat_exist(di); ++ if (di->pdata->bat_mode == MODE_VIRTUAL) ++ val->intval = VIRTUAL_PRESET; ++ break; ++ case POWER_SUPPLY_PROP_CAPACITY: ++ val->intval = di->dsoc; ++ if (di->pdata->bat_mode == MODE_VIRTUAL) ++ val->intval = VIRTUAL_SOC; ++ DBG("<%s>. report dsoc: %d\n", __func__, val->intval); ++ break; ++ case POWER_SUPPLY_PROP_HEALTH: ++ val->intval = POWER_SUPPLY_HEALTH_GOOD; ++ break; ++ case POWER_SUPPLY_PROP_TEMP: ++ val->intval = di->temperature; ++ if (di->pdata->bat_mode == MODE_VIRTUAL) ++ val->intval = VIRTUAL_TEMPERATURE; ++ break; ++ case POWER_SUPPLY_PROP_STATUS: ++ if (di->pdata->bat_mode == MODE_VIRTUAL) ++ val->intval = VIRTUAL_STATUS; ++ else if (di->dsoc == 100) ++ val->intval = POWER_SUPPLY_STATUS_FULL; ++ else if (rk818_bat_get_charge_state(di)) ++ val->intval = POWER_SUPPLY_STATUS_CHARGING; ++ else ++ val->intval = POWER_SUPPLY_STATUS_DISCHARGING; ++ break; ++ case POWER_SUPPLY_PROP_CHARGE_COUNTER: ++ val->intval = di->charge_count; ++ break; ++ case POWER_SUPPLY_PROP_CHARGE_FULL: ++ val->intval = di->pdata->design_capacity * 1000;/* uAh */ ++ break; ++ case POWER_SUPPLY_PROP_VOLTAGE_MAX: ++ val->intval = di->voltage_max * 1000; /* uV */ ++ break; ++ case POWER_SUPPLY_PROP_CURRENT_MAX: ++ val->intval = di->current_max * 1000; /* uA */ ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static const struct power_supply_desc rk818_bat_desc = { ++ .name = "battery", ++ .type = POWER_SUPPLY_TYPE_BATTERY, ++ .properties = rk818_bat_props, ++ .num_properties = ARRAY_SIZE(rk818_bat_props), ++ .get_property = rk818_battery_get_property, ++}; ++ ++static int rk818_bat_init_power_supply(struct rk818_battery *di) ++{ ++ struct power_supply_config psy_cfg = { .drv_data = di, }; ++ ++ di->bat = devm_power_supply_register(di->dev, &rk818_bat_desc, &psy_cfg); ++ if (IS_ERR(di->bat)) { ++ dev_err(di->dev, "register bat power supply fail\n"); ++ return PTR_ERR(di->bat); ++ } ++ ++ return 0; ++} ++ ++static void rk818_bat_save_cap(struct rk818_battery *di, int cap) ++{ ++ u8 buf; ++ static u32 old_cap; ++ ++ if (cap >= di->qmax) ++ cap = di->qmax; ++ if (cap <= 0) ++ cap = 0; ++ if (old_cap == cap) ++ return; ++ ++ old_cap = cap; ++ buf = (cap >> 24) & 0xff; ++ rk818_bat_write(di, RK818_REMAIN_CAP_REG3, buf); ++ buf = (cap >> 16) & 0xff; ++ rk818_bat_write(di, RK818_REMAIN_CAP_REG2, buf); ++ buf = (cap >> 8) & 0xff; ++ rk818_bat_write(di, RK818_REMAIN_CAP_REG1, buf); ++ buf = (cap >> 0) & 0xff; ++ rk818_bat_write(di, RK818_REMAIN_CAP_REG0, buf); ++} ++ ++static int rk818_bat_get_prev_cap(struct rk818_battery *di) ++{ ++ int val = 0; ++ ++ val |= rk818_bat_read(di, RK818_REMAIN_CAP_REG3) << 24; ++ val |= rk818_bat_read(di, RK818_REMAIN_CAP_REG2) << 16; ++ val |= rk818_bat_read(di, RK818_REMAIN_CAP_REG1) << 8; ++ val |= rk818_bat_read(di, RK818_REMAIN_CAP_REG0) << 0; ++ ++ return val; ++} ++ ++static void rk818_bat_save_fcc(struct rk818_battery *di, u32 fcc) ++{ ++ u8 buf; ++ ++ buf = (fcc >> 24) & 0xff; ++ rk818_bat_write(di, RK818_NEW_FCC_REG3, buf); ++ buf = (fcc >> 16) & 0xff; ++ rk818_bat_write(di, RK818_NEW_FCC_REG2, buf); ++ buf = (fcc >> 8) & 0xff; ++ rk818_bat_write(di, RK818_NEW_FCC_REG1, buf); ++ buf = (fcc >> 0) & 0xff; ++ rk818_bat_write(di, RK818_NEW_FCC_REG0, buf); ++ ++ BAT_INFO("save fcc: %d\n", fcc); ++} ++ ++static int rk818_bat_get_fcc(struct rk818_battery *di) ++{ ++ u32 fcc = 0; ++ ++ fcc |= rk818_bat_read(di, RK818_NEW_FCC_REG3) << 24; ++ fcc |= rk818_bat_read(di, RK818_NEW_FCC_REG2) << 16; ++ fcc |= rk818_bat_read(di, RK818_NEW_FCC_REG1) << 8; ++ fcc |= rk818_bat_read(di, RK818_NEW_FCC_REG0) << 0; ++ ++ if (fcc < MIN_FCC) { ++ BAT_INFO("invalid fcc(%d), use design cap", fcc); ++ fcc = di->pdata->design_capacity; ++ rk818_bat_save_fcc(di, fcc); ++ } else if (fcc > di->pdata->design_qmax) { ++ BAT_INFO("invalid fcc(%d), use qmax", fcc); ++ fcc = di->pdata->design_qmax; ++ rk818_bat_save_fcc(di, fcc); ++ } ++ ++ return fcc; ++} ++ ++static void rk818_bat_init_coulomb_cap(struct rk818_battery *di, u32 capacity) ++{ ++ u8 buf; ++ u32 cap; ++ ++ cap = capacity * 2390 / DIV(di->res_div); ++ buf = (cap >> 24) & 0xff; ++ rk818_bat_write(di, RK818_GASCNT_CAL_REG3, buf); ++ buf = (cap >> 16) & 0xff; ++ rk818_bat_write(di, RK818_GASCNT_CAL_REG2, buf); ++ buf = (cap >> 8) & 0xff; ++ rk818_bat_write(di, RK818_GASCNT_CAL_REG1, buf); ++ buf = ((cap >> 0) & 0xff); ++ rk818_bat_write(di, RK818_GASCNT_CAL_REG0, buf); ++ ++ DBG("<%s>. new coulomb cap = %d\n", __func__, capacity); ++ di->remain_cap = capacity; ++ di->rsoc = rk818_bat_get_rsoc(di); ++} ++ ++static void rk818_bat_save_dsoc(struct rk818_battery *di, u8 save_soc) ++{ ++ static int last_soc = -1; ++ ++ if (last_soc != save_soc) { ++ rk818_bat_write(di, RK818_SOC_REG, save_soc); ++ last_soc = save_soc; ++ } ++} ++ ++static int rk818_bat_get_prev_dsoc(struct rk818_battery *di) ++{ ++ return rk818_bat_read(di, RK818_SOC_REG); ++} ++ ++static void rk818_bat_save_reboot_cnt(struct rk818_battery *di, u8 save_cnt) ++{ ++ rk818_bat_write(di, RK818_REBOOT_CNT_REG, save_cnt); ++} ++ ++static int rk818_bat_fb_notifier(struct notifier_block *nb, ++ unsigned long event, void *data) ++{ ++ struct rk818_battery *di; ++ struct fb_event *evdata = data; ++ ++ if (event != FB_EARLY_EVENT_BLANK && event != FB_EVENT_BLANK) ++ return NOTIFY_OK; ++ ++ di = container_of(nb, struct rk818_battery, fb_nb); ++ di->fb_blank = *(int *)evdata->data; ++ ++ return 0; ++} ++ ++static int rk818_bat_register_fb_notify(struct rk818_battery *di) ++{ ++ memset(&di->fb_nb, 0, sizeof(di->fb_nb)); ++ di->fb_nb.notifier_call = rk818_bat_fb_notifier; ++ ++ return fb_register_client(&di->fb_nb); ++} ++ ++static int rk818_bat_unregister_fb_notify(struct rk818_battery *di) ++{ ++ return fb_unregister_client(&di->fb_nb); ++} ++ ++static u8 rk818_bat_get_halt_cnt(struct rk818_battery *di) ++{ ++ return rk818_bat_read(di, RK818_HALT_CNT_REG); ++} ++ ++static void rk818_bat_inc_halt_cnt(struct rk818_battery *di) ++{ ++ u8 cnt; ++ ++ cnt = rk818_bat_read(di, RK818_HALT_CNT_REG); ++ rk818_bat_write(di, RK818_HALT_CNT_REG, ++cnt); ++} ++ ++static bool is_rk818_bat_last_halt(struct rk818_battery *di) ++{ ++ int pre_cap = rk818_bat_get_prev_cap(di); ++ int now_cap = rk818_bat_get_coulomb_cap(di); ++ ++ /* over 10%: system halt last time */ ++ if (abs(now_cap - pre_cap) > (di->fcc / 10)) { ++ rk818_bat_inc_halt_cnt(di); ++ return true; ++ } else { ++ return false; ++ } ++} ++ ++static void rk818_bat_first_pwron(struct rk818_battery *di) ++{ ++ int ocv_vol; ++ ++ rk818_bat_save_fcc(di, di->design_cap); ++ ocv_vol = rk818_bat_get_ocv_voltage(di); ++ di->fcc = rk818_bat_get_fcc(di); ++ di->nac = rk818_bat_vol_to_ocvcap(di, ocv_vol); ++ di->rsoc = rk818_bat_vol_to_ocvsoc(di, ocv_vol); ++ di->dsoc = di->rsoc; ++ di->is_first_on = true; ++ ++ BAT_INFO("first on: dsoc=%d, rsoc=%d cap=%d, fcc=%d, ov=%d\n", ++ di->dsoc, di->rsoc, di->nac, di->fcc, ocv_vol); ++} ++ ++static void rk818_bat_not_first_pwron(struct rk818_battery *di) ++{ ++ int now_cap, pre_soc, pre_cap, ocv_cap, ocv_soc, ocv_vol; ++ ++ di->fcc = rk818_bat_get_fcc(di); ++ pre_soc = rk818_bat_get_prev_dsoc(di); ++ pre_cap = rk818_bat_get_prev_cap(di); ++ now_cap = rk818_bat_get_coulomb_cap(di); ++ di->is_halt = is_rk818_bat_last_halt(di); ++ di->halt_cnt = rk818_bat_get_halt_cnt(di); ++ di->is_initialized = is_rk818_bat_initialized(di); ++ di->is_ocv_calib = is_rk818_bat_ocv_valid(di); ++ ++ if (di->is_initialized) { ++ BAT_INFO("initialized yet..\n"); ++ goto finish; ++ } else if (di->is_halt) { ++ BAT_INFO("system halt last time... cap: pre=%d, now=%d\n", ++ pre_cap, now_cap); ++ if (now_cap < 0) ++ now_cap = 0; ++ rk818_bat_init_coulomb_cap(di, now_cap); ++ pre_cap = now_cap; ++ pre_soc = di->rsoc; ++ goto finish; ++ } else if (di->is_ocv_calib) { ++ ocv_vol = rk818_bat_get_ocv_voltage(di); ++ ocv_soc = rk818_bat_vol_to_ocvsoc(di, ocv_vol); ++ ocv_cap = rk818_bat_vol_to_ocvcap(di, ocv_vol); ++ pre_cap = ocv_cap; ++ di->ocv_pre_dsoc = pre_soc; ++ di->ocv_new_dsoc = ocv_soc; ++ if (abs(ocv_soc - pre_soc) >= di->pdata->max_soc_offset) { ++ di->ocv_pre_dsoc = pre_soc; ++ di->ocv_new_dsoc = ocv_soc; ++ di->is_max_soc_offset = true; ++ BAT_INFO("trigger max soc offset, dsoc: %d -> %d\n", ++ pre_soc, ocv_soc); ++ pre_soc = ocv_soc; ++ } ++ BAT_INFO("OCV calib: cap=%d, rsoc=%d\n", ocv_cap, ocv_soc); ++ } else if (di->pwroff_min > 0) { ++ ocv_vol = rk818_bat_get_ocv_voltage(di); ++ ocv_soc = rk818_bat_vol_to_ocvsoc(di, ocv_vol); ++ ocv_cap = rk818_bat_vol_to_ocvcap(di, ocv_vol); ++ di->force_pre_dsoc = pre_soc; ++ di->force_new_dsoc = ocv_soc; ++ if (abs(ocv_soc - pre_soc) >= 80) { ++ di->is_force_calib = true; ++ BAT_INFO("dsoc force calib: %d -> %d\n", ++ pre_soc, ocv_soc); ++ pre_soc = ocv_soc; ++ pre_cap = ocv_cap; ++ } ++ } ++ ++finish: ++ di->dsoc = pre_soc; ++ di->nac = pre_cap; ++ if (di->nac < 0) ++ di->nac = 0; ++ ++ BAT_INFO("dsoc=%d cap=%d v=%d ov=%d rv=%d min=%d psoc=%d pcap=%d\n", ++ di->dsoc, di->nac, rk818_bat_get_avg_voltage(di), ++ rk818_bat_get_ocv_voltage(di), rk818_bat_get_relax_voltage(di), ++ di->pwroff_min, rk818_bat_get_prev_dsoc(di), ++ rk818_bat_get_prev_cap(di)); ++} ++ ++static bool rk818_bat_ocv_sw_reset(struct rk818_battery *di) ++{ ++ u8 buf; ++ ++ buf = rk818_bat_read(di, RK818_MISC_MARK_REG); ++ if (((buf & FG_RESET_LATE) && di->pwroff_min >= 30) || ++ (buf & FG_RESET_NOW)) { ++ buf &= ~FG_RESET_LATE; ++ buf &= ~FG_RESET_NOW; ++ rk818_bat_write(di, RK818_MISC_MARK_REG, buf); ++ BAT_INFO("manual reset fuel gauge\n"); ++ return true; ++ } else { ++ return false; ++ } ++} ++ ++static void rk818_bat_init_rsoc(struct rk818_battery *di) ++{ ++ di->is_first_power_on = is_rk818_bat_first_pwron(di); ++ di->is_sw_reset = rk818_bat_ocv_sw_reset(di); ++ di->pwroff_min = rk818_bat_get_pwroff_min(di); ++ ++ if (di->is_first_power_on || di->is_sw_reset) ++ rk818_bat_first_pwron(di); ++ else ++ rk818_bat_not_first_pwron(di); ++} ++ ++static u8 rk818_bat_get_chrg_status(struct rk818_battery *di) ++{ ++ u8 status; ++ ++ status = rk818_bat_read(di, RK818_SUP_STS_REG) & CHRG_STATUS_MSK; ++ switch (status) { ++ case CHARGE_OFF: ++ DBG("CHARGE-OFF ...\n"); ++ break; ++ case DEAD_CHARGE: ++ BAT_INFO("DEAD CHARGE...\n"); ++ break; ++ case TRICKLE_CHARGE: ++ BAT_INFO("TRICKLE CHARGE...\n "); ++ break; ++ case CC_OR_CV: ++ DBG("CC or CV...\n"); ++ break; ++ case CHARGE_FINISH: ++ DBG("CHARGE FINISH...\n"); ++ break; ++ case USB_OVER_VOL: ++ BAT_INFO("USB OVER VOL...\n"); ++ break; ++ case BAT_TMP_ERR: ++ BAT_INFO("BAT TMP ERROR...\n"); ++ break; ++ case TIMER_ERR: ++ BAT_INFO("TIMER ERROR...\n"); ++ break; ++ case USB_EXIST: ++ BAT_INFO("USB EXIST...\n"); ++ break; ++ case USB_EFF: ++ BAT_INFO("USB EFF...\n"); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return status; ++} ++ ++static u8 rk818_bat_parse_fb_temperature(struct rk818_battery *di) ++{ ++ u8 reg; ++ int index, fb_temp; ++ ++ reg = DEFAULT_FB_TEMP; ++ fb_temp = di->pdata->fb_temp; ++ for (index = 0; index < ARRAY_SIZE(feedback_temp_array); index++) { ++ if (fb_temp < feedback_temp_array[index]) ++ break; ++ reg = (index << FB_TEMP_SHIFT); ++ } ++ ++ return reg; ++} ++ ++static u8 rk818_bat_parse_finish_ma(struct rk818_battery *di, int fcc) ++{ ++ u8 ma; ++ ++ if (di->pdata->sample_res == SAMPLE_RES_10MR) ++ ma = FINISH_100MA; ++ else if (fcc > 5000) ++ ma = FINISH_250MA; ++ else if (fcc >= 4000) ++ ma = FINISH_200MA; ++ else if (fcc >= 3000) ++ ma = FINISH_150MA; ++ else ++ ma = FINISH_100MA; ++ ++ return ma; ++} ++ ++static void rk818_bat_init_chrg_config(struct rk818_battery *di) ++{ ++ u8 usb_ctrl, chrg_ctrl2, chrg_ctrl3; ++ u8 thermal, ggcon, finish_ma, fb_temp; ++ ++ finish_ma = rk818_bat_parse_finish_ma(di, di->fcc); ++ fb_temp = rk818_bat_parse_fb_temperature(di); ++ ++ ggcon = rk818_bat_read(di, RK818_GGCON_REG); ++ thermal = rk818_bat_read(di, RK818_THERMAL_REG); ++ usb_ctrl = rk818_bat_read(di, RK818_USB_CTRL_REG); ++ chrg_ctrl2 = rk818_bat_read(di, RK818_CHRG_CTRL_REG2); ++ chrg_ctrl3 = rk818_bat_read(di, RK818_CHRG_CTRL_REG3); ++ ++ /* set charge finish current */ ++ chrg_ctrl3 |= CHRG_TERM_DIG_SIGNAL; ++ chrg_ctrl2 &= ~FINISH_CUR_MSK; ++ chrg_ctrl2 |= finish_ma; ++ ++ /* disable cccv mode */ ++ chrg_ctrl3 &= ~CHRG_TIMER_CCCV_EN; ++ ++ /* set feed back temperature */ ++ if (di->pdata->fb_temp) ++ usb_ctrl |= CHRG_CT_EN; ++ else ++ usb_ctrl &= ~CHRG_CT_EN; ++ thermal &= ~FB_TEMP_MSK; ++ thermal |= fb_temp; ++ ++ /* adc current mode */ ++ ggcon |= ADC_CUR_MODE; ++ ++ rk818_bat_write(di, RK818_GGCON_REG, ggcon); ++ rk818_bat_write(di, RK818_THERMAL_REG, thermal); ++ rk818_bat_write(di, RK818_USB_CTRL_REG, usb_ctrl); ++ rk818_bat_write(di, RK818_CHRG_CTRL_REG2, chrg_ctrl2); ++ rk818_bat_write(di, RK818_CHRG_CTRL_REG3, chrg_ctrl3); ++} ++ ++static void rk818_bat_init_coffset(struct rk818_battery *di) ++{ ++ int coffset, ioffset; ++ ++ ioffset = rk818_bat_get_ioffset(di); ++ di->poffset = rk818_bat_read(di, RK818_POFFSET_REG); ++ if (!di->poffset) ++ di->poffset = DEFAULT_POFFSET; ++ ++ coffset = di->poffset + ioffset; ++ if (coffset < INVALID_COFFSET_MIN || coffset > INVALID_COFFSET_MAX) ++ coffset = DEFAULT_COFFSET; ++ ++ rk818_bat_set_coffset(di, coffset); ++ ++ DBG("<%s>. offset: p=0x%x, i=0x%x, c=0x%x\n", ++ __func__, di->poffset, ioffset, rk818_bat_get_coffset(di)); ++} ++ ++static void rk818_bat_caltimer_isr(struct timer_list *t) ++{ ++ struct rk818_battery *di = from_timer(di, t, caltimer); ++ ++ mod_timer(&di->caltimer, jiffies + MINUTE(8) * HZ); ++ queue_delayed_work(di->bat_monitor_wq, &di->calib_delay_work, ++ msecs_to_jiffies(10)); ++} ++ ++static void rk818_bat_internal_calib(struct work_struct *work) ++{ ++ int ioffset, poffset; ++ struct rk818_battery *di = container_of(work, ++ struct rk818_battery, calib_delay_work.work); ++ ++ /* calib coffset */ ++ poffset = rk818_bat_read(di, RK818_POFFSET_REG); ++ if (poffset) ++ di->poffset = poffset; ++ else ++ di->poffset = DEFAULT_POFFSET; ++ ++ ioffset = rk818_bat_get_ioffset(di); ++ rk818_bat_set_coffset(di, ioffset + di->poffset); ++ ++ /* calib voltage kb */ ++ rk818_bat_init_voltage_kb(di); ++ BAT_INFO("caltimer: ioffset=0x%x, coffset=0x%x, poffset=%d\n", ++ ioffset, rk818_bat_get_coffset(di), di->poffset); ++} ++ ++static void rk818_bat_init_caltimer(struct rk818_battery *di) ++{ ++ timer_setup(&di->caltimer, rk818_bat_caltimer_isr, 0); ++ di->caltimer.expires = jiffies + MINUTE(8) * HZ; ++ add_timer(&di->caltimer); ++ INIT_DELAYED_WORK(&di->calib_delay_work, rk818_bat_internal_calib); ++} ++ ++static void rk818_bat_init_zero_table(struct rk818_battery *di) ++{ ++ int i, diff, min, max; ++ size_t ocv_size, length; ++ ++ ocv_size = di->pdata->ocv_size; ++ length = sizeof(di->pdata->zero_table) * ocv_size; ++ di->pdata->zero_table = ++ devm_kzalloc(di->dev, length, GFP_KERNEL); ++ if (!di->pdata->zero_table) { ++ di->pdata->zero_table = di->pdata->ocv_table; ++ dev_err(di->dev, "malloc zero table fail\n"); ++ return; ++ } ++ ++ min = di->pdata->pwroff_vol, ++ max = di->pdata->ocv_table[ocv_size - 4]; ++ diff = (max - min) / DIV(ocv_size - 1); ++ for (i = 0; i < ocv_size; i++) ++ di->pdata->zero_table[i] = min + (i * diff); ++ ++ for (i = 0; i < ocv_size; i++) ++ DBG("zero[%d] = %d\n", i, di->pdata->zero_table[i]); ++ ++ for (i = 0; i < ocv_size; i++) ++ DBG("ocv[%d] = %d\n", i, di->pdata->ocv_table[i]); ++} ++ ++static void rk818_bat_calc_sm_linek(struct rk818_battery *di) ++{ ++ int linek, current_avg; ++ u8 diff, delta; ++ ++ delta = abs(di->dsoc - di->rsoc); ++ diff = delta * 3;/* speed:3/4 */ ++ current_avg = rk818_bat_get_avg_current(di); ++ if (current_avg >= 0) { ++ if (di->dsoc < di->rsoc) ++ linek = 1000 * (delta + diff) / DIV(diff); ++ else if (di->dsoc > di->rsoc) ++ linek = 1000 * diff / DIV(delta + diff); ++ else ++ linek = 1000; ++ di->dbg_meet_soc = (di->dsoc >= di->rsoc) ? ++ (di->dsoc + diff) : (di->rsoc + diff); ++ } else { ++ if (di->dsoc < di->rsoc) ++ linek = -1000 * diff / DIV(delta + diff); ++ else if (di->dsoc > di->rsoc) ++ linek = -1000 * (delta + diff) / DIV(diff); ++ else ++ linek = -1000; ++ di->dbg_meet_soc = (di->dsoc >= di->rsoc) ? ++ (di->dsoc - diff) : (di->rsoc - diff); ++ } ++ ++ di->sm_linek = linek; ++ di->sm_remain_cap = di->remain_cap; ++ di->dbg_calc_dsoc = di->dsoc; ++ di->dbg_calc_rsoc = di->rsoc; ++ ++ DBG("<%s>.diff=%d, k=%d, cur=%d\n", __func__, diff, linek, current_avg); ++} ++ ++static void rk818_bat_calc_zero_linek(struct rk818_battery *di) ++{ ++ int dead_voltage, ocv_voltage; ++ int voltage_avg, current_avg, vsys; ++ int ocv_cap, dead_cap, xsoc; ++ int ocv_soc, dead_soc; ++ int pwroff_vol; ++ int i, cnt, vol_old, vol_now; ++ int org_linek = 0, min_gap_xsoc; ++ ++ if ((abs(di->current_avg) < 500) && (di->dsoc > 10)) ++ pwroff_vol = di->pdata->pwroff_vol + 50; ++ else ++ pwroff_vol = di->pdata->pwroff_vol; ++ ++ do { ++ vol_old = rk818_bat_get_avg_voltage(di); ++ msleep(100); ++ vol_now = rk818_bat_get_avg_voltage(di); ++ cnt++; ++ } while ((vol_old == vol_now) && (cnt < 11)); ++ ++ voltage_avg = 0; ++ for (i = 0; i < 10; i++) { ++ voltage_avg += rk818_bat_get_avg_voltage(di); ++ msleep(100); ++ } ++ ++ /* calc estimate ocv voltage */ ++ voltage_avg /= 10; ++ current_avg = rk818_bat_get_avg_current(di); ++ vsys = voltage_avg + (current_avg * DEF_PWRPATH_RES) / 1000; ++ ++ DBG("ZERO0: shtd_vol: org = %d, now = %d, zero_reserve_dsoc = %d\n", ++ di->pdata->pwroff_vol, pwroff_vol, di->pdata->zero_reserve_dsoc); ++ ++ dead_voltage = pwroff_vol - current_avg * ++ (di->bat_res + DEF_PWRPATH_RES) / 1000; ++ ocv_voltage = voltage_avg - (current_avg * di->bat_res) / 1000; ++ DBG("ZERO0: dead_voltage(shtd) = %d, ocv_voltage(now) = %d\n", ++ dead_voltage, ocv_voltage); ++ ++ /* calc estimate soc and cap */ ++ dead_soc = rk818_bat_vol_to_zerosoc(di, dead_voltage); ++ dead_cap = rk818_bat_vol_to_zerocap(di, dead_voltage); ++ DBG("ZERO0: dead_soc = %d, dead_cap = %d\n", ++ dead_soc, dead_cap); ++ ++ ocv_soc = rk818_bat_vol_to_zerosoc(di, ocv_voltage); ++ ocv_cap = rk818_bat_vol_to_zerocap(di, ocv_voltage); ++ DBG("ZERO0: ocv_soc = %d, ocv_cap = %d\n", ++ ocv_soc, ocv_cap); ++ ++ /* xsoc: available rsoc */ ++ xsoc = ocv_soc - dead_soc; ++ ++ /* min_gap_xsoc: reserve xsoc */ ++ if (abs(current_avg) > ZERO_LOAD_LVL1) ++ min_gap_xsoc = ZERO_GAP_XSOC3; ++ else if (abs(current_avg) > ZERO_LOAD_LVL2) ++ min_gap_xsoc = ZERO_GAP_XSOC2; ++ else ++ min_gap_xsoc = ZERO_GAP_XSOC1; ++ ++ if ((xsoc <= 30) && (di->dsoc >= di->pdata->zero_reserve_dsoc)) ++ min_gap_xsoc = min_gap_xsoc + ZERO_GAP_CALIB; ++ ++ di->zero_remain_cap = di->remain_cap; ++ di->zero_timeout_cnt = 0; ++ if ((di->dsoc <= 1) && (xsoc > 0)) { ++ di->zero_linek = 400; ++ di->zero_drop_sec = 0; ++ } else if (xsoc >= 0) { ++ di->zero_drop_sec = 0; ++ di->zero_linek = (di->zero_dsoc + xsoc / 2) / DIV(xsoc); ++ org_linek = di->zero_linek; ++ /* battery energy mode to use up voltage */ ++ if ((di->pdata->energy_mode) && ++ (xsoc - di->dsoc >= ZERO_GAP_XSOC3) && ++ (di->dsoc <= 10) && (di->zero_linek < 300)) { ++ di->zero_linek = 300; ++ DBG("ZERO-new: zero_linek adjust step0...\n"); ++ /* reserve enough power yet, slow down any way */ ++ } else if ((xsoc - di->dsoc >= min_gap_xsoc) || ++ ((xsoc - di->dsoc >= ZERO_GAP_XSOC2) && ++ (di->dsoc <= 10) && (xsoc > 15))) { ++ if (xsoc <= 20 && ++ di->dsoc >= di->pdata->zero_reserve_dsoc) ++ di->zero_linek = 1200; ++ else if (xsoc - di->dsoc >= 2 * min_gap_xsoc) ++ di->zero_linek = 400; ++ else if (xsoc - di->dsoc >= 3 + min_gap_xsoc) ++ di->zero_linek = 600; ++ else ++ di->zero_linek = 800; ++ DBG("ZERO-new: zero_linek adjust step1...\n"); ++ /* control zero mode beginning enter */ ++ } else if ((di->zero_linek > 1800) && (di->dsoc > 70)) { ++ di->zero_linek = 1800; ++ DBG("ZERO-new: zero_linek adjust step2...\n"); ++ /* dsoc close to xsoc: it must reserve power */ ++ } else if ((di->zero_linek > 1000) && (di->zero_linek < 1200)) { ++ di->zero_linek = 1200; ++ DBG("ZERO-new: zero_linek adjust step3...\n"); ++ /* dsoc[5~15], dsoc < xsoc */ ++ } else if ((di->dsoc <= 15 && di->dsoc > 5) && ++ (di->zero_linek <= 1200)) { ++ /* slow down */ ++ if (xsoc - di->dsoc >= min_gap_xsoc) ++ di->zero_linek = 800; ++ /* reserve power */ ++ else ++ di->zero_linek = 1200; ++ DBG("ZERO-new: zero_linek adjust step4...\n"); ++ /* dsoc[5, 100], dsoc < xsoc */ ++ } else if ((di->zero_linek < 1000) && (di->dsoc >= 5)) { ++ if ((xsoc - di->dsoc) < min_gap_xsoc) { ++ /* reserve power */ ++ di->zero_linek = 1200; ++ } else { ++ if (abs(di->current_avg) > 500)/* heavy */ ++ di->zero_linek = 900; ++ else ++ di->zero_linek = 1000; ++ } ++ DBG("ZERO-new: zero_linek adjust step5...\n"); ++ /* dsoc[0~5], dsoc < xsoc */ ++ } else if ((di->zero_linek < 1000) && (di->dsoc <= 5)) { ++ if ((xsoc - di->dsoc) <= 3) ++ di->zero_linek = 1200; ++ else ++ di->zero_linek = 800; ++ DBG("ZERO-new: zero_linek adjust step6...\n"); ++ } ++ } else { ++ /* xsoc < 0 */ ++ di->zero_linek = 1000; ++ if (!di->zero_drop_sec) ++ di->zero_drop_sec = get_boot_sec(); ++ if (base2sec(di->zero_drop_sec) >= WAIT_DSOC_DROP_SEC) { ++ DBG("ZERO0: t=%lu\n", base2sec(di->zero_drop_sec)); ++ di->zero_drop_sec = 0; ++ di->dsoc--; ++ di->zero_dsoc = (di->dsoc + 1) * 1000 - ++ MIN_ACCURACY; ++ } ++ } ++ ++ if (voltage_avg < pwroff_vol - 70) { ++ if (!di->shtd_drop_sec) ++ di->shtd_drop_sec = get_boot_sec(); ++ if (base2sec(di->shtd_drop_sec) > WAIT_SHTD_DROP_SEC) { ++ BAT_INFO("voltage extreme low...soc:%d->0\n", di->dsoc); ++ di->shtd_drop_sec = 0; ++ di->dsoc = 0; ++ } ++ } else { ++ di->shtd_drop_sec = 0; ++ } ++ ++ DBG("ZERO-new: org_linek=%d, zero_linek=%d, dsoc=%d, Xsoc=%d, " ++ "rsoc=%d, gap=%d, v=%d, vsys=%d\n" ++ "ZERO-new: di->zero_dsoc=%d, zero_remain_cap=%d, zero_drop=%ld, " ++ "sht_drop=%ld\n\n", ++ org_linek, di->zero_linek, di->dsoc, xsoc, di->rsoc, ++ min_gap_xsoc, voltage_avg, vsys, di->zero_dsoc, di->zero_remain_cap, ++ base2sec(di->zero_drop_sec), base2sec(di->shtd_drop_sec)); ++} ++ ++static void rk818_bat_finish_algo_prepare(struct rk818_battery *di) ++{ ++ di->finish_base = get_boot_sec(); ++ if (!di->finish_base) ++ di->finish_base = 1; ++} ++ ++static void rk818_bat_smooth_algo_prepare(struct rk818_battery *di) ++{ ++ int tmp_soc; ++ ++ tmp_soc = di->sm_chrg_dsoc / 1000; ++ if (tmp_soc != di->dsoc) ++ di->sm_chrg_dsoc = di->dsoc * 1000; ++ ++ tmp_soc = di->sm_dischrg_dsoc / 1000; ++ if (tmp_soc != di->dsoc) ++ di->sm_dischrg_dsoc = ++ (di->dsoc + 1) * 1000 - MIN_ACCURACY; ++ ++ DBG("<%s>. tmp_soc=%d, dsoc=%d, dsoc:sm_dischrg=%d, sm_chrg=%d\n", ++ __func__, tmp_soc, di->dsoc, di->sm_dischrg_dsoc, di->sm_chrg_dsoc); ++ ++ rk818_bat_calc_sm_linek(di); ++} ++ ++static void rk818_bat_zero_algo_prepare(struct rk818_battery *di) ++{ ++ int tmp_dsoc; ++ ++ di->zero_timeout_cnt = 0; ++ tmp_dsoc = di->zero_dsoc / 1000; ++ if (tmp_dsoc != di->dsoc) ++ di->zero_dsoc = (di->dsoc + 1) * 1000 - MIN_ACCURACY; ++ ++ DBG("<%s>. first calc, reinit linek\n", __func__); ++ ++ rk818_bat_calc_zero_linek(di); ++} ++ ++static void rk818_bat_calc_zero_algorithm(struct rk818_battery *di) ++{ ++ int tmp_soc = 0, sm_delta_dsoc = 0; ++ ++ tmp_soc = di->zero_dsoc / 1000; ++ if (tmp_soc == di->dsoc) ++ goto out; ++ ++ DBG("<%s>. enter: dsoc=%d, rsoc=%d\n", __func__, di->dsoc, di->rsoc); ++ /* when discharge slow down, take sm chrg into calc */ ++ if (di->dsoc < di->rsoc) { ++ /* take sm charge rest into calc */ ++ tmp_soc = di->sm_chrg_dsoc / 1000; ++ if (tmp_soc == di->dsoc) { ++ sm_delta_dsoc = di->sm_chrg_dsoc - di->dsoc * 1000; ++ di->sm_chrg_dsoc = di->dsoc * 1000; ++ di->zero_dsoc += sm_delta_dsoc; ++ DBG("ZERO1: take sm chrg,delta=%d\n", sm_delta_dsoc); ++ } ++ } ++ ++ /* when discharge speed up, take sm dischrg into calc */ ++ if (di->dsoc > di->rsoc) { ++ /* take sm discharge rest into calc */ ++ tmp_soc = di->sm_dischrg_dsoc / 1000; ++ if (tmp_soc == di->dsoc) { ++ sm_delta_dsoc = di->sm_dischrg_dsoc - ++ ((di->dsoc + 1) * 1000 - MIN_ACCURACY); ++ di->sm_dischrg_dsoc = (di->dsoc + 1) * 1000 - ++ MIN_ACCURACY; ++ di->zero_dsoc += sm_delta_dsoc; ++ DBG("ZERO1: take sm dischrg,delta=%d\n", sm_delta_dsoc); ++ } ++ } ++ ++ /* check overflow */ ++ if (di->zero_dsoc > (di->dsoc + 1) * 1000 - MIN_ACCURACY) { ++ DBG("ZERO1: zero dsoc overflow: %d\n", di->zero_dsoc); ++ di->zero_dsoc = (di->dsoc + 1) * 1000 - MIN_ACCURACY; ++ } ++ ++ /* check new dsoc */ ++ tmp_soc = di->zero_dsoc / 1000; ++ if (tmp_soc != di->dsoc) { ++ /* avoid dsoc jump when heavy load */ ++ if ((di->dsoc - tmp_soc) > 1) { ++ di->dsoc--; ++ di->zero_dsoc = (di->dsoc + 1) * 1000 - MIN_ACCURACY; ++ DBG("ZERO1: heavy load...\n"); ++ } else { ++ di->dsoc = tmp_soc; ++ } ++ di->zero_drop_sec = 0; ++ } ++ ++out: ++ DBG("ZERO1: zero_dsoc(Y0)=%d, dsoc=%d, rsoc=%d, tmp_soc=%d\n", ++ di->zero_dsoc, di->dsoc, di->rsoc, tmp_soc); ++ DBG("ZERO1: sm_dischrg_dsoc=%d, sm_chrg_dsoc=%d\n", ++ di->sm_dischrg_dsoc, di->sm_chrg_dsoc); ++} ++ ++static void rk818_bat_zero_algorithm(struct rk818_battery *di) ++{ ++ int delta_cap = 0, delta_soc = 0; ++ ++ di->zero_timeout_cnt++; ++ delta_cap = di->zero_remain_cap - di->remain_cap; ++ delta_soc = di->zero_linek * (delta_cap * 100) / DIV(di->fcc); ++ ++ DBG("ZERO1: zero_linek=%d, zero_dsoc(Y0)=%d, dsoc=%d, rsoc=%d\n" ++ "ZERO1: delta_soc(X0)=%d, delta_cap=%d, zero_remain_cap = %d\n" ++ "ZERO1: timeout_cnt=%d, sm_dischrg=%d, sm_chrg=%d\n\n", ++ di->zero_linek, di->zero_dsoc, di->dsoc, di->rsoc, ++ delta_soc, delta_cap, di->zero_remain_cap, ++ di->zero_timeout_cnt, di->sm_dischrg_dsoc, di->sm_chrg_dsoc); ++ ++ if ((delta_soc >= MIN_ZERO_DSOC_ACCURACY) || ++ (di->zero_timeout_cnt > MIN_ZERO_OVERCNT) || ++ (di->zero_linek == 0)) { ++ DBG("ZERO1:--------- enter calc -----------\n"); ++ di->zero_timeout_cnt = 0; ++ di->zero_dsoc -= delta_soc; ++ rk818_bat_calc_zero_algorithm(di); ++ rk818_bat_calc_zero_linek(di); ++ } ++} ++ ++static void rk818_bat_dump_time_table(struct rk818_battery *di) ++{ ++ u8 i; ++ static int old_index; ++ static int old_min; ++ int mod = di->dsoc % 10; ++ int index = di->dsoc / 10; ++ u32 time; ++ ++ if (rk818_bat_chrg_online(di)) ++ time = base2min(di->plug_in_base); ++ else ++ time = base2min(di->plug_out_base); ++ ++ if ((mod == 0) && (index > 0) && (old_index != index)) { ++ di->dbg_chrg_min[index - 1] = time - old_min; ++ old_min = time; ++ old_index = index; ++ } ++ ++ for (i = 1; i < 11; i++) ++ DBG("Time[%d]=%d, ", (i * 10), di->dbg_chrg_min[i - 1]); ++ DBG("\n"); ++} ++ ++static void rk818_bat_debug_info(struct rk818_battery *di) ++{ ++ u8 sup_tst, ggcon, ggsts, vb_mod, ts_ctrl, reboot_cnt; ++ u8 usb_ctrl, chrg_ctrl1, thermal; ++ u8 int_sts1, int_sts2; ++ u8 int_msk1, int_msk2; ++ u8 chrg_ctrl2, chrg_ctrl3, rtc, misc, dcdc_en; ++ char *work_mode[] = {"ZERO", "FINISH", "UN", "UN", "SMOOTH"}; ++ char *bat_mode[] = {"BAT", "VIRTUAL"}; ++ ++ if (rk818_bat_chrg_online(di)) ++ di->plug_out_base = get_boot_sec(); ++ else ++ di->plug_in_base = get_boot_sec(); ++ ++ rk818_bat_dump_time_table(di); ++ ++ if (!dbg_enable) ++ return; ++ ++ ts_ctrl = rk818_bat_read(di, RK818_TS_CTRL_REG); ++ misc = rk818_bat_read(di, RK818_MISC_MARK_REG); ++ ggcon = rk818_bat_read(di, RK818_GGCON_REG); ++ ggsts = rk818_bat_read(di, RK818_GGSTS_REG); ++ sup_tst = rk818_bat_read(di, RK818_SUP_STS_REG); ++ vb_mod = rk818_bat_read(di, RK818_VB_MON_REG); ++ usb_ctrl = rk818_bat_read(di, RK818_USB_CTRL_REG); ++ chrg_ctrl1 = rk818_bat_read(di, RK818_CHRG_CTRL_REG1); ++ chrg_ctrl2 = rk818_bat_read(di, RK818_CHRG_CTRL_REG2); ++ chrg_ctrl3 = rk818_bat_read(di, RK818_CHRG_CTRL_REG3); ++ rtc = rk818_bat_read(di, 0); ++ thermal = rk818_bat_read(di, RK818_THERMAL_REG); ++ int_sts1 = rk818_bat_read(di, RK818_INT_STS_REG1); ++ int_sts2 = rk818_bat_read(di, RK818_INT_STS_REG2); ++ int_msk1 = rk818_bat_read(di, RK818_INT_STS_MSK_REG1); ++ int_msk2 = rk818_bat_read(di, RK818_INT_STS_MSK_REG2); ++ dcdc_en = rk818_bat_read(di, RK818_DCDC_EN_REG); ++ reboot_cnt = rk818_bat_read(di, RK818_REBOOT_CNT_REG); ++ ++ DBG("\n------- DEBUG REGS, [Ver: %s] -------------------\n" ++ "GGCON=0x%2x, GGSTS=0x%2x, RTC=0x%2x, DCDC_EN2=0x%2x\n" ++ "SUP_STS= 0x%2x, VB_MOD=0x%2x, USB_CTRL=0x%2x\n" ++ "THERMAL=0x%2x, MISC_MARK=0x%2x, TS_CTRL=0x%2x\n" ++ "CHRG_CTRL:REG1=0x%2x, REG2=0x%2x, REG3=0x%2x\n" ++ "INT_STS: REG1=0x%2x, REG2=0x%2x\n" ++ "INT_MSK: REG1=0x%2x, REG2=0x%2x\n", ++ DRIVER_VERSION, ggcon, ggsts, rtc, dcdc_en, ++ sup_tst, vb_mod, usb_ctrl, ++ thermal, misc, ts_ctrl, ++ chrg_ctrl1, chrg_ctrl2, chrg_ctrl3, ++ int_sts1, int_sts2, int_msk1, int_msk2 ++ ); ++ ++ DBG("###############################################################\n" ++ "Dsoc=%d, Rsoc=%d, Vavg=%d, Iavg=%d, Cap=%d, Fcc=%d, d=%d\n" ++ "K=%d, Mode=%s, Oldcap=%d, Is=%d, Ip=%d, Vs=%d\n" ++ "fb_temp=%d, bat_temp=%d, sample_res=%d, USB=%d, DC=%d\n" ++ "off:i=0x%x, c=0x%x, p=%d, Rbat=%d, age_ocv_cap=%d, fb=%d, hot=%d\n" ++ "adp:finish=%lu, boot_min=%lu, sleep_min=%lu, adc=%d, Vsys=%d\n" ++ "bat:%s, meet: soc=%d, calc: dsoc=%d, rsoc=%d, Vocv=%d\n" ++ "pwr: dsoc=%d, rsoc=%d, vol=%d, halt: st=%d, cnt=%d, reboot=%d\n" ++ "ocv_c=%d: %d -> %d; max_c=%d: %d -> %d; force_c=%d: %d -> %d\n" ++ "min=%d, init=%d, sw=%d, below0=%d, first=%d, changed=%d\n" ++ "###############################################################\n", ++ di->dsoc, di->rsoc, di->voltage_avg, di->current_avg, ++ di->remain_cap, di->fcc, di->rsoc - di->dsoc, ++ di->sm_linek, work_mode[di->work_mode], di->sm_remain_cap, ++ di->res_div * chrg_cur_sel_array[chrg_ctrl1 & 0x0f], ++ chrg_cur_input_array[usb_ctrl & 0x0f], ++ chrg_vol_sel_array[(chrg_ctrl1 & 0x70) >> 4], ++ feedback_temp_array[(thermal & 0x0c) >> 2], di->temperature, ++ di->pdata->sample_res, di->usb_in, di->ac_in, ++ rk818_bat_get_ioffset(di), ++ rk818_bat_get_coffset(di), di->poffset, di->bat_res, ++ di->age_adjust_cap, di->fb_blank, !!(thermal & HOTDIE_STS), ++ base2min(di->finish_base), ++ base2min(di->boot_base), di->sleep_sum_sec / 60, ++ di->adc_allow_update, ++ di->voltage_avg + di->current_avg * DEF_PWRPATH_RES / 1000, ++ bat_mode[di->pdata->bat_mode], di->dbg_meet_soc, di->dbg_calc_dsoc, ++ di->dbg_calc_rsoc, di->voltage_ocv, di->dbg_pwr_dsoc, ++ di->dbg_pwr_rsoc, di->dbg_pwr_vol, di->is_halt, di->halt_cnt, ++ reboot_cnt, di->is_ocv_calib, di->ocv_pre_dsoc, di->ocv_new_dsoc, ++ di->is_max_soc_offset, di->max_pre_dsoc, di->max_new_dsoc, ++ di->is_force_calib, di->force_pre_dsoc, di->force_new_dsoc, ++ di->pwroff_min, di->is_initialized, di->is_sw_reset, ++ di->dbg_cap_low0, di->is_first_on, di->last_dsoc ++ ); ++} ++ ++static void rk818_bat_init_capacity(struct rk818_battery *di, u32 cap) ++{ ++ int delta_cap; ++ ++ delta_cap = cap - di->remain_cap; ++ if (!delta_cap) ++ return; ++ ++ di->age_adjust_cap += delta_cap; ++ rk818_bat_init_coulomb_cap(di, cap); ++ rk818_bat_smooth_algo_prepare(di); ++ rk818_bat_zero_algo_prepare(di); ++} ++ ++static void rk818_bat_update_age_fcc(struct rk818_battery *di) ++{ ++ int fcc, remain_cap, age_keep_min, lock_fcc; ++ ++ lock_fcc = rk818_bat_get_coulomb_cap(di); ++ remain_cap = lock_fcc - di->age_ocv_cap - di->age_adjust_cap; ++ age_keep_min = base2min(di->age_keep_sec); ++ ++ DBG("%s: lock_fcc=%d, age_ocv_cap=%d, age_adjust_cap=%d, remain_cap=%d," ++ "age_allow_update=%d, age_keep_min=%d\n", ++ __func__, lock_fcc, di->age_ocv_cap, di->age_adjust_cap, remain_cap, ++ di->age_allow_update, age_keep_min); ++ ++ if ((di->chrg_status == CHARGE_FINISH) && (di->age_allow_update) && ++ (age_keep_min < 1200)) { ++ di->age_allow_update = false; ++ fcc = remain_cap * 100 / DIV(100 - di->age_ocv_soc); ++ BAT_INFO("lock_fcc=%d, calc_cap=%d, age: soc=%d, cap=%d, " ++ "level=%d, fcc:%d->%d?\n", ++ lock_fcc, remain_cap, di->age_ocv_soc, ++ di->age_ocv_cap, di->age_level, di->fcc, fcc); ++ ++ if ((fcc < di->qmax) && (fcc > MIN_FCC)) { ++ BAT_INFO("fcc:%d->%d!\n", di->fcc, fcc); ++ di->fcc = fcc; ++ rk818_bat_init_capacity(di, di->fcc); ++ rk818_bat_save_fcc(di, di->fcc); ++ rk818_bat_save_age_level(di, di->age_level); ++ } ++ } ++} ++ ++static void rk818_bat_wait_finish_sig(struct rk818_battery *di) ++{ ++ int chrg_finish_vol = di->pdata->max_chrg_voltage; ++ ++ if (!rk818_bat_chrg_online(di)) ++ return; ++ ++ if ((di->chrg_status == CHARGE_FINISH) && (di->adc_allow_update) && ++ (di->voltage_avg > chrg_finish_vol - 150)) { ++ rk818_bat_update_age_fcc(di); ++ if (rk818_bat_adc_calib(di)) ++ di->adc_allow_update = false; ++ } ++} ++ ++static void rk818_bat_finish_algorithm(struct rk818_battery *di) ++{ ++ unsigned long finish_sec, soc_sec; ++ int plus_soc, finish_current, rest = 0; ++ ++ /* rsoc */ ++ if ((di->remain_cap != di->fcc) && ++ (rk818_bat_get_chrg_status(di) == CHARGE_FINISH)) { ++ di->age_adjust_cap += (di->fcc - di->remain_cap); ++ rk818_bat_init_coulomb_cap(di, di->fcc); ++ } ++ ++ /* dsoc */ ++ if (di->dsoc < 100) { ++ if (!di->finish_base) ++ di->finish_base = get_boot_sec(); ++ finish_current = (di->rsoc - di->dsoc) > FINISH_MAX_SOC_DELAY ? ++ FINISH_CHRG_CUR2 : FINISH_CHRG_CUR1; ++ finish_sec = base2sec(di->finish_base); ++ soc_sec = di->fcc * 3600 / 100 / DIV(finish_current); ++ plus_soc = finish_sec / DIV(soc_sec); ++ if (finish_sec > soc_sec) { ++ rest = finish_sec % soc_sec; ++ di->dsoc += plus_soc; ++ di->finish_base = get_boot_sec(); ++ if (di->finish_base > rest) ++ di->finish_base = get_boot_sec() - rest; ++ } ++ DBG("<%s>.CHARGE_FINISH:dsoc<100,dsoc=%d\n" ++ "soc_time=%lu, sec_finish=%lu, plus_soc=%d, rest=%d\n", ++ __func__, di->dsoc, soc_sec, finish_sec, plus_soc, rest); ++ } ++} ++ ++static void rk818_bat_calc_smooth_dischrg(struct rk818_battery *di) ++{ ++ int tmp_soc = 0, sm_delta_dsoc = 0, zero_delta_dsoc = 0; ++ ++ tmp_soc = di->sm_dischrg_dsoc / 1000; ++ if (tmp_soc == di->dsoc) ++ goto out; ++ ++ DBG("<%s>. enter: dsoc=%d, rsoc=%d\n", __func__, di->dsoc, di->rsoc); ++ /* when dischrge slow down, take sm charge rest into calc */ ++ if (di->dsoc < di->rsoc) { ++ tmp_soc = di->sm_chrg_dsoc / 1000; ++ if (tmp_soc == di->dsoc) { ++ sm_delta_dsoc = di->sm_chrg_dsoc - di->dsoc * 1000; ++ di->sm_chrg_dsoc = di->dsoc * 1000; ++ di->sm_dischrg_dsoc += sm_delta_dsoc; ++ DBG("<%s>. take sm dischrg, delta=%d\n", ++ __func__, sm_delta_dsoc); ++ } ++ } ++ ++ /* when discharge speed up, take zero discharge rest into calc */ ++ if (di->dsoc > di->rsoc) { ++ tmp_soc = di->zero_dsoc / 1000; ++ if (tmp_soc == di->dsoc) { ++ zero_delta_dsoc = di->zero_dsoc - ((di->dsoc + 1) * ++ 1000 - MIN_ACCURACY); ++ di->zero_dsoc = (di->dsoc + 1) * 1000 - MIN_ACCURACY; ++ di->sm_dischrg_dsoc += zero_delta_dsoc; ++ DBG("<%s>. take zero schrg, delta=%d\n", ++ __func__, zero_delta_dsoc); ++ } ++ } ++ ++ /* check up overflow */ ++ if ((di->sm_dischrg_dsoc) > ((di->dsoc + 1) * 1000 - MIN_ACCURACY)) { ++ DBG("<%s>. dischrg_dsoc up overflow\n", __func__); ++ di->sm_dischrg_dsoc = (di->dsoc + 1) * ++ 1000 - MIN_ACCURACY; ++ } ++ ++ /* check new dsoc */ ++ tmp_soc = di->sm_dischrg_dsoc / 1000; ++ if (tmp_soc != di->dsoc) { ++ di->dsoc = tmp_soc; ++ di->sm_chrg_dsoc = di->dsoc * 1000; ++ } ++out: ++ DBG("<%s>. dsoc=%d, rsoc=%d, dsoc:sm_dischrg=%d, sm_chrg=%d, zero=%d\n", ++ __func__, di->dsoc, di->rsoc, di->sm_dischrg_dsoc, di->sm_chrg_dsoc, ++ di->zero_dsoc); ++ ++} ++ ++static void rk818_bat_calc_smooth_chrg(struct rk818_battery *di) ++{ ++ int tmp_soc = 0, sm_delta_dsoc = 0, zero_delta_dsoc = 0; ++ ++ tmp_soc = di->sm_chrg_dsoc / 1000; ++ if (tmp_soc == di->dsoc) ++ goto out; ++ ++ DBG("<%s>. enter: dsoc=%d, rsoc=%d\n", __func__, di->dsoc, di->rsoc); ++ /* when charge slow down, take zero & sm dischrg into calc */ ++ if (di->dsoc > di->rsoc) { ++ /* take sm discharge rest into calc */ ++ tmp_soc = di->sm_dischrg_dsoc / 1000; ++ if (tmp_soc == di->dsoc) { ++ sm_delta_dsoc = di->sm_dischrg_dsoc - ++ ((di->dsoc + 1) * 1000 - MIN_ACCURACY); ++ di->sm_dischrg_dsoc = (di->dsoc + 1) * 1000 - ++ MIN_ACCURACY; ++ di->sm_chrg_dsoc += sm_delta_dsoc; ++ DBG("<%s>. take sm dischrg, delta=%d\n", ++ __func__, sm_delta_dsoc); ++ } ++ ++ /* take zero discharge rest into calc */ ++ tmp_soc = di->zero_dsoc / 1000; ++ if (tmp_soc == di->dsoc) { ++ zero_delta_dsoc = di->zero_dsoc - ++ ((di->dsoc + 1) * 1000 - MIN_ACCURACY); ++ di->zero_dsoc = (di->dsoc + 1) * 1000 - MIN_ACCURACY; ++ di->sm_chrg_dsoc += zero_delta_dsoc; ++ DBG("<%s>. take zero dischrg, delta=%d\n", ++ __func__, zero_delta_dsoc); ++ } ++ } ++ ++ /* check down overflow */ ++ if (di->sm_chrg_dsoc < di->dsoc * 1000) { ++ DBG("<%s>. chrg_dsoc down overflow\n", __func__); ++ di->sm_chrg_dsoc = di->dsoc * 1000; ++ } ++ ++ /* check new dsoc */ ++ tmp_soc = di->sm_chrg_dsoc / 1000; ++ if (tmp_soc != di->dsoc) { ++ di->dsoc = tmp_soc; ++ di->sm_dischrg_dsoc = (di->dsoc + 1) * 1000 - MIN_ACCURACY; ++ } ++out: ++ DBG("<%s>.dsoc=%d, rsoc=%d, dsoc: sm_dischrg=%d, sm_chrg=%d, zero=%d\n", ++ __func__, di->dsoc, di->rsoc, di->sm_dischrg_dsoc, di->sm_chrg_dsoc, ++ di->zero_dsoc); ++} ++ ++static void rk818_bat_smooth_algorithm(struct rk818_battery *di) ++{ ++ int ydsoc = 0, delta_cap = 0, old_cap = 0; ++ unsigned long tgt_sec = 0; ++ ++ di->remain_cap = rk818_bat_get_coulomb_cap(di); ++ ++ /* full charge: slow down */ ++ if ((di->dsoc == 99) && (di->chrg_status == CC_OR_CV) && ++ (di->current_avg > 0)) { ++ di->sm_linek = FULL_CHRG_K; ++ /* terminal charge, slow down */ ++ } else if ((di->current_avg >= TERM_CHRG_CURR) && ++ (di->chrg_status == CC_OR_CV) && (di->dsoc >= TERM_CHRG_DSOC)) { ++ di->sm_linek = TERM_CHRG_K; ++ DBG("<%s>. terminal mode..\n", __func__); ++ /* simulate charge, speed up */ ++ } else if ((di->current_avg <= SIMULATE_CHRG_CURR) && ++ (di->current_avg > 0) && (di->chrg_status == CC_OR_CV) && ++ (di->dsoc < TERM_CHRG_DSOC) && ++ ((di->rsoc - di->dsoc) >= SIMULATE_CHRG_INTV)) { ++ di->sm_linek = SIMULATE_CHRG_K; ++ DBG("<%s>. simulate mode..\n", __func__); ++ } else { ++ /* charge and discharge switch */ ++ if ((di->sm_linek * di->current_avg <= 0) || ++ (di->sm_linek == TERM_CHRG_K) || ++ (di->sm_linek == FULL_CHRG_K) || ++ (di->sm_linek == SIMULATE_CHRG_K)) { ++ DBG("<%s>. linek mode, retinit sm linek..\n", __func__); ++ rk818_bat_calc_sm_linek(di); ++ } ++ } ++ ++ old_cap = di->sm_remain_cap; ++ /* ++ * when dsoc equal rsoc(not include full, term, simulate case), ++ * sm_linek should change to -1000/1000 smoothly to avoid dsoc+1/-1 ++ * right away, so change it after flat seconds ++ */ ++ if ((di->dsoc == di->rsoc) && (abs(di->sm_linek) != 1000) && ++ (di->sm_linek != FULL_CHRG_K && di->sm_linek != TERM_CHRG_K && ++ di->sm_linek != SIMULATE_CHRG_K)) { ++ if (!di->flat_match_sec) ++ di->flat_match_sec = get_boot_sec(); ++ tgt_sec = di->fcc * 3600 / 100 / DIV(abs(di->current_avg)) / 3; ++ if (base2sec(di->flat_match_sec) >= tgt_sec) { ++ di->flat_match_sec = 0; ++ di->sm_linek = (di->current_avg >= 0) ? 1000 : -1000; ++ } ++ DBG("<%s>. flat_sec=%ld, tgt_sec=%ld, sm_k=%d\n", __func__, ++ base2sec(di->flat_match_sec), tgt_sec, di->sm_linek); ++ } else { ++ di->flat_match_sec = 0; ++ } ++ ++ /* abs(k)=1000 or dsoc=100, stop calc */ ++ if ((abs(di->sm_linek) == 1000) || (di->current_avg >= 0 && ++ di->chrg_status == CC_OR_CV && di->dsoc >= 100)) { ++ DBG("<%s>. sm_linek=%d\n", __func__, di->sm_linek); ++ if (abs(di->sm_linek) == 1000) { ++ di->dsoc = di->rsoc; ++ di->sm_linek = (di->sm_linek > 0) ? 1000 : -1000; ++ DBG("<%s>. dsoc == rsoc, sm_linek=%d\n", ++ __func__, di->sm_linek); ++ } ++ di->sm_remain_cap = di->remain_cap; ++ di->sm_chrg_dsoc = di->dsoc * 1000; ++ di->sm_dischrg_dsoc = (di->dsoc + 1) * 1000 - MIN_ACCURACY; ++ DBG("<%s>. sm_dischrg_dsoc=%d, sm_chrg_dsoc=%d\n", ++ __func__, di->sm_dischrg_dsoc, di->sm_chrg_dsoc); ++ } else { ++ delta_cap = di->remain_cap - di->sm_remain_cap; ++ if (delta_cap == 0) { ++ DBG("<%s>. delta_cap = 0\n", __func__); ++ return; ++ } ++ ydsoc = di->sm_linek * abs(delta_cap) * 100 / DIV(di->fcc); ++ if (ydsoc == 0) { ++ DBG("<%s>. ydsoc = 0\n", __func__); ++ return; ++ } ++ di->sm_remain_cap = di->remain_cap; ++ ++ DBG("<%s>. k=%d, ydsoc=%d; cap:old=%d, new:%d; delta_cap=%d\n", ++ __func__, di->sm_linek, ydsoc, old_cap, ++ di->sm_remain_cap, delta_cap); ++ ++ /* discharge mode */ ++ if (ydsoc < 0) { ++ di->sm_dischrg_dsoc += ydsoc; ++ rk818_bat_calc_smooth_dischrg(di); ++ /* charge mode */ ++ } else { ++ di->sm_chrg_dsoc += ydsoc; ++ rk818_bat_calc_smooth_chrg(di); ++ } ++ ++ if (di->s2r) { ++ di->s2r = false; ++ rk818_bat_calc_sm_linek(di); ++ } ++ } ++} ++ ++/* ++ * cccv and finish switch all the time will cause dsoc freeze, ++ * if so, do finish chrg, 100ma is less than min finish_ma. ++ */ ++static bool rk818_bat_fake_finish_mode(struct rk818_battery *di) ++{ ++ if ((di->rsoc == 100) && (rk818_bat_get_chrg_status(di) == CC_OR_CV) && ++ (abs(di->current_avg) <= 100)) ++ return true; ++ else ++ return false; ++} ++ ++static void rk818_bat_display_smooth(struct rk818_battery *di) ++{ ++ /* discharge: reinit "zero & smooth" algorithm to avoid handling dsoc */ ++ if (di->s2r && !di->sleep_chrg_online) { ++ DBG("s2r: discharge, reset algorithm...\n"); ++ di->s2r = false; ++ rk818_bat_zero_algo_prepare(di); ++ rk818_bat_smooth_algo_prepare(di); ++ return; ++ } ++ ++ if (di->work_mode == MODE_FINISH) { ++ DBG("step1: charge finish...\n"); ++ rk818_bat_finish_algorithm(di); ++ if ((rk818_bat_get_chrg_status(di) != CHARGE_FINISH) && ++ !rk818_bat_fake_finish_mode(di)) { ++ if ((di->current_avg < 0) && ++ (di->voltage_avg < di->pdata->zero_algorithm_vol)) { ++ DBG("step1: change to zero mode...\n"); ++ rk818_bat_zero_algo_prepare(di); ++ di->work_mode = MODE_ZERO; ++ } else { ++ DBG("step1: change to smooth mode...\n"); ++ rk818_bat_smooth_algo_prepare(di); ++ di->work_mode = MODE_SMOOTH; ++ } ++ } ++ } else if (di->work_mode == MODE_ZERO) { ++ DBG("step2: zero algorithm...\n"); ++ rk818_bat_zero_algorithm(di); ++ if ((di->voltage_avg >= di->pdata->zero_algorithm_vol + 50) || ++ (di->current_avg >= 0)) { ++ DBG("step2: change to smooth mode...\n"); ++ rk818_bat_smooth_algo_prepare(di); ++ di->work_mode = MODE_SMOOTH; ++ } else if ((rk818_bat_get_chrg_status(di) == CHARGE_FINISH) || ++ rk818_bat_fake_finish_mode(di)) { ++ DBG("step2: change to finish mode...\n"); ++ rk818_bat_finish_algo_prepare(di); ++ di->work_mode = MODE_FINISH; ++ } ++ } else { ++ DBG("step3: smooth algorithm...\n"); ++ rk818_bat_smooth_algorithm(di); ++ if ((di->current_avg < 0) && ++ (di->voltage_avg < di->pdata->zero_algorithm_vol)) { ++ DBG("step3: change to zero mode...\n"); ++ rk818_bat_zero_algo_prepare(di); ++ di->work_mode = MODE_ZERO; ++ } else if ((rk818_bat_get_chrg_status(di) == CHARGE_FINISH) || ++ rk818_bat_fake_finish_mode(di)) { ++ DBG("step3: change to finish mode...\n"); ++ rk818_bat_finish_algo_prepare(di); ++ di->work_mode = MODE_FINISH; ++ } ++ } ++} ++ ++static void rk818_bat_relax_vol_calib(struct rk818_battery *di) ++{ ++ int soc, cap, vol; ++ ++ vol = di->voltage_relax; ++ soc = rk818_bat_vol_to_ocvsoc(di, vol); ++ cap = rk818_bat_vol_to_ocvcap(di, vol); ++ rk818_bat_init_capacity(di, cap); ++ BAT_INFO("sleep ocv calib: rsoc=%d, cap=%d\n", soc, cap); ++} ++ ++static void rk818_bat_relife_age_flag(struct rk818_battery *di) ++{ ++ u8 ocv_soc, ocv_cap, soc_level; ++ ++ if (di->voltage_relax <= 0) ++ return; ++ ++ ocv_soc = rk818_bat_vol_to_ocvsoc(di, di->voltage_relax); ++ ocv_cap = rk818_bat_vol_to_ocvcap(di, di->voltage_relax); ++ DBG("<%s>. ocv_soc=%d, min=%lu, vol=%d\n", __func__, ++ ocv_soc, di->sleep_dischrg_sec / 60, di->voltage_relax); ++ ++ /* sleep enough time and ocv_soc enough low */ ++ if (!di->age_allow_update && ocv_soc <= 10) { ++ di->age_voltage = di->voltage_relax; ++ di->age_ocv_cap = ocv_cap; ++ di->age_ocv_soc = ocv_soc; ++ di->age_adjust_cap = 0; ++ ++ if (ocv_soc <= 1) ++ di->age_level = 100; ++ else if (ocv_soc < 5) ++ di->age_level = 90; ++ else ++ di->age_level = 80; ++ ++ soc_level = rk818_bat_get_age_level(di); ++ if (soc_level > di->age_level) { ++ di->age_allow_update = false; ++ } else { ++ di->age_allow_update = true; ++ di->age_keep_sec = get_boot_sec(); ++ } ++ ++ BAT_INFO("resume: age_vol:%d, age_ocv_cap:%d, age_ocv_soc:%d, " ++ "soc_level:%d, age_allow_update:%d, " ++ "age_level:%d\n", ++ di->age_voltage, di->age_ocv_cap, ocv_soc, soc_level, ++ di->age_allow_update, di->age_level); ++ } ++} ++ ++static int rk818_bat_sleep_dischrg(struct rk818_battery *di) ++{ ++ bool ocv_soc_updated = false; ++ int tgt_dsoc, gap_soc, sleep_soc = 0; ++ int pwroff_vol = di->pdata->pwroff_vol; ++ unsigned long sleep_sec = di->sleep_dischrg_sec; ++ ++ DBG("<%s>. enter: dsoc=%d, rsoc=%d, rv=%d, v=%d, sleep_min=%lu\n", ++ __func__, di->dsoc, di->rsoc, di->voltage_relax, ++ di->voltage_avg, sleep_sec / 60); ++ ++ if (di->voltage_relax >= di->voltage_avg) { ++ rk818_bat_relax_vol_calib(di); ++ rk818_bat_restart_relax(di); ++ rk818_bat_relife_age_flag(di); ++ ocv_soc_updated = true; ++ } ++ ++ /* handle dsoc */ ++ if (di->dsoc <= di->rsoc) { ++ di->sleep_sum_cap = (SLP_CURR_MIN * sleep_sec / 3600); ++ sleep_soc = di->sleep_sum_cap * 100 / DIV(di->fcc); ++ tgt_dsoc = di->dsoc - sleep_soc; ++ if (sleep_soc > 0) { ++ BAT_INFO("calib0: rl=%d, dl=%d, intval=%d\n", ++ di->rsoc, di->dsoc, sleep_soc); ++ if (di->dsoc < 5) { ++ di->dsoc--; ++ } else if ((tgt_dsoc < 5) && (di->dsoc >= 5)) { ++ if (di->dsoc == 5) ++ di->dsoc--; ++ else ++ di->dsoc = 5; ++ } else if (tgt_dsoc > 5) { ++ di->dsoc = tgt_dsoc; ++ } ++ } ++ ++ DBG("%s: dsoc<=rsoc, sum_cap=%d==>sleep_soc=%d, tgt_dsoc=%d\n", ++ __func__, di->sleep_sum_cap, sleep_soc, tgt_dsoc); ++ } else { ++ /* di->dsoc > di->rsoc */ ++ di->sleep_sum_cap = (SLP_CURR_MAX * sleep_sec / 3600); ++ sleep_soc = di->sleep_sum_cap / DIV(di->fcc / 100); ++ gap_soc = di->dsoc - di->rsoc; ++ ++ BAT_INFO("calib1: rsoc=%d, dsoc=%d, intval=%d\n", ++ di->rsoc, di->dsoc, sleep_soc); ++ if (gap_soc > sleep_soc) { ++ if ((gap_soc - 5) > (sleep_soc * 2)) ++ di->dsoc -= (sleep_soc * 2); ++ else ++ di->dsoc -= sleep_soc; ++ } else { ++ di->dsoc = di->rsoc; ++ } ++ ++ DBG("%s: dsoc>rsoc, sum_cap=%d=>sleep_soc=%d, gap_soc=%d\n", ++ __func__, di->sleep_sum_cap, sleep_soc, gap_soc); ++ } ++ ++ if (di->voltage_avg <= pwroff_vol - 70) { ++ di->dsoc = 0; ++ rk_send_wakeup_key(); ++ BAT_INFO("low power sleeping, shutdown... %d\n", di->dsoc); ++ } ++ ++ if (ocv_soc_updated && sleep_soc && (di->rsoc - di->dsoc) < 5 && ++ di->dsoc < 40) { ++ di->dsoc--; ++ BAT_INFO("low power sleeping, reserved... %d\n", di->dsoc); ++ } ++ ++ if (di->dsoc <= 0) { ++ di->dsoc = 0; ++ rk_send_wakeup_key(); ++ BAT_INFO("sleep dsoc is %d...\n", di->dsoc); ++ } ++ ++ DBG("<%s>. out: dsoc=%d, rsoc=%d, sum_cap=%d\n", ++ __func__, di->dsoc, di->rsoc, di->sleep_sum_cap); ++ ++ return sleep_soc; ++} ++ ++static void rk818_bat_power_supply_changed(struct rk818_battery *di) ++{ ++ u8 status, thermal; ++ static int old_soc = -1; ++ ++ if (di->dsoc > 100) ++ di->dsoc = 100; ++ else if (di->dsoc < 0) ++ di->dsoc = 0; ++ ++ if (di->dsoc == old_soc) ++ return; ++ ++ thermal = rk818_bat_read(di, RK818_THERMAL_REG); ++ status = rk818_bat_read(di, RK818_SUP_STS_REG); ++ status = (status & CHRG_STATUS_MSK) >> 4; ++ old_soc = di->dsoc; ++ di->last_dsoc = di->dsoc; ++ power_supply_changed(di->bat); ++ BAT_INFO("changed: dsoc=%d, rsoc=%d, v=%d, ov=%d c=%d, " ++ "cap=%d, f=%d, st=%s, hotdie=%d\n", ++ di->dsoc, di->rsoc, di->voltage_avg, di->voltage_ocv, ++ di->current_avg, di->remain_cap, di->fcc, bat_status[status], ++ !!(thermal & HOTDIE_STS)); ++ ++ BAT_INFO("dl=%d, rl=%d, v=%d, halt=%d, halt_n=%d, max=%d, " ++ "init=%d, sw=%d, calib=%d, below0=%d, force=%d\n", ++ di->dbg_pwr_dsoc, di->dbg_pwr_rsoc, di->dbg_pwr_vol, ++ di->is_halt, di->halt_cnt, di->is_max_soc_offset, ++ di->is_initialized, di->is_sw_reset, di->is_ocv_calib, ++ di->dbg_cap_low0, di->is_force_calib); ++} ++ ++static u8 rk818_bat_check_reboot(struct rk818_battery *di) ++{ ++ u8 cnt; ++ ++ cnt = rk818_bat_read(di, RK818_REBOOT_CNT_REG); ++ cnt++; ++ ++ if (cnt >= REBOOT_MAX_CNT) { ++ BAT_INFO("reboot: %d --> %d\n", di->dsoc, di->rsoc); ++ di->dsoc = di->rsoc; ++ if (di->dsoc > 100) ++ di->dsoc = 100; ++ else if (di->dsoc < 0) ++ di->dsoc = 0; ++ rk818_bat_save_dsoc(di, di->dsoc); ++ cnt = REBOOT_MAX_CNT; ++ } ++ ++ rk818_bat_save_reboot_cnt(di, cnt); ++ DBG("reboot cnt: %d\n", cnt); ++ ++ return cnt; ++} ++ ++static void rk818_bat_rsoc_daemon(struct rk818_battery *di) ++{ ++ int est_vol, remain_cap; ++ static unsigned long sec; ++ ++ if ((di->remain_cap < 0) && (di->fb_blank != 0)) { ++ if (!sec) ++ sec = get_boot_sec(); ++ // wake_lock_timeout(&di->wake_lock, ++ // (di->pdata->monitor_sec + 1) * HZ); ++ ++ DBG("sec=%ld, hold_sec=%ld\n", sec, base2sec(sec)); ++ if (base2sec(sec) >= 60) { ++ sec = 0; ++ di->dbg_cap_low0++; ++ est_vol = di->voltage_avg - ++ (di->bat_res * di->current_avg) / 1000; ++ remain_cap = rk818_bat_vol_to_ocvcap(di, est_vol); ++ rk818_bat_init_capacity(di, remain_cap); ++ BAT_INFO("adjust cap below 0 --> %d, rsoc=%d\n", ++ di->remain_cap, di->rsoc); ++ // wake_unlock(&di->wake_lock); ++ } ++ } else { ++ sec = 0; ++ } ++} ++ ++static void rk818_bat_update_info(struct rk818_battery *di) ++{ ++ int is_charging; ++ ++ di->voltage_avg = rk818_bat_get_avg_voltage(di); ++ di->current_avg = rk818_bat_get_avg_current(di); ++ di->voltage_relax = rk818_bat_get_relax_voltage(di); ++ di->rsoc = rk818_bat_get_rsoc(di); ++ di->remain_cap = rk818_bat_get_coulomb_cap(di); ++ di->chrg_status = rk818_bat_get_chrg_status(di); ++ is_charging = rk818_bat_get_charge_state(di); ++ if (is_charging != di->is_charging) { ++ di->is_charging = is_charging; ++ if (is_charging) ++ di->charge_count++; ++ } ++ if (di->voltage_avg > di->voltage_max) ++ di->voltage_max = di->voltage_avg; ++ if (di->current_avg > di->current_max) ++ di->current_max = di->current_avg; ++ ++ /* smooth charge */ ++ if (di->remain_cap > di->fcc) { ++ di->sm_remain_cap -= (di->remain_cap - di->fcc); ++ DBG("<%s>. cap: remain=%d, sm_remain=%d\n", ++ __func__, di->remain_cap, di->sm_remain_cap); ++ rk818_bat_init_coulomb_cap(di, di->fcc); ++ } ++ ++ if (di->chrg_status != CHARGE_FINISH) ++ di->finish_base = get_boot_sec(); ++ ++ /* ++ * we need update fcc in continuous charging state, if discharge state ++ * keep at least 2 hour, we decide not to update fcc, so clear the ++ * fcc update flag: age_allow_update. ++ */ ++ if (base2min(di->plug_out_base) > 120) ++ di->age_allow_update = false; ++ ++ /* do adc calib: status must from cccv mode to finish mode */ ++ if (di->chrg_status == CC_OR_CV) { ++ di->adc_allow_update = true; ++ di->adc_calib_cnt = 0; ++ } ++} ++ ++static void rk818_bat_init_ts1_detect(struct rk818_battery *di) ++{ ++ u8 buf; ++ u32 *ntc_table = di->pdata->ntc_table; ++ ++ if (!di->pdata->ntc_size) ++ return; ++ ++ /* select ua */ ++ buf = rk818_bat_read(di, RK818_TS_CTRL_REG); ++ buf &= ~TS1_CUR_MSK; ++ /* chose suitable UA for temperature detect */ ++ if (ntc_table[0] < NTC_80UA_MAX_MEASURE) { ++ di->pdata->ntc_factor = NTC_CALC_FACTOR_80UA; ++ di->pdata->ntc_uA = 80; ++ buf |= ADC_CUR_80UA; ++ } else if (ntc_table[0] < NTC_60UA_MAX_MEASURE) { ++ di->pdata->ntc_factor = NTC_CALC_FACTOR_60UA; ++ di->pdata->ntc_uA = 60; ++ buf |= ADC_CUR_60UA; ++ } else if (ntc_table[0] < NTC_40UA_MAX_MEASURE) { ++ di->pdata->ntc_factor = NTC_CALC_FACTOR_40UA; ++ di->pdata->ntc_uA = 40; ++ buf |= ADC_CUR_40UA; ++ } else { ++ di->pdata->ntc_factor = NTC_CALC_FACTOR_20UA; ++ di->pdata->ntc_uA = 20; ++ buf |= ADC_CUR_20UA; ++ } ++ rk818_bat_write(di, RK818_TS_CTRL_REG, buf); ++ ++ /* enable ADC_TS1_EN */ ++ buf = rk818_bat_read(di, RK818_ADC_CTRL_REG); ++ buf |= ADC_TS1_EN; ++ rk818_bat_write(di, RK818_ADC_CTRL_REG, buf); ++} ++ ++/* ++ * Due to hardware design issue, Vdelta = "(R_sample + R_other) * I_avg" will be ++ * included into TS1 adc value. We must subtract it to get correct adc value. ++ * The solution: ++ * ++ * (1) calculate Vdelta: ++ * ++ * adc1 - Vdelta ua1 (adc2 * ua1) - (adc1 * ua2) ++ * ------------- = ----- ==> equals: Vdelta = ----------------------------- ++ * adc2 - Vdelta ua2 ua1 - ua2 ++ * ++ * ++ * (2) calculate correct ADC value: ++ * ++ * charging: ADC = adc1 - abs(Vdelta); ++ * discharging: ADC = adc1 + abs(Vdelta); ++ */ ++static int rk818_bat_get_ntc_res(struct rk818_battery *di) ++{ ++ int adc1 = 0, adc2 = 0; ++ int ua1, ua2, v_delta, res, val; ++ u8 buf; ++ ++ /* read sample ua1 */ ++ buf = rk818_bat_read(di, RK818_TS_CTRL_REG); ++ DBG("<%s>. read adc1, sample uA=%d\n", ++ __func__, ((buf & 0x03) + 1) * 20); ++ ++ /* read adc adc1 */ ++ ua1 = di->pdata->ntc_uA; ++ adc1 |= rk818_bat_read(di, RK818_TS1_ADC_REGL) << 0; ++ adc1 |= rk818_bat_read(di, RK818_TS1_ADC_REGH) << 8; ++ ++ /* chose reference UA for adc2 */ ++ ua2 = (ua1 != 20) ? 20 : 40; ++ buf = rk818_bat_read(di, RK818_TS_CTRL_REG); ++ buf &= ~TS1_CUR_MSK; ++ buf |= ((ua2 - 20) / 20); ++ rk818_bat_write(di, RK818_TS_CTRL_REG, buf); ++ ++ /* read adc adc2 */ ++ msleep(1000); ++ ++ /* read sample ua2 */ ++ buf = rk818_bat_read(di, RK818_TS_CTRL_REG); ++ DBG("<%s>. read adc2, sample uA=%d\n", ++ __func__, ((buf & 0x03) + 1) * 20); ++ ++ adc2 |= rk818_bat_read(di, RK818_TS1_ADC_REGL) << 0; ++ adc2 |= rk818_bat_read(di, RK818_TS1_ADC_REGH) << 8; ++ ++ DBG("<%s>. ua1=%d, ua2=%d, adc1=%d, adc2=%d\n", ++ __func__, ua1, ua2, adc1, adc2); ++ ++ /* calculate delta voltage */ ++ if (adc2 != adc1) ++ v_delta = abs((adc2 * ua1 - adc1 * ua2) / (ua2 - ua1)); ++ else ++ v_delta = 0; ++ ++ /* considering current avg direction, calcuate real adc value */ ++ val = (di->current_avg >= 0) ? (adc1 - v_delta) : (adc1 + v_delta); ++ ++ DBG("<%s>. Iavg=%d, Vdelta=%d, Vadc=%d\n", ++ __func__, di->current_avg, v_delta, val); ++ ++ res = val * di->pdata->ntc_factor; ++ ++ DBG("<%s>. val=%d, ntc_res=%d, ntc_factor=%d, Rdelta=%d\n", ++ __func__, val, res, di->pdata->ntc_factor, ++ v_delta * di->pdata->ntc_factor); ++ ++ DBG("<%s>. t=[%d'C(%d) ~ %dC(%d)]\n", __func__, ++ di->pdata->ntc_degree_from, di->pdata->ntc_table[0], ++ di->pdata->ntc_degree_from + di->pdata->ntc_size - 1, ++ di->pdata->ntc_table[di->pdata->ntc_size - 1]); ++ ++ rk818_bat_init_ts1_detect(di); ++ ++ return res; ++} ++ ++static BLOCKING_NOTIFIER_HEAD(rk818_bat_notifier_chain); ++ ++int rk818_bat_temp_notifier_register(struct notifier_block *nb) ++{ ++ return blocking_notifier_chain_register(&rk818_bat_notifier_chain, nb); ++} ++ ++int rk818_bat_temp_notifier_unregister(struct notifier_block *nb) ++{ ++ return blocking_notifier_chain_unregister(&rk818_bat_notifier_chain, nb); ++} ++ ++static void rk818_bat_temp_notifier_callback(int temp) ++{ ++ blocking_notifier_call_chain(&rk818_bat_notifier_chain, temp, NULL); ++} ++ ++static void rk818_bat_update_temperature(struct rk818_battery *di) ++{ ++ static int old_temp, first_time = 1; ++ u32 ntc_size, *ntc_table; ++ int i, res, temp; ++ ++ ntc_table = di->pdata->ntc_table; ++ ntc_size = di->pdata->ntc_size; ++ di->temperature = VIRTUAL_TEMPERATURE; ++ ++ if (ntc_size) { ++ res = rk818_bat_get_ntc_res(di); ++ if (res < ntc_table[ntc_size - 1]) { ++ di->temperature = di->pdata->ntc_degree_from + ++ di->pdata->ntc_size - 1; ++ BAT_INFO("bat ntc upper max degree: R=%d\n", res); ++ } else if (res > ntc_table[0]) { ++ di->temperature = di->pdata->ntc_degree_from; ++ BAT_INFO("bat ntc lower min degree: R=%d\n", res); ++ } else { ++ for (i = 0; i < ntc_size; i++) { ++ if (res >= ntc_table[i]) ++ break; ++ } ++ ++ /* if first in, init old_temp */ ++ temp = (i + di->pdata->ntc_degree_from) * 10; ++ if (first_time == 1) { ++ di->temperature = temp; ++ old_temp = temp; ++ first_time = 0; ++ } ++ ++ /* ++ * compare with old one, it's invalid when over 50 ++ * and we should use old data. ++ */ ++ if (abs(temp - old_temp) > 50) ++ temp = old_temp; ++ else ++ old_temp = temp; ++ ++ di->temperature = temp; ++ DBG("<%s>. temperature = %d\n", ++ __func__, di->temperature); ++ rk818_bat_temp_notifier_callback(di->temperature / 10); ++ } ++ } ++} ++ ++static void rk818_bat_init_dsoc_algorithm(struct rk818_battery *di) ++{ ++ u8 buf; ++ int16_t rest = 0; ++ unsigned long soc_sec; ++ const char *mode_name[] = { "MODE_ZERO", "MODE_FINISH", ++ "MODE_SMOOTH_CHRG", "MODE_SMOOTH_DISCHRG", "MODE_SMOOTH", }; ++ ++ /* get rest */ ++ rest |= rk818_bat_read(di, RK818_CALC_REST_REGH) << 8; ++ rest |= rk818_bat_read(di, RK818_CALC_REST_REGL) << 0; ++ ++ /* get mode */ ++ buf = rk818_bat_read(di, RK818_MISC_MARK_REG); ++ di->algo_rest_mode = (buf & ALGO_REST_MODE_MSK) >> ALGO_REST_MODE_SHIFT; ++ ++ if (rk818_bat_get_chrg_status(di) == CHARGE_FINISH) { ++ if (di->algo_rest_mode == MODE_FINISH) { ++ soc_sec = di->fcc * 3600 / 100 / FINISH_CHRG_CUR1; ++ if ((rest / DIV(soc_sec)) > 0) { ++ if (di->dsoc < 100) { ++ di->dsoc++; ++ di->algo_rest_val = rest % soc_sec; ++ BAT_INFO("algorithm rest(%d) dsoc " ++ "inc: %d\n", ++ rest, di->dsoc); ++ } else { ++ di->algo_rest_val = 0; ++ } ++ } else { ++ di->algo_rest_val = rest; ++ } ++ } else { ++ di->algo_rest_val = rest; ++ } ++ } else { ++ /* charge speed up */ ++ if ((rest / 1000) > 0 && rk818_bat_chrg_online(di)) { ++ if (di->dsoc < di->rsoc) { ++ di->dsoc++; ++ di->algo_rest_val = rest % 1000; ++ BAT_INFO("algorithm rest(%d) dsoc inc: %d\n", ++ rest, di->dsoc); ++ } else { ++ di->algo_rest_val = 0; ++ } ++ /* discharge speed up */ ++ } else if (((rest / 1000) < 0) && !rk818_bat_chrg_online(di)) { ++ if (di->dsoc > di->rsoc) { ++ di->dsoc--; ++ di->algo_rest_val = rest % 1000; ++ BAT_INFO("algorithm rest(%d) dsoc sub: %d\n", ++ rest, di->dsoc); ++ } else { ++ di->algo_rest_val = 0; ++ } ++ } else { ++ di->algo_rest_val = rest; ++ } ++ } ++ ++ if (di->dsoc >= 100) ++ di->dsoc = 100; ++ else if (di->dsoc <= 0) ++ di->dsoc = 0; ++ ++ /* init current mode */ ++ di->voltage_avg = rk818_bat_get_avg_voltage(di); ++ di->current_avg = rk818_bat_get_avg_current(di); ++ if (rk818_bat_get_chrg_status(di) == CHARGE_FINISH) { ++ rk818_bat_finish_algo_prepare(di); ++ di->work_mode = MODE_FINISH; ++ } else { ++ rk818_bat_smooth_algo_prepare(di); ++ di->work_mode = MODE_SMOOTH; ++ } ++ ++ DBG("<%s>. init: org_rest=%d, rest=%d, mode=%s; " ++ "doc(x1000): zero=%d, chrg=%d, dischrg=%d, finish=%lu\n", ++ __func__, rest, di->algo_rest_val, mode_name[di->algo_rest_mode], ++ di->zero_dsoc, di->sm_chrg_dsoc, di->sm_dischrg_dsoc, ++ di->finish_base); ++} ++ ++static void rk818_bat_save_algo_rest(struct rk818_battery *di) ++{ ++ u8 buf, mode; ++ int16_t algo_rest = 0; ++ int tmp_soc; ++ int zero_rest = 0, sm_chrg_rest = 0; ++ int sm_dischrg_rest = 0, finish_rest = 0; ++ const char *mode_name[] = { "MODE_ZERO", "MODE_FINISH", ++ "MODE_SMOOTH_CHRG", "MODE_SMOOTH_DISCHRG", "MODE_SMOOTH", }; ++ ++ /* zero dischrg */ ++ tmp_soc = (di->zero_dsoc) / 1000; ++ if (tmp_soc == di->dsoc) ++ zero_rest = di->zero_dsoc - ((di->dsoc + 1) * 1000 - ++ MIN_ACCURACY); ++ ++ /* sm chrg */ ++ tmp_soc = di->sm_chrg_dsoc / 1000; ++ if (tmp_soc == di->dsoc) ++ sm_chrg_rest = di->sm_chrg_dsoc - di->dsoc * 1000; ++ ++ /* sm dischrg */ ++ tmp_soc = (di->sm_dischrg_dsoc) / 1000; ++ if (tmp_soc == di->dsoc) ++ sm_dischrg_rest = di->sm_dischrg_dsoc - ((di->dsoc + 1) * 1000 - ++ MIN_ACCURACY); ++ ++ /* last time is also finish chrg, then add last rest */ ++ if (di->algo_rest_mode == MODE_FINISH && di->algo_rest_val) ++ finish_rest = base2sec(di->finish_base) + di->algo_rest_val; ++ else ++ finish_rest = base2sec(di->finish_base); ++ ++ /* total calc */ ++ if ((rk818_bat_chrg_online(di) && (di->dsoc > di->rsoc)) || ++ (!rk818_bat_chrg_online(di) && (di->dsoc < di->rsoc)) || ++ (di->dsoc == di->rsoc)) { ++ di->algo_rest_val = 0; ++ algo_rest = 0; ++ DBG("<%s>. step1..\n", __func__); ++ } else if (di->work_mode == MODE_FINISH) { ++ algo_rest = finish_rest; ++ DBG("<%s>. step2..\n", __func__); ++ } else if (di->algo_rest_mode == MODE_FINISH) { ++ algo_rest = zero_rest + sm_dischrg_rest + sm_chrg_rest; ++ DBG("<%s>. step3..\n", __func__); ++ } else { ++ if (rk818_bat_chrg_online(di) && (di->dsoc < di->rsoc)) ++ algo_rest = sm_chrg_rest + di->algo_rest_val; ++ else if (!rk818_bat_chrg_online(di) && (di->dsoc > di->rsoc)) ++ algo_rest = zero_rest + sm_dischrg_rest + ++ di->algo_rest_val; ++ else ++ algo_rest = zero_rest + sm_dischrg_rest + sm_chrg_rest + ++ di->algo_rest_val; ++ DBG("<%s>. step4..\n", __func__); ++ } ++ ++ /* check mode */ ++ if ((di->work_mode == MODE_FINISH) || (di->work_mode == MODE_ZERO)) { ++ mode = di->work_mode; ++ } else {/* MODE_SMOOTH */ ++ if (di->sm_linek > 0) ++ mode = MODE_SMOOTH_CHRG; ++ else ++ mode = MODE_SMOOTH_DISCHRG; ++ } ++ ++ /* save mode */ ++ buf = rk818_bat_read(di, RK818_MISC_MARK_REG); ++ buf &= ~ALGO_REST_MODE_MSK; ++ buf |= (mode << ALGO_REST_MODE_SHIFT); ++ rk818_bat_write(di, RK818_MISC_MARK_REG, buf); ++ ++ /* save rest */ ++ buf = (algo_rest >> 8) & 0xff; ++ rk818_bat_write(di, RK818_CALC_REST_REGH, buf); ++ buf = (algo_rest >> 0) & 0xff; ++ rk818_bat_write(di, RK818_CALC_REST_REGL, buf); ++ ++ DBG("<%s>. rest: algo=%d, mode=%s, last_rest=%d; zero=%d, " ++ "chrg=%d, dischrg=%d, finish=%lu\n", ++ __func__, algo_rest, mode_name[mode], di->algo_rest_val, zero_rest, ++ sm_chrg_rest, sm_dischrg_rest, base2sec(di->finish_base)); ++} ++ ++static void rk818_bat_save_data(struct rk818_battery *di) ++{ ++ rk818_bat_save_dsoc(di, di->dsoc); ++ rk818_bat_save_cap(di, di->remain_cap); ++ rk818_bat_save_algo_rest(di); ++} ++ ++static void rk818_battery_work(struct work_struct *work) ++{ ++ struct rk818_battery *di = ++ container_of(work, struct rk818_battery, bat_delay_work.work); ++ ++ rk818_bat_update_info(di); ++ rk818_bat_wait_finish_sig(di); ++ rk818_bat_rsoc_daemon(di); ++ rk818_bat_update_temperature(di); ++ rk818_bat_display_smooth(di); ++ rk818_bat_power_supply_changed(di); ++ rk818_bat_save_data(di); ++ rk818_bat_debug_info(di); ++ ++ queue_delayed_work(di->bat_monitor_wq, &di->bat_delay_work, ++ msecs_to_jiffies(di->monitor_ms)); ++} ++ ++static irqreturn_t rk818_vb_low_irq(int irq, void *bat) ++{ ++ struct rk818_battery *di = (struct rk818_battery *)bat; ++ ++ di->dsoc = 0; ++ rk_send_wakeup_key(); ++ BAT_INFO("lower power yet, power off system! v=%d, c=%d, dsoc=%d\n", ++ di->voltage_avg, di->current_avg, di->dsoc); ++ ++ return IRQ_HANDLED; ++} ++ ++static void rk818_bat_init_sysfs(struct rk818_battery *di) ++{ ++ int i, ret; ++ ++ for (i = 0; i < ARRAY_SIZE(rk818_bat_attr); i++) { ++ ret = sysfs_create_file(&di->dev->kobj, ++ &rk818_bat_attr[i].attr); ++ if (ret) ++ dev_err(di->dev, "create bat node(%s) error\n", ++ rk818_bat_attr[i].attr.name); ++ } ++} ++ ++static int rk818_bat_init_irqs(struct rk818_battery *di) ++{ ++ struct rk808 *rk818 = di->rk818; ++ struct platform_device *pdev = di->pdev; ++ int ret, vb_lo_irq; ++ ++ vb_lo_irq = regmap_irq_get_virq(rk818->irq_data, RK818_IRQ_VB_LO); ++ if (vb_lo_irq < 0) { ++ dev_err(di->dev, "vb_lo_irq request failed!\n"); ++ return vb_lo_irq; ++ } ++ ++ ret = devm_request_threaded_irq(di->dev, vb_lo_irq, NULL, ++ rk818_vb_low_irq, ++ IRQF_TRIGGER_HIGH | IRQF_ONESHOT, ++ "rk818_vb_low", di); ++ if (ret) { ++ dev_err(&pdev->dev, "vb_lo_irq request failed!\n"); ++ return ret; ++ } ++ enable_irq_wake(vb_lo_irq); ++ ++ return 0; ++} ++ ++static void rk818_bat_init_info(struct rk818_battery *di) ++{ ++ di->design_cap = di->pdata->design_capacity; ++ di->qmax = di->pdata->design_qmax; ++ di->bat_res = di->pdata->bat_res; ++ di->monitor_ms = di->pdata->monitor_sec * TIMER_MS_COUNTS; ++ di->boot_base = POWER_ON_SEC_BASE; ++ di->res_div = (di->pdata->sample_res == SAMPLE_RES_20MR) ? ++ SAMPLE_RES_DIV1 : SAMPLE_RES_DIV2; ++} ++ ++static time64_t rk818_get_rtc_sec(void) ++{ ++ int err; ++ struct rtc_time tm; ++ struct rtc_device *rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE); ++ ++ err = rtc_read_time(rtc, &tm); ++ if (err) { ++ dev_err(rtc->dev.parent, "read hardware clk failed\n"); ++ return 0; ++ } ++ ++ err = rtc_valid_tm(&tm); ++ if (err) { ++ dev_err(rtc->dev.parent, "invalid date time\n"); ++ return 0; ++ } ++ ++ return rtc_tm_to_time64(&tm); ++} ++ ++static int rk818_bat_rtc_sleep_sec(struct rk818_battery *di) ++{ ++ int interval_sec; ++ ++ interval_sec = rk818_get_rtc_sec() - di->rtc_base; ++ ++ return (interval_sec > 0) ? interval_sec : 0; ++} ++ ++static void rk818_bat_set_shtd_vol(struct rk818_battery *di) ++{ ++ u8 val; ++ ++ /* set vbat lowest 3.0v shutdown */ ++ val = rk818_bat_read(di, RK818_VB_MON_REG); ++ val &= ~(VBAT_LOW_VOL_MASK | VBAT_LOW_ACT_MASK); ++ val |= (RK818_VBAT_LOW_3V0 | EN_VABT_LOW_SHUT_DOWN); ++ rk818_bat_write(di, RK818_VB_MON_REG, val); ++ ++ /* disable low irq */ ++ rk818_bat_set_bits(di, RK818_INT_STS_MSK_REG1, ++ VB_LOW_INT_EN, VB_LOW_INT_EN); ++} ++ ++static void rk818_bat_init_fg(struct rk818_battery *di) ++{ ++ rk818_bat_enable_gauge(di); ++ rk818_bat_init_voltage_kb(di); ++ rk818_bat_init_coffset(di); ++ rk818_bat_set_relax_sample(di); ++ rk818_bat_set_ioffset_sample(di); ++ rk818_bat_set_ocv_sample(di); ++ rk818_bat_init_ts1_detect(di); ++ rk818_bat_init_rsoc(di); ++ rk818_bat_init_coulomb_cap(di, di->nac); ++ rk818_bat_init_age_algorithm(di); ++ rk818_bat_init_chrg_config(di); ++ rk818_bat_set_shtd_vol(di); ++ rk818_bat_init_zero_table(di); ++ rk818_bat_init_caltimer(di); ++ rk818_bat_init_dsoc_algorithm(di); ++ ++ di->voltage_avg = rk818_bat_get_avg_voltage(di); ++ di->voltage_ocv = rk818_bat_get_ocv_voltage(di); ++ di->voltage_relax = rk818_bat_get_relax_voltage(di); ++ di->current_avg = rk818_bat_get_avg_current(di); ++ di->remain_cap = rk818_bat_get_coulomb_cap(di); ++ di->dbg_pwr_dsoc = di->dsoc; ++ di->dbg_pwr_rsoc = di->rsoc; ++ di->dbg_pwr_vol = di->voltage_avg; ++ ++ rk818_bat_dump_regs(di, 0x99, 0xee); ++ DBG("nac=%d cap=%d ov=%d v=%d rv=%d dl=%d rl=%d c=%d\n", ++ di->nac, di->remain_cap, di->voltage_ocv, di->voltage_avg, ++ di->voltage_relax, di->dsoc, di->rsoc, di->current_avg); ++} ++ ++#ifdef CONFIG_OF ++static int rk818_bat_parse_dt(struct rk818_battery *di) ++{ ++ u32 out_value; ++ int length, ret; ++ size_t size; ++ struct device_node *np = di->dev->of_node; ++ struct battery_platform_data *pdata; ++ struct device *dev = di->dev; ++ ++ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); ++ if (!pdata) ++ return -ENOMEM; ++ ++ di->pdata = pdata; ++ /* init default param */ ++ pdata->bat_res = DEFAULT_BAT_RES; ++ pdata->monitor_sec = DEFAULT_MONITOR_SEC; ++ pdata->pwroff_vol = DEFAULT_PWROFF_VOL_THRESD; ++ pdata->sleep_exit_current = DEFAULT_SLP_EXIT_CUR; ++ pdata->sleep_enter_current = DEFAULT_SLP_ENTER_CUR; ++ pdata->bat_mode = MODE_BATTARY; ++ pdata->max_soc_offset = DEFAULT_MAX_SOC_OFFSET; ++ pdata->sample_res = DEFAULT_SAMPLE_RES; ++ pdata->energy_mode = DEFAULT_ENERGY_MODE; ++ pdata->fb_temp = DEFAULT_FB_TEMP; ++ pdata->zero_reserve_dsoc = DEFAULT_ZERO_RESERVE_DSOC; ++ ++ /* parse necessary param */ ++ if (!of_find_property(np, "ocv_table", &length)) { ++ dev_err(dev, "ocv_table not found!\n"); ++ return -EINVAL; ++ } ++ ++ pdata->ocv_size = length / sizeof(u32); ++ if (pdata->ocv_size <= 0) { ++ dev_err(dev, "invalid ocv table\n"); ++ return -EINVAL; ++ } ++ ++ size = sizeof(*pdata->ocv_table) * pdata->ocv_size; ++ pdata->ocv_table = devm_kzalloc(di->dev, size, GFP_KERNEL); ++ if (!pdata->ocv_table) ++ return -ENOMEM; ++ ++ ret = of_property_read_u32_array(np, "ocv_table", ++ pdata->ocv_table, ++ pdata->ocv_size); ++ if (ret < 0) ++ return ret; ++ ++ ret = of_property_read_u32(np, "design_capacity", &out_value); ++ if (ret < 0) { ++ dev_err(dev, "design_capacity not found!\n"); ++ return ret; ++ } ++ pdata->design_capacity = out_value; ++ ++ ret = of_property_read_u32(np, "design_qmax", &out_value); ++ if (ret < 0) { ++ dev_err(dev, "design_qmax not found!\n"); ++ return ret; ++ } ++ pdata->design_qmax = out_value; ++ ret = of_property_read_u32(np, "max_chrg_voltage", &out_value); ++ if (ret < 0) { ++ dev_err(dev, "max_chrg_voltage missing!\n"); ++ return ret; ++ } ++ pdata->max_chrg_voltage = out_value; ++ if (out_value >= 4300) ++ pdata->zero_algorithm_vol = DEFAULT_ALGR_VOL_THRESD2; ++ else ++ pdata->zero_algorithm_vol = DEFAULT_ALGR_VOL_THRESD1; ++ ++ ret = of_property_read_u32(np, "fb_temperature", &pdata->fb_temp); ++ if (ret < 0) ++ dev_err(dev, "fb_temperature missing!\n"); ++ ++ ret = of_property_read_u32(np, "sample_res", &pdata->sample_res); ++ if (ret < 0) ++ dev_err(dev, "sample_res missing!\n"); ++ ++ ret = of_property_read_u32(np, "energy_mode", &pdata->energy_mode); ++ if (ret < 0) ++ dev_err(dev, "energy_mode missing!\n"); ++ ++ ret = of_property_read_u32(np, "max_soc_offset", ++ &pdata->max_soc_offset); ++ if (ret < 0) ++ dev_err(dev, "max_soc_offset missing!\n"); ++ ++ ret = of_property_read_u32(np, "monitor_sec", &pdata->monitor_sec); ++ if (ret < 0) ++ dev_err(dev, "monitor_sec missing!\n"); ++ ++ ret = of_property_read_u32(np, "zero_algorithm_vol", ++ &pdata->zero_algorithm_vol); ++ if (ret < 0) ++ dev_err(dev, "zero_algorithm_vol missing!\n"); ++ ++ ret = of_property_read_u32(np, "zero_reserve_dsoc", ++ &pdata->zero_reserve_dsoc); ++ ++ ret = of_property_read_u32(np, "virtual_power", &pdata->bat_mode); ++ if (ret < 0) ++ dev_err(dev, "virtual_power missing!\n"); ++ ++ ret = of_property_read_u32(np, "bat_res", &pdata->bat_res); ++ if (ret < 0) ++ dev_err(dev, "bat_res missing!\n"); ++ ++ ret = of_property_read_u32(np, "sleep_enter_current", ++ &pdata->sleep_enter_current); ++ if (ret < 0) ++ dev_err(dev, "sleep_enter_current missing!\n"); ++ ++ ret = of_property_read_u32(np, "sleep_exit_current", ++ &pdata->sleep_exit_current); ++ if (ret < 0) ++ dev_err(dev, "sleep_exit_current missing!\n"); ++ ++ ret = of_property_read_u32(np, "power_off_thresd", &pdata->pwroff_vol); ++ if (ret < 0) ++ dev_err(dev, "power_off_thresd missing!\n"); ++ ++ if (!of_find_property(np, "ntc_table", &length)) { ++ pdata->ntc_size = 0; ++ } else { ++ /* get ntc degree base value */ ++ ret = of_property_read_u32_index(np, "ntc_degree_from", 1, ++ &pdata->ntc_degree_from); ++ if (ret) { ++ dev_err(dev, "invalid ntc_degree_from\n"); ++ return -EINVAL; ++ } ++ ++ of_property_read_u32_index(np, "ntc_degree_from", 0, ++ &out_value); ++ if (out_value) ++ pdata->ntc_degree_from = -pdata->ntc_degree_from; ++ ++ pdata->ntc_size = length / sizeof(u32); ++ } ++ ++ if (pdata->ntc_size) { ++ size = sizeof(*pdata->ntc_table) * pdata->ntc_size; ++ pdata->ntc_table = devm_kzalloc(di->dev, size, GFP_KERNEL); ++ if (!pdata->ntc_table) ++ return -ENOMEM; ++ ++ ret = of_property_read_u32_array(np, "ntc_table", ++ pdata->ntc_table, ++ pdata->ntc_size); ++ if (ret < 0) ++ return ret; ++ } ++ ++ DBG("the battery dts info dump:\n" ++ "bat_res:%d\n" ++ "design_capacity:%d\n" ++ "design_qmax :%d\n" ++ "sleep_enter_current:%d\n" ++ "sleep_exit_current:%d\n" ++ "zero_algorithm_vol:%d\n" ++ "zero_reserve_dsoc:%d\n" ++ "monitor_sec:%d\n" ++ "max_soc_offset:%d\n" ++ "virtual_power:%d\n" ++ "pwroff_vol:%d\n" ++ "sample_res:%d\n" ++ "ntc_size=%d\n" ++ "ntc_degree_from:%d\n" ++ "ntc_degree_to:%d\n", ++ pdata->bat_res, pdata->design_capacity, pdata->design_qmax, ++ pdata->sleep_enter_current, pdata->sleep_exit_current, ++ pdata->zero_algorithm_vol, pdata->zero_reserve_dsoc, ++ pdata->monitor_sec, ++ pdata->max_soc_offset, pdata->bat_mode, pdata->pwroff_vol, ++ pdata->sample_res, pdata->ntc_size, pdata->ntc_degree_from, ++ pdata->ntc_degree_from + pdata->ntc_size - 1 ++ ); ++ ++ return 0; ++} ++#else ++static int rk818_bat_parse_dt(struct rk818_battery *di) ++{ ++ return -ENODEV; ++} ++#endif ++ ++static const struct of_device_id rk818_battery_of_match[] = { ++ {.compatible = "rk818-battery",}, ++ { }, ++}; ++ ++static int rk818_battery_probe(struct platform_device *pdev) ++{ ++ const struct of_device_id *of_id = ++ of_match_device(rk818_battery_of_match, &pdev->dev); ++ struct rk818_battery *di; ++ struct rk808 *rk818 = dev_get_drvdata(pdev->dev.parent); ++ int ret; ++ ++ if (!of_id) { ++ dev_err(&pdev->dev, "Failed to find matching dt id\n"); ++ return -ENODEV; ++ } ++ ++ di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL); ++ if (!di) ++ return -ENOMEM; ++ ++ di->rk818 = rk818; ++ di->pdev = pdev; ++ di->dev = &pdev->dev; ++ di->regmap = rk818->regmap; ++ platform_set_drvdata(pdev, di); ++ ++ ret = rk818_bat_parse_dt(di); ++ if (ret < 0) { ++ dev_err(di->dev, "rk818 battery parse dt failed!\n"); ++ return ret; ++ } ++ ++ if (!is_rk818_bat_exist(di)) { ++ di->pdata->bat_mode = MODE_VIRTUAL; ++ dev_err(di->dev, "no battery, virtual power mode\n"); ++ } ++ ++ ret = rk818_bat_init_irqs(di); ++ if (ret != 0) { ++ dev_err(di->dev, "rk818 bat init irqs failed!\n"); ++ return ret; ++ } ++ ++ ret = rk818_bat_init_power_supply(di); ++ if (ret) { ++ dev_err(di->dev, "rk818 power supply register failed!\n"); ++ return ret; ++ } ++ ++ rk818_bat_init_info(di); ++ rk818_bat_init_fg(di); ++ rk818_bat_init_sysfs(di); ++ rk818_bat_register_fb_notify(di); ++ //wake_lock_init(&di->wake_lock, WAKE_LOCK_SUSPEND, "rk818_bat_lock"); ++ di->bat_monitor_wq = alloc_ordered_workqueue("%s", ++ WQ_MEM_RECLAIM | WQ_FREEZABLE, "rk818-bat-monitor-wq"); ++ INIT_DELAYED_WORK(&di->bat_delay_work, rk818_battery_work); ++ queue_delayed_work(di->bat_monitor_wq, &di->bat_delay_work, ++ msecs_to_jiffies(TIMER_MS_COUNTS * 5)); ++ ++ BAT_INFO("driver version %s\n", DRIVER_VERSION); ++ ++ return ret; ++} ++ ++static int rk818_battery_suspend(struct platform_device *dev, ++ pm_message_t state) ++{ ++ struct rk818_battery *di = platform_get_drvdata(dev); ++ u8 val, st; ++ ++ cancel_delayed_work_sync(&di->bat_delay_work); ++ ++ di->s2r = false; ++ di->sleep_chrg_online = rk818_bat_chrg_online(di); ++ di->sleep_chrg_status = rk818_bat_get_chrg_status(di); ++ di->current_avg = rk818_bat_get_avg_current(di); ++ di->remain_cap = rk818_bat_get_coulomb_cap(di); ++ di->rsoc = rk818_bat_get_rsoc(di); ++ di->rtc_base = rk818_get_rtc_sec(); ++ rk818_bat_save_data(di); ++ st = (rk818_bat_read(di, RK818_SUP_STS_REG) & CHRG_STATUS_MSK) >> 4; ++ ++ /* if not CHARGE_FINISH, reinit finish_base. ++ * avoid sleep loop between suspend and resume ++ */ ++ if (di->sleep_chrg_status != CHARGE_FINISH) ++ di->finish_base = get_boot_sec(); ++ ++ /* avoid: enter suspend from MODE_ZERO: load from heavy to light */ ++ if ((di->work_mode == MODE_ZERO) && ++ (di->sleep_chrg_online) && (di->current_avg >= 0)) { ++ DBG("suspend: MODE_ZERO exit...\n"); ++ /* it need't do prepare for mode finish and smooth, it will ++ * be done in display_smooth ++ */ ++ if (di->sleep_chrg_status == CHARGE_FINISH) { ++ di->work_mode = MODE_FINISH; ++ di->finish_base = get_boot_sec(); ++ } else { ++ di->work_mode = MODE_SMOOTH; ++ rk818_bat_smooth_algo_prepare(di); ++ } ++ } ++ ++ /* set vbat low than 3.4v to generate a wakeup irq */ ++ val = rk818_bat_read(di, RK818_VB_MON_REG); ++ val &= (~(VBAT_LOW_VOL_MASK | VBAT_LOW_ACT_MASK)); ++ val |= (RK818_VBAT_LOW_3V4 | EN_VBAT_LOW_IRQ); ++ rk818_bat_write(di, RK818_VB_MON_REG, val); ++ rk818_bat_set_bits(di, RK818_INT_STS_MSK_REG1, VB_LOW_INT_EN, 0); ++ ++ BAT_INFO("suspend: dl=%d rl=%d c=%d v=%d cap=%d at=%ld ch=%d st=%s\n", ++ di->dsoc, di->rsoc, di->current_avg, ++ rk818_bat_get_avg_voltage(di), rk818_bat_get_coulomb_cap(di), ++ di->sleep_dischrg_sec, di->sleep_chrg_online, bat_status[st]); ++ ++ return 0; ++} ++ ++static int rk818_battery_resume(struct platform_device *dev) ++{ ++ struct rk818_battery *di = platform_get_drvdata(dev); ++ int interval_sec, time_step, pwroff_vol; ++ u8 val, st; ++ ++ di->s2r = true; ++ di->current_avg = rk818_bat_get_avg_current(di); ++ di->voltage_relax = rk818_bat_get_relax_voltage(di); ++ di->voltage_avg = rk818_bat_get_avg_voltage(di); ++ di->remain_cap = rk818_bat_get_coulomb_cap(di); ++ di->rsoc = rk818_bat_get_rsoc(di); ++ interval_sec = rk818_bat_rtc_sleep_sec(di); ++ di->sleep_sum_sec += interval_sec; ++ pwroff_vol = di->pdata->pwroff_vol; ++ st = (rk818_bat_read(di, RK818_SUP_STS_REG) & CHRG_STATUS_MSK) >> 4; ++ ++ if (!di->sleep_chrg_online) { ++ /* only add up discharge sleep seconds */ ++ di->sleep_dischrg_sec += interval_sec; ++ if (di->voltage_avg <= pwroff_vol + 50) ++ time_step = DISCHRG_TIME_STEP1; ++ else ++ time_step = DISCHRG_TIME_STEP2; ++ } ++ ++ BAT_INFO("resume: dl=%d rl=%d c=%d v=%d rv=%d " ++ "cap=%d dt=%d at=%ld ch=%d st=%s\n", ++ di->dsoc, di->rsoc, di->current_avg, di->voltage_avg, ++ di->voltage_relax, rk818_bat_get_coulomb_cap(di), interval_sec, ++ di->sleep_dischrg_sec, di->sleep_chrg_online, bat_status[st]); ++ ++ /* sleep: enough time and discharge */ ++ if ((di->sleep_dischrg_sec > time_step) && (!di->sleep_chrg_online)) { ++ if (rk818_bat_sleep_dischrg(di)) ++ di->sleep_dischrg_sec = 0; ++ } ++ ++ rk818_bat_save_data(di); ++ ++ /* set vbat lowest 3.0v shutdown */ ++ val = rk818_bat_read(di, RK818_VB_MON_REG); ++ val &= ~(VBAT_LOW_VOL_MASK | VBAT_LOW_ACT_MASK); ++ val |= (RK818_VBAT_LOW_3V0 | EN_VABT_LOW_SHUT_DOWN); ++ rk818_bat_write(di, RK818_VB_MON_REG, val); ++ rk818_bat_set_bits(di, RK818_INT_STS_MSK_REG1, ++ VB_LOW_INT_EN, VB_LOW_INT_EN); ++ ++ /* charge/lowpower lock: for battery work to update dsoc and rsoc */ ++ // if ((di->sleep_chrg_online) || ++ // (!di->sleep_chrg_online && di->voltage_avg < di->pdata->pwroff_vol)) ++ // wake_lock_timeout(&di->wake_lock, msecs_to_jiffies(2000)); ++ ++ queue_delayed_work(di->bat_monitor_wq, &di->bat_delay_work, ++ msecs_to_jiffies(1000)); ++ ++ return 0; ++} ++ ++static void rk818_battery_shutdown(struct platform_device *dev) ++{ ++ u8 cnt = 0; ++ struct rk818_battery *di = platform_get_drvdata(dev); ++ ++ cancel_delayed_work_sync(&di->bat_delay_work); ++ cancel_delayed_work_sync(&di->calib_delay_work); ++ rk818_bat_unregister_fb_notify(di); ++ del_timer(&di->caltimer); ++ if (base2sec(di->boot_base) < REBOOT_PERIOD_SEC) ++ cnt = rk818_bat_check_reboot(di); ++ else ++ rk818_bat_save_reboot_cnt(di, 0); ++ ++ BAT_INFO("shutdown: dl=%d rl=%d c=%d v=%d cap=%d f=%d ch=%d n=%d " ++ "mode=%d rest=%d\n", ++ di->dsoc, di->rsoc, di->current_avg, di->voltage_avg, ++ di->remain_cap, di->fcc, rk818_bat_chrg_online(di), cnt, ++ di->algo_rest_mode, di->algo_rest_val); ++} ++ ++static struct platform_driver rk818_battery_driver = { ++ .probe = rk818_battery_probe, ++ .suspend = rk818_battery_suspend, ++ .resume = rk818_battery_resume, ++ .shutdown = rk818_battery_shutdown, ++ .driver = { ++ .name = "rk818-battery", ++ .of_match_table = rk818_battery_of_match, ++ }, ++}; ++ ++static int __init battery_init(void) ++{ ++ return platform_driver_register(&rk818_battery_driver); ++} ++fs_initcall_sync(battery_init); ++ ++static void __exit battery_exit(void) ++{ ++ platform_driver_unregister(&rk818_battery_driver); ++} ++module_exit(battery_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:rk818-battery"); ++MODULE_AUTHOR("chenjh"); +\ No newline at end of file +diff --git a/drivers/power/supply/rk818_battery.h b/drivers/power/supply/rk818_battery.h +new file mode 100644 +index 00000000..2f4430a +--- /dev/null ++++ b/drivers/power/supply/rk818_battery.h +@@ -0,0 +1,168 @@ ++/* ++ * rk818_battery.h: fuel gauge driver structures ++ * ++ * Copyright (C) 2016 Rockchip Electronics Co., Ltd ++ * Author: chenjh ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope 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. ++ */ ++ ++#ifndef RK818_BATTERY ++#define RK818_BATTERY ++ ++/* RK818_INT_STS_MSK_REG2 */ ++#define PLUG_IN_MSK BIT(0) ++#define PLUG_OUT_MSK BIT(1) ++#define CHRG_CVTLMT_INT_MSK BIT(6) ++ ++/* RK818_TS_CTRL_REG */ ++#define GG_EN BIT(7) ++#define ADC_CUR_EN BIT(6) ++#define ADC_TS1_EN BIT(5) ++#define ADC_TS2_EN BIT(4) ++#define TS1_CUR_MSK 0x03 ++ ++/* RK818_GGCON */ ++#define OCV_SAMP_MIN_MSK 0x0c ++#define OCV_SAMP_8MIN (0x00 << 2) ++ ++#define ADC_CAL_MIN_MSK 0x30 ++#define ADC_CAL_8MIN (0x00 << 4) ++#define ADC_CUR_MODE BIT(1) ++ ++/* RK818_GGSTS */ ++#define BAT_CON BIT(4) ++#define RELAX_VOL1_UPD BIT(3) ++#define RELAX_VOL2_UPD BIT(2) ++#define RELAX_VOL12_UPD_MSK (RELAX_VOL1_UPD | RELAX_VOL2_UPD) ++ ++/* RK818_SUP_STS_REG */ ++#define CHRG_STATUS_MSK 0x70 ++#define BAT_EXS BIT(7) ++#define CHARGE_OFF (0x0 << 4) ++#define DEAD_CHARGE (0x1 << 4) ++#define TRICKLE_CHARGE (0x2 << 4) ++#define CC_OR_CV (0x3 << 4) ++#define CHARGE_FINISH (0x4 << 4) ++#define USB_OVER_VOL (0x5 << 4) ++#define BAT_TMP_ERR (0x6 << 4) ++#define TIMER_ERR (0x7 << 4) ++#define USB_VLIMIT_EN BIT(3) ++#define USB_CLIMIT_EN BIT(2) ++#define USB_EXIST BIT(1) ++#define USB_EFF BIT(0) ++ ++/* RK818_USB_CTRL_REG */ ++#define CHRG_CT_EN BIT(7) ++#define FINISH_CUR_MSK 0xc0 ++#define TEMP_105C (0x02 << 2) ++#define FINISH_100MA (0x00 << 6) ++#define FINISH_150MA (0x01 << 6) ++#define FINISH_200MA (0x02 << 6) ++#define FINISH_250MA (0x03 << 6) ++ ++/* RK818_CHRG_CTRL_REG3 */ ++#define CHRG_TERM_MODE_MSK BIT(5) ++#define CHRG_TERM_ANA_SIGNAL (0 << 5) ++#define CHRG_TERM_DIG_SIGNAL BIT(5) ++#define CHRG_TIMER_CCCV_EN BIT(2) ++#define CHRG_EN BIT(7) ++ ++/* RK818_VB_MON_REG */ ++#define RK818_VBAT_LOW_3V0 0x02 ++#define RK818_VBAT_LOW_3V4 0x06 ++#define PLUG_IN_STS BIT(6) ++ ++/* RK818_THERMAL_REG */ ++#define FB_TEMP_MSK 0x0c ++#define HOTDIE_STS BIT(1) ++ ++/* RK818_INT_STS_MSK_REG1 */ ++#define VB_LOW_INT_EN BIT(1) ++ ++/* RK818_MISC_MARK_REG */ ++#define FG_INIT BIT(5) ++#define FG_RESET_LATE BIT(4) ++#define FG_RESET_NOW BIT(3) ++#define ALGO_REST_MODE_MSK (0xc0) ++#define ALGO_REST_MODE_SHIFT 6 ++ ++/* bit shift */ ++#define FB_TEMP_SHIFT 2 ++ ++/* parse ocv table param */ ++#define TIMER_MS_COUNTS 1000 ++#define MAX_PERCENTAGE 100 ++#define MAX_INTERPOLATE 1000 ++#define MAX_INT 0x7FFF ++ ++#define DRIVER_VERSION "7.1" ++ ++struct battery_platform_data { ++ u32 *ocv_table; ++ u32 *zero_table; ++ u32 *ntc_table; ++ u32 ocv_size; ++ u32 max_chrg_voltage; ++ u32 ntc_size; ++ int ntc_degree_from; ++ u32 pwroff_vol; ++ u32 monitor_sec; ++ u32 zero_algorithm_vol; ++ u32 zero_reserve_dsoc; ++ u32 bat_res; ++ u32 design_capacity; ++ u32 design_qmax; ++ u32 sleep_enter_current; ++ u32 sleep_exit_current; ++ u32 max_soc_offset; ++ u32 sample_res; ++ u32 bat_mode; ++ u32 fb_temp; ++ u32 energy_mode; ++ u32 cccv_hour; ++ u32 ntc_uA; ++ u32 ntc_factor; ++}; ++ ++enum work_mode { ++ MODE_ZERO = 0, ++ MODE_FINISH, ++ MODE_SMOOTH_CHRG, ++ MODE_SMOOTH_DISCHRG, ++ MODE_SMOOTH, ++}; ++ ++enum bat_mode { ++ MODE_BATTARY = 0, ++ MODE_VIRTUAL, ++}; ++ ++static const u16 feedback_temp_array[] = { ++ 85, 95, 105, 115 ++}; ++ ++static const u16 chrg_vol_sel_array[] = { ++ 4050, 4100, 4150, 4200, 4250, 4300, 4350 ++}; ++ ++static const u16 chrg_cur_sel_array[] = { ++ 1000, 1200, 1400, 1600, 1800, 2000, 2250, 2400, 2600, 2800, 3000 ++}; ++ ++static const u16 chrg_cur_input_array[] = { ++ 450, 80, 850, 1000, 1250, 1500, 1750, 2000, 2250, 2500, 2750, 3000 ++}; ++ ++void kernel_power_off(void); ++int rk818_bat_temp_notifier_register(struct notifier_block *nb); ++int rk818_bat_temp_notifier_unregister(struct notifier_block *nb); ++ ++#endif +\ No newline at end of file +diff --git a/include/linux/mfd/rk808.h b/include/linux/mfd/rk808.h +index 2ec0520..5e33996 100644 +--- a/include/linux/mfd/rk808.h ++++ b/include/linux/mfd/rk808.h +@@ -138,6 +138,8 @@ enum rk818_reg { + RK818_ID_OTG_SWITCH, + }; + ++#define RK818_VB_MON_REG 0x21 ++#define RK818_THERMAL_REG 0x22 + #define RK818_DCDC_EN_REG 0x23 + #define RK818_LDO_EN_REG 0x24 + #define RK818_SLEEP_SET_OFF_REG1 0x25 +@@ -184,13 +186,90 @@ enum rk818_reg { + #define RK818_INT_STS_REG2 0x4e + #define RK818_INT_STS_MSK_REG2 0x4f + #define RK818_IO_POL_REG 0x50 ++#define RK818_OTP_VDD_EN_REG 0x51 + #define RK818_H5V_EN_REG 0x52 + #define RK818_SLEEP_SET_OFF_REG3 0x53 + #define RK818_BOOST_LDO9_ON_VSEL_REG 0x54 + #define RK818_BOOST_LDO9_SLP_VSEL_REG 0x55 + #define RK818_BOOST_CTRL_REG 0x56 +-#define RK818_DCDC_ILMAX 0x90 ++#define RK818_DCDC_ILMAX_REG 0x90 ++#define RK818_CHRG_COMP_REG 0x9a ++#define RK818_SUP_STS_REG 0xa0 + #define RK818_USB_CTRL_REG 0xa1 ++#define RK818_CHRG_CTRL_REG1 0xa3 ++#define RK818_CHRG_CTRL_REG2 0xa4 ++#define RK818_CHRG_CTRL_REG3 0xa5 ++#define RK818_BAT_CTRL_REG 0xa6 ++#define RK818_BAT_HTS_TS1_REG 0xa8 ++#define RK818_BAT_LTS_TS1_REG 0xa9 ++#define RK818_BAT_HTS_TS2_REG 0xaa ++#define RK818_BAT_LTS_TS2_REG 0xab ++#define RK818_TS_CTRL_REG 0xac ++#define RK818_ADC_CTRL_REG 0xad ++#define RK818_ON_SOURCE_REG 0xae ++#define RK818_OFF_SOURCE_REG 0xaf ++#define RK818_GGCON_REG 0xb0 ++#define RK818_GGSTS_REG 0xb1 ++#define RK818_FRAME_SMP_INTERV_REG 0xb2 ++#define RK818_AUTO_SLP_CUR_THR_REG 0xb3 ++#define RK818_GASCNT_CAL_REG3 0xb4 ++#define RK818_GASCNT_CAL_REG2 0xb5 ++#define RK818_GASCNT_CAL_REG1 0xb6 ++#define RK818_GASCNT_CAL_REG0 0xb7 ++#define RK818_GASCNT3_REG 0xb8 ++#define RK818_GASCNT2_REG 0xb9 ++#define RK818_GASCNT1_REG 0xba ++#define RK818_GASCNT0_REG 0xbb ++#define RK818_BAT_CUR_AVG_REGH 0xbc ++#define RK818_BAT_CUR_AVG_REGL 0xbd ++#define RK818_TS1_ADC_REGH 0xbe ++#define RK818_TS1_ADC_REGL 0xbf ++#define RK818_TS2_ADC_REGH 0xc0 ++#define RK818_TS2_ADC_REGL 0xc1 ++#define RK818_BAT_OCV_REGH 0xc2 ++#define RK818_BAT_OCV_REGL 0xc3 ++#define RK818_BAT_VOL_REGH 0xc4 ++#define RK818_BAT_VOL_REGL 0xc5 ++#define RK818_RELAX_ENTRY_THRES_REGH 0xc6 ++#define RK818_RELAX_ENTRY_THRES_REGL 0xc7 ++#define RK818_RELAX_EXIT_THRES_REGH 0xc8 ++#define RK818_RELAX_EXIT_THRES_REGL 0xc9 ++#define RK818_RELAX_VOL1_REGH 0xca ++#define RK818_RELAX_VOL1_REGL 0xcb ++#define RK818_RELAX_VOL2_REGH 0xcc ++#define RK818_RELAX_VOL2_REGL 0xcd ++#define RK818_BAT_CUR_R_CALC_REGH 0xce ++#define RK818_BAT_CUR_R_CALC_REGL 0xcf ++#define RK818_BAT_VOL_R_CALC_REGH 0xd0 ++#define RK818_BAT_VOL_R_CALC_REGL 0xd1 ++#define RK818_CAL_OFFSET_REGH 0xd2 ++#define RK818_CAL_OFFSET_REGL 0xd3 ++#define RK818_NON_ACT_TIMER_CNT_REG 0xd4 ++#define RK818_VCALIB0_REGH 0xd5 ++#define RK818_VCALIB0_REGL 0xd6 ++#define RK818_VCALIB1_REGH 0xd7 ++#define RK818_VCALIB1_REGL 0xd8 ++#define RK818_IOFFSET_REGH 0xdd ++#define RK818_IOFFSET_REGL 0xde ++#define RK818_SOC_REG 0xe0 ++#define RK818_REMAIN_CAP_REG3 0xe1 ++#define RK818_REMAIN_CAP_REG2 0xe2 ++#define RK818_REMAIN_CAP_REG1 0xe3 ++#define RK818_REMAIN_CAP_REG0 0xe4 ++#define RK818_UPDAT_LEVE_REG 0xe5 ++#define RK818_NEW_FCC_REG3 0xe6 ++#define RK818_NEW_FCC_REG2 0xe7 ++#define RK818_NEW_FCC_REG1 0xe8 ++#define RK818_NEW_FCC_REG0 0xe9 ++#define RK818_NON_ACT_TIMER_CNT_SAVE_REG 0xea ++#define RK818_OCV_VOL_VALID_REG 0xeb ++#define RK818_REBOOT_CNT_REG 0xec ++#define RK818_POFFSET_REG 0xed ++#define RK818_MISC_MARK_REG 0xee ++#define RK818_HALT_CNT_REG 0xef ++#define RK818_CALC_REST_REGH 0xf0 ++#define RK818_CALC_REST_REGL 0xf1 ++#define RK818_SAVE_DATA19 0xf2 + + #define RK818_H5V_EN BIT(0) + #define RK818_REF_RDY_CTRL BIT(1) diff --git a/sys-kernel/pinephone-sources/files/0013-power-supply-rk818-battery-Use-a-more-propper-compat.patch b/sys-kernel/pinephone-sources/files/0013-power-supply-rk818-battery-Use-a-more-propper-compat.patch new file mode 100644 index 0000000..fd97202 --- /dev/null +++ b/sys-kernel/pinephone-sources/files/0013-power-supply-rk818-battery-Use-a-more-propper-compat.patch @@ -0,0 +1,46 @@ +From: Ondrej Jirman +Date: Sun, 7 Nov 2021 19:30:07 +0100 +Subject: [PATCH 13/36] power: supply: rk818-battery: Use a more propper + compatible string + +Prefix with vendor name. + +Signed-off-by: Ondrej Jirman +--- + drivers/mfd/rk808.c | 2 +- + drivers/power/supply/rk818_battery.c | 4 ++-- + 2 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/mfd/rk808.c b/drivers/mfd/rk808.c +index 7d1f000..a99fec0 100644 +--- a/drivers/mfd/rk808.c ++++ b/drivers/mfd/rk808.c +@@ -205,7 +205,7 @@ static const struct mfd_cell rk817s[] = { + static const struct mfd_cell rk818s[] = { + { .name = "rk808-clkout", }, + { .name = "rk808-regulator", }, +- { .name = "rk818-battery", .of_compatible = "rk818-battery", }, ++ { .name = "rk818-battery", .of_compatible = "rockchip,rk818-battery", }, + { + .name = "rk808-rtc", + .num_resources = ARRAY_SIZE(rtc_resources), +diff --git a/drivers/power/supply/rk818_battery.c b/drivers/power/supply/rk818_battery.c +index f09f456..665f043 100644 +--- a/drivers/power/supply/rk818_battery.c ++++ b/drivers/power/supply/rk818_battery.c +@@ -3339,7 +3339,7 @@ static int rk818_bat_parse_dt(struct rk818_battery *di) + #endif + + static const struct of_device_id rk818_battery_of_match[] = { +- {.compatible = "rk818-battery",}, ++ { .compatible = "rockchip,rk818-battery", }, + { }, + }; + +@@ -3565,4 +3565,4 @@ module_exit(battery_exit); + + MODULE_LICENSE("GPL"); + MODULE_ALIAS("platform:rk818-battery"); +-MODULE_AUTHOR("chenjh"); +\ No newline at end of file ++MODULE_AUTHOR("chenjh"); diff --git a/sys-kernel/pinephone-sources/files/0014-power-supply-core-Don-t-ignore-max_current-of-0-when.patch b/sys-kernel/pinephone-sources/files/0014-power-supply-core-Don-t-ignore-max_current-of-0-when.patch new file mode 100644 index 0000000..ce6a96f --- /dev/null +++ b/sys-kernel/pinephone-sources/files/0014-power-supply-core-Don-t-ignore-max_current-of-0-when.patch @@ -0,0 +1,85 @@ +From: Ondrej Jirman +Date: Sun, 14 Nov 2021 21:24:05 +0100 +Subject: [PATCH 14/36] power: supply: core: Don't ignore max_current of 0 + when setting current limit + +If we ignore current limit of 0, the dependent power source will not +set input current limit to that value when the supplier changes max +current to 0. This may happen when USB power is disconnected from the +device. + +On next connection, the dependent power supply will start consuming +power at the previously set limit even before the PD/BC1.2 power +negotiation has a chance to complete. + +Signed-off-by: Ondrej Jirman +--- + drivers/power/supply/power_supply_core.c | 47 ++++++++++++++------------------ + 1 file changed, 20 insertions(+), 27 deletions(-) + +diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c +index fc12a4f..f681372 100644 +--- a/drivers/power/supply/power_supply_core.c ++++ b/drivers/power/supply/power_supply_core.c +@@ -375,41 +375,34 @@ int power_supply_is_system_supplied(void) + } + EXPORT_SYMBOL_GPL(power_supply_is_system_supplied); + +-static int __power_supply_get_supplier_max_current(struct device *dev, +- void *data) +-{ +- union power_supply_propval ret = {0,}; +- struct power_supply *epsy = dev_get_drvdata(dev); +- struct power_supply *psy = data; +- +- if (__power_supply_is_supplied_by(epsy, psy)) +- if (!epsy->desc->get_property(epsy, +- POWER_SUPPLY_PROP_CURRENT_MAX, +- &ret)) +- return ret.intval; +- +- return 0; +-} +- + int power_supply_set_input_current_limit_from_supplier(struct power_supply *psy) + { + union power_supply_propval val = {0,}; +- int curr; ++ struct class_dev_iter iter; ++ struct power_supply *epsy; ++ struct device *dev; ++ int ret; + + if (!psy->desc->set_property) + return -EINVAL; + +- /* +- * This function is not intended for use with a supply with multiple +- * suppliers, we simply pick the first supply to report a non 0 +- * max-current. +- */ +- curr = class_for_each_device(power_supply_class, NULL, psy, +- __power_supply_get_supplier_max_current); +- if (curr <= 0) +- return (curr == 0) ? -ENODEV : curr; ++ class_dev_iter_init(&iter, power_supply_class, NULL, NULL); ++ while ((dev = class_dev_iter_next(&iter))) { ++ epsy = dev_get_drvdata(dev); ++ ++ if (!__power_supply_is_supplied_by(epsy, psy)) ++ continue; + +- val.intval = curr; ++ ret = epsy->desc->get_property(epsy, ++ POWER_SUPPLY_PROP_CURRENT_MAX, ++ &val); ++ if (!ret) ++ break; ++ } ++ class_dev_iter_exit(&iter); ++ ++ if (ret) ++ return ret; + + return psy->desc->set_property(psy, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &val); diff --git a/sys-kernel/pinephone-sources/files/0015-power-supply-rk818-charger-Implement-charger-driver-.patch b/sys-kernel/pinephone-sources/files/0015-power-supply-rk818-charger-Implement-charger-driver-.patch new file mode 100644 index 0000000..37ea4fc --- /dev/null +++ b/sys-kernel/pinephone-sources/files/0015-power-supply-rk818-charger-Implement-charger-driver-.patch @@ -0,0 +1,699 @@ +From: Ondrej Jirman +Date: Sun, 7 Nov 2021 20:09:02 +0100 +Subject: [PATCH 15/36] power: supply: rk818-charger: Implement charger driver + for RK818 PMIC + +For now this driver is just meant to watch Type-C power supply +and apply current limits to RK818, to not overload the Type-C +partner. + +Signed-off-by: Ondrej Jirman +--- + drivers/mfd/rk808.c | 1 + + drivers/power/supply/Kconfig | 8 + + drivers/power/supply/Makefile | 1 + + drivers/power/supply/rk818_charger.c | 637 +++++++++++++++++++++++++++++++++++ + 4 files changed, 647 insertions(+) + create mode 100644 drivers/power/supply/rk818_charger.c + +diff --git a/drivers/mfd/rk808.c b/drivers/mfd/rk808.c +index a99fec0..75d18de 100644 +--- a/drivers/mfd/rk808.c ++++ b/drivers/mfd/rk808.c +@@ -206,6 +206,7 @@ static const struct mfd_cell rk818s[] = { + { .name = "rk808-clkout", }, + { .name = "rk808-regulator", }, + { .name = "rk818-battery", .of_compatible = "rockchip,rk818-battery", }, ++ { .name = "rk818-charger", .of_compatible = "rockchip,rk818-charger", }, + { + .name = "rk808-rtc", + .num_resources = ARRAY_SIZE(rtc_resources), +diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig +index f5d4434..6bf0834 100644 +--- a/drivers/power/supply/Kconfig ++++ b/drivers/power/supply/Kconfig +@@ -862,4 +862,12 @@ config BATTERY_RK818 + If you say yes here you will get support for the battery of RK818 PMIC. + This driver can give support for Rk818 Battery Charge Interface. + ++config CHARGER_RK818 ++ bool "RK818 Charger driver" ++ depends on MFD_RK808 ++ default n ++ help ++ If you say yes here you will get support for the charger of RK818 PMIC. ++ This driver can give support for Rk818 Charger Interface. ++ + endif # POWER_SUPPLY +diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile +index 1c725ee..3a1526f 100644 +--- a/drivers/power/supply/Makefile ++++ b/drivers/power/supply/Makefile +@@ -105,3 +105,4 @@ obj-$(CONFIG_BATTERY_ACER_A500) += acer_a500_battery.o + obj-$(CONFIG_BATTERY_SURFACE) += surface_battery.o + obj-$(CONFIG_CHARGER_SURFACE) += surface_charger.o + obj-$(CONFIG_BATTERY_RK818) += rk818_battery.o ++obj-$(CONFIG_CHARGER_RK818) += rk818_charger.o +diff --git a/drivers/power/supply/rk818_charger.c b/drivers/power/supply/rk818_charger.c +new file mode 100644 +index 00000000..7b67a0b +--- /dev/null ++++ b/drivers/power/supply/rk818_charger.c +@@ -0,0 +1,637 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * rk818 usb power driver ++ * ++ * Copyright (c) 2021 OndÅ™ej Jirman ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define RK818_CHG_STS_MASK (7u << 4) /* charger status */ ++#define RK818_CHG_STS_NONE (0u << 4) ++#define RK818_CHG_STS_WAKEUP_CUR (1u << 4) ++#define RK818_CHG_STS_TRICKLE_CUR (2u << 4) ++#define RK818_CHG_STS_CC_OR_CV (3u << 4) ++#define RK818_CHG_STS_TERMINATED (4u << 4) ++#define RK818_CHG_STS_USB_OV (5u << 4) ++#define RK818_CHG_STS_BAT_TEMP_FAULT (6u << 4) ++#define RK818_CHG_STS_TIMEOUT (7u << 4) ++ ++/* RK818_SUP_STS_REG */ ++#define RK818_SUP_STS_USB_VLIM_EN BIT(3) /* input voltage limit enable */ ++#define RK818_SUP_STS_USB_ILIM_EN BIT(2) /* input current limit enable */ ++#define RK818_SUP_STS_USB_EXS BIT(1) /* USB power connected */ ++#define RK818_SUP_STS_USB_EFF BIT(0) /* USB fault */ ++ ++/* RK818_USB_CTRL_REG */ ++#define RK818_USB_CTRL_USB_ILIM_MASK (0xfu) ++#define RK818_USB_CTRL_USB_CHG_SD_VSEL_OFFSET 4 ++#define RK818_USB_CTRL_USB_CHG_SD_VSEL_MASK (0x7u << 4) ++ ++/* RK818_CHRG_CTRL_REG1 */ ++#define RK818_CHRG_CTRL_REG1_CHRG_EN BIT(7) ++#define RK818_CHRG_CTRL_REG1_CHRG_VOL_SEL_OFFSET 4 ++#define RK818_CHRG_CTRL_REG1_CHRG_VOL_SEL_MASK (0x7u << 4) ++#define RK818_CHRG_CTRL_REG1_CHRG_CUR_SEL_OFFSET 0 ++#define RK818_CHRG_CTRL_REG1_CHRG_CUR_SEL_MASK (0xfu << 0) ++ ++/* RK818_CHRG_CTRL_REG3 */ ++#define RK818_CHRG_CTRL_REG3_CHRG_TERM_DIGITAL BIT(5) ++ ++struct rk818_charger { ++ struct device *dev; ++ struct rk808 *rk818; ++ struct regmap *regmap; ++ ++ struct power_supply *usb_psy; ++ struct power_supply *charger_psy; ++}; ++ ++// {{{ USB supply ++ ++static int rk818_usb_set_input_current_max(struct rk818_charger *cg, ++ int val) ++{ ++ int ret; ++ unsigned reg; ++ ++ if (val < 450000) ++ reg = 1; ++ else if (val < 850000) ++ reg = 0; ++ else if (val < 1000000) ++ reg = 2; ++ else if (val < 3000000) ++ reg = 3 + (val - 1000000) / 250000; ++ else ++ reg = 11; ++ ++ ret = regmap_update_bits(cg->regmap, RK818_USB_CTRL_REG, ++ RK818_USB_CTRL_USB_ILIM_MASK, reg); ++ if (ret) ++ dev_err(cg->dev, ++ "USB input current limit setting failed (%d)\n", ret); ++ ++ return ret; ++} ++ ++static int rk818_usb_get_input_current_max(struct rk818_charger *cg, ++ int *val) ++{ ++ unsigned reg; ++ int ret; ++ ++ ret = regmap_read(cg->regmap, RK818_USB_CTRL_REG, ®); ++ if (ret) { ++ dev_err(cg->dev, ++ "USB input current limit getting failed (%d)\n", ret); ++ return ret; ++ } ++ ++ reg &= RK818_USB_CTRL_USB_ILIM_MASK; ++ if (reg == 0) ++ *val = 450000; ++ else if (reg == 1) ++ *val = 80000; ++ else if (reg == 2) ++ *val = 850000; ++ else if (reg < 11) ++ *val = 1000000 + (reg - 3) * 250000; ++ else ++ *val = 3000000; ++ ++ return 0; ++} ++ ++static int rk818_usb_set_input_voltage_min(struct rk818_charger *cg, ++ int val) ++{ ++ unsigned reg; ++ int ret; ++ ++ if (val < 2780000) ++ reg = 0; ++ else if (val < 3270000) ++ reg = (val - 2780000) / 70000; ++ else ++ reg = 7; ++ ++ ret = regmap_update_bits(cg->regmap, RK818_USB_CTRL_REG, ++ RK818_USB_CTRL_USB_CHG_SD_VSEL_MASK, ++ reg << RK818_USB_CTRL_USB_CHG_SD_VSEL_OFFSET); ++ if (ret) ++ dev_err(cg->dev, ++ "USB input voltage limit setting failed (%d)\n", ret); ++ ++ return ret; ++} ++ ++static int rk818_usb_get_input_voltage_min(struct rk818_charger *cg, ++ int *val) ++{ ++ unsigned reg; ++ int ret; ++ ++ ret = regmap_read(cg->regmap, RK818_USB_CTRL_REG, ®); ++ if (ret) { ++ dev_err(cg->dev, ++ "USB input voltage limit getting failed (%d)\n", ret); ++ return ret; ++ } ++ ++ reg &= RK818_USB_CTRL_USB_CHG_SD_VSEL_MASK; ++ reg >>= RK818_USB_CTRL_USB_CHG_SD_VSEL_OFFSET; ++ ++ *val = 2780000 + (reg * 70000); ++ ++ return 0; ++} ++ ++static int rk818_usb_power_get_property(struct power_supply *psy, ++ enum power_supply_property psp, ++ union power_supply_propval *val) ++{ ++ struct rk818_charger *cg = power_supply_get_drvdata(psy); ++ unsigned reg; ++ int ret; ++ ++ switch (psp) { ++ case POWER_SUPPLY_PROP_PRESENT: ++ ret = regmap_read(cg->regmap, RK818_SUP_STS_REG, ®); ++ if (ret) ++ return ret; ++ ++ val->intval = !!(reg & RK818_SUP_STS_USB_EXS); ++ break; ++ ++ case POWER_SUPPLY_PROP_HEALTH: ++ ret = regmap_read(cg->regmap, RK818_SUP_STS_REG, ®); ++ if (ret) ++ return ret; ++ ++ if (!(reg & RK818_SUP_STS_USB_EXS)) { ++ val->intval = POWER_SUPPLY_HEALTH_UNKNOWN; ++ } else if (reg & RK818_SUP_STS_USB_EFF) { ++ val->intval = POWER_SUPPLY_HEALTH_GOOD; ++ } else { ++ val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; ++ } ++ ++ break; ++ ++ case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: ++ return rk818_usb_get_input_voltage_min(cg, &val->intval); ++ ++ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: ++ return rk818_usb_get_input_current_max(cg, &val->intval); ++ ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int rk818_usb_power_set_property(struct power_supply *psy, ++ enum power_supply_property psp, ++ const union power_supply_propval *val) ++{ ++ struct rk818_charger *cg = power_supply_get_drvdata(psy); ++ ++ switch (psp) { ++ case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: ++ return rk818_usb_set_input_voltage_min(cg, val->intval); ++ ++ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: ++ return rk818_usb_set_input_current_max(cg, val->intval); ++ ++ default: ++ return -EINVAL; ++ } ++} ++ ++static int rk818_usb_power_prop_writeable(struct power_supply *psy, ++ enum power_supply_property psp) ++{ ++ switch (psp) { ++ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: ++ case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: ++ return 1; ++ ++ default: ++ return 0; ++ } ++} ++ ++/* Sync the input-current-limit with our parent supply (if we have one) */ ++static void rk818_usb_power_external_power_changed(struct power_supply *psy) ++{ ++ struct rk818_charger *cg = power_supply_get_drvdata(psy); ++ ++ power_supply_set_input_current_limit_from_supplier(cg->usb_psy); ++} ++ ++static enum power_supply_property rk818_usb_power_props[] = { ++ POWER_SUPPLY_PROP_PRESENT, ++ POWER_SUPPLY_PROP_HEALTH, ++ POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, ++ POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT, ++}; ++ ++static const struct power_supply_desc rk818_usb_desc = { ++ .name = "rk818-usb", ++ .type = POWER_SUPPLY_TYPE_USB, ++ .properties = rk818_usb_power_props, ++ .num_properties = ARRAY_SIZE(rk818_usb_power_props), ++ .property_is_writeable = rk818_usb_power_prop_writeable, ++ .get_property = rk818_usb_power_get_property, ++ .set_property = rk818_usb_power_set_property, ++ .external_power_changed = rk818_usb_power_external_power_changed, ++}; ++ ++// }}} ++// {{{ Charger supply ++ ++static int rk818_charger_set_current_max(struct rk818_charger *cg, int val) ++{ ++ unsigned reg; ++ int ret; ++ ++ if (val < 1000000) ++ reg = 0; ++ else if (val < 3000000) ++ reg = (val - 1000000) / 200000; ++ else ++ reg = 10; ++ ++ ret = regmap_update_bits(cg->regmap, RK818_CHRG_CTRL_REG1, ++ RK818_CHRG_CTRL_REG1_CHRG_CUR_SEL_MASK, ++ reg << RK818_CHRG_CTRL_REG1_CHRG_CUR_SEL_OFFSET); ++ if (ret) ++ dev_err(cg->dev, ++ "Charging max current setting failed (%d)\n", ret); ++ ++ return ret; ++} ++ ++static int rk818_charger_get_current_max(struct rk818_charger *cg, int *val) ++{ ++ unsigned reg; ++ int ret; ++ ++ ret = regmap_read(cg->regmap, RK818_CHRG_CTRL_REG1, ®); ++ if (ret) { ++ dev_err(cg->dev, ++ "Charging max current getting failed (%d)\n", ret); ++ return ret; ++ } ++ ++ reg &= RK818_CHRG_CTRL_REG1_CHRG_CUR_SEL_MASK; ++ reg >>= RK818_CHRG_CTRL_REG1_CHRG_CUR_SEL_OFFSET; ++ ++ *val = 1000000 + reg * 200000; ++ ++ return 0; ++} ++ ++static int rk818_charger_set_voltage_max(struct rk818_charger *cg, int val) ++{ ++ unsigned reg; ++ int ret; ++ ++ if (val < 4050000) ++ reg = 0; ++ else if (val < 4350000) ++ reg = (val - 4050000) / 50000; ++ else ++ reg = 6; ++ ++ ret = regmap_update_bits(cg->regmap, RK818_CHRG_CTRL_REG1, ++ RK818_CHRG_CTRL_REG1_CHRG_VOL_SEL_MASK, ++ reg << RK818_CHRG_CTRL_REG1_CHRG_VOL_SEL_OFFSET); ++ if (ret) ++ dev_err(cg->dev, ++ "Charging end voltage setting failed (%d)\n", ret); ++ ++ return ret; ++} ++ ++static int rk818_charger_get_voltage_max(struct rk818_charger *cg, int *val) ++{ ++ unsigned reg; ++ int ret; ++ ++ ret = regmap_read(cg->regmap, RK818_CHRG_CTRL_REG1, ®); ++ if (ret) { ++ dev_err(cg->dev, ++ "Charging end voltage getting failed (%d)\n", ret); ++ return ret; ++ } ++ ++ reg &= RK818_CHRG_CTRL_REG1_CHRG_VOL_SEL_MASK; ++ reg >>= RK818_CHRG_CTRL_REG1_CHRG_VOL_SEL_OFFSET; ++ ++ *val = 4050000 + reg * 50000; ++ ++ return 0; ++} ++ ++static int rk818_charger_get_property(struct power_supply *psy, ++ enum power_supply_property psp, ++ union power_supply_propval *val) ++{ ++ struct rk818_charger *cg = power_supply_get_drvdata(psy); ++ unsigned reg; ++ int ret; ++ ++ switch (psp) { ++ case POWER_SUPPLY_PROP_ONLINE: ++ ret = regmap_read(cg->regmap, RK818_CHRG_CTRL_REG1, ®); ++ if (ret) { ++ dev_err(cg->dev, "failed to read the charger state (%d)\n", ret); ++ return ret; ++ } ++ ++ val->intval = !!(reg & RK818_CHRG_CTRL_REG1_CHRG_EN); ++ break; ++ ++ case POWER_SUPPLY_PROP_STATUS: ++ ret = regmap_read(cg->regmap, RK818_SUP_STS_REG, ®); ++ if (ret) ++ return ret; ++ ++ switch (reg & RK818_CHG_STS_MASK) { ++ case RK818_CHG_STS_WAKEUP_CUR: ++ case RK818_CHG_STS_TRICKLE_CUR: ++ case RK818_CHG_STS_CC_OR_CV: ++ val->intval = POWER_SUPPLY_STATUS_CHARGING; ++ break; ++ case RK818_CHG_STS_TERMINATED: ++ default: ++ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; ++ break; ++ } ++ ++ break; ++ ++ case POWER_SUPPLY_PROP_CHARGE_TYPE: ++ ret = regmap_read(cg->regmap, RK818_SUP_STS_REG, ®); ++ if (ret) ++ return ret; ++ ++ switch (reg & RK818_CHG_STS_MASK) { ++ case RK818_CHG_STS_WAKEUP_CUR: ++ case RK818_CHG_STS_TRICKLE_CUR: ++ val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; ++ break; ++ case RK818_CHG_STS_CC_OR_CV: ++ val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; ++ break; ++ case RK818_CHG_STS_TERMINATED: ++ val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; ++ break; ++ default: ++ val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; ++ break; ++ } ++ ++ break; ++ ++ case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: ++ ret = regmap_read(cg->regmap, RK818_CHRG_CTRL_REG2, ®); ++ if (ret) ++ return ret; ++ ++ val->intval = 100000 + ((reg >> 6) & 3) * 50000; ++ break; ++ ++ case POWER_SUPPLY_PROP_HEALTH: ++ ret = regmap_read(cg->regmap, RK818_SUP_STS_REG, ®); ++ if (ret) ++ return ret; ++ ++ switch (reg & RK818_CHG_STS_MASK) { ++ case RK818_CHG_STS_USB_OV: ++ val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; ++ break; ++ case RK818_CHG_STS_BAT_TEMP_FAULT: ++ val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; ++ break; ++ case RK818_CHG_STS_TIMEOUT: ++ val->intval = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE; ++ break; ++ default: ++ val->intval = POWER_SUPPLY_HEALTH_GOOD; ++ break; ++ } ++ ++ break; ++ ++ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: ++ return rk818_charger_get_voltage_max(cg, &val->intval); ++ ++ case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: ++ ret = rk818_charger_get_current_max(cg, &val->intval); ++ val->intval /= 10; ++ return ret; ++ ++ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: ++ return rk818_charger_get_current_max(cg, &val->intval); ++ ++ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: ++ val->intval = 4350000; ++ break; ++ ++ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: ++ val->intval = 3000000; ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int rk818_charger_set_property(struct power_supply *psy, ++ enum power_supply_property psp, ++ const union power_supply_propval *val) ++{ ++ struct rk818_charger *cg = power_supply_get_drvdata(psy); ++ int ret; ++ ++ switch (psp) { ++ case POWER_SUPPLY_PROP_ONLINE: ++ ret = regmap_update_bits(cg->regmap, RK818_CHRG_CTRL_REG1, ++ RK818_CHRG_CTRL_REG1_CHRG_EN, ++ val->intval ? RK818_CHRG_CTRL_REG1_CHRG_EN : 0); ++ if (ret) ++ dev_err(cg->dev, "failed to setup the charger (%d)\n", ret); ++ ++ return ret; ++ ++ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: ++ return rk818_charger_set_voltage_max(cg, val->intval); ++ ++ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: ++ return rk818_charger_set_current_max(cg, val->intval); ++ ++ default: ++ return -EINVAL; ++ } ++} ++ ++static int rk818_charger_prop_writeable(struct power_supply *psy, ++ enum power_supply_property psp) ++{ ++ switch (psp) { ++ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: ++ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: ++ case POWER_SUPPLY_PROP_ONLINE: ++ return 1; ++ ++ default: ++ return 0; ++ } ++} ++ ++static enum power_supply_property rk818_charger_props[] = { ++ POWER_SUPPLY_PROP_ONLINE, ++ POWER_SUPPLY_PROP_HEALTH, ++ POWER_SUPPLY_PROP_STATUS, ++ POWER_SUPPLY_PROP_CHARGE_TYPE, ++ POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, ++ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, ++ POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, ++ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, ++ POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, ++ POWER_SUPPLY_PROP_PRECHARGE_CURRENT, ++}; ++ ++/* ++ * TODO: This functionality should be in a battery driver/supply, but that one ++ * is such a mess, I don't want to touch it now. Let's have a separate supply ++ * for controlling the charger for now, and a prayer for the poor soul that ++ * will have to understand and clean up the battery driver. ++ */ ++static const struct power_supply_desc rk818_charger_desc = { ++ .name = "rk818-charger", ++ .type = POWER_SUPPLY_TYPE_BATTERY, ++ .properties = rk818_charger_props, ++ .num_properties = ARRAY_SIZE(rk818_charger_props), ++ .property_is_writeable = rk818_charger_prop_writeable, ++ .get_property = rk818_charger_get_property, ++ .set_property = rk818_charger_set_property, ++}; ++ ++// }}} ++ ++static int rk818_charger_probe(struct platform_device *pdev) ++{ ++ struct rk808 *rk818 = dev_get_drvdata(pdev->dev.parent); ++ struct power_supply_config psy_cfg = { }; ++ struct device *dev = &pdev->dev; ++ struct rk818_charger *cg; ++ int ret; ++ ++ cg = devm_kzalloc(dev, sizeof(*cg), GFP_KERNEL); ++ if (!cg) ++ return -ENOMEM; ++ ++ cg->rk818 = rk818; ++ cg->dev = dev; ++ cg->regmap = rk818->regmap; ++ platform_set_drvdata(pdev, cg); ++ ++ psy_cfg.drv_data = cg; ++ psy_cfg.of_node = dev->of_node; ++ ++ cg->usb_psy = devm_power_supply_register(dev, &rk818_usb_desc, ++ &psy_cfg); ++ if (IS_ERR(cg->usb_psy)) ++ return dev_err_probe(dev, PTR_ERR(cg->usb_psy), ++ "register usb power supply fail\n"); ++ ++ cg->charger_psy = devm_power_supply_register(dev, &rk818_charger_desc, ++ &psy_cfg); ++ if (IS_ERR(cg->charger_psy)) ++ return dev_err_probe(dev, PTR_ERR(cg->charger_psy), ++ "register charger power supply fail\n"); ++ ++ /* disable voltage limit and enable input current limit */ ++ ret = regmap_update_bits(cg->regmap, RK818_SUP_STS_REG, ++ RK818_SUP_STS_USB_ILIM_EN | RK818_SUP_STS_USB_VLIM_EN, ++ RK818_SUP_STS_USB_ILIM_EN); ++ if (ret) ++ dev_warn(cg->dev, "failed to enable input current limit (%d)\n", ret); ++ ++ /* make sure analog control loop is enabled */ ++ ret = regmap_update_bits(cg->regmap, RK818_CHRG_CTRL_REG3, ++ RK818_CHRG_CTRL_REG3_CHRG_TERM_DIGITAL, ++ 0); ++ if (ret) ++ dev_warn(cg->dev, "failed to enable analog control loop (%d)\n", ret); ++ ++ /* enable charger and set some reasonable limits on each boot */ ++ ret = regmap_write(cg->regmap, RK818_CHRG_CTRL_REG1, ++ RK818_CHRG_CTRL_REG1_CHRG_EN ++ | (1) /* 1.2A */ ++ | (5 << 4) /* 4.3V */); ++ if (ret) ++ dev_warn(cg->dev, "failed to enable charger (%d)\n", ret); ++ ++ power_supply_set_input_current_limit_from_supplier(cg->usb_psy); ++ ++ return 0; ++} ++ ++static int rk818_charger_remove(struct platform_device *pdev) ++{ ++ //struct rk818_charger *cg = platform_get_drvdata(pdev); ++ ++ return 0; ++} ++ ++static void rk818_charger_shutdown(struct platform_device *pdev) ++{ ++} ++ ++static int rk818_charger_suspend(struct platform_device *pdev, ++ pm_message_t state) ++{ ++ return 0; ++} ++ ++static int rk818_charger_resume(struct platform_device *pdev) ++{ ++ return 0; ++} ++ ++static const struct of_device_id rk818_charger_of_match[] = { ++ { .compatible = "rockchip,rk818-charger", }, ++ { }, ++}; ++ ++static struct platform_driver rk818_charger_driver = { ++ .probe = rk818_charger_probe, ++ .remove = rk818_charger_remove, ++ .suspend = rk818_charger_suspend, ++ .resume = rk818_charger_resume, ++ .shutdown = rk818_charger_shutdown, ++ .driver = { ++ .name = "rk818-charger", ++ .of_match_table = rk818_charger_of_match, ++ }, ++}; ++ ++module_platform_driver(rk818_charger_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:rk818-charger"); ++MODULE_AUTHOR("OndÅ™ej Jirman "); diff --git a/sys-kernel/pinephone-sources/files/0016-usb-typec-fusb302-Set-the-current-before-enabling-pu.patch b/sys-kernel/pinephone-sources/files/0016-usb-typec-fusb302-Set-the-current-before-enabling-pu.patch new file mode 100644 index 0000000..f42aed2 --- /dev/null +++ b/sys-kernel/pinephone-sources/files/0016-usb-typec-fusb302-Set-the-current-before-enabling-pu.patch @@ -0,0 +1,47 @@ +From: Ondrej Jirman +Date: Sun, 7 Nov 2021 19:28:27 +0100 +Subject: [PATCH 26/36] 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 72f9001..776a949 100644 +--- a/drivers/usb/typec/tcpm/fusb302.c ++++ b/drivers/usb/typec/tcpm/fusb302.c +@@ -635,6 +635,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) { +@@ -645,14 +653,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: diff --git a/sys-kernel/pinephone-sources/files/0017-usb-typec-fusb302-Extend-debugging-interface-with-dr.patch b/sys-kernel/pinephone-sources/files/0017-usb-typec-fusb302-Extend-debugging-interface-with-dr.patch new file mode 100644 index 0000000..0ab3229 --- /dev/null +++ b/sys-kernel/pinephone-sources/files/0017-usb-typec-fusb302-Extend-debugging-interface-with-dr.patch @@ -0,0 +1,108 @@ +From: Ondrej Jirman +Date: Sun, 7 Nov 2021 19:29:06 +0100 +Subject: [PATCH 27/36] 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 776a949..1a758e3 100644 +--- a/drivers/usb/typec/tcpm/fusb302.c ++++ b/drivers/usb/typec/tcpm/fusb302.c +@@ -207,6 +207,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]; +@@ -216,6 +291,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) diff --git a/sys-kernel/pinephone-sources/files/0018-usb-typec-fusb302-Retry-reading-of-CC-pins-status-if.patch b/sys-kernel/pinephone-sources/files/0018-usb-typec-fusb302-Retry-reading-of-CC-pins-status-if.patch new file mode 100644 index 0000000..096673e --- /dev/null +++ b/sys-kernel/pinephone-sources/files/0018-usb-typec-fusb302-Retry-reading-of-CC-pins-status-if.patch @@ -0,0 +1,72 @@ +From: Ondrej Jirman +Date: Tue, 23 Nov 2021 17:53:27 +0100 +Subject: [PATCH 28/36] 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 1a758e3..7386805 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; + diff --git a/sys-kernel/pinephone-sources/files/0019-usb-typec-fusb302-More-useful-of-logging-status-on-i.patch b/sys-kernel/pinephone-sources/files/0019-usb-typec-fusb302-More-useful-of-logging-status-on-i.patch new file mode 100644 index 0000000..2cec92e --- /dev/null +++ b/sys-kernel/pinephone-sources/files/0019-usb-typec-fusb302-More-useful-of-logging-status-on-i.patch @@ -0,0 +1,186 @@ +From: Ondrej Jirman +Date: Tue, 23 Nov 2021 17:55:34 +0100 +Subject: [PATCH 29/36] 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 7386805..70b0e15 100644 +--- a/drivers/usb/typec/tcpm/fusb302.c ++++ b/drivers/usb/typec/tcpm/fusb302.c +@@ -68,7 +68,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", +- comp_result ? "true" : "false"); +- 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; diff --git a/sys-kernel/pinephone-sources/files/0020-usb-typec-fusb302-Update-VBUS-state-even-if-VBUS-int.patch b/sys-kernel/pinephone-sources/files/0020-usb-typec-fusb302-Update-VBUS-state-even-if-VBUS-int.patch new file mode 100644 index 0000000..9335999 --- /dev/null +++ b/sys-kernel/pinephone-sources/files/0020-usb-typec-fusb302-Update-VBUS-state-even-if-VBUS-int.patch @@ -0,0 +1,39 @@ +From: Ondrej Jirman +Date: Tue, 23 Nov 2021 17:57:06 +0100 +Subject: [PATCH 30/36] 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 70b0e15..1d5affa 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", + vbus_present ? "On" : "Off"); +- 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", ++ vbus_present ? "On" : "Off"); ++ tcpm_vbus_change(chip->tcpm_port); + } + + if (interrupta & FUSB_REG_INTERRUPTA_TOGDONE) { diff --git a/sys-kernel/pinephone-sources/files/0021-usb-typec-fusb302-Make-tcpm-fusb302-logs-less-pollut.patch b/sys-kernel/pinephone-sources/files/0021-usb-typec-fusb302-Make-tcpm-fusb302-logs-less-pollut.patch new file mode 100644 index 0000000..9a880fd --- /dev/null +++ b/sys-kernel/pinephone-sources/files/0021-usb-typec-fusb302-Make-tcpm-fusb302-logs-less-pollut.patch @@ -0,0 +1,160 @@ +From: Ondrej Jirman +Date: Tue, 23 Nov 2021 17:58:05 +0100 +Subject: [PATCH 31/36] usb: typec: fusb302: Make tcpm/fusb302 logs less + polluted by PD comm stuff + +This adds clarity to debugging. + +Signed-off-by: Ondrej Jirman +--- + drivers/usb/typec/tcpm/fusb302.c | 18 ++++++++++-------- + drivers/usb/typec/tcpm/tcpm.c | 18 ++++++++++-------- + 2 files changed, 20 insertions(+), 16 deletions(-) + +diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c +index 1d5affa..ae3b930 100644 +--- a/drivers/usb/typec/tcpm/fusb302.c ++++ b/drivers/usb/typec/tcpm/fusb302.c +@@ -787,7 +787,7 @@ static int tcpm_get_cc(struct tcpc_dev *dev, enum typec_cc_status *cc1, + mutex_lock(&chip->lock); + *cc1 = chip->cc1; + *cc2 = chip->cc2; +- fusb302_log(chip, "cc1=%s, cc2=%s", typec_cc_status_name[*cc1], ++ fusb302_log(chip, "tcpm_get_cc => cc1=%s, cc2=%s (cached)", typec_cc_status_name[*cc1], + typec_cc_status_name[*cc2]); + mutex_unlock(&chip->lock); + +@@ -1073,8 +1073,8 @@ static int fusb302_pd_send_message(struct fusb302_chip *chip, + ret = fusb302_i2c_block_write(chip, FUSB_REG_FIFOS, pos, buf); + if (ret < 0) + return ret; +- fusb302_log(chip, "sending PD message header: %x", msg->header); +- fusb302_log(chip, "sending PD message len: %d", len); ++ //fusb302_log(chip, "sending PD message header: %x", msg->header); ++ //fusb302_log(chip, "sending PD message len: %d", len); + + return ret; + } +@@ -1365,8 +1365,10 @@ static int fusb302_get_src_cc_status(struct fusb302_chip *chip, + if (ret < 0) + return ret; + ++ //XXX resolve activity conflicts while measuring ++ + fusb302_i2c_read(chip, FUSB_REG_SWITCHES0, &status0); +- fusb302_log(chip, "get_src_cc_status switches: 0x%0x", status0); ++ //fusb302_log(chip, "get_src_cc_status switches: 0x%0x", status0); + + /* Step 2: Set compararator volt to differentiate between Open and Rd */ + ret = fusb302_i2c_write(chip, FUSB_REG_MEASURE, rd_mda); +@@ -1378,7 +1380,7 @@ static int fusb302_get_src_cc_status(struct fusb302_chip *chip, + if (ret < 0) + return ret; + +- fusb302_log(chip, "get_src_cc_status rd_mda status0: 0x%0x", status0); ++ //fusb302_log(chip, "get_src_cc_status rd_mda status0: 0x%0x", status0); + if (status0 & FUSB_REG_STATUS0_COMP) { + *cc = TYPEC_CC_OPEN; + return 0; +@@ -1394,7 +1396,7 @@ static int fusb302_get_src_cc_status(struct fusb302_chip *chip, + if (ret < 0) + return ret; + +- fusb302_log(chip, "get_src_cc_status ra_mda status0: 0x%0x", status0); ++ //fusb302_log(chip, "get_src_cc_status ra_mda status0: 0x%0x", status0); + if (status0 & FUSB_REG_STATUS0_COMP) + *cc = TYPEC_CC_RD; + else +@@ -1559,8 +1561,8 @@ static int fusb302_pd_read_message(struct fusb302_chip *chip, + ret = fusb302_i2c_block_read(chip, FUSB_REG_FIFOS, 4, crc); + if (ret < 0) + return ret; +- fusb302_log(chip, "PD message header: %x", msg->header); +- fusb302_log(chip, "PD message len: %d", len); ++ //fusb302_log(chip, "PD message header: %x", msg->header); ++ //fusb302_log(chip, "PD message len: %d", len); + + /* + * Check if we've read off a GoodCRC message. If so then indicate to +diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c +index 59d4fa2..0451441 100644 +--- a/drivers/usb/typec/tcpm/tcpm.c ++++ b/drivers/usb/typec/tcpm/tcpm.c +@@ -776,7 +776,7 @@ static void tcpm_debugfs_exit(const struct tcpm_port *port) { } + + static void tcpm_set_cc(struct tcpm_port *port, enum typec_cc_status cc) + { +- tcpm_log(port, "cc:=%d", cc); ++ //tcpm_log(port, "cc:=%d", cc); + port->cc_req = cc; + port->tcpc->set_cc(port->tcpc, cc); + } +@@ -869,10 +869,12 @@ static int tcpm_pd_transmit(struct tcpm_port *port, + unsigned long timeout; + int ret; + ++ /* + if (msg) + tcpm_log(port, "PD TX, header: %#x", le16_to_cpu(msg->header)); + else + tcpm_log(port, "PD TX, type: %#x", type); ++ */ + + reinit_completion(&port->tx_complete); + ret = port->tcpc->pd_transmit(port->tcpc, type, msg, port->negotiated_rev); +@@ -918,7 +920,7 @@ static int tcpm_pd_transmit(struct tcpm_port *port, + void tcpm_pd_transmit_complete(struct tcpm_port *port, + enum tcpm_transmit_status status) + { +- tcpm_log(port, "PD TX complete, status: %u", status); ++ //tcpm_log(port, "PD TX complete, status: %u", status); + port->tx_status = status; + complete(&port->tx_complete); + } +@@ -951,7 +953,7 @@ static int tcpm_set_polarity(struct tcpm_port *port, + { + int ret; + +- tcpm_log(port, "polarity %d", polarity); ++ //tcpm_log(port, "polarity %d", polarity); + + ret = port->tcpc->set_polarity(port->tcpc, polarity); + if (ret < 0) +@@ -966,7 +968,7 @@ static int tcpm_set_vconn(struct tcpm_port *port, bool enable) + { + int ret; + +- tcpm_log(port, "vconn:=%d", enable); ++ //tcpm_log(port, "vconn:=%d", enable); + + ret = port->tcpc->set_vconn(port->tcpc, enable); + if (!ret) { +@@ -2871,8 +2873,8 @@ static void tcpm_pd_rx_handler(struct kthread_work *work) + + mutex_lock(&port->lock); + +- tcpm_log(port, "PD RX, header: %#x [%d]", le16_to_cpu(msg->header), +- port->attached); ++ //tcpm_log(port, "PD RX, header: %#x [%d]", le16_to_cpu(msg->header), ++ //port->attached); + + if (port->attached) { + enum pd_ctrl_msg_type type = pd_header_type_le(msg->header); +@@ -5041,7 +5043,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 +@@ -5131,7 +5133,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) { diff --git a/sys-kernel/pinephone-sources/files/0022-usb-typec-fusb302-Add-OF-extcon-support.patch b/sys-kernel/pinephone-sources/files/0022-usb-typec-fusb302-Add-OF-extcon-support.patch new file mode 100644 index 0000000..64f2ffa --- /dev/null +++ b/sys-kernel/pinephone-sources/files/0022-usb-typec-fusb302-Add-OF-extcon-support.patch @@ -0,0 +1,34 @@ +From: Ondrej Jirman +Date: Sun, 14 Nov 2021 01:14:25 +0100 +Subject: [PATCH 32/36] 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 ae3b930..0c5dd00 100644 +--- a/drivers/usb/typec/tcpm/fusb302.c ++++ b/drivers/usb/typec/tcpm/fusb302.c +@@ -518,6 +518,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; + diff --git a/sys-kernel/pinephone-sources/files/0023-usb-typec-fusb302-Fix-register-definitions.patch b/sys-kernel/pinephone-sources/files/0023-usb-typec-fusb302-Fix-register-definitions.patch new file mode 100644 index 0000000..f06e87f --- /dev/null +++ b/sys-kernel/pinephone-sources/files/0023-usb-typec-fusb302-Fix-register-definitions.patch @@ -0,0 +1,45 @@ +From: Ondrej Jirman +Date: Sat, 20 Nov 2021 14:33:58 +0100 +Subject: [PATCH 33/36] 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 edc0e4b..f37d226 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 diff --git a/sys-kernel/pinephone-sources/files/0024-usb-typec-fusb302-Clear-interrupts-before-we-start-t.patch b/sys-kernel/pinephone-sources/files/0024-usb-typec-fusb302-Clear-interrupts-before-we-start-t.patch new file mode 100644 index 0000000..5909161 --- /dev/null +++ b/sys-kernel/pinephone-sources/files/0024-usb-typec-fusb302-Clear-interrupts-before-we-start-t.patch @@ -0,0 +1,37 @@ +From: Ondrej Jirman +Date: Sat, 20 Nov 2021 14:35:10 +0100 +Subject: [PATCH 34/36] 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 0c5dd00..011dce5 100644 +--- a/drivers/usb/typec/tcpm/fusb302.c ++++ b/drivers/usb/typec/tcpm/fusb302.c +@@ -586,6 +586,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, +@@ -644,6 +645,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); diff --git a/sys-kernel/pinephone-sources/files/0025-usb-typec-typec-extcon-Add-typec-extcon-bridge-drive.patch b/sys-kernel/pinephone-sources/files/0025-usb-typec-typec-extcon-Add-typec-extcon-bridge-drive.patch new file mode 100644 index 0000000..d8c007c --- /dev/null +++ b/sys-kernel/pinephone-sources/files/0025-usb-typec-typec-extcon-Add-typec-extcon-bridge-drive.patch @@ -0,0 +1,388 @@ +From: Ondrej Jirman +Date: Sun, 7 Nov 2021 19:24:40 +0100 +Subject: [PATCH 35/36] 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 | 337 +++++++++++++++++++++++++++++++++++++++ + 3 files changed, 345 insertions(+) + create mode 100644 drivers/usb/typec/typec-extcon.c + +diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig +index ab480f3..01ecc5e 100644 +--- a/drivers/usb/typec/Kconfig ++++ b/drivers/usb/typec/Kconfig +@@ -88,6 +88,13 @@ config TYPEC_QCOM_PMIC + It will also enable the VBUS output to connected devices when a + DFP connection is made. + ++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 a0adb89..d9d8293 100644 +--- a/drivers/usb/typec/Makefile ++++ b/drivers/usb/typec/Makefile +@@ -8,4 +8,5 @@ obj-$(CONFIG_TYPEC_TPS6598X) += tipd/ + obj-$(CONFIG_TYPEC_HD3SS3220) += hd3ss3220.o + obj-$(CONFIG_TYPEC_QCOM_PMIC) += qcom-pmic-typec.o + obj-$(CONFIG_TYPEC_STUSB160X) += stusb160x.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 00000000..143ff24 +--- /dev/null ++++ b/drivers/usb/typec/typec-extcon.c +@@ -0,0 +1,337 @@ ++/* ++ * 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 *sw; ++ struct typec_mux *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); ++ ++ extcon_set_state_sync(tce->extcon, EXTCON_CHG_USB_SDP, false); ++ extcon_set_state_sync(tce->extcon, EXTCON_CHG_USB_DCP, false); ++ extcon_set_state_sync(tce->extcon, EXTCON_CHG_USB_CDP, false); ++ extcon_set_state_sync(tce->extcon, EXTCON_CHG_USB_ACA, false); ++ ++ 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 *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 *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 int 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); ++ ++ return 0; ++} ++ ++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"); diff --git a/sys-kernel/pinephone-sources/files/0026-phy-rockchip-typec-Make-sure-the-plug-orientation-is.patch b/sys-kernel/pinephone-sources/files/0026-phy-rockchip-typec-Make-sure-the-plug-orientation-is.patch new file mode 100644 index 0000000..abb3c5c --- /dev/null +++ b/sys-kernel/pinephone-sources/files/0026-phy-rockchip-typec-Make-sure-the-plug-orientation-is.patch @@ -0,0 +1,64 @@ +From: Ondrej Jirman +Date: Tue, 23 Nov 2021 17:32:18 +0100 +Subject: [PATCH 36/36] phy: rockchip-typec: Make sure the plug orientation is + respected + +RK3399 TRM says about bit 8: + +typec_conn_dir_sel: TypeC connect direction select + +- 0: select typec_conn_dir (bit0 of this register) to TypeC PHY +- 1: select TCPC ouput typec_con_dir to TypeC PHY (default value) + +This means that by default, typec_conn_dir bit is not respected. +Fix setting of typec_conn_dir by setting typec_conn_dir to 0 first. + +Signed-off-by: Ondrej Jirman +--- + drivers/phy/rockchip/phy-rockchip-typec.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c +index d2bbdc9..fa10ee9 100644 +--- a/drivers/phy/rockchip/phy-rockchip-typec.c ++++ b/drivers/phy/rockchip/phy-rockchip-typec.c +@@ -350,6 +350,7 @@ struct usb3phy_reg { + * struct rockchip_usb3phy_port_cfg - usb3-phy port configuration. + * @reg: the base address for usb3-phy config. + * @typec_conn_dir: the register of type-c connector direction. ++ * @typec_conn_dir_sel: the register of type-c connector direction source. + * @usb3tousb2_en: the register of type-c force usb2 to usb2 enable. + * @external_psm: the register of type-c phy external psm clock. + * @pipe_status: the register of type-c phy pipe status. +@@ -360,6 +361,7 @@ struct usb3phy_reg { + struct rockchip_usb3phy_port_cfg { + unsigned int reg; + struct usb3phy_reg typec_conn_dir; ++ struct usb3phy_reg typec_conn_dir_sel; + struct usb3phy_reg usb3tousb2_en; + struct usb3phy_reg external_psm; + struct usb3phy_reg pipe_status; +@@ -434,6 +436,7 @@ static const struct rockchip_usb3phy_port_cfg rk3399_usb3phy_port_cfgs[] = { + { + .reg = 0xff7c0000, + .typec_conn_dir = { 0xe580, 0, 16 }, ++ .typec_conn_dir_sel = { 0xe580, 8, 16+8 }, + .usb3tousb2_en = { 0xe580, 3, 19 }, + .external_psm = { 0xe588, 14, 30 }, + .pipe_status = { 0xe5c0, 0, 0 }, +@@ -444,6 +447,7 @@ static const struct rockchip_usb3phy_port_cfg rk3399_usb3phy_port_cfgs[] = { + { + .reg = 0xff800000, + .typec_conn_dir = { 0xe58c, 0, 16 }, ++ .typec_conn_dir_sel = { 0xe58c, 8, 16+8 }, + .usb3tousb2_en = { 0xe58c, 3, 19 }, + .external_psm = { 0xe594, 14, 30 }, + .pipe_status = { 0xe5c0, 16, 16 }, +@@ -739,6 +743,7 @@ static int tcphy_phy_init(struct rockchip_typec_phy *tcphy, u8 mode) + + reset_control_deassert(tcphy->tcphy_rst); + ++ property_enable(tcphy, &cfg->typec_conn_dir_sel, 0); + property_enable(tcphy, &cfg->typec_conn_dir, tcphy->flip); + tcphy_dp_aux_set_flip(tcphy); + diff --git a/sys-kernel/pinephone-sources/files/0027-media-i2c-imx258-Add-support-for-powerdown-gpio.patch b/sys-kernel/pinephone-sources/files/0027-media-i2c-imx258-Add-support-for-powerdown-gpio.patch new file mode 100644 index 0000000..6d20e9e --- /dev/null +++ b/sys-kernel/pinephone-sources/files/0027-media-i2c-imx258-Add-support-for-powerdown-gpio.patch @@ -0,0 +1,56 @@ +From: Ondrej Jirman +Date: Fri, 22 Oct 2021 18:10:18 +0200 +Subject: [PATCH 20/36] media: i2c: imx258: Add support for powerdown gpio + +On some boards powerdown signal needs to be deasserted for this +sensor to be enabled. + +Signed-off-by: Ondrej Jirman +--- + drivers/media/i2c/imx258.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c +index c249507..be5adcc 100644 +--- a/drivers/media/i2c/imx258.c ++++ b/drivers/media/i2c/imx258.c +@@ -612,6 +612,8 @@ struct imx258 { + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *exposure; + ++ struct gpio_desc *pwdn_gpio; ++ + /* Current mode */ + const struct imx258_mode *cur_mode; + +@@ -1010,6 +1012,8 @@ static int imx258_power_on(struct device *dev) + struct imx258 *imx258 = to_imx258(sd); + int ret; + ++ gpiod_set_value_cansleep(imx258->pwdn_gpio, 0); ++ + ret = clk_prepare_enable(imx258->clk); + if (ret) + dev_err(dev, "failed to enable clock\n"); +@@ -1024,6 +1028,8 @@ static int imx258_power_off(struct device *dev) + + clk_disable_unprepare(imx258->clk); + ++ gpiod_set_value_cansleep(imx258->pwdn_gpio, 1); ++ + return 0; + } + +@@ -1284,6 +1290,12 @@ static int imx258_probe(struct i2c_client *client) + if (ret || val != 180) + return -EINVAL; + ++ /* request optional power down pin */ ++ imx258->pwdn_gpio = devm_gpiod_get_optional(&client->dev, "powerdown", ++ GPIOD_OUT_HIGH); ++ if (IS_ERR(imx258->pwdn_gpio)) ++ return PTR_ERR(imx258->pwdn_gpio); ++ + /* Initialize subdev */ + v4l2_i2c_subdev_init(&imx258->sd, client, &imx258_subdev_ops); + diff --git a/sys-kernel/pinephone-sources/files/0028-media-i2c-imx258-Don-t-be-too-strict-about-clock-rat.patch b/sys-kernel/pinephone-sources/files/0028-media-i2c-imx258-Don-t-be-too-strict-about-clock-rat.patch new file mode 100644 index 0000000..ef345ec --- /dev/null +++ b/sys-kernel/pinephone-sources/files/0028-media-i2c-imx258-Don-t-be-too-strict-about-clock-rat.patch @@ -0,0 +1,41 @@ +From: Ondrej Jirman +Date: Fri, 22 Oct 2021 18:11:26 +0200 +Subject: [PATCH 21/36] media: i2c: imx258: Don't be too strict about clock + rate + +On Pinephone Pro, we are not able to set 19.2MHz precisely. +Allow some slack in clock rate. + +Signed-off-by: Ondrej Jirman +--- + drivers/media/i2c/imx258.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c +index be5adcc..e64fadc 100644 +--- a/drivers/media/i2c/imx258.c ++++ b/drivers/media/i2c/imx258.c +@@ -79,7 +79,9 @@ + #define REG_CONFIG_FLIP_TEST_PATTERN 0x02 + + /* Input clock frequency in Hz */ ++#define IMX258_INPUT_CLOCK_FREQ_MIN 19000000 + #define IMX258_INPUT_CLOCK_FREQ 19200000 ++#define IMX258_INPUT_CLOCK_FREQ_MAX 19400000 + + struct imx258_reg { + u16 address; +@@ -1277,8 +1279,11 @@ static int imx258_probe(struct i2c_client *client) + } else { + val = clk_get_rate(imx258->clk); + } +- if (val != IMX258_INPUT_CLOCK_FREQ) { +- dev_err(&client->dev, "input clock frequency not supported\n"); ++ ++ if (val < IMX258_INPUT_CLOCK_FREQ_MIN ++ || val > IMX258_INPUT_CLOCK_FREQ_MAX) { ++ dev_err(&client->dev, "input clock frequency %u not supported\n", ++ val); + return -EINVAL; + } + diff --git a/sys-kernel/pinephone-sources/files/0029-media-i2c-imx258-Add-support-for-reset-gpio.patch b/sys-kernel/pinephone-sources/files/0029-media-i2c-imx258-Add-support-for-reset-gpio.patch new file mode 100644 index 0000000..af85962 --- /dev/null +++ b/sys-kernel/pinephone-sources/files/0029-media-i2c-imx258-Add-support-for-reset-gpio.patch @@ -0,0 +1,57 @@ +From: Ondrej Jirman +Date: Fri, 22 Oct 2021 21:44:13 +0200 +Subject: [PATCH 22/36] media: i2c: imx258: Add support for reset gpio + +It was documented in DT, but not implemented. + +Signed-off-by: Ondrej Jirman +--- + drivers/media/i2c/imx258.c | 14 +++++++++++++- + 1 file changed, 13 insertions(+), 1 deletion(-) + +diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c +index e64fadc..ec6e919 100644 +--- a/drivers/media/i2c/imx258.c ++++ b/drivers/media/i2c/imx258.c +@@ -615,6 +615,7 @@ struct imx258 { + struct v4l2_ctrl *exposure; + + struct gpio_desc *pwdn_gpio; ++ struct gpio_desc *reset_gpio; + + /* Current mode */ + const struct imx258_mode *cur_mode; +@@ -1020,7 +1021,11 @@ static int imx258_power_on(struct device *dev) + if (ret) + dev_err(dev, "failed to enable clock\n"); + +- return ret; ++ gpiod_set_value_cansleep(imx258->reset_gpio, 0); ++ ++ usleep_range(400, 500); ++ ++ return 0; + } + + static int imx258_power_off(struct device *dev) +@@ -1030,6 +1035,7 @@ static int imx258_power_off(struct device *dev) + + clk_disable_unprepare(imx258->clk); + ++ gpiod_set_value_cansleep(imx258->reset_gpio, 1); + gpiod_set_value_cansleep(imx258->pwdn_gpio, 1); + + return 0; +@@ -1301,6 +1307,12 @@ static int imx258_probe(struct i2c_client *client) + if (IS_ERR(imx258->pwdn_gpio)) + return PTR_ERR(imx258->pwdn_gpio); + ++ /* request optional reset pin */ ++ imx258->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", ++ GPIOD_OUT_HIGH); ++ if (IS_ERR(imx258->reset_gpio)) ++ return PTR_ERR(imx258->reset_gpio); ++ + /* Initialize subdev */ + v4l2_i2c_subdev_init(&imx258->sd, client, &imx258_subdev_ops); + diff --git a/sys-kernel/pinephone-sources/files/0030-media-i2c-imx258-Add-support-for-power-supplies.patch b/sys-kernel/pinephone-sources/files/0030-media-i2c-imx258-Add-support-for-power-supplies.patch new file mode 100644 index 0000000..23c09d8 --- /dev/null +++ b/sys-kernel/pinephone-sources/files/0030-media-i2c-imx258-Add-support-for-power-supplies.patch @@ -0,0 +1,100 @@ +From: Ondrej Jirman +Date: Fri, 22 Oct 2021 21:44:30 +0200 +Subject: [PATCH 23/36] media: i2c: imx258: Add support for power supplies + +They were documented in DT, but not implemented. + +Signed-off-by: Ondrej Jirman +--- + drivers/media/i2c/imx258.c | 39 +++++++++++++++++++++++++++++++++++++-- + 1 file changed, 37 insertions(+), 2 deletions(-) + +diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c +index ec6e919..2570b51 100644 +--- a/drivers/media/i2c/imx258.c ++++ b/drivers/media/i2c/imx258.c +@@ -602,6 +602,15 @@ static const struct imx258_mode supported_modes[] = { + }, + }; + ++/* regulator supplies */ ++static const char * const imx258_supply_names[] = { ++ "vana", /* Analog (2.8V) supply */ ++ "vdig", /* Digital Core (1.5V) supply */ ++ "vif", /* Digital I/O (1.8V) supply */ ++}; ++ ++#define IMX258_SUPPLY_COUNT ARRAY_SIZE(imx258_supply_names) ++ + struct imx258 { + struct v4l2_subdev sd; + struct media_pad pad; +@@ -616,6 +625,7 @@ struct imx258 { + + struct gpio_desc *pwdn_gpio; + struct gpio_desc *reset_gpio; ++ struct regulator_bulk_data supplies[IMX258_SUPPLY_COUNT]; + + /* Current mode */ + const struct imx258_mode *cur_mode; +@@ -1015,11 +1025,26 @@ static int imx258_power_on(struct device *dev) + struct imx258 *imx258 = to_imx258(sd); + int ret; + ++ ret = regulator_bulk_enable(IMX258_SUPPLY_COUNT, imx258->supplies); ++ if (ret) { ++ dev_err(dev, "failed to enable regulators\n"); ++ return ret; ++ } ++ ++ mdelay(20); ++ + gpiod_set_value_cansleep(imx258->pwdn_gpio, 0); + ++ mdelay(5); ++ + ret = clk_prepare_enable(imx258->clk); +- if (ret) ++ if (ret) { + dev_err(dev, "failed to enable clock\n"); ++ regulator_bulk_disable(IMX258_SUPPLY_COUNT, imx258->supplies); ++ return ret; ++ } ++ ++ usleep_range(1000, 2000); + + gpiod_set_value_cansleep(imx258->reset_gpio, 0); + +@@ -1038,6 +1063,8 @@ static int imx258_power_off(struct device *dev) + gpiod_set_value_cansleep(imx258->reset_gpio, 1); + gpiod_set_value_cansleep(imx258->pwdn_gpio, 1); + ++ regulator_bulk_disable(IMX258_SUPPLY_COUNT, imx258->supplies); ++ + return 0; + } + +@@ -1266,7 +1293,7 @@ static void imx258_free_controls(struct imx258 *imx258) + static int imx258_probe(struct i2c_client *client) + { + struct imx258 *imx258; +- int ret; ++ int ret, i; + u32 val = 0; + + imx258 = devm_kzalloc(&client->dev, sizeof(*imx258), GFP_KERNEL); +@@ -1301,6 +1328,14 @@ static int imx258_probe(struct i2c_client *client) + if (ret || val != 180) + return -EINVAL; + ++ for (i = 0; i < IMX258_SUPPLY_COUNT; i++) ++ imx258->supplies[i].supply = imx258_supply_names[i]; ++ ret = devm_regulator_bulk_get(&client->dev, ++ IMX258_SUPPLY_COUNT, ++ imx258->supplies); ++ if (ret) ++ return dev_err_probe(&client->dev, ret, "Failed to get supplies\n"); ++ + /* request optional power down pin */ + imx258->pwdn_gpio = devm_gpiod_get_optional(&client->dev, "powerdown", + GPIOD_OUT_HIGH); diff --git a/sys-kernel/pinephone-sources/files/0031-media-ov5640-Add-more-framerates-to-the-driver-some-.patch b/sys-kernel/pinephone-sources/files/0031-media-ov5640-Add-more-framerates-to-the-driver-some-.patch new file mode 100644 index 0000000..5f3faf5 --- /dev/null +++ b/sys-kernel/pinephone-sources/files/0031-media-ov5640-Add-more-framerates-to-the-driver-some-.patch @@ -0,0 +1,52 @@ +From: Ondrej Jirman +Date: Fri, 24 Jan 2020 18:25:12 +0100 +Subject: [PATCH 091/194] media: ov5640: Add more framerates to the driver + (some of them even work!) + +Signed-off-by: Ondrej Jirman +--- + drivers/media/i2c/ov5640.c | 14 +++++++++++--- + 1 file changed, 11 insertions(+), 3 deletions(-) + +diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c +index ddbd713..624a3c0 100644 +--- a/drivers/media/i2c/ov5640.c ++++ b/drivers/media/i2c/ov5640.c +@@ -112,7 +112,11 @@ enum ov5640_mode_id { + }; + + enum ov5640_frame_rate { +- OV5640_15_FPS = 0, ++ OV5640_2_FPS = 0, ++ OV5640_3_FPS, ++ OV5640_5_FPS, ++ OV5640_7_FPS, ++ OV5640_15_FPS, + OV5640_30_FPS, + OV5640_60_FPS, + OV5640_NUM_FRAMERATES, +@@ -156,6 +160,10 @@ MODULE_PARM_DESC(virtual_channel, + "MIPI CSI-2 virtual channel (0..3), default 0"); + + static const int ov5640_framerates[] = { ++ [OV5640_2_FPS] = 2, ++ [OV5640_3_FPS] = 3, ++ [OV5640_5_FPS] = 5, ++ [OV5640_7_FPS] = 7, + [OV5640_15_FPS] = 15, + [OV5640_30_FPS] = 30, + [OV5640_60_FPS] = 60, +@@ -2193,11 +2201,11 @@ static int ov5640_try_frame_interval(struct ov5640_dev *sensor, + u32 width, u32 height) + { + const struct ov5640_mode_info *mode; +- enum ov5640_frame_rate rate = OV5640_15_FPS; ++ enum ov5640_frame_rate rate = OV5640_2_FPS; + int minfps, maxfps, best_fps, fps; + int i; + +- minfps = ov5640_framerates[OV5640_15_FPS]; ++ minfps = ov5640_framerates[OV5640_2_FPS]; + maxfps = ov5640_framerates[OV5640_60_FPS]; + + if (fi->numerator == 0) { diff --git a/sys-kernel/pinephone-sources/files/0032-media-ov5640-Experiment-Try-to-disable-denoising-sha.patch b/sys-kernel/pinephone-sources/files/0032-media-ov5640-Experiment-Try-to-disable-denoising-sha.patch new file mode 100644 index 0000000..a9b034a --- /dev/null +++ b/sys-kernel/pinephone-sources/files/0032-media-ov5640-Experiment-Try-to-disable-denoising-sha.patch @@ -0,0 +1,47 @@ +From: Ondrej Jirman +Date: Fri, 24 Jan 2020 18:25:59 +0100 +Subject: [PATCH 158/465] 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 624a3c0..2f6f97e 100644 +--- a/drivers/media/i2c/ov5640.c ++++ b/drivers/media/i2c/ov5640.c +@@ -1772,6 +1772,7 @@ static int ov5640_set_mode(struct ov5640_dev *sensor) + bool auto_exp = sensor->ctrls.auto_exp->val == V4L2_EXPOSURE_AUTO; + unsigned long rate; + int ret; ++ u8 tmp; + + dn_mode = mode->dn_mode; + orig_dn_mode = orig_mode->dn_mode; +@@ -1844,6 +1845,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; + diff --git a/sys-kernel/pinephone-sources/files/0033-media-ov5640-Sleep-after-poweroff-to-ensure-next-pow.patch b/sys-kernel/pinephone-sources/files/0033-media-ov5640-Sleep-after-poweroff-to-ensure-next-pow.patch new file mode 100644 index 0000000..4f68862 --- /dev/null +++ b/sys-kernel/pinephone-sources/files/0033-media-ov5640-Sleep-after-poweroff-to-ensure-next-pow.patch @@ -0,0 +1,25 @@ +From: Ondrej Jirman +Date: Sun, 26 Jan 2020 00:19:40 +0100 +Subject: [PATCH 093/194] 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 2f6f97e..8a53c0c 100644 +--- a/drivers/media/i2c/ov5640.c ++++ b/drivers/media/i2c/ov5640.c +@@ -1971,6 +1971,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) diff --git a/sys-kernel/pinephone-sources/files/0034-media-ov5640-Don-t-powerup-the-sensor-during-driver-.patch b/sys-kernel/pinephone-sources/files/0034-media-ov5640-Don-t-powerup-the-sensor-during-driver-.patch new file mode 100644 index 0000000..3da1f5d --- /dev/null +++ b/sys-kernel/pinephone-sources/files/0034-media-ov5640-Don-t-powerup-the-sensor-during-driver-.patch @@ -0,0 +1,86 @@ +From: Ondrej Jirman +Date: Sun, 26 Jan 2020 00:28:10 +0100 +Subject: [PATCH 094/194] 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 | 40 ++++++++-------------------------------- + 1 file changed, 8 insertions(+), 32 deletions(-) + +diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c +index 8a53c0c..75f71e6 100644 +--- a/drivers/media/i2c/ov5640.c ++++ b/drivers/media/i2c/ov5640.c +@@ -1932,6 +1932,7 @@ static void ov5640_reset(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); +@@ -1956,6 +1957,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: +@@ -3039,34 +3047,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_set_power_on(sensor); +- if (ret) +- return ret; +- +- 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; +- } +- +- if (chip_id != 0x5640) { +- dev_err(&client->dev, "%s: wrong chip identifier, expected 0x5640, got 0x%x\n", +- __func__, chip_id); +- ret = -ENXIO; +- } +- +-power_off: +- ov5640_set_power_off(sensor); +- return ret; +-} +- + static int ov5640_probe(struct i2c_client *client) + { + struct device *dev = &client->dev; +@@ -3184,10 +3164,6 @@ static int ov5640_probe(struct i2c_client *client) + + mutex_init(&sensor->lock); + +- ret = ov5640_check_chip_id(sensor); +- if (ret) +- goto entity_cleanup; +- + ret = ov5640_init_controls(sensor); + if (ret) + goto entity_cleanup; diff --git a/sys-kernel/pinephone-sources/files/media-ov5640-Implement-autofocus.patch b/sys-kernel/pinephone-sources/files/0035-media-ov5640-Implement-autofocus.patch similarity index 91% rename from sys-kernel/pinephone-sources/files/media-ov5640-Implement-autofocus.patch rename to sys-kernel/pinephone-sources/files/0035-media-ov5640-Implement-autofocus.patch index 68c6581..4260878 100644 --- a/sys-kernel/pinephone-sources/files/media-ov5640-Implement-autofocus.patch +++ b/sys-kernel/pinephone-sources/files/0035-media-ov5640-Implement-autofocus.patch @@ -1,7 +1,6 @@ -From f062022f2a2781d6b8ca63c460b0e72ebac30870 Mon Sep 17 00:00:00 2001 From: Martijn Braam Date: Mon, 28 Sep 2020 14:26:11 +0200 -Subject: [PATCH] media: ov5640: Implement autofocus +Subject: [PATCH 120/194] media: ov5640: Implement autofocus The autofocus functionality needs a firmware blob loaded into the internal microcontroller. @@ -11,11 +10,11 @@ this at least makes it possible to focus on the center of the sensor. Signed-off-by: Martijn Braam --- - drivers/media/i2c/ov5640.c | 254 +++++++++++++++++++++++++++++++++++++ - 1 file changed, 254 insertions(+) + drivers/media/i2c/ov5640.c | 273 +++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 273 insertions(+) diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c -index df0a507c211f..08a5304c0e95 100644 +index 75f71e6..246563e 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -9,6 +9,7 @@ @@ -61,7 +60,7 @@ index df0a507c211f..08a5304c0e95 100644 #define OV5640_REG_TIMING_DVPHO 0x3808 #define OV5640_REG_TIMING_DVPVO 0x380a #define OV5640_REG_TIMING_HTS 0x380c -@@ -95,6 +109,20 @@ +@@ -96,6 +110,20 @@ #define OV5640_REG_SDE_CTRL4 0x5584 #define OV5640_REG_SDE_CTRL5 0x5585 #define OV5640_REG_AVG_READOUT 0x56a1 @@ -81,8 +80,8 @@ index df0a507c211f..08a5304c0e95 100644 +#define OV5640_FW_CMD_DEFAULT_ZONES 0x80 enum ov5640_mode_id { - OV5640_MODE_QCIF_176_144 = 0, -@@ -218,6 +246,12 @@ struct ov5640_ctrls { + OV5640_MODE_QQVGA_160_120 = 0, +@@ -222,6 +250,12 @@ struct ov5640_ctrls { struct v4l2_ctrl *auto_gain; struct v4l2_ctrl *gain; }; @@ -95,7 +94,7 @@ index df0a507c211f..08a5304c0e95 100644 struct v4l2_ctrl *brightness; struct v4l2_ctrl *light_freq; struct v4l2_ctrl *saturation; -@@ -261,6 +295,8 @@ struct ov5640_dev { +@@ -265,6 +299,8 @@ struct ov5640_dev { bool pending_mode_change; bool streaming; @@ -104,7 +103,7 @@ index df0a507c211f..08a5304c0e95 100644 }; static inline struct ov5640_dev *to_ov5640_dev(struct v4l2_subdev *sd) -@@ -1967,6 +2003,118 @@ static void ov5640_reset(struct ov5640_dev *sensor) +@@ -1929,6 +1965,118 @@ static void ov5640_reset(struct ov5640_dev *sensor) usleep_range(20000, 25000); } @@ -223,7 +222,7 @@ index df0a507c211f..08a5304c0e95 100644 static int ov5640_set_power_on(struct ov5640_dev *sensor) { struct i2c_client *client = sensor->i2c_client; -@@ -1988,6 +2117,8 @@ static int ov5640_set_power_on(struct ov5640_dev *sensor) +@@ -1950,6 +2098,8 @@ static int ov5640_set_power_on(struct ov5640_dev *sensor) goto xclk_off; } @@ -232,7 +231,7 @@ index df0a507c211f..08a5304c0e95 100644 ov5640_reset(sensor); ov5640_power(sensor, true); -@@ -2416,6 +2547,35 @@ static int ov5640_set_framefmt(struct ov5640_dev *sensor, +@@ -2467,6 +2617,35 @@ static int ov5640_set_framefmt(struct ov5640_dev *sensor, is_jpeg ? (BIT(5) | BIT(3)) : 0); } @@ -268,7 +267,7 @@ index df0a507c211f..08a5304c0e95 100644 /* * Sensor Controls. */ -@@ -2532,6 +2692,41 @@ static int ov5640_set_ctrl_exposure(struct ov5640_dev *sensor, +@@ -2583,6 +2762,41 @@ static int ov5640_set_ctrl_exposure(struct ov5640_dev *sensor, return ret; } @@ -310,7 +309,7 @@ index df0a507c211f..08a5304c0e95 100644 static int ov5640_set_ctrl_gain(struct ov5640_dev *sensor, bool auto_gain) { struct ov5640_ctrls *ctrls = &sensor->ctrls; -@@ -2638,6 +2833,32 @@ static int ov5640_set_ctrl_vflip(struct ov5640_dev *sensor, int value) +@@ -2689,6 +2903,32 @@ static int ov5640_set_ctrl_vflip(struct ov5640_dev *sensor, int value) (BIT(2) | BIT(1)) : 0); } @@ -343,7 +342,7 @@ index df0a507c211f..08a5304c0e95 100644 static int ov5640_g_volatile_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = ctrl_to_sd(ctrl); -@@ -2659,6 +2880,12 @@ static int ov5640_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +@@ -2710,6 +2950,12 @@ static int ov5640_g_volatile_ctrl(struct v4l2_ctrl *ctrl) return val; sensor->ctrls.exposure->val = val; break; @@ -356,7 +355,7 @@ index df0a507c211f..08a5304c0e95 100644 } return 0; -@@ -2690,6 +2917,18 @@ static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl) +@@ -2741,6 +2987,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; @@ -375,7 +374,7 @@ index df0a507c211f..08a5304c0e95 100644 case V4L2_CID_HUE: ret = ov5640_set_ctrl_hue(sensor, ctrl->val); break; -@@ -2762,6 +3001,20 @@ static int ov5640_init_controls(struct ov5640_dev *sensor) +@@ -2813,6 +3071,20 @@ static int ov5640_init_controls(struct ov5640_dev *sensor) ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN, 0, 1023, 1, 0); @@ -396,7 +395,7 @@ index df0a507c211f..08a5304c0e95 100644 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, -@@ -2795,6 +3048,7 @@ static int ov5640_init_controls(struct ov5640_dev *sensor) +@@ -2846,6 +3118,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); @@ -404,6 +403,3 @@ index df0a507c211f..08a5304c0e95 100644 sensor->sd.ctrl_handler = hdl; return 0; --- -2.25.4 - diff --git a/sys-kernel/pinephone-sources/files/0036-media-ov5640-set-default-ae-target-lower.patch b/sys-kernel/pinephone-sources/files/0036-media-ov5640-set-default-ae-target-lower.patch new file mode 100644 index 0000000..009d1bb --- /dev/null +++ b/sys-kernel/pinephone-sources/files/0036-media-ov5640-set-default-ae-target-lower.patch @@ -0,0 +1,23 @@ +From: Martijn Braam +Date: Wed, 7 Oct 2020 17:33:43 +0200 +Subject: [PATCH 121/194] 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 246563e..f01bf29 100644 +--- a/drivers/media/i2c/ov5640.c ++++ b/drivers/media/i2c/ov5640.c +@@ -3355,7 +3355,7 @@ static int ov5640_probe(struct i2c_client *client) + &ov5640_mode_data[OV5640_MODE_VGA_640_480]; + sensor->last_mode = sensor->current_mode; + +- sensor->ae_target = 52; ++ sensor->ae_target = 28; + + /* optional indication of physical rotation of sensor */ + ret = fwnode_property_read_u32(dev_fwnode(&client->dev), "rotation", diff --git a/sys-kernel/pinephone-sources/files/0037-drm-panel-hx8394-Add-driver-for-HX8394-based-HannSta.patch b/sys-kernel/pinephone-sources/files/0037-drm-panel-hx8394-Add-driver-for-HX8394-based-HannSta.patch new file mode 100644 index 0000000..fc6634b --- /dev/null +++ b/sys-kernel/pinephone-sources/files/0037-drm-panel-hx8394-Add-driver-for-HX8394-based-HannSta.patch @@ -0,0 +1,464 @@ +From: =?utf-8?q?Kamil_Trzci=C5=84ski?= +Date: Wed, 8 Sep 2021 13:50:04 +0200 +Subject: [PATCH 05/36] drm: panel: hx8394: Add driver for HX8394 based + HannStar HSD060BHW4 panel +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit + +... + +Signed-off-by: Kamil TrzciÅ„ski +Signed-off-by: Ondrej Jirman +--- + drivers/gpu/drm/panel/Kconfig | 9 + + drivers/gpu/drm/panel/Makefile | 1 + + drivers/gpu/drm/panel/panel-himax-hx8394.c | 410 +++++++++++++++++++++++++++++ + 3 files changed, 420 insertions(+) + create mode 100644 drivers/gpu/drm/panel/panel-himax-hx8394.c + +diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig +index cfc8d64..166bd4a 100644 +--- a/drivers/gpu/drm/panel/Kconfig ++++ b/drivers/gpu/drm/panel/Kconfig +@@ -129,6 +129,15 @@ config DRM_PANEL_FEIYANG_FY07024DI26A30D + Say Y if you want to enable support for panels based on the + Feiyang FY07024DI26A30-D MIPI-DSI interface. + ++config DRM_PANEL_HIMAX_HX8394 ++ tristate "HIMAX HX8394 MIPI-DSI LCD panel" ++ depends on OF ++ depends on DRM_MIPI_DSI ++ depends on BACKLIGHT_CLASS_DEVICE ++ help ++ Say Y if you want to enable support for panels based on the ++ HIMAX HX8394 MIPI-DSI interface. ++ + config DRM_PANEL_ILITEK_IL9322 + tristate "Ilitek ILI9322 320x240 QVGA panels" + depends on OF && SPI +diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile +index bca4cc1..486926c 100644 +--- a/drivers/gpu/drm/panel/Makefile ++++ b/drivers/gpu/drm/panel/Makefile +@@ -67,3 +67,4 @@ obj-$(CONFIG_DRM_PANEL_TRULY_NT35597_WQXGA) += panel-truly-nt35597.o + obj-$(CONFIG_DRM_PANEL_VISIONOX_RM69299) += panel-visionox-rm69299.o + obj-$(CONFIG_DRM_PANEL_WIDECHIPS_WS2401) += panel-widechips-ws2401.o + obj-$(CONFIG_DRM_PANEL_XINPENG_XPP055C272) += panel-xinpeng-xpp055c272.o ++obj-$(CONFIG_DRM_PANEL_HIMAX_HX8394) += panel-himax-hx8394.o +diff --git a/drivers/gpu/drm/panel/panel-himax-hx8394.c b/drivers/gpu/drm/panel/panel-himax-hx8394.c +new file mode 100644 +index 00000000..14659cb +--- /dev/null ++++ b/drivers/gpu/drm/panel/panel-himax-hx8394.c +@@ -0,0 +1,410 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Driver for panels based on Himax HX8394 controller, souch as: ++ * ++ * - HannStar HSD060BHW4 5.99" MIPI-DSI panel ++ * ++ * Copyright (C) Kamil TrzciÅ„ski ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include