From b77350d9a27657139c303cec4461230c179e9e14 Mon Sep 17 00:00:00 2001 From: Seung-Woo Kim Date: Tue, 2 Feb 2021 16:30:56 +0900 Subject: [PATCH 01/16] arm64: dts: amlogic: VIM3/VIM3L: change display to drm Instead of fbdev, change display to drm. Change-Id: I1c49c92f83ae4c9904116143881839d55cac2634 Signed-off-by: Seung-Woo Kim --- arch/arm64/boot/dts/amlogic/kvim3_linux.dts | 17 ++++++++++++++++- arch/arm64/boot/dts/amlogic/kvim3l_linux.dts | 17 ++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/amlogic/kvim3_linux.dts b/arch/arm64/boot/dts/amlogic/kvim3_linux.dts index 6583f3a..8961974 100644 --- a/arch/arm64/boot/dts/amlogic/kvim3_linux.dts +++ b/arch/arm64/boot/dts/amlogic/kvim3_linux.dts @@ -20,6 +20,7 @@ #include "mesong12b.dtsi" #include "partition_linux.dtsi" #include "khadas-ts050-panel.dtsi" +#include "mesong12b_drm.dtsi" / { model = "Khadas VIM3"; @@ -876,7 +877,7 @@ }; &meson_fb { - status = "okay"; + status = "disabled"; display_size_default = <1920 1080 1920 2160 32>; mem_size = <0x00800000 0x4b80000 0x100000 0x100000 0x800000>; logo_addr = "0x7f800000"; @@ -885,6 +886,20 @@ 4k2k_fb = <1>; }; +&drm_vpu { + status = "okay"; + logo_addr = "0x7f800000"; +}; + +&drm_amhdmitx { + status = "okay"; + hdcp = "disabled"; +}; + +&drm_lcd { + status = "disabled"; +}; + &pwm_ab { status = "okay"; }; diff --git a/arch/arm64/boot/dts/amlogic/kvim3l_linux.dts b/arch/arm64/boot/dts/amlogic/kvim3l_linux.dts index 1c38e44..de3e240 100644 --- a/arch/arm64/boot/dts/amlogic/kvim3l_linux.dts +++ b/arch/arm64/boot/dts/amlogic/kvim3l_linux.dts @@ -20,6 +20,7 @@ #include "mesonsm1.dtsi" #include "partition_linux.dtsi" #include "khadas-ts050-panel.dtsi" +#include "mesong12a_drm.dtsi" / { model = "Khadas VIM3L"; @@ -1070,7 +1071,7 @@ }; /* end of / */ &meson_fb { - status = "okay"; + status = "disabled"; display_size_default = <1920 1080 1920 2160 32>; mem_size = <0x00800000 0x4b80000 0x100000 0x100000 0x800000>; logo_addr = "0x7f800000"; @@ -1079,6 +1080,20 @@ 4k2k_fb = <1>; }; +&drm_vpu { + status = "okay"; + logo_addr = "0x7f800000"; +}; + +&drm_amhdmitx { + status = "okay"; + hdcp = "disabled"; +}; + +&drm_lcd { + status = "disabled"; +}; + &pwm_AO_cd { status = "okay"; }; -- 2.7.4 From 4ec7cc3e85ec61360059af79ef5947c65ad5b889 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 2 Feb 2021 19:16:12 +0100 Subject: [PATCH 02/16] Add perf package build Add build of the perf performance monitoring tool package. Based on spec from the platform/kernel/linux-rpi3 kernel tree. Change-Id: Iedee81ac5f81f63c40ed6a379f2fca3f793c4d8b Signed-off-by: Sylwester Nawrocki --- packaging/linux-amlogic.spec | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/packaging/linux-amlogic.spec b/packaging/linux-amlogic.spec index fb61b40..2aef3dc 100644 --- a/packaging/linux-amlogic.spec +++ b/packaging/linux-amlogic.spec @@ -34,6 +34,12 @@ BuildRequires: module-init-tools BuildRequires: bison BuildRequires: flex BuildRequires: libopenssl1.1-devel +BuildRequires: libunwind-devel +BuildRequires: libdw-devel +BuildRequires: libelf-devel +BuildRequires: elfutils +BuildRequires: xz-devel +BuildRequires: binutils-devel %description The Linux Kernel, the operating system core itself @@ -115,6 +121,15 @@ Provides: linux-kernel-devel-amlogic-kvim = %{version}-%{CHIPSET}-kvim %description -n linux-kernel-devel-amlogic-kvim This package provides kernel map and etc information. %endif + +%package -n linux-kernel-perf +Summary: The perf performance counter tool +Group: System/Kernel +Provides: perf = %{fullVersion} + +%description -n linux-kernel-perf +This package provides the "perf" tool that can be used to monitor performance +counter events as well as various kernel internal events. ## 0. End of Packages lists %prep @@ -212,6 +227,14 @@ mv %{_builddir}/uapi-headers/usr %{buildroot} mv %{_builddir}/boot/* %{buildroot}/boot mv %{_builddir}/lib %{buildroot} %endif + +# 2-3. Install perf +install -d %{buildroot}/usr +make -s -C tools/perf EXTRA_CFLAGS="-fPIE -rdynamic" DESTDIR=%{buildroot}/usr install NO_LIBPERL=1 +rm -rf %{buildroot}/usr/etc +rm -rf %{buildroot}/usr/lib/debug +rm -rf %{buildroot}/usr/lib/perf +rm -rf %{buildroot}/usr/share ## 2. End of install steps %clean @@ -252,4 +275,10 @@ rm -rf %{_builddir}/lib %files -n linux-kernel-devel-amlogic-kvim /boot/%{TARGET_VIMS}/kernel/* %endif + +%files -n linux-kernel-perf +%license COPYING +/usr/bin/* +/usr/libexec/* +/usr/lib/traceevent/* ## 3. End of rpm pack -- 2.7.4 From f856681273ba71fa7e833f57cf23cb92e034cb0c Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 2 Feb 2021 11:24:36 +0100 Subject: [PATCH 03/16] of: fix reserved memory handling Mainline u-boot marks some secure monitor relate memory with the /memreserve/ device-tree method. On the other hand, the same memory is already defined in /reserved-memory node with the all details needed to use it by the respective secure monitor deamon. Check for such case and properly initialize such reserved memory regions instead of returning a failure. This allows to use vendor kernel with mainline u-boot on Amlogic SoCs. Signed-off-by: Marek Szyprowski Change-Id: I0bd3b51899cc17f17d1bcbd0c2c5a0d88686b245 --- drivers/of/of_reserved_mem.c | 8 ++++++++ include/linux/memblock.h | 1 + mm/memblock.c | 10 ++++++++++ 3 files changed, 19 insertions(+) diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c index c4a7b04..aa5ed94 100644 --- a/drivers/of/of_reserved_mem.c +++ b/drivers/of/of_reserved_mem.c @@ -43,6 +43,14 @@ int __init __weak early_init_dt_alloc_reserved_memory_arch(phys_addr_t size, phys_addr_t *res_base) { phys_addr_t base; + + /* check if the region has been alredy reserved by the bootloader */ + if (start && end && memblock_is_region_reserved(start, end - start)) { + pr_info("OF: region (%pa--%pa) is reserved both in /memreserve/ and /reserved-memory, fixing\n", + &start, &end); + memblock_unreserve(start, end - start); + } + /* * We use __memblock_alloc_base() because memblock_alloc_base() * panic()s on allocation failure. diff --git a/include/linux/memblock.h b/include/linux/memblock.h index 4024af0..debfcb9 100644 --- a/include/linux/memblock.h +++ b/include/linux/memblock.h @@ -84,6 +84,7 @@ int memblock_add(phys_addr_t base, phys_addr_t size); int memblock_remove(phys_addr_t base, phys_addr_t size); int memblock_free(phys_addr_t base, phys_addr_t size); int memblock_reserve(phys_addr_t base, phys_addr_t size); +int memblock_unreserve(phys_addr_t base, phys_addr_t size); void memblock_trim_memory(phys_addr_t align); bool memblock_overlaps_region(struct memblock_type *type, phys_addr_t base, phys_addr_t size); diff --git a/mm/memblock.c b/mm/memblock.c index 42b98af..71867c2 100644 --- a/mm/memblock.c +++ b/mm/memblock.c @@ -733,6 +733,16 @@ int __init_memblock memblock_reserve(phys_addr_t base, phys_addr_t size) return memblock_add_range(&memblock.reserved, base, size, MAX_NUMNODES, 0); } +int __init_memblock memblock_unreserve(phys_addr_t base, phys_addr_t size) +{ + memblock_dbg(" memblock_unreserve: [%#016llx-%#016llx] %pF\n", + (unsigned long long)base, + (unsigned long long)base + size - 1, + (void *)_RET_IP_); + + return memblock_remove_range(&memblock.reserved, base, size); +} + /** * * This function isolates region [@base, @base + @size), and sets/clears flag -- 2.7.4 From 1156b464e225eacd149a405134d46b3fd9649a54 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 5 Feb 2021 14:15:35 +0100 Subject: [PATCH 04/16] arm64: dts: amlogic: Use only meson64_odroidn2.dtb for OdroidN2 boards Switch OdroidN2 board to use meson64_odroidn2.dtb. For Tizen, the DRM subsystem is enabled for the all Amlogic boards, so there is no need to have separate dtb with the DRM enabled for OdroidN2 board. This also fixes DRM support for the OdroidN2+ variant, which use meson64_odroidn2_plus.dtb, which is based on meson64_odroidn2.dts. Signed-off-by: Marek Szyprowski Change-Id: Ideeefcaabe5355e59f564abba730e1ad88ea2ce8 --- arch/arm64/boot/dts/amlogic/Makefile | 1 - arch/arm64/boot/dts/amlogic/meson64_odroidn2.dts | 19 +++++++++++ .../boot/dts/amlogic/meson64_odroidn2_drm.dts | 39 ---------------------- 3 files changed, 19 insertions(+), 40 deletions(-) delete mode 100644 arch/arm64/boot/dts/amlogic/meson64_odroidn2_drm.dts diff --git a/arch/arm64/boot/dts/amlogic/Makefile b/arch/arm64/boot/dts/amlogic/Makefile index 81034e2..d18eaa5d 100644 --- a/arch/arm64/boot/dts/amlogic/Makefile +++ b/arch/arm64/boot/dts/amlogic/Makefile @@ -24,7 +24,6 @@ dtb-$(CONFIG_ARCH_MESON64_ODROIDC4) += meson64_odroidc4.dtb dtb-$(CONFIG_ARCH_MESON64_ODROIDC4) += meson64_odroidhc4.dtb dtb-$(CONFIG_ARCH_MESON64_ODROIDN2) += meson64_odroidn2.dtb dtb-$(CONFIG_ARCH_MESON64_ODROIDN2) += meson64_odroidn2_plus.dtb -dtb-$(CONFIG_ARCH_MESON64_ODROIDN2) += meson64_odroidn2_drm.dtb subdir-$(CONFIG_ARCH_MESON64_ODROIDC4) += overlays/odroidc4 subdir-$(CONFIG_ARCH_MESON64_ODROIDN2) += overlays/odroidn2 diff --git a/arch/arm64/boot/dts/amlogic/meson64_odroidn2.dts b/arch/arm64/boot/dts/amlogic/meson64_odroidn2.dts index a2a0ea9..4da3b15 100644 --- a/arch/arm64/boot/dts/amlogic/meson64_odroidn2.dts +++ b/arch/arm64/boot/dts/amlogic/meson64_odroidn2.dts @@ -19,6 +19,7 @@ #include "mesong12b_a.dtsi" #include "meson64_odroidn2.dtsi" +#include "mesong12b_drm.dtsi" / { model = "Hardkernel ODROID-N2"; @@ -146,3 +147,21 @@ status = "okay"; }; }; /* end of / */ + +&meson_fb { + status = "disabled"; +}; + +&drm_vpu { + status = "okay"; + logo_addr = "0x7f800000"; +}; + +&drm_amhdmitx { + status = "okay"; + hdcp = "disabled"; +}; + +&drm_lcd { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/amlogic/meson64_odroidn2_drm.dts b/arch/arm64/boot/dts/amlogic/meson64_odroidn2_drm.dts deleted file mode 100644 index 6c5aa70..0000000 --- a/arch/arm64/boot/dts/amlogic/meson64_odroidn2_drm.dts +++ /dev/null @@ -1,39 +0,0 @@ -/* - * arch/arm64/boot/dts/amlogic/meson64_odroidn2_drm.dts - * - * Copyright (C) 2019 Hardkernel Co,. Ltd. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - */ - -/dts-v1/; - -#include "meson64_odroidn2.dts" -#include "mesong12b_drm.dtsi" - -&meson_fb { - status = "disabled"; -}; - -&drm_vpu { - status = "okay"; - logo_addr = "0x7f800000"; -}; - -&drm_amhdmitx { - status = "okay"; - hdcp = "disabled"; -}; - -&drm_lcd { - status = "disabled"; -}; -- 2.7.4 From 8cb922922e39823a2e214519fbcb86bdc4f9e9f6 Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Wed, 17 Feb 2021 10:32:21 +0900 Subject: [PATCH 05/16] ARM/ARM64: defconfig: disable SECURITY_SMACK_NETFILTER config Disable SECURITY_SMACK_NETFILTER configuration. Change-Id: Ifc649881900c93064d9e48ef1e762d5ee335a9e2 Signed-off-by: Jaehoon Chung --- arch/arm64/configs/tizen_kvims_defconfig | 1 - arch/arm64/configs/tizen_odroidg12_defconfig | 1 - 2 files changed, 2 deletions(-) diff --git a/arch/arm64/configs/tizen_kvims_defconfig b/arch/arm64/configs/tizen_kvims_defconfig index cf5e291..3351f0d 100644 --- a/arch/arm64/configs/tizen_kvims_defconfig +++ b/arch/arm64/configs/tizen_kvims_defconfig @@ -690,7 +690,6 @@ CONFIG_SECURITYFS=y CONFIG_SECURITY_PATH=y CONFIG_HARDENED_USERCOPY=y CONFIG_SECURITY_SMACK=y -CONFIG_SECURITY_SMACK_NETFILTER=y CONFIG_SECURITY_SMACK_APPEND_SIGNALS=y CONFIG_CRYPTO_MD4=y CONFIG_CRYPTO_MICHAEL_MIC=y diff --git a/arch/arm64/configs/tizen_odroidg12_defconfig b/arch/arm64/configs/tizen_odroidg12_defconfig index 0fb59ea..2ee8981 100644 --- a/arch/arm64/configs/tizen_odroidg12_defconfig +++ b/arch/arm64/configs/tizen_odroidg12_defconfig @@ -769,7 +769,6 @@ CONFIG_SECURITYFS=y CONFIG_SECURITY_PATH=y CONFIG_HARDENED_USERCOPY=y CONFIG_SECURITY_SMACK=y -CONFIG_SECURITY_SMACK_NETFILTER=y CONFIG_SECURITY_SMACK_APPEND_SIGNALS=y CONFIG_CRYPTO_MD4=y CONFIG_CRYPTO_MICHAEL_MIC=y -- 2.7.4 From 678d419a343f75547de83ca20cd0656c0f464c5f Mon Sep 17 00:00:00 2001 From: "Sihyun, Park" Date: Mon, 12 Apr 2021 15:36:32 +0900 Subject: [PATCH 06/16] amlogic: drm: disable 'meson_fb is NULL! warning log The log is printed when ovelay layer has no content to display, and it is normal thing. Change-Id: I83f078a2670f5f98098d5508603b77b6d5820d4e Signed-off-by: Sihyun, Park Signed-off-by: Seung-Woo Kim --- drivers/amlogic/drm/meson_plane.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/amlogic/drm/meson_plane.c b/drivers/amlogic/drm/meson_plane.c index 08270f1..7a66a9c 100644 --- a/drivers/amlogic/drm/meson_plane.c +++ b/drivers/amlogic/drm/meson_plane.c @@ -152,7 +152,7 @@ static int meson_plane_fb_check(struct drm_plane *plane, #ifdef CONFIG_DRM_MESON_USE_ION meson_fb = container_of(fb, struct am_meson_fb, base); if (!meson_fb) { - DRM_INFO("meson_fb is NULL!\n"); + DRM_DEBUG("meson_fb is NULL!\n"); return -EINVAL; } phyaddr = am_meson_gem_object_get_phyaddr(drv, meson_fb->bufp); -- 2.7.4 From c1ddcee7e73b2242ed2a85cc5f41cc51cbebae67 Mon Sep 17 00:00:00 2001 From: "Sihyun, Park" Date: Mon, 12 Apr 2021 15:38:35 +0900 Subject: [PATCH 07/16] amlogic: drm: mmap a dmabuf imported from another driver Change-Id: Iff7d5c204bbffac22e528840f264f669cae21d96 Signed-off-by: Sihyun, Park Signed-off-by: Seung-Woo Kim --- drivers/amlogic/drm/meson_gem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/amlogic/drm/meson_gem.c b/drivers/amlogic/drm/meson_gem.c index fba2bb7..7c472e3 100644 --- a/drivers/amlogic/drm/meson_gem.c +++ b/drivers/amlogic/drm/meson_gem.c @@ -160,7 +160,7 @@ int am_meson_gem_object_mmap( vma->vm_pgoff = 0; if (obj->base.import_attach) { - DRM_ERROR("Not support import buffer from other driver.\n"); + return dma_buf_mmap(obj->base.dma_buf, vma, 0); } else { buffer = obj->handle->buffer; -- 2.7.4 From 1fcbd6d000def5bd7c6099b2a3e74bc30b7cd752 Mon Sep 17 00:00:00 2001 From: Ao Xu Date: Mon, 12 Apr 2021 15:38:45 +0900 Subject: [PATCH 08/16] amlogic: drm: don't call irq in crtc init stage vblank_disable_fn and am_meson_vpu_irq exist deadlock. Disable vblank_disable_fn to avoid spin_lock_irqsave to resolve the deadlock issue. Change-Id: I59b454d2c2c0535f2e786e3beb4ca31c124483b0 Signed-off-by: Ao Xu Signed-off-by: Sihyun, Park Signed-off-by: Seung-Woo Kim --- drivers/amlogic/drm/meson_crtc.c | 13 ++----------- drivers/amlogic/drm/meson_crtc.h | 4 +--- drivers/amlogic/drm/meson_drv.c | 15 ++------------- drivers/amlogic/drm/meson_vpu.c | 35 +++++++---------------------------- 4 files changed, 12 insertions(+), 55 deletions(-) diff --git a/drivers/amlogic/drm/meson_crtc.c b/drivers/amlogic/drm/meson_crtc.c index 011f871..05e79bd 100644 --- a/drivers/amlogic/drm/meson_crtc.c +++ b/drivers/amlogic/drm/meson_crtc.c @@ -97,7 +97,6 @@ static bool am_meson_crtc_mode_fixup(struct drm_crtc *crtc, static void am_meson_crtc_enable(struct drm_crtc *crtc) { - unsigned long flags; char *name; enum vmode_e mode; struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode; @@ -124,16 +123,12 @@ static void am_meson_crtc_enable(struct drm_crtc *crtc) update_vout_viu(); memcpy(&pipeline->mode, adjusted_mode, sizeof(struct drm_display_mode)); - spin_lock_irqsave(&amcrtc->vblank_irq_lock, flags); - amcrtc->vblank_enable = 1; - spin_unlock_irqrestore(&amcrtc->vblank_irq_lock, flags); - enable_irq(amcrtc->vblank_irq); + enable_irq(amcrtc->irq); } static void am_meson_crtc_disable(struct drm_crtc *crtc) { struct am_meson_crtc *amcrtc = to_am_meson_crtc(crtc); - unsigned long flags; DRM_INFO("%s\n", __func__); if (crtc->state->event && !crtc->state->active) { @@ -143,11 +138,7 @@ static void am_meson_crtc_disable(struct drm_crtc *crtc) crtc->state->event = NULL; } - spin_lock_irqsave(&amcrtc->vblank_irq_lock, flags); - amcrtc->vblank_enable = 0; - spin_unlock_irqrestore(&amcrtc->vblank_irq_lock, flags); - - disable_irq(amcrtc->vblank_irq); + disable_irq(amcrtc->irq); } static void am_meson_crtc_commit(struct drm_crtc *crtc) diff --git a/drivers/amlogic/drm/meson_crtc.h b/drivers/amlogic/drm/meson_crtc.h index d97dd7b..7370177 100644 --- a/drivers/amlogic/drm/meson_crtc.h +++ b/drivers/amlogic/drm/meson_crtc.h @@ -46,9 +46,7 @@ struct am_meson_crtc { struct drm_pending_vblank_event *event; - unsigned int vblank_irq; - spinlock_t vblank_irq_lock;/*atomic*/ - u32 vblank_enable; + unsigned int irq; struct dentry *crtc_debugfs_dir; diff --git a/drivers/amlogic/drm/meson_drv.c b/drivers/amlogic/drm/meson_drv.c index bfa6893..f94d45b 100644 --- a/drivers/amlogic/drm/meson_drv.c +++ b/drivers/amlogic/drm/meson_drv.c @@ -95,23 +95,11 @@ EXPORT_SYMBOL(am_meson_unregister_crtc_funcs); static int am_meson_enable_vblank(struct drm_device *dev, unsigned int crtc) { - struct meson_drm *priv = dev->dev_private; - - if (crtc >= MESON_MAX_CRTC) - return -EBADFD; - - priv->crtc_funcs[crtc]->enable_vblank(priv->crtc); return 0; } static void am_meson_disable_vblank(struct drm_device *dev, unsigned int crtc) { - struct meson_drm *priv = dev->dev_private; - - if (crtc >= MESON_MAX_CRTC) - return; - - priv->crtc_funcs[crtc]->disable_vblank(priv->crtc); } static void am_meson_load(struct drm_device *dev) @@ -271,7 +259,8 @@ static int am_meson_drm_bind(struct device *dev) drm->mode_config.funcs = &meson_mode_config_funcs; drm->mode_config.allow_fb_modifiers = true; /* - * irq will init in each crtc, just mark the enable flag here. + * enable drm irq mode. + * - with irq_enabled = true, we can use the vblank feature. */ drm->irq_enabled = true; diff --git a/drivers/amlogic/drm/meson_vpu.c b/drivers/amlogic/drm/meson_vpu.c index ca025a8..83caebe 100644 --- a/drivers/amlogic/drm/meson_vpu.c +++ b/drivers/amlogic/drm/meson_vpu.c @@ -291,10 +291,10 @@ static int am_meson_crtc_loader_protect(struct drm_crtc *crtc, bool on) DRM_INFO("%s %d\n", __func__, on); if (on) { - enable_irq(amcrtc->vblank_irq); + enable_irq(amcrtc->irq); drm_crtc_vblank_on(crtc); } else { - disable_irq(amcrtc->vblank_irq); + disable_irq(amcrtc->irq); drm_crtc_vblank_off(crtc); } @@ -303,24 +303,11 @@ static int am_meson_crtc_loader_protect(struct drm_crtc *crtc, bool on) static int am_meson_crtc_enable_vblank(struct drm_crtc *crtc) { - unsigned long flags; - struct am_meson_crtc *amcrtc = to_am_meson_crtc(crtc); - - spin_lock_irqsave(&amcrtc->vblank_irq_lock, flags); - amcrtc->vblank_enable = true; - spin_unlock_irqrestore(&amcrtc->vblank_irq_lock, flags); - return 0; } static void am_meson_crtc_disable_vblank(struct drm_crtc *crtc) { - unsigned long flags; - struct am_meson_crtc *amcrtc = to_am_meson_crtc(crtc); - - spin_lock_irqsave(&amcrtc->vblank_irq_lock, flags); - amcrtc->vblank_enable = false; - spin_unlock_irqrestore(&amcrtc->vblank_irq_lock, flags); } const struct meson_crtc_funcs meson_private_crtc_funcs = { @@ -369,15 +356,9 @@ void am_meson_crtc_handle_vsync(struct am_meson_crtc *amcrtc) void am_meson_crtc_irq(struct meson_drm *priv) { - unsigned long flags; struct am_meson_crtc *amcrtc = to_am_meson_crtc(priv->crtc); - spin_lock_irqsave(&amcrtc->vblank_irq_lock, flags); - if (amcrtc->vblank_enable) { - osd_drm_vsync_isr_handler(); - am_meson_crtc_handle_vsync(amcrtc); - } - spin_unlock_irqrestore(&amcrtc->vblank_irq_lock, flags); + am_meson_crtc_handle_vsync(amcrtc); } static irqreturn_t am_meson_vpu_irq(int irq, void *arg) @@ -484,17 +465,15 @@ static int am_meson_vpu_bind(struct device *dev, dev_err(dev, "cannot find irq for vpu\n"); return irq; } - amcrtc->vblank_irq = (unsigned int)irq; - - spin_lock_init(&amcrtc->vblank_irq_lock); - amcrtc->vblank_enable = false; + amcrtc->irq = (unsigned int)irq; - ret = devm_request_irq(dev, amcrtc->vblank_irq, am_meson_vpu_irq, + ret = devm_request_irq(dev, amcrtc->irq, am_meson_vpu_irq, IRQF_SHARED, dev_name(dev), drm_dev); if (ret) return ret; - disable_irq(amcrtc->vblank_irq); + /* IRQ is initially disabled; it gets enabled in crtc_enable */ + disable_irq(amcrtc->irq); INIT_DELAYED_WORK(&osd_dwork, mem_free_work); schedule_delayed_work(&osd_dwork, msecs_to_jiffies(60 * 1000)); -- 2.7.4 From 4972b2edd43dd88301e6454124f3f0eceb78e93a Mon Sep 17 00:00:00 2001 From: Seung-Woo Kim Date: Mon, 12 Apr 2021 15:40:26 +0900 Subject: [PATCH 09/16] amlogic: drm: support fence/cache op and gem_info Support fence operation, cache operation and geminfo in debugfs from amlogic meson drm. Change-Id: I0d7bc71c7d489e65c0c1051df21fc7b034c3af53 Signed-off-by: Seung-Woo Kim amlogic: drm: add am_meson_gem_get_ioctl am_meson_gem_get_ioctl is used by libtbm-meson. Change-Id: I1f2fd5b8413d26bbf774b0f12a09534fa911d305 Signed-off-by: Sihyun, Park Signed-off-by: Seung-Woo Kim --- drivers/amlogic/drm/Makefile | 3 + drivers/amlogic/drm/meson_crtc.c | 9 +- drivers/amlogic/drm/meson_crtc.h | 4 +- drivers/amlogic/drm/meson_debugfs.c | 59 ++++++- drivers/amlogic/drm/meson_drv.c | 70 +++++--- drivers/amlogic/drm/meson_drv.h | 11 +- drivers/amlogic/drm/meson_fb.c | 21 ++- drivers/amlogic/drm/meson_fb.h | 38 ++++- drivers/amlogic/drm/meson_fence.c | 110 +++++++++++++ drivers/amlogic/drm/meson_fence.h | 40 +++++ drivers/amlogic/drm/meson_gem.c | 219 ++++++++++++++++++++++++- drivers/amlogic/drm/meson_gem.h | 34 ++++ drivers/amlogic/drm/meson_hdmi.c | 23 ++- drivers/amlogic/drm/meson_lcd.c | 14 +- drivers/amlogic/drm/meson_plane.c | 119 +++++++++----- drivers/amlogic/drm/meson_plane.h | 2 + drivers/amlogic/drm/meson_vpu.c | 103 ++++++++---- drivers/amlogic/drm/meson_vpu.h | 2 +- drivers/amlogic/drm/meson_vpu_pipeline.c | 1 + drivers/amlogic/drm/meson_vpu_pipeline.h | 4 +- drivers/amlogic/drm/meson_vpu_util.c | 15 -- drivers/amlogic/drm/vpu-hw/meson_osd_scaler.c | 61 +++++-- drivers/amlogic/drm/vpu-hw/meson_osd_scaler.h | 9 + drivers/amlogic/drm/vpu-hw/meson_vpu_osd_mif.c | 34 ++-- drivers/gpu/drm/drm_fops.c | 1 + include/drm/drmP.h | 1 + include/uapi/drm/meson_drm.h | 60 ++++++- 27 files changed, 878 insertions(+), 189 deletions(-) create mode 100644 drivers/amlogic/drm/meson_fence.c create mode 100644 drivers/amlogic/drm/meson_fence.h diff --git a/drivers/amlogic/drm/Makefile b/drivers/amlogic/drm/Makefile index 022ca0b..2159b3a 100644 --- a/drivers/amlogic/drm/Makefile +++ b/drivers/amlogic/drm/Makefile @@ -1,3 +1,5 @@ +ccflags-y += -I$(srctree)/drivers/amlogic + ifeq ($(CONFIG_DRM_MESON_USE_ION),y) meson-drm-y += meson_gem.o meson_fb.o ccflags-y += -Idrivers/staging/android/ @@ -22,6 +24,7 @@ endif meson-drm-y += meson_drv.o meson_plane.o meson_vpu_pipeline_traverse.o \ meson_crtc.o meson_vpu_pipeline.o meson_vpu_pipeline_private.o \ meson_debugfs.o meson_vpu_util.o \ + meson_fence.o meson-drm-y += \ vpu-hw/meson_vpu_osd_mif.o \ diff --git a/drivers/amlogic/drm/meson_crtc.c b/drivers/amlogic/drm/meson_crtc.c index 05e79bd..444155e 100644 --- a/drivers/amlogic/drm/meson_crtc.c +++ b/drivers/amlogic/drm/meson_crtc.c @@ -242,7 +242,8 @@ static const struct drm_crtc_helper_funcs am_crtc_helper_funcs = { .atomic_flush = am_meson_crtc_atomic_flush, }; -int am_meson_crtc_create(struct am_meson_crtc *amcrtc) +int am_meson_crtc_create(struct am_meson_crtc *amcrtc, + struct osd_device_data_s *osd_dev) { struct meson_drm *priv = amcrtc->priv; struct drm_crtc *crtc = &amcrtc->base; @@ -254,7 +255,7 @@ int am_meson_crtc_create(struct am_meson_crtc *amcrtc) DRM_INFO("%s\n", __func__); ret = drm_crtc_init_with_planes(priv->drm, crtc, - priv->primary_plane, priv->cursor_plane, + priv->primary_plane, NULL, &am_meson_crtc_funcs, "amlogic vpu"); if (ret) { dev_err(amcrtc->dev, "Failed to init CRTC\n"); @@ -262,7 +263,7 @@ int am_meson_crtc_create(struct am_meson_crtc *amcrtc) } drm_crtc_helper_add(crtc, &am_crtc_helper_funcs); - osd_drm_init(&osd_meson_dev); + osd_drm_init(osd_dev); #ifdef CONFIG_AMLOGIC_MEDIA_ENHANCEMENT amvecm_drm_init(0); @@ -279,4 +280,4 @@ int am_meson_crtc_create(struct am_meson_crtc *amcrtc) return 0; } - +EXPORT_SYMBOL(am_meson_crtc_create); diff --git a/drivers/amlogic/drm/meson_crtc.h b/drivers/amlogic/drm/meson_crtc.h index 7370177..82f4483 100644 --- a/drivers/amlogic/drm/meson_crtc.h +++ b/drivers/amlogic/drm/meson_crtc.h @@ -63,7 +63,7 @@ struct am_meson_crtc { struct am_meson_crtc, base) #define to_am_meson_crtc_state(x) container_of(x, \ struct am_meson_crtc_state, base) - -int am_meson_crtc_create(struct am_meson_crtc *amcrtc); +int am_meson_crtc_create(struct am_meson_crtc *amcrtc, + struct osd_device_data_s *osd_dev); #endif diff --git a/drivers/amlogic/drm/meson_debugfs.c b/drivers/amlogic/drm/meson_debugfs.c index ff9eea8..8eb3e52 100644 --- a/drivers/amlogic/drm/meson_debugfs.c +++ b/drivers/amlogic/drm/meson_debugfs.c @@ -18,7 +18,11 @@ #ifdef CONFIG_DEBUG_FS #include #include -#endif + +#include +#include + +#endif /* CONFIG_DEBUG_FS */ #include "meson_drv.h" #include "meson_crtc.h" @@ -26,6 +30,11 @@ #ifdef CONFIG_DEBUG_FS +struct meson_gem_info_debugfs { + struct drm_file *filp; + struct seq_file *m; +}; + static int meson_dump_show(struct seq_file *sf, void *data) { struct drm_crtc *crtc = sf->private; @@ -266,8 +275,54 @@ static int mm_show(struct seq_file *sf, void *arg) &dev->vma_offset_manager->vm_addr_space_mm); } +static int meson_gem_info_print(int id, void *ptr, void *data) +{ + struct drm_gem_object *obj = ptr; + struct meson_gem_info_debugfs *gem_info = data; + + if(!obj) { + pr_err("obj is already null\n"); + return 0; + } + + if(gem_info) { + seq_printf(gem_info->m, " %5d %3d %4d %8zd %3d %3d %6d\n", + gem_info->filp->drm_pid, + obj->name, + id, + obj->size, + atomic_read(&obj->refcount.refcount), + obj->handle_count, + obj->import_attach ? 1 : 0); + } + return 0; +} + +static int meson_gem_info(struct seq_file *m, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct drm_device *drm_dev = node->minor->dev; + struct meson_gem_info_debugfs gem_info; + + seq_printf(m, " pid name id size ref handles import\n"); + + gem_info.m = m; + + mutex_lock(&drm_dev->struct_mutex); + list_for_each_entry_reverse(gem_info.filp, &drm_dev->filelist, lhead) { + spin_lock(&gem_info.filp->table_lock); + idr_for_each(&gem_info.filp->object_idr, meson_gem_info_print, + &gem_info); + spin_unlock(&gem_info.filp->table_lock); + } + mutex_unlock(&drm_dev->struct_mutex); + + return 0; +} + static struct drm_info_list meson_debugfs_list[] = { {"mm", mm_show, 0}, + {"gem_info", meson_gem_info, 0}, }; int meson_debugfs_init(struct drm_minor *minor) @@ -295,4 +350,4 @@ void meson_debugfs_cleanup(struct drm_minor *minor) drm_debugfs_remove_files(meson_debugfs_list, ARRAY_SIZE(meson_debugfs_list), minor); } -#endif +#endif /* CONFIG_DEBUG_FS */ diff --git a/drivers/amlogic/drm/meson_drv.c b/drivers/amlogic/drm/meson_drv.c index f94d45b..ae1f242 100644 --- a/drivers/amlogic/drm/meson_drv.c +++ b/drivers/amlogic/drm/meson_drv.c @@ -34,14 +34,16 @@ #include #include +#include "osd_hw.h" #include "meson_fbdev.h" #ifdef CONFIG_DRM_MESON_USE_ION #include "meson_gem.h" #include "meson_fb.h" #endif #include "meson_drv.h" +#include "meson_vpu.h" #include "meson_vpu_pipeline.h" - +#include "meson_fence.h" #define DRIVER_NAME "meson" #define DRIVER_DESC "Amlogic Meson DRM driver" @@ -102,26 +104,40 @@ static void am_meson_disable_vblank(struct drm_device *dev, unsigned int crtc) { } -static void am_meson_load(struct drm_device *dev) -{ -#if 0 - struct meson_drm *priv = dev->dev_private; - struct drm_crtc *crtc = priv->crtc; - int pipe = drm_crtc_index(crtc); - - if (priv->crtc_funcs[pipe] && - priv->crtc_funcs[pipe]->loader_protect) - priv->crtc_funcs[pipe]->loader_protect(crtc, true); -#endif -} #ifdef CONFIG_DRM_MESON_USE_ION static const struct drm_ioctl_desc meson_ioctls[] = { DRM_IOCTL_DEF_DRV(MESON_GEM_CREATE, am_meson_gem_create_ioctl, - DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), + DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(MESON_GEM_GET, am_meson_gem_get_ioctl, + DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(MESON_SET_BLANK, am_meson_drv_set_blank_ioctl, + DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(MESON_GEM_SYNC, am_meson_gem_sync_ioctl, + DRM_UNLOCKED | DRM_AUTH), + DRM_IOCTL_DEF_DRV(MESON_GEM_CPU_PREP, am_meson_gem_cpu_prep_ioctl, + DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(MESON_GEM_CPU_FINI, am_meson_gem_cpu_fini_ioctl, + DRM_UNLOCKED), }; #endif +int am_meson_drv_set_blank_ioctl( + struct drm_device *dev, + void *data, + struct drm_file *file_priv) +{ + struct drm_meson_plane_blank *args = data; + u32 index, enable; + + index = (args->plane_type == DRM_PLANE_TYPE_OVERLAY) ? OSD2 : OSD1; + enable = (args->onoff) ? 0 : 1; + + osd_drm_plane_enable_hw(index, enable); + + return 0; +} + static const struct file_operations fops = { .owner = THIS_MODULE, .open = drm_open, @@ -155,7 +171,7 @@ static struct drm_driver meson_driver = { /* PRIME Ops */ .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, - + .gem_open_object = am_meson_gem_open_object, .gem_prime_export = drm_gem_prime_export, .gem_prime_get_sg_table = am_meson_gem_prime_get_sg_table, @@ -249,9 +265,14 @@ static int am_meson_drm_bind(struct device *dev) goto err_gem; DRM_INFO("mode_config crtc number:%d\n", drm->mode_config.num_crtc); + priv->dev_fctx = am_meson_fence_context_create("meson.drm-fence"); + + if (IS_ERR(priv->dev_fctx)) + goto err_unbind_all; + ret = drm_vblank_init(drm, drm->mode_config.num_crtc); if (ret) - goto err_unbind_all; + goto err_fctx; drm_mode_config_reset(drm); drm->mode_config.max_width = 4096; @@ -266,7 +287,10 @@ static int am_meson_drm_bind(struct device *dev) drm_kms_helper_poll_init(drm); - am_meson_load(drm); + if (priv->primary_plane != NULL && priv->overlay_plane != NULL) { + drm_plane_create_zpos_property(priv->primary_plane, 0, 0, OSD_MAX); + drm_plane_create_zpos_property(priv->overlay_plane, 1, 0, OSD_MAX); + } #ifdef CONFIG_DRM_MESON_EMULATE_FBDEV ret = am_meson_drm_fbdev_init(drm); @@ -279,7 +303,6 @@ static int am_meson_drm_bind(struct device *dev) return 0; - err_fbdev_fini: #ifdef CONFIG_DRM_MESON_EMULATE_FBDEV am_meson_drm_fbdev_fini(drm); @@ -290,6 +313,8 @@ err_poll_fini: drm_vblank_cleanup(drm); err_unbind_all: component_unbind_all(dev, drm); +err_fctx: + am_meson_fence_context_destroy(priv->dev_fctx); err_gem: drm_mode_config_cleanup(drm); #ifdef CONFIG_DRM_MESON_USE_ION @@ -307,6 +332,7 @@ err_free1: static void am_meson_drm_unbind(struct device *dev) { struct drm_device *drm = dev_get_drvdata(dev); + struct meson_drm *priv = dev->driver_data; drm_dev_unregister(drm); #ifdef CONFIG_DRM_MESON_EMULATE_FBDEV @@ -316,6 +342,7 @@ static void am_meson_drm_unbind(struct device *dev) drm->irq_enabled = false; drm_vblank_cleanup(drm); component_unbind_all(dev, drm); + am_meson_fence_context_destroy(priv->dev_fctx); drm_mode_config_cleanup(drm); #ifdef CONFIG_DRM_MESON_USE_ION am_meson_gem_cleanup(drm->dev_private); @@ -373,10 +400,10 @@ static bool am_meson_drv_use_osd(void) if (strcmp(str, "okay") && strcmp(str, "ok")) { DRM_INFO("device %s status is %s\n", - node->name, str); + node->name, str); } else { DRM_INFO("device %s status is %s\n", - node->name, str); + node->name, str); return true; } } @@ -455,6 +482,7 @@ static int am_meson_drv_probe(struct platform_device *pdev) struct component_match *match = NULL; int i; + pr_info("[%s] in\n", __func__); if (am_meson_drv_use_osd()) return am_meson_drv_probe_prune(pdev); @@ -507,7 +535,7 @@ static int am_meson_drv_probe(struct platform_device *pdev) am_meson_add_endpoints(dev, &match, port); of_node_put(port); } - + pr_info("[%s] out\n", __func__); return component_master_add_with_match(dev, &am_meson_drm_ops, match); } diff --git a/drivers/amlogic/drm/meson_drv.h b/drivers/amlogic/drm/meson_drv.h index 4260a3c..7047b67 100644 --- a/drivers/amlogic/drm/meson_drv.h +++ b/drivers/amlogic/drm/meson_drv.h @@ -50,15 +50,18 @@ struct meson_drm { struct drm_fb_helper *fbdev_helper; struct drm_gem_object *fbdev_bo; struct drm_plane *primary_plane; - struct drm_plane *cursor_plane; + struct drm_plane *overlay_plane; struct drm_property_blob *gamma_lut_blob; #ifdef CONFIG_DRM_MESON_USE_ION struct ion_client *gem_client; #endif + struct meson_fence_context *dev_fctx; + struct meson_vpu_pipeline *pipeline; struct meson_vpu_funcs *funcs; + struct am_meson_logo *logo; u32 num_crtcs; struct am_meson_crtc *crtcs[MESON_MAX_CRTC]; @@ -67,6 +70,11 @@ struct meson_drm { struct am_osd_plane *planes[MESON_MAX_OSD]; }; +int am_meson_drv_set_blank_ioctl( + struct drm_device *dev, + void *data, + struct drm_file *file_priv); + static inline int meson_vpu_is_compatible(struct meson_drm *priv, const char *compat) { @@ -76,6 +84,7 @@ static inline int meson_vpu_is_compatible(struct meson_drm *priv, extern int am_meson_register_crtc_funcs(struct drm_crtc *crtc, const struct meson_crtc_funcs *crtc_funcs); extern void am_meson_unregister_crtc_funcs(struct drm_crtc *crtc); +struct drm_connector *am_meson_hdmi_connector(void); #ifdef CONFIG_DEBUG_FS int meson_debugfs_init(struct drm_minor *minor); diff --git a/drivers/amlogic/drm/meson_fb.c b/drivers/amlogic/drm/meson_fb.c index 1c364da..359ed1f 100644 --- a/drivers/amlogic/drm/meson_fb.c +++ b/drivers/amlogic/drm/meson_fb.c @@ -18,8 +18,7 @@ #include #include "meson_fb.h" - -#define to_am_meson_fb(x) container_of(x, struct am_meson_fb, base) +#include "meson_vpu.h" void am_meson_fb_destroy(struct drm_framebuffer *fb) { @@ -27,6 +26,7 @@ void am_meson_fb_destroy(struct drm_framebuffer *fb) drm_gem_object_unreference_unlocked(&meson_fb->bufp->base); drm_framebuffer_cleanup(fb); + DRM_DEBUG("meson_fb=0x%p,\n", meson_fb); kfree(meson_fb); } @@ -58,9 +58,12 @@ am_meson_fb_alloc(struct drm_device *dev, if (!meson_fb) return ERR_PTR(-ENOMEM); - meson_gem = container_of(obj, struct am_meson_gem_object, base); - meson_fb->bufp = meson_gem; - + if (obj) { + meson_gem = container_of(obj, struct am_meson_gem_object, base); + meson_fb->bufp = meson_gem; + } else { + meson_fb->bufp = NULL; + } drm_helper_mode_fill_fb_struct(&meson_fb->base, mode_cmd); ret = drm_framebuffer_init(dev, &meson_fb->base, @@ -70,6 +73,10 @@ am_meson_fb_alloc(struct drm_device *dev, ret); goto err_free_fb; } + DRM_INFO("meson_fb[id:%d,ref:%d]=0x%p,meson_fb->bufp=0x%p\n", + meson_fb->base.base.id, + atomic_read(&meson_fb->base.base.refcount.refcount), + meson_fb, meson_fb->bufp); return &meson_fb->base; @@ -113,6 +120,10 @@ struct drm_framebuffer *am_meson_fb_create(struct drm_device *dev, kfree(meson_fb); return ERR_PTR(ret); } + DRM_DEBUG("meson_fb[in:%d,ref:%d]=0x%px,meson_fb->bufp=0x%p\n", + meson_fb->base.base.id, + atomic_read(&meson_fb->base.base.refcount.refcount), + meson_fb, meson_fb->bufp); return &meson_fb->base; } diff --git a/drivers/amlogic/drm/meson_fb.h b/drivers/amlogic/drm/meson_fb.h index 9c76d2e..0226ee7 100644 --- a/drivers/amlogic/drm/meson_fb.h +++ b/drivers/amlogic/drm/meson_fb.h @@ -24,16 +24,40 @@ #include "meson_gem.h" +#define to_am_meson_fb(x) container_of(x, struct am_meson_fb, base) + +#define VMODE_NAME_LEN_MAX 64 + +struct am_meson_logo { + struct page *logo_page; + phys_addr_t start; + u32 size; + u32 width; + u32 height; + u32 bpp; + u32 alloc_flag; + u32 info_loaded_mask; + char *outputmode_t; + char outputmode[VMODE_NAME_LEN_MAX]; +}; + struct am_meson_fb { struct drm_framebuffer base; struct am_meson_gem_object *bufp; + struct am_meson_logo *logo; }; -struct drm_framebuffer *am_meson_fb_create(struct drm_device *dev, - struct drm_file *file_priv, - const struct drm_mode_fb_cmd2 *mode_cmd); -struct drm_framebuffer *am_meson_drm_framebuffer_init( - struct drm_device *dev, - struct drm_mode_fb_cmd2 *mode_cmd, - struct drm_gem_object *obj); +struct drm_framebuffer * +am_meson_fb_create(struct drm_device *dev, + struct drm_file *file_priv, + const struct drm_mode_fb_cmd2 *mode_cmd); +struct drm_framebuffer * +am_meson_drm_framebuffer_init(struct drm_device *dev, + struct drm_mode_fb_cmd2 *mode_cmd, + struct drm_gem_object *obj); +struct drm_framebuffer * +am_meson_fb_alloc(struct drm_device *dev, + struct drm_mode_fb_cmd2 *mode_cmd, + struct drm_gem_object *obj); + #endif diff --git a/drivers/amlogic/drm/meson_fence.c b/drivers/amlogic/drm/meson_fence.c new file mode 100644 index 0000000..f1f30f6 --- /dev/null +++ b/drivers/amlogic/drm/meson_fence.c @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2019 Samsung Electronics Co.Ltd + * + * based on: drivers/gpu/drm/tilcdc/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "meson_fence.h" + +#define to_meson_fence(fence) container_of(fence, struct meson_fence, base) + +struct meson_fence_context* +am_meson_fence_context_create(const char *name) +{ + struct meson_fence_context *fence_context; + + fence_context = kmalloc(sizeof(*fence_context), GFP_KERNEL); + if (!fence_context) + return NULL; + + fence_context->context = fence_context_alloc(1); + fence_context->name = name; + atomic_set(&fence_context->seqno, 0); + atomic_set(&fence_context->fence_count, 0); + + return fence_context; +} + +void +am_meson_fence_context_destroy( + struct meson_fence_context *fence_context) +{ + unsigned fence_count; + + fence_count = atomic_read(&fence_context->fence_count); + if (WARN_ON(fence_count)) + DRM_DEBUG_DRIVER("%s context has %u fence(s) remaining\n", + fence_context->name, fence_count); + + kfree(fence_context); +} + +static const char* +am_meson_fence_get_driver_name(struct fence *fence) +{ + return "meson.drm"; +} + +static const char* +am_meson_fence_get_timeline_name(struct fence *fence) +{ + struct meson_fence *meson_fence = to_meson_fence(fence); + return meson_fence->fence_context->name; +} + +static bool +am_meson_fence_enable_signaling(struct fence *fence) +{ + return true; +} + +static void +am_meson_fence_release(struct fence *fence) +{ + struct meson_fence *meson_fence = to_meson_fence(fence); + + atomic_dec(&meson_fence->fence_context->fence_count); + kfree(meson_fence); +} + +static struct fence_ops meson_fence_ops = { + .get_driver_name = am_meson_fence_get_driver_name, + .get_timeline_name = am_meson_fence_get_timeline_name, + .enable_signaling = am_meson_fence_enable_signaling, + .wait = fence_default_wait, + .release = am_meson_fence_release, +}; + +static inline unsigned +am_meson_fence_context_seqno_next( + struct meson_fence_context *fence_context) +{ + return atomic_inc_return(&fence_context->seqno) - 1; +} + +struct fence * +am_meson_fence_create(struct meson_fence_context *fence_context) +{ + struct meson_fence *meson_fence; + unsigned int seqno; + + meson_fence = kmalloc(sizeof(*meson_fence), GFP_KERNEL); + if (!meson_fence) + return NULL; + + spin_lock_init(&meson_fence->lock); + meson_fence->fence_context = fence_context; + + seqno = am_meson_fence_context_seqno_next(fence_context); + fence_init(&meson_fence->base, &meson_fence_ops, &meson_fence->lock, + fence_context->context, seqno); + + atomic_inc(&fence_context->fence_count); + + return &meson_fence->base; +} + diff --git a/drivers/amlogic/drm/meson_fence.h b/drivers/amlogic/drm/meson_fence.h new file mode 100644 index 0000000..c6fa538 --- /dev/null +++ b/drivers/amlogic/drm/meson_fence.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2019 Samsung Electronics Co.Ltd + * + * based on: drivers/gpu/drm/tilcdc/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __MESON_FENCE_H__ +#define __MESON_FENCE_H__ + +#include +#include +#include + +struct meson_fence_context { + unsigned int context; + const char *name; + atomic_t seqno; + atomic_t fence_count; +}; + +struct meson_fence { + struct meson_fence_context *fence_context; + struct fence base; + spinlock_t lock; +}; + +struct meson_fence_context* +am_meson_fence_context_create(const char *name); + +struct fence * +am_meson_fence_create(struct meson_fence_context *fence_context); + +void +am_meson_fence_context_destroy(struct meson_fence_context *fence_context); + +#endif /* __MESON_FENCE_H__ */ + diff --git a/drivers/amlogic/drm/meson_gem.c b/drivers/amlogic/drm/meson_gem.c index 7c472e3..6864ed4 100644 --- a/drivers/amlogic/drm/meson_gem.c +++ b/drivers/amlogic/drm/meson_gem.c @@ -22,10 +22,11 @@ #include #include #include -#include +#include #include #include "meson_gem.h" +#include "meson_fence.h" #define to_am_meson_gem_obj(x) container_of(x, struct am_meson_gem_object, base) @@ -45,7 +46,7 @@ static int am_meson_gem_alloc_ion_buff( //check flags to set different ion heap type. //if flags is set to 0, need to use ion dma buffer. - if (((flags & (MESON_USE_SCANOUT | MESON_USE_CURSOR)) != 0) + if (((flags & (MESON_USE_SCANOUT | MESON_USE_CURSOR | MESON_USE_OVERLAY)) != 0) || (flags == 0)) { handle = ion_alloc(client, meson_gem_obj->base.size, 0, (1 << ION_HEAP_TYPE_DMA), 0); @@ -55,7 +56,7 @@ static int am_meson_gem_alloc_ion_buff( bscatter = true; } - if (IS_ERR(handle)) { + if (IS_ERR_OR_NULL(handle)) { DRM_ERROR("%s: FAILED, flags:0x%x.\n", __func__, flags); return -ENOMEM; @@ -63,6 +64,7 @@ static int am_meson_gem_alloc_ion_buff( meson_gem_obj->handle = handle; meson_gem_obj->bscatter = bscatter; + meson_gem_obj->flags = flags; DRM_DEBUG("%s: allocate handle (%p).\n", __func__, meson_gem_obj->handle); return 0; @@ -116,6 +118,12 @@ struct am_meson_gem_object *am_meson_gem_object_create( goto error; } + /*for release check*/ + meson_gem_obj->flags = flags; + meson_gem_obj->size = size; + + reservation_object_init(&meson_gem_obj->resv); + return meson_gem_obj; error: @@ -130,6 +138,8 @@ void am_meson_gem_object_free(struct drm_gem_object *obj) DRM_DEBUG("am_meson_gem_object_free %p handle count = %d\n", meson_gem_obj, obj->handle_count); + reservation_object_fini(&meson_gem_obj->resv); + if (obj->import_attach == false) am_meson_gem_free_ion_buf(obj->dev, meson_gem_obj); else @@ -212,6 +222,16 @@ int am_meson_gem_mmap( return ret; } +int am_meson_gem_open_object( + struct drm_gem_object *obj, + struct drm_file *file_priv) +{ + if (obj && file_priv) + file_priv->drm_pid = task_tgid_nr(current); + + return 0; +} + phys_addr_t am_meson_gem_object_get_phyaddr( struct meson_drm *drm, struct am_meson_gem_object *meson_gem) @@ -305,8 +325,11 @@ int am_meson_gem_dumb_map_offset( } ret = drm_gem_create_mmap_offset(obj); - if (ret) + if (ret) { + struct am_meson_gem_object *meson_gem_obj = to_am_meson_gem_obj(obj); + reservation_object_fini(&meson_gem_obj->resv); goto out; + } *offset = drm_vma_node_offset_addr(&obj->vma_node); DRM_DEBUG("offset = 0x%lx\n", (unsigned long)*offset); @@ -376,6 +399,194 @@ void am_meson_gem_cleanup(struct meson_drm *drmdrv) } } +int am_meson_gem_get_ioctl( + struct drm_device *dev, + void *data, + struct drm_file *file_priv) +{ + struct drm_gem_object *obj; + struct drm_meson_gem_info *args; + struct am_meson_gem_object *meson_gem_obj; + + args = (struct drm_meson_gem_info *)data; + obj = drm_gem_object_lookup(file_priv, args->handle); + if (!obj) { + DRM_ERROR("failed to lookup gem object.\n"); + return -EINVAL; + } + meson_gem_obj = to_am_meson_gem_obj(obj); + args->flags = meson_gem_obj->flags; + args->size = meson_gem_obj->size; + + drm_gem_object_unreference_unlocked(obj); + + return 0; +} + +int am_meson_gem_sync_ioctl( + struct drm_device *dev, + void *data, + struct drm_file *file_priv) +{ + struct drm_gem_object *obj; + struct drm_meson_gem_sync *args; + struct am_meson_gem_object *meson_gem_obj; + struct ion_buffer *buffer; + int ret = 0; + + mutex_lock(&dev->struct_mutex); + + args = (struct drm_meson_gem_sync *)data; + obj = drm_gem_object_lookup(file_priv, args->handle); + if (!obj) { + DRM_ERROR("failed to lookup gem object.\n"); + ret = -EINVAL; + goto unlock; + } + meson_gem_obj = to_am_meson_gem_obj(obj); + if (!meson_gem_obj) { + DRM_ERROR("failed to lookup am_meson_gem_object.\n"); + ret = -EINVAL; + goto out; + } + if (meson_gem_obj->bscatter && + !(meson_gem_obj->flags & MESON_USE_RENDER_VIP)) { + buffer = meson_gem_obj->handle->buffer; + dma_sync_sg_for_cpu(dev->dev, buffer->sg_table->sgl, + buffer->sg_table->nents, DMA_FROM_DEVICE); + } + +out: + drm_gem_object_unreference(obj); +unlock: + mutex_unlock(&dev->struct_mutex); + return ret; +} + +int am_meson_gem_cpu_prep_ioctl( + struct drm_device *dev, + void *data, + struct drm_file *file_priv) +{ + int err = 0; + struct drm_meson_gem_cpu_access *args = (struct drm_meson_gem_cpu_access *)data; + struct meson_drm *priv = dev->dev_private; + struct drm_gem_object *obj; + bool write = !!(args->flags & MESON_GEM_CPU_PREP_WRITE); + bool wait = !(args->flags & MESON_GEM_CPU_PREP_NOWAIT); + struct am_meson_gem_object *meson_gem_obj; + + if (args->flags & ~(MESON_GEM_CPU_PREP_READ | + MESON_GEM_CPU_PREP_WRITE | + MESON_GEM_CPU_PREP_NOWAIT)) { + DRM_ERROR("invalid flags: %#08x\n", args->flags); + return -EINVAL; + } + + mutex_lock(&dev->struct_mutex); + obj = drm_gem_object_lookup(file_priv, args->handle); + mutex_unlock(&dev->struct_mutex); + if (!obj) { + err = -ENOENT; + goto exit_unlock; + } + if (obj->import_attach) { + err = -EINVAL; + goto exit_unref; + } + + meson_gem_obj = to_am_meson_gem_obj(obj); + if (meson_gem_obj->prep_data.cpu_prep) { + err = -EBUSY; + DRM_DEBUG("EBUSY cpu_prep not unlocked yet by caller\n"); + goto exit_unref; + } + + if (wait) { + long lerr; + lerr = reservation_object_wait_timeout_rcu(&meson_gem_obj->resv, write, true, 10 * HZ); + if (!lerr) { + err = -EBUSY; + goto exit_unref; + } else if (lerr < 0) { + err = lerr; + goto exit_unref; + } + + meson_gem_obj->prep_data.fence = am_meson_fence_create(priv->dev_fctx); + if (!meson_gem_obj->prep_data.fence) { + err = -ENOMEM; + goto exit_unref; + } + + if (write) { + ww_mutex_lock(&meson_gem_obj->resv.lock, NULL); + reservation_object_add_excl_fence( + &meson_gem_obj->resv, meson_gem_obj->prep_data.fence); + ww_mutex_unlock(&meson_gem_obj->resv.lock); + } else { + ww_mutex_lock(&meson_gem_obj->resv.lock, NULL); + err = reservation_object_reserve_shared(&meson_gem_obj->resv); + if (err) { + fence_put(meson_gem_obj->prep_data.fence); + } else { + reservation_object_add_shared_fence( + &meson_gem_obj->resv, meson_gem_obj->prep_data.fence); + } + ww_mutex_unlock(&meson_gem_obj->resv.lock); + } + } else { + if (!reservation_object_test_signaled_rcu(&meson_gem_obj->resv, write)) + err = -EBUSY; + } + + if (!err) + meson_gem_obj->prep_data.cpu_prep = true; + +exit_unref: + drm_gem_object_unreference_unlocked(obj); +exit_unlock: + return err; +} + +int am_meson_gem_cpu_fini_ioctl( + struct drm_device *dev, + void *data, + struct drm_file *file_priv) +{ + int err = 0; + struct drm_meson_gem_cpu_access *args = (struct drm_meson_gem_cpu_access *)data; + struct drm_gem_object *obj; + struct am_meson_gem_object *meson_gem_obj; + + mutex_lock(&dev->struct_mutex); + obj = drm_gem_object_lookup(file_priv, args->handle); + if (!obj) { + err = -ENOENT; + goto exit_unlock; + } + + meson_gem_obj = to_am_meson_gem_obj(obj); + if (!meson_gem_obj->prep_data.cpu_prep) { + err = -EINVAL; + goto exit_unref; + } + + if (meson_gem_obj->prep_data.fence) { + WARN_ON(fence_signal(meson_gem_obj->prep_data.fence)); /* Signal the fence */ + fence_put(meson_gem_obj->prep_data.fence); + meson_gem_obj->prep_data.fence = NULL; + } + meson_gem_obj->prep_data.cpu_prep = false; + +exit_unref: + drm_gem_object_unreference_unlocked(obj); +exit_unlock: + mutex_unlock(&dev->struct_mutex); + + return err; +} + struct sg_table *am_meson_gem_prime_get_sg_table( struct drm_gem_object *obj) { diff --git a/drivers/amlogic/drm/meson_gem.h b/drivers/amlogic/drm/meson_gem.h index cab22a5..1c59f9f 100644 --- a/drivers/amlogic/drm/meson_gem.h +++ b/drivers/amlogic/drm/meson_gem.h @@ -20,9 +20,15 @@ #include #include #include +#include #include "meson_drv.h" +struct fence_prep_data { + int cpu_prep; + struct fence *fence; +}; + struct am_meson_gem_object { struct drm_gem_object base; unsigned int flags; @@ -34,6 +40,10 @@ struct am_meson_gem_object { /* for buffer import form other driver */ phys_addr_t addr; struct sg_table *sg; + + struct reservation_object resv; + struct fence_prep_data prep_data; + unsigned int size; }; /* GEM MANAGER CREATE*/ @@ -67,6 +77,26 @@ int am_meson_gem_dumb_map_offset( uint32_t handle, uint64_t *offset); +int am_meson_gem_get_ioctl( + struct drm_device *dev, + void *data, + struct drm_file *file_priv); + +int am_meson_gem_sync_ioctl( + struct drm_device *dev, + void *data, + struct drm_file *file_priv); + +int am_meson_gem_cpu_prep_ioctl( + struct drm_device *dev, + void *data, + struct drm_file *file_priv); + +int am_meson_gem_cpu_fini_ioctl( + struct drm_device *dev, + void *data, + struct drm_file *file_priv); + /* GEM OBJECT OPERATIONS */ struct am_meson_gem_object *am_meson_gem_object_create( struct drm_device *dev, unsigned int flags, @@ -78,6 +108,10 @@ int am_meson_gem_object_mmap( struct am_meson_gem_object *obj, struct vm_area_struct *vma); +int am_meson_gem_open_object( + struct drm_gem_object *obj, + struct drm_file *file_priv); + extern phys_addr_t am_meson_gem_object_get_phyaddr( struct meson_drm *drm, struct am_meson_gem_object *meson_gem); diff --git a/drivers/amlogic/drm/meson_hdmi.c b/drivers/amlogic/drm/meson_hdmi.c index e5f0ce6..7cbe8c1 100644 --- a/drivers/amlogic/drm/meson_hdmi.c +++ b/drivers/amlogic/drm/meson_hdmi.c @@ -35,17 +35,11 @@ #include #include "meson_hdmi.h" #include "meson_hdcp.h" +#include "meson_vpu.h" #define DEVICE_NAME "amhdmitx" struct am_hdmi_tx am_hdmi_info; -struct am_vout_mode { - char name[DRM_DISPLAY_MODE_LEN]; - enum vmode_e mode; - int width, height, vrefresh; - unsigned int flags; -}; - static struct am_vout_mode am_vout_modes[] = { { "1080p60hz", VMODE_HDMI, 1920, 1080, 60, 0}, { "1080p30hz", VMODE_HDMI, 1920, 1080, 30, 0}, @@ -300,7 +294,6 @@ void am_hdmi_encoder_enable(struct drm_encoder *encoder) vout_notifier_call_chain(VOUT_EVENT_MODE_CHANGE_PRE, &vmode); set_vout_vmode(vmode); vout_notifier_call_chain(VOUT_EVENT_MODE_CHANGE, &vmode); - am_hdmi->hdcp_work = NULL; mdelay(1000); am_hdmi_hdcp_work_state_change(am_hdmi, 0); } @@ -584,6 +577,11 @@ static const struct of_device_id am_meson_hdmi_dt_ids[] = { MODULE_DEVICE_TABLE(of, am_meson_hdmi_dt_ids); +struct drm_connector *am_meson_hdmi_connector(void) +{ + return &am_hdmi_info.connector; +} + static int am_meson_hdmi_bind(struct device *dev, struct device *master, void *data) { @@ -596,12 +594,9 @@ static int am_meson_hdmi_bind(struct device *dev, int ret; int irq; - am_hdmi = devm_kzalloc(priv->dev, sizeof(*am_hdmi), - GFP_KERNEL); - if (!am_hdmi) - return -ENOMEM; - memcpy(&am_hdmi_info, am_hdmi, sizeof(*am_hdmi)); + DRM_INFO("[%s] in\n", __func__); am_hdmi = &am_hdmi_info; + memset(am_hdmi, 0, sizeof(*am_hdmi)); DRM_INFO("drm hdmitx init and version:%s\n", DRM_HDMITX_VER); am_hdmi->priv = priv; @@ -662,6 +657,7 @@ static int am_meson_hdmi_bind(struct device *dev, DRM_DEBUG_KMS("HDCP init failed, skipping.\n"); } } + DRM_INFO("[%s] out\n", __func__); return 0; } @@ -679,6 +675,7 @@ static const struct component_ops am_meson_hdmi_ops = { static int am_meson_hdmi_probe(struct platform_device *pdev) { + DRM_INFO("[%s] in\n", __func__); return component_add(&pdev->dev, &am_meson_hdmi_ops); } diff --git a/drivers/amlogic/drm/meson_lcd.c b/drivers/amlogic/drm/meson_lcd.c index e9c6f21..029878e 100644 --- a/drivers/amlogic/drm/meson_lcd.c +++ b/drivers/amlogic/drm/meson_lcd.c @@ -116,6 +116,9 @@ static int am_lcd_connector_get_modes(struct drm_connector *connector) __func__, lcd->lcd_drv->lcd_config->lcd_basic.h_active, lcd->lcd_drv->lcd_config->lcd_basic.v_active); + connector->display_info.width_mm = mode->width_mm; + connector->display_info.height_mm = mode->height_mm; + drm_mode_probed_add(connector, mode); count = 1; pr_info("am_drm_lcd: %s %d\n", __func__, __LINE__); @@ -607,10 +610,6 @@ int am_drm_lcd_notify_callback(struct notifier_block *block, unsigned long cmd, return 0; } -static struct notifier_block am_drm_lcd_notifier_nb = { - .notifier_call = am_drm_lcd_notify_callback, -}; - static const struct of_device_id am_meson_lcd_dt_ids[] = { { .compatible = "amlogic,drm-lcd", }, {}, @@ -636,12 +635,7 @@ static int am_meson_lcd_bind(struct device *dev, struct device *master, pr_err("invalid lcd driver, exit\n"); return -ENODEV; } - /* - * register vout client for timing init, - * avoid init with null info when lcd probe with unifykey case. - */ - vout_register_client(&am_drm_lcd_notifier_nb); - + am_drm_lcd_display_mode_timing_init(am_drm_lcd); drm_panel_init(&am_drm_lcd->panel); am_drm_lcd->panel.dev = NULL; am_drm_lcd->panel.funcs = &am_drm_lcd_funcs; diff --git a/drivers/amlogic/drm/meson_plane.c b/drivers/amlogic/drm/meson_plane.c index 7a66a9c..855e190 100644 --- a/drivers/amlogic/drm/meson_plane.c +++ b/drivers/amlogic/drm/meson_plane.c @@ -42,12 +42,18 @@ static u64 afbc_wb_modifier[] = { DRM_FORMAT_MOD_INVALID }; -static void meson_plane_position_calc( - struct meson_vpu_osd_layer_info *plane_info, - struct drm_plane_state *state, - struct drm_display_mode *mode) +static void +meson_plane_position_calc(struct meson_vpu_osd_layer_info *plane_info, + struct drm_plane_state *state, + struct drm_display_mode *disp_mode) { u32 dst_w, dst_h, src_w, src_h, scan_mode_out; + struct drm_display_mode *mode; + + if (IS_ERR_OR_NULL(state->crtc)) + mode = disp_mode; + else + mode = &state->crtc->mode; scan_mode_out = mode->flags & DRM_MODE_FLAG_INTERLACE; plane_info->src_x = state->src_x; @@ -147,7 +153,7 @@ static int meson_plane_fb_check(struct drm_plane *plane, #else struct drm_gem_cma_object *gem; #endif - dma_addr_t phyaddr; + phys_addr_t phyaddr; #ifdef CONFIG_DRM_MESON_USE_ION meson_fb = container_of(fb, struct am_meson_fb, base); @@ -155,9 +161,21 @@ static int meson_plane_fb_check(struct drm_plane *plane, DRM_DEBUG("meson_fb is NULL!\n"); return -EINVAL; } - phyaddr = am_meson_gem_object_get_phyaddr(drv, meson_fb->bufp); - if (meson_fb->bufp->bscatter) - DRM_ERROR("am_meson_plane meet a scatter framebuffer.\n"); + DRM_DEBUG("meson_fb[id:%d,ref:%d]=0x%p\n", + meson_fb->base.base.id, + atomic_read(&meson_fb->base.base.refcount.refcount), + meson_fb); + if (meson_fb->logo && meson_fb->logo->alloc_flag && + meson_fb->logo->start) { + phyaddr = meson_fb->logo->start; + DRM_DEBUG("logo->phyaddr=0x%pa\n", &phyaddr); + } else if (meson_fb->bufp) { + phyaddr = am_meson_gem_object_get_phyaddr(drv, meson_fb->bufp); + } else { + phyaddr = 0; + DRM_INFO("don't find phyaddr!\n"); + return -EINVAL; + } #else if (!fb) { DRM_INFO("fb is NULL!\n"); @@ -182,12 +200,6 @@ static int meson_plane_get_fb_info(struct drm_plane *plane, struct am_osd_plane *osd_plane = to_am_osd_plane(plane); struct drm_framebuffer *fb = new_state->fb; struct meson_drm *drv = osd_plane->drv; - #ifdef CONFIG_DRM_MESON_USE_ION - struct am_meson_fb *meson_fb; - #else - struct drm_gem_cma_object *gem; - #endif - dma_addr_t phyaddr; if (!drv) { DRM_INFO("%s new_state/meson_drm is NULL!\n", __func__); @@ -197,29 +209,7 @@ static int meson_plane_get_fb_info(struct drm_plane *plane, DRM_INFO("%s invalid plane_index!\n", __func__); return -EINVAL; } - - #ifdef CONFIG_DRM_MESON_USE_ION - meson_fb = container_of(fb, struct am_meson_fb, base); - if (!meson_fb) { - DRM_INFO("meson_fb is NULL!\n"); - return 0; - } - phyaddr = am_meson_gem_object_get_phyaddr(drv, meson_fb->bufp); - if (meson_fb->bufp->bscatter) - DRM_ERROR("ERROR:am_meson_plane meet a scatter framebuffer.\n"); - plane_info->fb_size = meson_fb->bufp->base.size; - #else - if (!fb) { - DRM_INFO("fb is NULL!\n"); - return -EINVAL; - } - /* Update Canvas with buffer address */ - gem = drm_fb_cma_get_gem_obj(fb, 0); - phyaddr = gem->paddr; - #endif - plane_info->pixel_format = fb->pixel_format; - plane_info->phy_addr = phyaddr; plane_info->byte_stride = fb->pitches[0]; /*setup afbc info*/ @@ -261,6 +251,13 @@ static int meson_plane_atomic_get_property(struct drm_plane *plane, struct drm_property *property, uint64_t *val) { + struct am_osd_plane *osd_plane; + struct am_meson_plane_state *plane_state; + + osd_plane = to_am_osd_plane(plane); + plane_state = to_am_meson_plane_state(state); + if (property == osd_plane->prop_premult_en) + *val = plane_state->premult_en; return 0; } @@ -269,6 +266,14 @@ static int meson_plane_atomic_set_property(struct drm_plane *plane, struct drm_property *property, uint64_t val) { + struct am_osd_plane *osd_plane; + struct am_meson_plane_state *plane_state; + + osd_plane = to_am_osd_plane(plane); + plane_state = to_am_meson_plane_state(state); + if (property == osd_plane->prop_premult_en) + plane_state->premult_en = val; + return 0; } @@ -333,13 +338,11 @@ bool am_meson_vpu_check_format_mod(struct drm_plane *plane, ret = true; break; case DRM_FORMAT_MOD_MESON_AFBC: - if (osd_meson_dev.afbc_type == MESON_AFBC && - plane->type == DRM_PLANE_TYPE_PRIMARY) + if (plane->type == DRM_PLANE_TYPE_PRIMARY) ret = true; break; case DRM_FORMAT_MOD_MESON_AFBC_WB: - if (osd_meson_dev.afbc_type == MALI_AFBC && - plane->type == DRM_PLANE_TYPE_PRIMARY) { + if (plane->type == DRM_PLANE_TYPE_PRIMARY) { if (format == DRM_FORMAT_BGR565) ret = false; else @@ -376,7 +379,7 @@ static void meson_plane_cleanup_fb(struct drm_plane *plane, { struct am_osd_plane *osd_plane = to_am_osd_plane(plane); - DRM_DEBUG("%s osd %d.\n", __func__, osd_plane->plane_index); + DRM_DEBUG("osd %d.\n", osd_plane->plane_index); } static void meson_plane_atomic_update(struct drm_plane *plane, @@ -393,6 +396,7 @@ static int meson_plane_atomic_check(struct drm_plane *plane, struct meson_vpu_pipeline_state *mvps; struct am_osd_plane *osd_plane = to_am_osd_plane(plane); struct meson_drm *drv = osd_plane->drv; + struct am_meson_plane_state *plane_state; int ret; if (!state || !drv) { @@ -431,6 +435,8 @@ static int meson_plane_atomic_check(struct drm_plane *plane, return ret; } + plane_state = to_am_meson_plane_state(state); + plane_info->premult_en = plane_state->premult_en; plane_info->enable = 1; DRM_DEBUG("index=%d, zorder=%d\n", plane_info->plane_index, plane_info->zorder); @@ -459,6 +465,24 @@ static const struct drm_plane_helper_funcs am_osd_helper_funcs = { .atomic_disable = meson_plane_atomic_disable, }; +int drm_plane_create_premult_en_property(struct drm_plane *plane) +{ + struct drm_device *dev = plane->dev; + struct drm_property *prop; + struct am_osd_plane *osd_plane; + + osd_plane = to_am_osd_plane(plane); + prop = drm_property_create_bool(dev, DRM_MODE_PROP_ATOMIC, + "PREMULT_EN"); + if (!prop) + return -ENOMEM; + + drm_object_attach_property(&plane->base, prop, 0); + osd_plane->prop_premult_en = prop; + + return 0; +} + static struct am_osd_plane *am_plane_create(struct meson_drm *priv, int i) { struct am_osd_plane *osd_plane; @@ -472,10 +496,12 @@ static struct am_osd_plane *am_plane_create(struct meson_drm *priv, int i) if (!osd_plane) return 0; - if (i == 0) + if (i == OSD1) + type = DRM_PLANE_TYPE_OVERLAY; + else if (i == OSD2) type = DRM_PLANE_TYPE_PRIMARY; else - type = DRM_PLANE_TYPE_OVERLAY; + type = DRM_PLANE_TYPE_CURSOR; osd_plane->drv = priv; osd_plane->plane_index = i; @@ -490,6 +516,7 @@ static struct am_osd_plane *am_plane_create(struct meson_drm *priv, int i) format_modifiers, type, plane_name); + drm_plane_create_premult_en_property(plane); drm_plane_helper_add(plane, &am_osd_helper_funcs); osd_drm_debugfs_add(&osd_plane->plane_debugfs_dir, plane_name, osd_plane->plane_index); @@ -510,9 +537,10 @@ int am_meson_plane_create(struct meson_drm *priv) if (!plane) return -ENOMEM; - if (i == 0) + if (i == OSD1) + priv->overlay_plane = &plane->base; + else if (i == OSD2) priv->primary_plane = &plane->base; - priv->planes[priv->num_planes++] = plane; } @@ -520,3 +548,4 @@ int am_meson_plane_create(struct meson_drm *priv) return 0; } +EXPORT_SYMBOL(am_meson_plane_create); diff --git a/drivers/amlogic/drm/meson_plane.h b/drivers/amlogic/drm/meson_plane.h index a1e6f4b..5be8adc 100644 --- a/drivers/amlogic/drm/meson_plane.h +++ b/drivers/amlogic/drm/meson_plane.h @@ -31,6 +31,7 @@ struct am_meson_plane_state { struct drm_plane_state base; + u32 premult_en; }; struct am_osd_plane { @@ -38,6 +39,7 @@ struct am_osd_plane { struct meson_drm *drv; //point to struct parent. struct dentry *plane_debugfs_dir; int plane_index; + struct drm_property *prop_premult_en; }; #define to_am_osd_plane(x) container_of(x, \ diff --git a/drivers/amlogic/drm/meson_vpu.c b/drivers/amlogic/drm/meson_vpu.c index 83caebe..af3262c 100644 --- a/drivers/amlogic/drm/meson_vpu.c +++ b/drivers/amlogic/drm/meson_vpu.c @@ -256,11 +256,9 @@ static struct osd_device_data_s osd_tm2 = { .osd0_sc_independ = 1, }; struct osd_device_data_s osd_meson_dev; -static u32 logo_memsize; -static struct page *logo_page; -static struct delayed_work osd_dwork; static struct platform_device *gp_dev; static unsigned long gem_mem_start, gem_mem_size; +struct am_meson_logo logo; int am_meson_crtc_dts_info_set(const void *dt_match_data) { @@ -337,6 +335,28 @@ char *am_meson_crtc_get_voutmode(struct drm_display_mode *mode) return NULL; } +bool am_meson_crtc_check_mode(struct drm_display_mode *mode, char *outputmode) +{ + int i; + + if (!mode || !outputmode) + return false; + if (!strcmp(mode->name, "panel")) + return true; + + for (i = 0; i < ARRAY_SIZE(am_vout_modes); i++) { + if (!strcmp(am_vout_modes[i].name, outputmode) && + am_vout_modes[i].width == mode->hdisplay && + am_vout_modes[i].height == mode->vdisplay && + am_vout_modes[i].vrefresh == mode->vrefresh && + am_vout_modes[i].flags == + (mode->flags & DRM_MODE_FLAG_INTERLACE)) { + return true; + } + } + return false; +} + void am_meson_crtc_handle_vsync(struct am_meson_crtc *amcrtc) { unsigned long flags; @@ -371,18 +391,26 @@ static irqreturn_t am_meson_vpu_irq(int irq, void *arg) return IRQ_HANDLED; } -static void mem_free_work(struct work_struct *work) +static int am_meson_logo_info_update(struct meson_drm *priv) { - if (logo_memsize > 0) { -#ifdef CONFIG_CMA - pr_info("%s, free memory: addr:0x%x\n", - __func__, logo_memsize); - - dma_release_from_contiguous(&gp_dev->dev, - logo_page, - logo_memsize >> PAGE_SHIFT); -#endif + logo.start = page_to_phys(logo.logo_page); + logo.alloc_flag = 1; + /*config 1080p logo as default*/ + if (!logo.width || !logo.height) { + logo.width = 1920; + logo.height = 1080; + } + if (!logo.bpp) + logo.bpp = 16; + if (!logo.outputmode_t) { + strcpy(logo.outputmode, "1080p60hz"); + } else { + strncpy(logo.outputmode, logo.outputmode_t, VMODE_NAME_LEN_MAX); + logo.outputmode[VMODE_NAME_LEN_MAX - 1] = '\0'; } + priv->logo = &logo; + + return 0; } static int am_meson_vpu_bind(struct device *dev, @@ -399,7 +427,7 @@ static int am_meson_vpu_bind(struct device *dev, int ret, irq; /* Allocate crtc struct */ - pr_info("[%s] in\n", __func__); + DRM_INFO("[%s] in\n", __func__); amcrtc = devm_kzalloc(dev, sizeof(*amcrtc), GFP_KERNEL); if (!amcrtc) @@ -415,41 +443,46 @@ static int am_meson_vpu_bind(struct device *dev, ret = of_reserved_mem_device_init(&pdev->dev); if (ret != 0) { dev_err(dev, "failed to init reserved memory\n"); + } else { #ifdef CONFIG_CMA gp_dev = pdev; cma = dev_get_cma_area(&pdev->dev); if (cma) { - logo_memsize = cma_get_size(cma); - pr_info("reserved memory base:0x%x, size:0x%x\n", - (u32)cma_get_base(cma), logo_memsize); - if (logo_memsize > 0) { - logo_page = + logo.size = cma_get_size(cma); + DRM_INFO("reserved memory base:0x%x, size:0x%x\n", + (u32)cma_get_base(cma), logo.size); + if (logo.size > 0) { + logo.logo_page = dma_alloc_from_contiguous(&pdev->dev, - logo_memsize >> + logo.size >> PAGE_SHIFT, 0); - if (!logo_page) { - pr_err("allocate buffer failed:%d\n", - logo_memsize); - } + if (!logo.logo_page) + DRM_INFO("allocate buffer failed\n"); + else + am_meson_logo_info_update(private); } } else { - pr_info("------ NO CMA\n"); + DRM_INFO("------ NO CMA\n"); } #endif - } else { - dma_declare_coherent_memory(drm_dev->dev, gem_mem_start, - gem_mem_start, gem_mem_size, - DMA_MEMORY_EXCLUSIVE); - pr_info("meson drm mem_start = 0x%x, size = 0x%x\n", - (u32)gem_mem_start, (u32)gem_mem_size); + if (gem_mem_start) { + dma_declare_coherent_memory(drm_dev->dev, + gem_mem_start, + gem_mem_start, + gem_mem_size, + DMA_MEMORY_EXCLUSIVE); + pr_info("meson drm mem_start = 0x%x, size = 0x%x\n", + (u32)gem_mem_start, (u32)gem_mem_size); + } else { + DRM_INFO("------ NO reserved dma\n"); + } } ret = am_meson_plane_create(private); if (ret) return ret; - - ret = am_meson_crtc_create(amcrtc); + ret = am_meson_crtc_create(amcrtc, &osd_meson_dev); if (ret) return ret; @@ -475,9 +508,7 @@ static int am_meson_vpu_bind(struct device *dev, /* IRQ is initially disabled; it gets enabled in crtc_enable */ disable_irq(amcrtc->irq); - INIT_DELAYED_WORK(&osd_dwork, mem_free_work); - schedule_delayed_work(&osd_dwork, msecs_to_jiffies(60 * 1000)); - pr_info("[%s] out\n", __func__); + DRM_INFO("[%s] out\n", __func__); return 0; } diff --git a/drivers/amlogic/drm/meson_vpu.h b/drivers/amlogic/drm/meson_vpu.h index e288a33..594fa34 100644 --- a/drivers/amlogic/drm/meson_vpu.h +++ b/drivers/amlogic/drm/meson_vpu.h @@ -31,7 +31,7 @@ struct am_vout_mode { unsigned int flags; }; -extern struct osd_device_data_s osd_meson_dev; char *am_meson_crtc_get_voutmode(struct drm_display_mode *mode); +bool am_meson_crtc_check_mode(struct drm_display_mode *mode, char *outputmode); #endif /* __AM_MESON_VPU_H */ diff --git a/drivers/amlogic/drm/meson_vpu_pipeline.c b/drivers/amlogic/drm/meson_vpu_pipeline.c index bb1d72c..e5a380c 100644 --- a/drivers/amlogic/drm/meson_vpu_pipeline.c +++ b/drivers/amlogic/drm/meson_vpu_pipeline.c @@ -345,6 +345,7 @@ void vpu_pipeline_init(struct meson_vpu_pipeline *pipeline) VPU_PIPELINE_HW_INIT(&pipeline->postblend->base); } +EXPORT_SYMBOL(vpu_pipeline_init); /* maybe use graph traverse is a good choice */ int vpu_pipeline_update(struct meson_vpu_pipeline *pipeline, diff --git a/drivers/amlogic/drm/meson_vpu_pipeline.h b/drivers/amlogic/drm/meson_vpu_pipeline.h index 8b26bbd..2ce8d9f 100644 --- a/drivers/amlogic/drm/meson_vpu_pipeline.h +++ b/drivers/amlogic/drm/meson_vpu_pipeline.h @@ -39,7 +39,7 @@ #define MESON_BLOCK_MAX_NAME_LEN 32 /*ratio base for scaler calc;maybe need bigger than 1000*/ #define RATIO_BASE 1000 -#define MESON_OSD_INPUT_W_LIMIT 1920 +#define MESON_OSD_INPUT_W_LIMIT 3840 #define MAX_DIN_NUM 4 #define MAX_DOUT_NUM 2 @@ -169,6 +169,7 @@ struct meson_vpu_osd_layer_info { u32 afbc_inter_format; u32 afbc_en; u32 fb_size; + u32 premult_en; }; struct meson_vpu_osd { @@ -205,6 +206,7 @@ struct meson_vpu_osd_state { int r_mode; u32 plane_index; u32 fb_size; + u32 premult_en; }; struct meson_vpu_afbc { diff --git a/drivers/amlogic/drm/meson_vpu_util.c b/drivers/amlogic/drm/meson_vpu_util.c index 9ad172c..1a97940 100644 --- a/drivers/amlogic/drm/meson_vpu_util.c +++ b/drivers/amlogic/drm/meson_vpu_util.c @@ -128,18 +128,3 @@ int meson_drm_clr_reg_mask(u32 addr, u32 mask) return 0; } #endif - -/** canvas config **/ - -void meson_drm_canvas_config(u32 index, unsigned long addr, u32 width, - u32 height, u32 wrap, u32 blkmode) -{ - canvas_config(index, addr, width, height, wrap, blkmode); -} - -int meson_drm_canvas_pool_alloc_table(const char *owner, u32 *table, int size, - enum canvas_map_type_e type) -{ - return canvas_pool_alloc_canvas_table(owner, table, size, type); -} - diff --git a/drivers/amlogic/drm/vpu-hw/meson_osd_scaler.c b/drivers/amlogic/drm/vpu-hw/meson_osd_scaler.c index 96c2579..c70fcd7 100644 --- a/drivers/amlogic/drm/vpu-hw/meson_osd_scaler.c +++ b/drivers/amlogic/drm/vpu-hw/meson_osd_scaler.c @@ -80,6 +80,34 @@ static unsigned int __osd_filter_coefs_bicubic[] = { /* bicubic coef0 */ 0xf84d42f9, 0xf84a45f9, 0xf84848f8 }; +static unsigned int __osd_filter_coefs_2point_binilear[] = { + /* 2 point bilinear, bank_length == 2 coef2 */ + 0x80000000, 0x7e020000, 0x7c040000, 0x7a060000, 0x78080000, 0x760a0000, + 0x740c0000, 0x720e0000, 0x70100000, 0x6e120000, 0x6c140000, 0x6a160000, + 0x68180000, 0x661a0000, 0x641c0000, 0x621e0000, 0x60200000, 0x5e220000, + 0x5c240000, 0x5a260000, 0x58280000, 0x562a0000, 0x542c0000, 0x522e0000, + 0x50300000, 0x4e320000, 0x4c340000, 0x4a360000, 0x48380000, 0x463a0000, + 0x443c0000, 0x423e0000, 0x40400000 +}; + +static unsigned int __osd_filter_coefs_4point_triangle[] = { + 0x20402000, 0x20402000, 0x1f3f2101, 0x1f3f2101, + 0x1e3e2202, 0x1e3e2202, 0x1d3d2303, 0x1d3d2303, + 0x1c3c2404, 0x1c3c2404, 0x1b3b2505, 0x1b3b2505, + 0x1a3a2606, 0x1a3a2606, 0x19392707, 0x19392707, + 0x18382808, 0x18382808, 0x17372909, 0x17372909, + 0x16362a0a, 0x16362a0a, 0x15352b0b, 0x15352b0b, + 0x14342c0c, 0x14342c0c, 0x13332d0d, 0x13332d0d, + 0x12322e0e, 0x12322e0e, 0x11312f0f, 0x11312f0f, + 0x10303010 +}; + +static unsigned int *osd_scaler_filter_table[] = { + __osd_filter_coefs_bicubic, + __osd_filter_coefs_2point_binilear, + __osd_filter_coefs_4point_triangle +}; + /*********vsc config begin**********/ /*vsc phase_step=(v_in << 20)/v_out */ void osd_vsc_phase_step_set(struct osd_scaler_reg_s *reg, u32 phase_step) @@ -309,7 +337,7 @@ void osd_sc_out_vert_set(struct osd_scaler_reg_s *reg, u32 start, u32 end) *1:config horizontal coef *0:config vertical coef */ -void osd_sc_coef_set(struct osd_scaler_reg_s *reg, bool flag) +void osd_sc_coef_set(struct osd_scaler_reg_s *reg, bool flag, u32 *coef) { u8 i; @@ -320,8 +348,7 @@ void osd_sc_coef_set(struct osd_scaler_reg_s *reg, bool flag) (flag << 8) | (0 << 0)/*coef index 7bits*/); for (i = 0; i < 33; i++) - VSYNCOSD_WR_MPEG_REG(reg->vpp_osd_scale_coef, - __osd_filter_coefs_bicubic[i]); + VSYNCOSD_WR_MPEG_REG(reg->vpp_osd_scale_coef, coef[i]); } /*********sc top ctrl end************/ static void f2v_get_vertical_phase( @@ -381,6 +408,8 @@ void osd_scaler_config(struct osd_scaler_reg_s *reg, u32 width_out = scaler_state->output_width; u32 height_out = scaler_state->output_height; u32 scan_mode_out = scaler_state->scan_mode_out; + u32 vsc_double_line_mode; + u32 *coef_h, *coef_v; bool scaler_enable; if (width_in == width_out && height_in == height_out && @@ -389,10 +418,13 @@ void osd_scaler_config(struct osd_scaler_reg_s *reg, else scaler_enable = true; - if (width_out > linebuffer) + if (width_in > linebuffer) { vsc_bank_length = bank_length >> 1; - else + vsc_double_line_mode = 1; + } else { vsc_bank_length = bank_length; + vsc_double_line_mode = 0; + } hsc_init_rec_num = bank_length; hsc_bank_length = bank_length; hsc_init_rpt_p0_num = bank_length / 2 - 1; @@ -432,6 +464,17 @@ void osd_scaler_config(struct osd_scaler_reg_s *reg, phase_step_v <<= (OSD_ZOOM_TOTAL_BITS - OSD_ZOOM_HEIGHT_BITS); phase_step_h = (width_in << OSD_ZOOM_WIDTH_BITS) / width_out; phase_step_h <<= (OSD_ZOOM_TOTAL_BITS - OSD_ZOOM_WIDTH_BITS); + /*check coef*/ + if (scan_mode_out && width_out <= 720) { + coef_h = osd_scaler_filter_table[COEFS_4POINT_TRIANGLE]; + coef_v = osd_scaler_filter_table[COEFS_4POINT_TRIANGLE]; + } else if (vsc_double_line_mode == 1) { + coef_h = osd_scaler_filter_table[COEFS_BICUBIC]; + coef_v = osd_scaler_filter_table[COEFS_2POINT_BINILEAR]; + } else { + coef_h = osd_scaler_filter_table[COEFS_BICUBIC]; + coef_v = osd_scaler_filter_table[COEFS_BICUBIC]; + } /*input size config*/ osd_sc_in_h_set(reg, height_in); @@ -449,13 +492,14 @@ void osd_scaler_config(struct osd_scaler_reg_s *reg, osd_sc_dummy_data_set(reg, 0x80808080); /*h/v coef config*/ - osd_sc_coef_set(reg, 1); - osd_sc_coef_set(reg, 0); + osd_sc_coef_set(reg, OSD_SCALER_COEFF_H, coef_h); + osd_sc_coef_set(reg, OSD_SCALER_COEFF_V, coef_v); /*init recv line num*/ osd_vsc_top_ini_rcv_num_set(reg, vsc_top_init_rec_num); osd_vsc_bot_ini_rcv_num_set(reg, vsc_bot_init_rec_num); osd_hsc_ini_rcv_num0_set(reg, hsc_init_rec_num); + osd_vsc_double_line_mode_set(reg, vsc_double_line_mode); /*repeate line0 num*/ osd_vsc_top_rpt_l0_num_set(reg, vsc_top_rpt_l0_num); @@ -650,9 +694,6 @@ static void scaler_hw_init(struct meson_vpu_block *vblk) scaler->reg = &osd_scaler_reg[vblk->index]; scaler->linebuffer = OSD_SCALE_LINEBUFFER; scaler->bank_length = OSD_SCALE_BANK_LENGTH; - /*disable sc*/ - osd_sc_en_set(scaler->reg, 0); - osd_sc_path_en_set(scaler->reg, 0); DRM_DEBUG("%s hw_init called.\n", scaler->base.name); } diff --git a/drivers/amlogic/drm/vpu-hw/meson_osd_scaler.h b/drivers/amlogic/drm/vpu-hw/meson_osd_scaler.h index 34ebb03..91b3d73 100644 --- a/drivers/amlogic/drm/vpu-hw/meson_osd_scaler.h +++ b/drivers/amlogic/drm/vpu-hw/meson_osd_scaler.h @@ -79,6 +79,15 @@ #define OSD_ZOOM_TOTAL_BITS 24 #define OSD_PHASE_BITS 16 +#define OSD_SCALER_COEFF_H 1 +#define OSD_SCALER_COEFF_V 0 + +enum scaler_coef_e { + COEFS_BICUBIC = 0, + COEFS_2POINT_BINILEAR, + COEFS_4POINT_TRIANGLE +}; + enum f2v_vphase_type_e { F2V_IT2IT = 0, F2V_IB2IB, diff --git a/drivers/amlogic/drm/vpu-hw/meson_vpu_osd_mif.c b/drivers/amlogic/drm/vpu-hw/meson_vpu_osd_mif.c index 487dfe0..473ba6b 100644 --- a/drivers/amlogic/drm/vpu-hw/meson_vpu_osd_mif.c +++ b/drivers/amlogic/drm/vpu-hw/meson_vpu_osd_mif.c @@ -227,9 +227,22 @@ void osd_block_enable(struct osd_mif_reg_s *reg, bool flag) VSYNCOSD_WR_MPEG_REG_BITS(reg->viu_osd_ctrl_stat, flag, 0, 1); } +/*osd alpha_div en + *if input is premult,alpha_div=1,else alpha_div=0 + */ +void osd_alpha_div_enable(struct osd_mif_reg_s *reg, bool flag) +{ + VSYNCOSD_WR_MPEG_REG_BITS(reg->viu_osd_mali_unpack_ctrl, flag, 28, 1); +} + /*osd ctrl config*/ -void osd_ctrl_set(struct osd_mif_reg_s *reg) +void osd_ctrl_set(struct osd_mif_reg_s *reg, u32 osd_idx, u32 canvas_index) { + u32 enable = 1; + + if (osd_idx > OSD2) + enable = 0; + VSYNCOSD_WR_MPEG_REG(reg->viu_osd_ctrl_stat, (0 << 31) |/*osd_cfg_sync_en*/ (0 << 30) |/*Enable free_clk*/ @@ -237,7 +250,8 @@ void osd_ctrl_set(struct osd_mif_reg_s *reg) (0 << 11) |/*TEST_RD_EN*/ (0 << 2) |/*osd_mem_mode 0:canvas_addr*/ (0 << 1) |/*premult_en*/ - (0 << 0)/*OSD_BLK_ENABLE*/); + (enable << 0)/*OSD_BLK_ENABLE*/ + ); VSYNCOSD_WR_MPEG_REG(reg->viu_osd_ctrl_stat2, (1 << 14) |/*replaced_alpha_en*/ (0xff << 6) |/*replaced_alpha*/ @@ -268,7 +282,7 @@ void osd_ctrl_set(struct osd_mif_reg_s *reg) (0 << 30) |/*read from ddr[0]/afbc[1]*/ (0 << 29) |/*y reverse disable*/ (0 << 28) |/*x reverse disable*/ - (osd_canvas[0][0] << 16) |/*canvas index*/ + (canvas_index << 16) | (1 << 15) |/*little endian in ddr*/ (0 << 14) |/*no repeat display y pre line*/ (0 << 12) |/*no interpolation per pixel*/ @@ -277,12 +291,6 @@ void osd_ctrl_set(struct osd_mif_reg_s *reg) (1 << 2) |/*ARGB format for 32bit mode*/ (0 << 1) |/*interlace en*/ (0 << 0)/*output odd/even lines sel*/); - VSYNCOSD_WR_MPEG_REG(reg->viu_osd_blk0_cfg_w1, - (1919 << 16) |/*x_end pixels[13bits]*/ - (0 << 0)/*x_start pixels[13bits]*/); - VSYNCOSD_WR_MPEG_REG(reg->viu_osd_blk0_cfg_w2, - (1079 << 16) |/*y_end pixels[13bits]*/ - (0 << 0)/*y_start pixels[13bits]*/); /*frame addr in linear addr*/ VSYNCOSD_WR_MPEG_REG(reg->viu_osd_blk1_cfg_w4, 0); /*line_stride in linear addr*/ @@ -354,6 +362,7 @@ static int osd_check_state(struct meson_vpu_block *vblk, mvos->phy_addr = plane_info->phy_addr; mvos->pixel_format = plane_info->pixel_format; mvos->fb_size = plane_info->fb_size; + mvos->premult_en = plane_info->premult_en; return 0; } @@ -371,6 +380,7 @@ static void osd_set_state(struct meson_vpu_block *vblk, u32 pixel_format, canvas_index, src_h, byte_stride, phy_addr; struct osd_scope_s scope_src = {0, 1919, 0, 1079}; struct osd_mif_reg_s *reg = osd->reg; + bool alpha_div_en; crtc = vblk->pipeline->crtc; amc = to_am_meson_crtc(crtc); @@ -379,6 +389,7 @@ static void osd_set_state(struct meson_vpu_block *vblk, DRM_DEBUG("set_state break for NULL.\n"); return; } + alpha_div_en = mvos->premult_en ? 1 : 0; src_h = mvos->src_h; byte_stride = mvos->byte_stride; phy_addr = mvos->phy_addr; @@ -388,12 +399,14 @@ static void osd_set_state(struct meson_vpu_block *vblk, scope_src.v_end = mvos->src_y + mvos->src_h - 1; pixel_format = mvos->pixel_format; canvas_index = osd_canvas[vblk->index][osd_canvas_index[vblk->index]]; + /*Toto: need to separate*/ + osd_ctrl_set(osd->reg, vblk->index, canvas_index); canvas_config(canvas_index, phy_addr, byte_stride, src_h, CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR); osd_canvas_index[vblk->index] ^= 1; - osd_canvas_config(reg, canvas_index); osd_input_size_config(reg, scope_src); osd_color_config(reg, pixel_format); + osd_alpha_div_enable(reg, alpha_div_en); DRM_DEBUG("plane_index=%d,HW-OSD=%d\n", mvos->plane_index, vblk->index); DRM_DEBUG("canvas_index[%d]=0x%x,phy_addr=0x%x\n", @@ -519,7 +532,6 @@ static void osd_hw_init(struct meson_vpu_block *vblk) return; } osd->reg = &osd_mif_reg[vblk->index]; - osd_ctrl_set(osd->reg); DRM_DEBUG("%s hw_init done.\n", osd->base.name); } diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 8d9c190..0743321 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -206,6 +206,7 @@ static int drm_open_helper(struct file *filp, struct drm_minor *minor) /* for compatibility root is always authenticated */ priv->authenticated = capable(CAP_SYS_ADMIN); priv->lock_count = 0; + priv->drm_pid = task_tgid_nr(current); INIT_LIST_HEAD(&priv->lhead); INIT_LIST_HEAD(&priv->fbs); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 0c4f9c67..72542c3 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -431,6 +431,7 @@ struct drm_file { struct mutex event_read_lock; + pid_t drm_pid; struct drm_prime_file_private prime; }; diff --git a/include/uapi/drm/meson_drm.h b/include/uapi/drm/meson_drm.h index 5b12674..cb1d54a 100644 --- a/include/uapi/drm/meson_drm.h +++ b/include/uapi/drm/meson_drm.h @@ -12,7 +12,11 @@ #ifndef _MESON_DRM_H #define _MESON_DRM_H -#include +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif /* Use flags */ #define MESON_USE_NONE 0 @@ -25,7 +29,13 @@ #define MESON_USE_CAMERA_WRITE (1ull << 13) #define MESON_USE_CAMERA_READ (1ull << 14) #define MESON_USE_TEXTURE (1ull << 17) +#define MESON_USE_RENDER_VIP (1ull << 18) +#define MESON_USE_OVERLAY (1ull << 19) +/* For Buffer synchronization using dma-buf fence */ +#define MESON_GEM_CPU_PREP_READ (1 << 0) +#define MESON_GEM_CPU_PREP_WRITE (1 << 1) +#define MESON_GEM_CPU_PREP_NOWAIT (1 << 2) /** * User-desired buffer creation information structure. @@ -41,9 +51,57 @@ struct drm_meson_gem_create { __u32 handle; }; +/** + * A structure to gem information. + * + * @handle: a handle to gem object created. + * @flags: flag value including memory type and cache attribute and + * this value would be set by driver. + * @size: size to memory region allocated by gem and this size would + * be set by driver. + */ +struct drm_meson_gem_info { + uint32_t handle; + uint32_t flags; + uint64_t size; +}; + +struct drm_meson_plane_blank { + uint32_t plane_type; + uint32_t onoff; +}; + +struct drm_meson_gem_sync { + uint32_t handle; +}; + +struct drm_meson_gem_cpu_access { + uint32_t handle; + uint32_t flags; +}; + #define DRM_MESON_GEM_CREATE 0x00 +#define DRM_MESON_GEM_GET 0x01 +#define DRM_MESON_SET_BLANK 0x02 +#define DRM_MESON_GEM_SYNC 0x03 +#define DRM_MESON_GEM_CPU_PREP 0x04 +#define DRM_MESON_GEM_CPU_FINI 0x05 #define DRM_IOCTL_MESON_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + \ DRM_MESON_GEM_CREATE, struct drm_meson_gem_create) +#define DRM_IOCTL_MESON_GEM_GET DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_MESON_GEM_GET, struct drm_meson_gem_info) +#define DRM_IOCTL_MESON_SET_BLANK DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_MESON_SET_BLANK, struct drm_meson_plane_blank) +#define DRM_IOCTL_MESON_GEM_SYNC DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_MESON_GEM_SYNC, struct drm_meson_gem_sync) +#define DRM_IOCTL_MESON_GEM_CPU_PREP DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_MESON_GEM_CPU_PREP, struct drm_meson_gem_cpu_access) +#define DRM_IOCTL_MESON_GEM_CPU_FINI DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_MESON_GEM_CPU_FINI, struct drm_meson_gem_cpu_access) + +#if defined(__cplusplus) +} +#endif #endif /* _MESON_DRM_H */ -- 2.7.4 From 35945cc8e5661e23701ee5ddf414296f363b5570 Mon Sep 17 00:00:00 2001 From: "Sihyun, Park" Date: Mon, 31 May 2021 19:19:37 +0900 Subject: [PATCH 10/16] amlogic: drm: support zpos update Add to support zpos property update. Change-Id: I854c644673b46fbf4f620ce1249e57b7206d6fce Signed-off-by: Sihyun, Park Signed-off-by: Seung-Woo Kim --- drivers/amlogic/drm/meson_plane.c | 7 ++++++- drivers/amlogic/drm/meson_plane.h | 3 +++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/amlogic/drm/meson_plane.c b/drivers/amlogic/drm/meson_plane.c index 855e190..78ea0da 100644 --- a/drivers/amlogic/drm/meson_plane.c +++ b/drivers/amlogic/drm/meson_plane.c @@ -487,7 +487,7 @@ static struct am_osd_plane *am_plane_create(struct meson_drm *priv, int i) { struct am_osd_plane *osd_plane; struct drm_plane *plane; - u32 type = 0; + u32 type = 0, zpos; char plane_name[8]; const u64 *format_modifiers = afbc_wb_modifier; @@ -506,6 +506,8 @@ static struct am_osd_plane *am_plane_create(struct meson_drm *priv, int i) osd_plane->drv = priv; osd_plane->plane_index = i; + zpos = osd_plane->plane_index; + plane = &osd_plane->base; sprintf(plane_name, "osd%d", i); @@ -517,6 +519,9 @@ static struct am_osd_plane *am_plane_create(struct meson_drm *priv, int i) type, plane_name); drm_plane_create_premult_en_property(plane); + drm_plane_create_zpos_property(plane, zpos, + MESON_PLANE_BEGIN_ZORDER, + MESON_PLANE_END_ZORDER); drm_plane_helper_add(plane, &am_osd_helper_funcs); osd_drm_debugfs_add(&osd_plane->plane_debugfs_dir, plane_name, osd_plane->plane_index); diff --git a/drivers/amlogic/drm/meson_plane.h b/drivers/amlogic/drm/meson_plane.h index 5be8adc..63e2709 100644 --- a/drivers/amlogic/drm/meson_plane.h +++ b/drivers/amlogic/drm/meson_plane.h @@ -29,6 +29,9 @@ #include "osd_drm.h" #include "meson_fb.h" +#define MESON_PLANE_BEGIN_ZORDER 1 +#define MESON_PLANE_END_ZORDER 65 + struct am_meson_plane_state { struct drm_plane_state base; u32 premult_en; -- 2.7.4 From 9519fc2cb1daeb592094e299e5b7c538ae7752c1 Mon Sep 17 00:00:00 2001 From: terry Date: Fri, 19 Jul 2019 14:39:01 +0800 Subject: [PATCH 11/16] Gsensor: add kxtj3 driver support Signed-off-by: Nick Xie [sw0312.kim: pick khadas 3-axis sensor commit from https://github.com/khadas/linux/commit/0ff70f7087ecad3d28b06c8632ea93fd8cd056af ] Signed-off-by: Seung-Woo Kim Change-Id: Id6166357a6816a3bca1d77072dcfc0ea9499aafc --- arch/arm64/boot/dts/amlogic/kvim3_linux.dts | 11 + arch/arm64/configs/kvims_defconfig | 1 + drivers/input/Kconfig | 2 + drivers/input/Makefile | 1 + drivers/input/sensors/Kconfig | 15 + drivers/input/sensors/Makefile | 6 + drivers/input/sensors/accel/Kconfig | 21 + drivers/input/sensors/accel/Makefile | 2 + drivers/input/sensors/accel/kxtj3.c | 326 +++++ drivers/input/sensors/sensor-dev.c | 1852 +++++++++++++++++++++++++++ drivers/input/sensors/sensor-i2c.c | 241 ++++ include/linux/sensor-dev.h | 263 ++++ 12 files changed, 2741 insertions(+) create mode 100755 drivers/input/sensors/Kconfig create mode 100755 drivers/input/sensors/Makefile create mode 100755 drivers/input/sensors/accel/Kconfig create mode 100755 drivers/input/sensors/accel/Makefile create mode 100755 drivers/input/sensors/accel/kxtj3.c create mode 100755 drivers/input/sensors/sensor-dev.c create mode 100755 drivers/input/sensors/sensor-i2c.c create mode 100755 include/linux/sensor-dev.h diff --git a/arch/arm64/boot/dts/amlogic/kvim3_linux.dts b/arch/arm64/boot/dts/amlogic/kvim3_linux.dts index 8961974..2c18f54 100644 --- a/arch/arm64/boot/dts/amlogic/kvim3_linux.dts +++ b/arch/arm64/boot/dts/amlogic/kvim3_linux.dts @@ -956,6 +956,17 @@ fan,trig_temp_level2 = <70>; hwver = "VIM3.V11"; /* Will be updated in uboot. */ }; + + khadas-kxtj3 { + compatible = "kxtj3"; + reg = <0x0E>; + type = <2>; + layout = <2>; + irq_enable = <0>; + poll_delay_ms = <30>; + irq-gpio = <&gpio_ao GPIOAO_9 IRQ_TYPE_EDGE_RISING>; + status = "okay"; + }; }; &audiobus { diff --git a/arch/arm64/configs/kvims_defconfig b/arch/arm64/configs/kvims_defconfig index 2563bb5..b66806f 100644 --- a/arch/arm64/configs/kvims_defconfig +++ b/arch/arm64/configs/kvims_defconfig @@ -512,6 +512,7 @@ CONFIG_JOYSTICK_XPAD=y CONFIG_JOYSTICK_XPAD_FF=y CONFIG_JOYSTICK_XPAD_LEDS=y CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_SENSOR_DEVICE=y CONFIG_INPUT_MISC=y CONFIG_INPUT_UINPUT=y CONFIG_GAMEPORT_NS558=m diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 34ffa02..06b0c36 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -212,6 +212,8 @@ source "drivers/input/tablet/Kconfig" source "drivers/input/touchscreen/Kconfig" +source "drivers/input/sensors/Kconfig" + source "drivers/input/misc/Kconfig" source "drivers/input/rmi4/Kconfig" diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 6a3281c..e4a4652 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_INPUT_MOUSE) += mouse/ obj-$(CONFIG_INPUT_JOYSTICK) += joystick/ obj-$(CONFIG_INPUT_TABLET) += tablet/ obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/ +obj-$(CONFIG_SENSOR_DEVICE) += sensors/ obj-$(CONFIG_INPUT_MISC) += misc/ obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o diff --git a/drivers/input/sensors/Kconfig b/drivers/input/sensors/Kconfig new file mode 100755 index 0000000..5a82763 --- /dev/null +++ b/drivers/input/sensors/Kconfig @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# all sensors drivers configuration +# + +comment "handle all sensors" + +menuconfig SENSOR_DEVICE + tristate "handle accel" + default n + +if SENSOR_DEVICE +source "drivers/input/sensors/accel/Kconfig" + +endif diff --git a/drivers/input/sensors/Makefile b/drivers/input/sensors/Makefile new file mode 100755 index 0000000..61c8687 --- /dev/null +++ b/drivers/input/sensors/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +# sensor drivers +obj-$(CONFIG_GSENSOR_DEVICE) += accel/ + +obj-$(CONFIG_SENSOR_DEVICE) += sensor-i2c.o +obj-$(CONFIG_SENSOR_DEVICE) += sensor-dev.o diff --git a/drivers/input/sensors/accel/Kconfig b/drivers/input/sensors/accel/Kconfig new file mode 100755 index 0000000..012c156 --- /dev/null +++ b/drivers/input/sensors/accel/Kconfig @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# gsensor drivers configuration +# + +menuconfig GSENSOR_DEVICE + tristate "g_sensor device support" + default y + help + Enable this to be able to choose the drivers for controlling the + g_sensor on some platforms, for example on PDAs. + +if GSENSOR_DEVICE +config GS_KXTJ3 + bool "gsensor kxtj3" + default y + help + To have support for your specific gsesnor you will have to + select the proper drivers which depend on this option. + +endif diff --git a/drivers/input/sensors/accel/Makefile b/drivers/input/sensors/accel/Makefile new file mode 100755 index 0000000..69babf04 --- /dev/null +++ b/drivers/input/sensors/accel/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_GS_KXTJ3) += kxtj3.o diff --git a/drivers/input/sensors/accel/kxtj3.c b/drivers/input/sensors/accel/kxtj3.c new file mode 100755 index 0000000..41fd910 --- /dev/null +++ b/drivers/input/sensors/accel/kxtj3.c @@ -0,0 +1,326 @@ +/* drivers/input/sensors/access/kxtj3.c + * + * Copyright (C) 2019 Khadas. + * Author: Waylon + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif +#include + + +#define KXTJ3_DEVID 0x35 //chip id +#define KXTJ3_RANGE (2 * 16384) + +#define KXTJ3_XOUT_HPF_L (0x00) /* 0000 0000 */ +#define KXTJ3_XOUT_HPF_H (0x01) /* 0000 0001 */ +#define KXTJ3_YOUT_HPF_L (0x02) /* 0000 0010 */ +#define KXTJ3_YOUT_HPF_H (0x03) /* 0000 0011 */ +#define KXTJ3_ZOUT_HPF_L (0x04) /* 0001 0100 */ +#define KXTJ3_ZOUT_HPF_H (0x05) /* 0001 0101 */ +#define KXTJ3_XOUT_L (0x06) /* 0000 0110 */ +#define KXTJ3_XOUT_H (0x07) /* 0000 0111 */ +#define KXTJ3_YOUT_L (0x08) /* 0000 1000 */ +#define KXTJ3_YOUT_H (0x09) /* 0000 1001 */ +#define KXTJ3_ZOUT_L (0x0A) /* 0001 1010 */ +#define KXTJ3_ZOUT_H (0x0B) /* 0001 1011 */ +#define KXTJ3_ST_RESP (0x0C) /* 0000 1100 */ +#define KXTJ3_WHO_AM_I (0x0F) /* 0000 1111 */ +#define KXTJ3_TILT_POS_CUR (0x10) /* 0001 0000 */ +#define KXTJ3_TILT_POS_PRE (0x11) /* 0001 0001 */ +#define KXTJ3_INT_SRC_REG1 (0x15) /* 0001 0101 */ +#define KXTJ3_INT_SRC_REG2 (0x16) /* 0001 0110 */ +#define KXTJ3_STATUS_REG (0x18) /* 0001 1000 */ +#define KXTJ3_INT_REL (0x1A) /* 0001 1010 */ +#define KXTJ3_CTRL_REG1 (0x1B) /* 0001 1011 */ +#define KXTJ3_CTRL_REG2 (0x1C) /* 0001 1100 */ +#define KXTJ3_CTRL_REG3 (0x1D) /* 0001 1101 */ +#define KXTJ3_INT_CTRL_REG1 (0x1E) /* 0001 1110 */ +#define KXTJ3_INT_CTRL_REG2 (0x1F) /* 0001 1111 */ +#define KXTJ3_INT_CTRL_REG3 (0x20) /* 0010 0000 */ +#define KXTJ3_DATA_CTRL_REG (0x21) /* 0010 0001 */ +#define KXTJ3_TILT_TIMER (0x28) /* 0010 1000 */ +#define KXTJ3_WUF_TIMER (0x29) /* 0010 1001 */ +#define KXTJ3_TDT_TIMER (0x2B) /* 0010 1011 */ +#define KXTJ3_TDT_H_THRESH (0x2C) /* 0010 1100 */ +#define KXTJ3_TDT_L_THRESH (0x2D) /* 0010 1101 */ +#define KXTJ3_TDT_TAP_TIMER (0x2E) /* 0010 1110 */ +#define KXTJ3_TDT_TOTAL_TIMER (0x2F) /* 0010 1111 */ +#define KXTJ3_TDT_LATENCY_TIMER (0x30) /* 0011 0000 */ +#define KXTJ3_TDT_WINDOW_TIMER (0x31) /* 0011 0001 */ +#define KXTJ3_WUF_THRESH (0x5A) /* 0101 1010 */ +#define KXTJ3_TILT_ANGLE (0x5C) /* 0101 1100 */ +#define KXTJ3_HYST_SET (0x5F) /* 0101 1111 */ + +/* CONTROL REGISTER 1 BITS */ +#define KXTJ3_DISABLE 0x7F +#define KXTJ3_ENABLE (1 << 7) +#define KXTJ3_INT_ENABLE (1 << 5) +/* INPUT_ABS CONSTANTS */ +#define FUZZ 3 +#define FLAT 3 +/* RESUME STATE INDICES */ +#define RES_DATA_CTRL 0 +#define RES_CTRL_REG1 1 +#define RES_INT_CTRL1 2 +#define RESUME_ENTRIES 3 + +/* CTRL_REG1: set resolution, g-range, data ready enable */ +/* Output resolution: 8-bit valid or 12-bit valid */ +#define KXTJ3_RES_8BIT 0 +#define KXTJ3_RES_12BIT (1 << 6) +/* Output g-range: +/-2g, 4g, or 8g */ +#define KXTJ3_G_2G 0 +#define KXTJ3_G_4G (1 << 3) +#define KXTJ3_G_8G (1 << 4) + +/* DATA_CTRL_REG: controls the output data rate of the part */ +#define KXTJ3_ODR12_5F 0 +#define KXTJ3_ODR25F 1 +#define KXTJ3_ODR50F 2 +#define KXTJ3_ODR100F 3 +#define KXTJ3_ODR200F 4 +#define KXTJ3_ODR400F 5 +#define KXTJ3_ODR800F 6 + +/* kxtj3 */ +#define KXTJ3_PRECISION 12 +#define KXTJ3_BOUNDARY (0x1 << (KXTJ3_PRECISION - 1)) +#define KXTJ3_GRAVITY_STEP KXTJ3_RANGE / KXTJ3_BOUNDARY + + +/****************operate according to sensor chip:start************/ + +static int sensor_active(struct i2c_client *client, int enable, int rate) +{ + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + int result = 0; + int status = 0; + + sensor->ops->ctrl_data = sensor_read_reg(client, sensor->ops->ctrl_reg); + + //register setting according to chip datasheet + if(enable) + { + status = KXTJ3_ENABLE; //kxtj3 + sensor->ops->ctrl_data |= status; + } + else + { + status = ~KXTJ3_ENABLE; //kxtj3 + sensor->ops->ctrl_data &= status; + } + + DBG("%s:reg=0x%x,reg_ctrl=0x%x,enable=%d\n",__func__,sensor->ops->ctrl_reg, sensor->ops->ctrl_data, enable); + result = sensor_write_reg(client, sensor->ops->ctrl_reg, sensor->ops->ctrl_data); + if(result) + printk("%s:fail to active sensor\n",__func__); + + return result; + +} + +static int sensor_init(struct i2c_client *client) +{ + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + int result = 0; + + result = sensor->ops->active(client,0,0); + if(result) + { + printk("%s:line=%d,error\n",__func__,__LINE__); + return result; + } + + sensor->status_cur = SENSOR_OFF; + + result = sensor_write_reg(client, KXTJ3_DATA_CTRL_REG, KXTJ3_ODR400F); + if(result) + { + printk("%s:line=%d,error\n",__func__,__LINE__); + return result; + } + + if(sensor->pdata->irq_enable) //open interrupt + { + result = sensor_write_reg(client, KXTJ3_INT_CTRL_REG1, 0x34);//enable int,active high,need read INT_REL + if(result) + { + printk("%s:line=%d,error\n",__func__,__LINE__); + return result; + } + } + + sensor->ops->ctrl_data = (KXTJ3_RES_12BIT | KXTJ3_G_2G); + if(sensor->pdata->irq_enable) + sensor->ops->ctrl_data |= KXTJ3_INT_ENABLE; + result = sensor_write_reg(client, sensor->ops->ctrl_reg, sensor->ops->ctrl_data); + if(result) + { + printk("%s:line=%d,error\n",__func__,__LINE__); + return result; + } + return result; +} + +static short sensor_convert_data(struct i2c_client *client, char high_byte, char low_byte) +{ + short result; + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + //int precision = sensor->ops->precision; + switch (sensor->devid) { + case KXTJ3_DEVID: + result = (((short)high_byte << 8) | ((short)low_byte)) >> 4; + result *= KXTJ3_GRAVITY_STEP; + break; + + default: + printk(KERN_ERR "%s: devid wasn't set correctly\n",__func__); + return -EFAULT; + } + + return result; +} + +static int gsensor_report_value(struct i2c_client *client, struct sensor_axis *axis) +{ + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + + if (sensor->status_cur == SENSOR_ON) { + /* Report acceleration sensor information */ + input_report_abs(sensor->input_dev, ABS_X, axis->x); + input_report_abs(sensor->input_dev, ABS_Y, axis->y); + input_report_abs(sensor->input_dev, ABS_Z, axis->z); + input_sync(sensor->input_dev); + } + + return 0; +} + +#define GSENSOR_MIN 10 +static int sensor_report_value(struct i2c_client *client) +{ + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + struct sensor_platform_data *pdata = sensor->pdata; + int ret = 0; + short x, y, z; + struct sensor_axis axis; + char buffer[6] = {0}; + char value = 0; + + if(sensor->ops->read_len < 6) //sensor->ops->read_len = 6 + { + printk("%s:lenth is error,len=%d\n",__func__,sensor->ops->read_len); + return -1; + } + + memset(buffer, 0, 6); + + /* Data bytes from hardware xL, xH, yL, yH, zL, zH */ + do { + *buffer = sensor->ops->read_reg; + ret = sensor_rx_data(client, buffer, sensor->ops->read_len); + if (ret < 0) + return ret; + } while (0); + + //this gsensor need 6 bytes buffer + x = sensor_convert_data(sensor->client, buffer[1], buffer[0]); //buffer[1]:high bit + y = sensor_convert_data(sensor->client, buffer[3], buffer[2]); + z = sensor_convert_data(sensor->client, buffer[5], buffer[4]); + + axis.x = (pdata->orientation[0])*x + (pdata->orientation[1])*y + (pdata->orientation[2])*z; + axis.y = (pdata->orientation[3])*x + (pdata->orientation[4])*y + (pdata->orientation[5])*z; + axis.z = (pdata->orientation[6])*x + (pdata->orientation[7])*y + (pdata->orientation[8])*z; + + DBG( "%s: axis = %d %d %d \n", __func__, axis.x, axis.y, axis.z); + + gsensor_report_value(client, &axis); + + mutex_lock(&sensor->data_mutex); + sensor->axis = axis; + mutex_unlock(&sensor->data_mutex); + + if((sensor->pdata->irq_enable)&& (sensor->ops->int_status_reg >= 0)) //read sensor intterupt status register + { + + value = sensor_read_reg(client, sensor->ops->int_status_reg); + DBG("%s:sensor int status :0x%x\n",__func__,value); + } + return ret; +} + +struct sensor_operate gsensor_kxtj3_ops = { + .name = "kxtj3", + .type = SENSOR_TYPE_ACCEL, + .id_i2c = ACCEL_ID_KXTJ3, + .read_reg = KXTJ3_XOUT_L, + .read_len = 6, + .id_reg = KXTJ3_WHO_AM_I, + .id_data = KXTJ3_DEVID, + .precision = KXTJ3_PRECISION, + .ctrl_reg = KXTJ3_CTRL_REG1, + .int_status_reg = KXTJ3_INT_REL, + .range = {-32768, 32768}, + .trig = IRQF_TRIGGER_LOW | IRQF_ONESHOT, + .active = sensor_active, + .init = sensor_init, + .report = sensor_report_value, +}; + +/****************operate according to sensor chip:end************/ + +//function name should not be changed +static struct sensor_operate *gsensor_get_ops(void) +{ + return &gsensor_kxtj3_ops; +} + +static int __init gsensor_kxtj3_init(void) +{ + struct sensor_operate *ops = gsensor_get_ops(); + int result = 0; + int type = ops->type; + result = sensor_register_slave(type, NULL, NULL, gsensor_get_ops); + return result; +} + +static void __exit gsensor_kxtj3_exit(void) +{ + struct sensor_operate *ops = gsensor_get_ops(); + int type = ops->type; + sensor_unregister_slave(type, NULL, NULL, gsensor_get_ops); +} + +module_init(gsensor_kxtj3_init); +module_exit(gsensor_kxtj3_exit); + diff --git a/drivers/input/sensors/sensor-dev.c b/drivers/input/sensors/sensor-dev.c new file mode 100755 index 0000000..5c351ef --- /dev/null +++ b/drivers/input/sensors/sensor-dev.c @@ -0,0 +1,1852 @@ +/* drivers/input/sensors/sensor-dev.c - handle all gsensor in this file + * + * Copyright (C) 2019 Khadas. + * Author: waylon + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif +#include +#include +#ifdef CONFIG_COMPAT +#include +#endif + +#define SENSOR_CALIBRATION_LEN 64 +struct sensor_calibration_data { + s32 accel_offset[3]; + s32 gyro_offset[3]; + u8 is_accel_calibrated; + u8 is_gyro_calibrated; +}; + +static struct sensor_private_data *g_sensor[SENSOR_NUM_TYPES]; +static struct sensor_operate *sensor_ops[SENSOR_NUM_ID]; +static int sensor_probe_times[SENSOR_NUM_ID]; +static struct class *sensor_class; +static struct sensor_calibration_data sensor_cali_data; + +static int sensor_calibration_data_read(struct sensor_calibration_data *calibration_data) +{ +#if 0 + int ret; + u8 data[SENSOR_CALIBRATION_LEN] = {0}; + struct sensor_calibration_data *cdata = (struct sensor_calibration_data *)data; + + ret = rk_vendor_read(SENSOR_CALIBRATION_ID, (void *)data, SENSOR_CALIBRATION_LEN); + if (ret < 0) { + printk(KERN_ERR "%s failed\n", __func__); + return ret; + } + if (cdata->is_accel_calibrated == 1) { + calibration_data->accel_offset[0] = cdata->accel_offset[0]; + calibration_data->accel_offset[1] = cdata->accel_offset[1]; + calibration_data->accel_offset[2] = cdata->accel_offset[2]; + calibration_data->is_accel_calibrated = 1; + } + if (cdata->is_gyro_calibrated == 1) { + calibration_data->gyro_offset[0] = cdata->gyro_offset[0]; + calibration_data->gyro_offset[1] = cdata->gyro_offset[1]; + calibration_data->gyro_offset[2] = cdata->gyro_offset[2]; + calibration_data->is_gyro_calibrated = 1; + } +#endif + + return 0; +} + +static ssize_t accel_calibration_show(struct class *class, + struct class_attribute *attr, char *buf) +{ +#if 0 + int ret; + struct sensor_private_data *sensor = g_sensor[SENSOR_TYPE_ACCEL]; + + if (sensor == NULL) + return sprintf(buf, "no accel sensor find\n"); + + if (sensor_cali_data.is_accel_calibrated == 1) + return sprintf(buf, "accel calibration: %d, %d, %d\n", sensor_cali_data.accel_offset[0], + sensor_cali_data.accel_offset[1], sensor_cali_data.accel_offset[2]); + + ret = sensor_calibration_data_read(&sensor_cali_data); + if (ret) { + dev_err(&sensor->client->dev, "read accel sensor calibration data failed\n"); + return sprintf(buf, "read error\n"); + } + + if (sensor_cali_data.is_accel_calibrated == 1) + return sprintf(buf, "accel calibration: %d, %d, %d\n", sensor_cali_data.accel_offset[0], + sensor_cali_data.accel_offset[1], sensor_cali_data.accel_offset[2]); +#endif + return sprintf(buf, "read error\n"); +} + +#define ACCEL_CAPTURE_TIMES 20 +#define ACCEL_SENSITIVE 16384 +/* +-1 * 16384 / 9.8 */ +#define ACCEL_OFFSET_MAX 1600 + +static ssize_t accel_calibration_store(struct class *class, + struct class_attribute *attr, const char *buf, size_t count) +{ +#if 0 + struct sensor_private_data *sensor = g_sensor[SENSOR_TYPE_ACCEL]; + int val, ret; + int pre_status; + + if (sensor == NULL) + return -1; + + ret = kstrtoint(buf, 10, &val); + if (ret) { + dev_err(&sensor->client->dev, "%s: kstrtoint error return %d\n", __func__, ret); + return -1; + } + if (val != 1) { + dev_err(&sensor->client->dev, "%s: error value\n", __func__); + return -1; + } + atomic_set(&sensor->is_factory, 1); + + pre_status = sensor->status_cur; + if (pre_status == SENSOR_OFF) { + mutex_lock(&sensor->operation_mutex); + sensor->ops->active(sensor->client, SENSOR_ON, sensor->pdata->poll_delay_ms); + mutex_unlock(&sensor->operation_mutex); + } else { + sensor->stop_work = 1; + if (sensor->pdata->irq_enable) + disable_irq_nosync(sensor->client->irq); + else + cancel_delayed_work_sync(&sensor->delaywork); + } + + ret = accel_do_calibration(sensor); + if (ret < 0) { + dev_err(&sensor->client->dev, "accel do calibration failed\n"); + goto OUT; + } + ret = sensor_calibration_data_write(&sensor_cali_data); + if (ret) + dev_err(&sensor->client->dev, "write accel sensor calibration data failed\n"); + +OUT: + if (pre_status == SENSOR_ON) { + sensor->stop_work = 0; + if (sensor->pdata->irq_enable) + enable_irq(sensor->client->irq); + else + schedule_delayed_work(&sensor->delaywork, msecs_to_jiffies(sensor->pdata->poll_delay_ms)); + } else { + mutex_lock(&sensor->operation_mutex); + sensor->ops->active(sensor->client, SENSOR_OFF, sensor->pdata->poll_delay_ms); + mutex_unlock(&sensor->operation_mutex); + } + + atomic_set(&sensor->is_factory, 0); + wake_up(&sensor->is_factory_ok); + + return ret ? ret : count; +#endif + return count; +} + +static CLASS_ATTR(accel_calibration, 0664, accel_calibration_show, accel_calibration_store); + +static ssize_t gyro_calibration_show(struct class *class, + struct class_attribute *attr, char *buf) +{ +#if 0 + int ret; + struct sensor_private_data *sensor = g_sensor[SENSOR_TYPE_GYROSCOPE]; + + if (sensor == NULL) + return sprintf(buf, "no gyro sensor find\n"); + + if (sensor_cali_data.is_gyro_calibrated == 1) + return sprintf(buf, "gyro calibration: %d, %d, %d\n", sensor_cali_data.gyro_offset[0], + sensor_cali_data.gyro_offset[1], sensor_cali_data.gyro_offset[2]); + + ret = sensor_calibration_data_read(&sensor_cali_data); + if (ret) { + dev_err(&sensor->client->dev, "read gyro sensor calibration data failed\n"); + return sprintf(buf, "read error\n"); + } + + if (sensor_cali_data.is_gyro_calibrated == 1) + return sprintf(buf, "gyro calibration: %d, %d, %d\n", sensor_cali_data.gyro_offset[0], + sensor_cali_data.gyro_offset[1], sensor_cali_data.gyro_offset[2]); + +#endif + return sprintf(buf, "read error\n"); +} + +#define GYRO_CAPTURE_TIMES 20 + +static ssize_t gyro_calibration_store(struct class *class, + struct class_attribute *attr, const char *buf, size_t count) +{ +#if 0 + struct sensor_private_data *sensor = g_sensor[SENSOR_TYPE_GYROSCOPE]; + int val, ret; + int pre_status; + + if (sensor == NULL) + return -1; + + ret = kstrtoint(buf, 10, &val); + if (ret) { + dev_err(&sensor->client->dev, "%s: kstrtoint error return %d\n", __func__, ret); + return -1; + } + if (val != 1) { + dev_err(&sensor->client->dev, "%s error value\n", __func__); + return -1; + } + atomic_set(&sensor->is_factory, 1); + + pre_status = sensor->status_cur; + if (pre_status == SENSOR_OFF) { + mutex_lock(&sensor->operation_mutex); + sensor->ops->active(sensor->client, SENSOR_ON, sensor->pdata->poll_delay_ms); + mutex_unlock(&sensor->operation_mutex); + } else { + sensor->stop_work = 1; + if (sensor->pdata->irq_enable) + disable_irq_nosync(sensor->client->irq); + else + cancel_delayed_work_sync(&sensor->delaywork); + } + + ret = gyro_do_calibration(sensor); + if (ret < 0) { + dev_err(&sensor->client->dev, "gyro do calibration failed\n"); + goto OUT; + } + + ret = sensor_calibration_data_write(&sensor_cali_data); + if (ret) + dev_err(&sensor->client->dev, "write gyro sensor calibration data failed\n"); + +OUT: + if (pre_status == SENSOR_ON) { + sensor->stop_work = 0; + if (sensor->pdata->irq_enable) + enable_irq(sensor->client->irq); + else + schedule_delayed_work(&sensor->delaywork, msecs_to_jiffies(sensor->pdata->poll_delay_ms)); + } else { + mutex_lock(&sensor->operation_mutex); + sensor->ops->active(sensor->client, SENSOR_OFF, sensor->pdata->poll_delay_ms); + mutex_unlock(&sensor->operation_mutex); + } + + atomic_set(&sensor->is_factory, 0); + wake_up(&sensor->is_factory_ok); + + return ret ? ret : count; +#endif + return count; +} + +static CLASS_ATTR(gyro_calibration, 0664, gyro_calibration_show, gyro_calibration_store); + +static int sensor_class_init(void) +{ + int ret ; + + sensor_class = class_create(THIS_MODULE, "sensor_class"); + ret = class_create_file(sensor_class, &class_attr_accel_calibration); + if (ret) { + printk(KERN_ERR "%s:Fail to creat accel class file\n", __func__); + return ret; + } + + ret = class_create_file(sensor_class, &class_attr_gyro_calibration); + if (ret) { + printk(KERN_ERR "%s:Fail to creat gyro class file\n", __func__); + return ret; + } + return 0; +} + +static int sensor_get_id(struct i2c_client *client, int *value) +{ + struct sensor_private_data *sensor = (struct sensor_private_data *) i2c_get_clientdata(client); + int result = 0; + char temp = sensor->ops->id_reg; + int i = 0; + + if (sensor->ops->id_reg >= 0) { + for (i = 0; i < 3; i++) { + result = sensor_rx_data(client, &temp, 1); + *value = temp; + if (!result) + break; + } + + if (result) + return result; + + if (*value != sensor->ops->id_data) { + dev_err(&client->dev, "%s:id=0x%x is not 0x%x\n", __func__, *value, sensor->ops->id_data); + result = -1; + } + } + + return result; +} + +static int sensor_initial(struct i2c_client *client) +{ + struct sensor_private_data *sensor = (struct sensor_private_data *) i2c_get_clientdata(client); + int result = 0; + + /* register setting according to chip datasheet */ + result = sensor->ops->init(client); + if (result < 0) { + dev_err(&client->dev, "%s:fail to init sensor\n", __func__); + return result; + } + + return result; +} + +static int sensor_chip_init(struct i2c_client *client) +{ + struct sensor_private_data *sensor = (struct sensor_private_data *) i2c_get_clientdata(client); + struct sensor_operate *ops = sensor_ops[(int)sensor->i2c_id->driver_data]; + int result = 0; + + if (ops) { + sensor->ops = ops; + } else { + dev_err(&client->dev, "%s:ops is null,sensor name is %s\n", __func__, sensor->i2c_id->name); + result = -1; + goto error; + } + + if ((sensor->type != ops->type) || ((int)sensor->i2c_id->driver_data != ops->id_i2c)) { + dev_err(&client->dev, "%s:type or id is different:type=%d,%d,id=%d,%d\n", __func__, sensor->type, ops->type, (int)sensor->i2c_id->driver_data, ops->id_i2c); + result = -1; + goto error; + } + + if (!ops->init || !ops->active || !ops->report) { + dev_err(&client->dev, "%s:error:some function is needed\n", __func__); + result = -1; + goto error; + } + + result = sensor_get_id(sensor->client, &sensor->devid); + if (result < 0) { + dev_err(&client->dev, "%s:fail to read %s devid:0x%x\n", __func__, sensor->i2c_id->name, sensor->devid); + result = -2; + goto error; + } + + dev_info(&client->dev, "%s:%s:devid=0x%x,ops=0x%p\n", __func__, sensor->i2c_id->name, sensor->devid, sensor->ops); + + result = sensor_initial(sensor->client); + if (result < 0) { + dev_err(&client->dev, "%s:fail to init sensor\n", __func__); + result = -2; + goto error; + } + return 0; + +error: + return result; +} + +static int sensor_reset_rate(struct i2c_client *client, int rate) +{ + struct sensor_private_data *sensor = (struct sensor_private_data *) i2c_get_clientdata(client); + int result = 0; + + if (rate < 5) + rate = 5; + else if (rate > 200) + rate = 200; + + dev_info(&client->dev, "set sensor poll time to %dms\n", rate); + + /* work queue is always slow, we need more quickly to match hal rate */ + if (sensor->pdata->poll_delay_ms == (rate - 2)) + return 0; + + sensor->pdata->poll_delay_ms = rate - 2; + + if (sensor->status_cur == SENSOR_ON) { + if (!sensor->pdata->irq_enable) { + sensor->stop_work = 1; + cancel_delayed_work_sync(&sensor->delaywork); + } + result = sensor->ops->active(client, SENSOR_OFF, rate); + result = sensor->ops->active(client, SENSOR_ON, rate); + if (!sensor->pdata->irq_enable) { + sensor->stop_work = 0; + schedule_delayed_work(&sensor->delaywork, msecs_to_jiffies(sensor->pdata->poll_delay_ms)); + } + } + + return result; +} + +static void sensor_delaywork_func(struct work_struct *work) +{ + struct delayed_work *delaywork = container_of(work, struct delayed_work, work); + struct sensor_private_data *sensor = container_of(delaywork, struct sensor_private_data, delaywork); + struct i2c_client *client = sensor->client; + int result; + + mutex_lock(&sensor->sensor_mutex); + result = sensor->ops->report(client); + if (result < 0) + dev_err(&client->dev, "%s: Get data failed\n", __func__); + mutex_unlock(&sensor->sensor_mutex); + + //if ((!sensor->pdata->irq_enable) && (sensor->stop_work == 0)) + schedule_delayed_work(&sensor->delaywork, msecs_to_jiffies(sensor->pdata->poll_delay_ms)); +} + +/* + * This is a threaded IRQ handler so can access I2C/SPI. Since all + * interrupts are clear on read the IRQ line will be reasserted and + * the physical IRQ will be handled again if another interrupt is + * asserted while we run - in the normal course of events this is a + * rare occurrence so we save I2C/SPI reads. We're also assuming that + * it's rare to get lots of interrupts firing simultaneously so try to + * minimise I/O. + */ +static irqreturn_t sensor_interrupt(int irq, void *dev_id) +{ + struct sensor_private_data *sensor = + (struct sensor_private_data *)dev_id; + struct i2c_client *client = sensor->client; + + mutex_lock(&sensor->sensor_mutex); + if (sensor->ops->report(client) < 0) + dev_err(&client->dev, "%s: Get data failed\n", __func__); + mutex_unlock(&sensor->sensor_mutex); + + return IRQ_HANDLED; +} + +static int sensor_irq_init(struct i2c_client *client) +{ + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + int result = 0; + int irq; + + if ((sensor->pdata->irq_enable) && (sensor->pdata->irq_flags != SENSOR_UNKNOW_DATA)) { + if (sensor->pdata->poll_delay_ms <= 0) + sensor->pdata->poll_delay_ms = 30; + result = gpio_request(client->irq, sensor->i2c_id->name); + if (result) + dev_err(&client->dev, "%s:fail to request gpio :%d\n", __func__, client->irq); + + irq = gpio_to_irq(client->irq); + result = devm_request_threaded_irq(&client->dev, irq, NULL, sensor_interrupt, sensor->pdata->irq_flags | IRQF_ONESHOT, sensor->ops->name, sensor); + if (result) { + dev_err(&client->dev, "%s:fail to request irq = %d, ret = 0x%x\n", __func__, irq, result); + goto error; + } + + client->irq = irq; + //disable_irq_nosync(client->irq); + sensor->ops->active(client, 1, sensor->pdata->poll_delay_ms); + + dev_info(&client->dev, "%s:use irq=%d\n", __func__, irq); + } else if (!sensor->pdata->irq_enable) { + INIT_DELAYED_WORK(&sensor->delaywork, sensor_delaywork_func); + sensor->stop_work = 1; + if (sensor->pdata->poll_delay_ms <= 0) + sensor->pdata->poll_delay_ms = 30; + + sensor->ops->active(client, 1, sensor->pdata->poll_delay_ms); + schedule_delayed_work(&sensor->delaywork, msecs_to_jiffies(sensor->pdata->poll_delay_ms)); + + dev_info(&client->dev, "%s:use polling,delay=%d ms\n", __func__, sensor->pdata->poll_delay_ms); + } + +error: + return result; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void sensor_suspend(struct early_suspend *h) +{ + struct sensor_private_data *sensor = + container_of(h, struct sensor_private_data, early_suspend); + + if (sensor->ops->suspend) + sensor->ops->suspend(sensor->client); +} + +static void sensor_resume(struct early_suspend *h) +{ + struct sensor_private_data *sensor = + container_of(h, struct sensor_private_data, early_suspend); + + if (sensor->ops->resume) + sensor->ops->resume(sensor->client); +} +#endif + +#ifdef CONFIG_PM +static int __maybe_unused sensor_of_suspend(struct device *dev) +{ + struct sensor_private_data *sensor = dev_get_drvdata(dev); + + if (sensor->ops->suspend) + sensor->ops->suspend(sensor->client); + + return 0; +} + +static int __maybe_unused sensor_of_resume(struct device *dev) +{ + struct sensor_private_data *sensor = dev_get_drvdata(dev); + + if (sensor->ops->resume) + sensor->ops->resume(sensor->client); + if (sensor->pdata->power_off_in_suspend) + sensor_initial(sensor->client); + + return 0; +} + +static const struct dev_pm_ops sensor_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(sensor_of_suspend, sensor_of_resume) +}; + +#define SENSOR_PM_OPS (&sensor_pm_ops) +#else +#define SENSOR_PM_OPS NULL +#endif + +static int angle_dev_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int angle_dev_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static int sensor_enable(struct sensor_private_data *sensor, int enable) +{ + int result = 0; + struct i2c_client *client = sensor->client; + + if (enable == SENSOR_ON) { + result = sensor->ops->active(client, 1, sensor->pdata->poll_delay_ms); + if (result < 0) { + dev_err(&client->dev, "%s:fail to active sensor,ret=%d\n", __func__, result); + return result; + } + sensor->status_cur = SENSOR_ON; + sensor->stop_work = 0; + if (sensor->pdata->irq_enable) + enable_irq(client->irq); + else + schedule_delayed_work(&sensor->delaywork, msecs_to_jiffies(sensor->pdata->poll_delay_ms)); + dev_info(&client->dev, "sensor on: starting poll sensor data %dms\n", sensor->pdata->poll_delay_ms); + } else { + sensor->stop_work = 1; + if (sensor->pdata->irq_enable) + disable_irq_nosync(client->irq); + else + cancel_delayed_work_sync(&sensor->delaywork); + result = sensor->ops->active(client, 0, sensor->pdata->poll_delay_ms); + if (result < 0) { + dev_err(&client->dev, "%s:fail to disable sensor,ret=%d\n", __func__, result); + return result; + } + sensor->status_cur = SENSOR_OFF; + } + + return result; +} + +/* ioctl - I/O control */ +static long angle_dev_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct sensor_private_data *sensor = g_sensor[SENSOR_TYPE_ANGLE]; + struct i2c_client *client = sensor->client; + void __user *argp = (void __user *)arg; + struct sensor_axis axis = {0}; + short rate; + int result = 0; + + switch (cmd) { + case GSENSOR_IOCTL_APP_SET_RATE: + if (copy_from_user(&rate, argp, sizeof(rate))) { + result = -EFAULT; + goto error; + } + break; + default: + break; + } + + switch (cmd) { + case GSENSOR_IOCTL_START: + mutex_lock(&sensor->operation_mutex); + if (++sensor->start_count == 1) { + if (sensor->status_cur == SENSOR_OFF) { + sensor_enable(sensor, SENSOR_ON); + } + } + mutex_unlock(&sensor->operation_mutex); + break; + + case GSENSOR_IOCTL_CLOSE: + mutex_lock(&sensor->operation_mutex); + if (--sensor->start_count == 0) { + if (sensor->status_cur == SENSOR_ON) { + sensor_enable(sensor, SENSOR_OFF); + } + } + mutex_unlock(&sensor->operation_mutex); + break; + + case GSENSOR_IOCTL_APP_SET_RATE: + mutex_lock(&sensor->operation_mutex); + result = sensor_reset_rate(client, rate); + if (result < 0) { + mutex_unlock(&sensor->operation_mutex); + goto error; + } + mutex_unlock(&sensor->operation_mutex); + break; + + case GSENSOR_IOCTL_GETDATA: + mutex_lock(&sensor->data_mutex); + memcpy(&axis, &sensor->axis, sizeof(sensor->axis)); + mutex_unlock(&sensor->data_mutex); + break; + + default: + result = -ENOTTY; + + goto error; + } + + switch (cmd) { + case GSENSOR_IOCTL_GETDATA: + if (copy_to_user(argp, &axis, sizeof(axis))) { + dev_err(&client->dev, "failed to copy sense data to user space.\n"); + result = -EFAULT; + goto error; + } + break; + default: + break; + } + +error: + return result; +} + + +static int gsensor_dev_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int gsensor_dev_release(struct inode *inode, struct file *file) +{ + return 0; +} + +/* ioctl - I/O control */ +static long gsensor_dev_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct sensor_private_data *sensor = g_sensor[SENSOR_TYPE_ACCEL]; + struct i2c_client *client = sensor->client; + void __user *argp = (void __user *)arg; + struct sensor_axis axis = {0}; + short rate; + int result = 0; + + wait_event_interruptible(sensor->is_factory_ok, (atomic_read(&sensor->is_factory) == 0)); + + switch (cmd) { + case GSENSOR_IOCTL_APP_SET_RATE: + if (copy_from_user(&rate, argp, sizeof(rate))) { + result = -EFAULT; + goto error; + } + break; + default: + break; + } + + switch (cmd) { + case GSENSOR_IOCTL_START: + mutex_lock(&sensor->operation_mutex); + if (++sensor->start_count == 1) { + if (sensor->status_cur == SENSOR_OFF) { + sensor_enable(sensor, SENSOR_ON); + } + } + mutex_unlock(&sensor->operation_mutex); + break; + + case GSENSOR_IOCTL_CLOSE: + mutex_lock(&sensor->operation_mutex); + if (--sensor->start_count == 0) { + if (sensor->status_cur == SENSOR_ON) { + sensor_enable(sensor, SENSOR_OFF); + } + } + mutex_unlock(&sensor->operation_mutex); + break; + + case GSENSOR_IOCTL_APP_SET_RATE: + mutex_lock(&sensor->operation_mutex); + result = sensor_reset_rate(client, rate); + if (result < 0) { + mutex_unlock(&sensor->operation_mutex); + goto error; + } + mutex_unlock(&sensor->operation_mutex); + break; + + case GSENSOR_IOCTL_GETDATA: + mutex_lock(&sensor->data_mutex); + memcpy(&axis, &sensor->axis, sizeof(sensor->axis)); + mutex_unlock(&sensor->data_mutex); + break; + + case GSENSOR_IOCTL_GET_CALIBRATION: + if (sensor_cali_data.is_accel_calibrated != 1) { + if (sensor_calibration_data_read(&sensor_cali_data)) { + dev_err(&client->dev, "failed to read accel offset data from storage\n"); + result = -EFAULT; + goto error; + } + } + if (sensor_cali_data.is_accel_calibrated == 1) { + if (copy_to_user(argp, sensor_cali_data.accel_offset, sizeof(sensor_cali_data.accel_offset))) { + dev_err(&client->dev, "failed to copy accel offset data to user\n"); + result = -EFAULT; + goto error; + } + } + break; + + default: + result = -ENOTTY; + goto error; + } + + switch (cmd) { + case GSENSOR_IOCTL_GETDATA: + if (copy_to_user(argp, &axis, sizeof(axis))) { + dev_err(&client->dev, "failed to copy sense data to user space.\n"); + result = -EFAULT; + goto error; + } + break; + default: + break; + } + +error: + return result; +} + +static int compass_dev_open(struct inode *inode, struct file *file) +{ + struct sensor_private_data *sensor = g_sensor[SENSOR_TYPE_COMPASS]; + int result = 0; + int flag = 0; + + flag = atomic_read(&sensor->flags.open_flag); + if (!flag) { + atomic_set(&sensor->flags.open_flag, 1); + wake_up(&sensor->flags.open_wq); + } + + return result; +} + +static int compass_dev_release(struct inode *inode, struct file *file) +{ + struct sensor_private_data *sensor = g_sensor[SENSOR_TYPE_COMPASS]; + int result = 0; + int flag = 0; + + flag = atomic_read(&sensor->flags.open_flag); + if (flag) { + atomic_set(&sensor->flags.open_flag, 0); + wake_up(&sensor->flags.open_wq); + } + + return result; +} + +#ifdef CONFIG_COMPAT +/* ioctl - I/O control */ +static long compass_dev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + void __user *arg64 = compat_ptr(arg); + int result = 0; + + if (!file->f_op || !file->f_op->unlocked_ioctl) { + printk(KERN_ERR "file->f_op or file->f_op->unlocked_ioctl is null\n"); + return -ENOTTY; + } + + switch (cmd) { + case COMPAT_ECS_IOCTL_APP_SET_MFLAG: + if (file->f_op->unlocked_ioctl) + result = file->f_op->unlocked_ioctl(file, ECS_IOCTL_APP_SET_MFLAG, (unsigned long)arg64); + break; + case COMPAT_ECS_IOCTL_APP_GET_MFLAG: + if (file->f_op->unlocked_ioctl) + result = file->f_op->unlocked_ioctl(file, ECS_IOCTL_APP_GET_MFLAG, (unsigned long)arg64); + break; + case COMPAT_ECS_IOCTL_APP_SET_AFLAG: + if (file->f_op->unlocked_ioctl) + result = file->f_op->unlocked_ioctl(file, ECS_IOCTL_APP_SET_AFLAG, (unsigned long)arg64); + break; + case COMPAT_ECS_IOCTL_APP_GET_AFLAG: + if (file->f_op->unlocked_ioctl) + result = file->f_op->unlocked_ioctl(file, ECS_IOCTL_APP_GET_AFLAG, (unsigned long)arg64); + break; + case COMPAT_ECS_IOCTL_APP_SET_MVFLAG: + if (file->f_op->unlocked_ioctl) + result = file->f_op->unlocked_ioctl(file, ECS_IOCTL_APP_SET_MVFLAG, (unsigned long)arg64); + break; + case COMPAT_ECS_IOCTL_APP_GET_MVFLAG: + if (file->f_op->unlocked_ioctl) + result = file->f_op->unlocked_ioctl(file, ECS_IOCTL_APP_GET_MVFLAG, (unsigned long)arg64); + break; + case COMPAT_ECS_IOCTL_APP_SET_DELAY: + if (file->f_op->unlocked_ioctl) + result = file->f_op->unlocked_ioctl(file, ECS_IOCTL_APP_SET_DELAY, (unsigned long)arg64); + break; + case COMPAT_ECS_IOCTL_APP_GET_DELAY: + if (file->f_op->unlocked_ioctl) + result = file->f_op->unlocked_ioctl(file, ECS_IOCTL_APP_GET_DELAY, (unsigned long)arg64); + break; + default: + break; + } + + return result; +} +#endif + +/* ioctl - I/O control */ +static long compass_dev_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct sensor_private_data *sensor = g_sensor[SENSOR_TYPE_COMPASS]; + void __user *argp = (void __user *)arg; + int result = 0; + short flag; + + switch (cmd) { + case ECS_IOCTL_APP_SET_MFLAG: + case ECS_IOCTL_APP_SET_AFLAG: + case ECS_IOCTL_APP_SET_MVFLAG: + if (copy_from_user(&flag, argp, sizeof(flag))) + return -EFAULT; + if (flag < 0 || flag > 1) + return -EINVAL; + break; + case ECS_IOCTL_APP_SET_DELAY: + if (copy_from_user(&flag, argp, sizeof(flag))) + return -EFAULT; + break; + default: + break; + } + + switch (cmd) { + case ECS_IOCTL_APP_SET_MFLAG: + atomic_set(&sensor->flags.m_flag, flag); + break; + case ECS_IOCTL_APP_GET_MFLAG: + flag = atomic_read(&sensor->flags.m_flag); + break; + case ECS_IOCTL_APP_SET_AFLAG: + atomic_set(&sensor->flags.a_flag, flag); + break; + case ECS_IOCTL_APP_GET_AFLAG: + flag = atomic_read(&sensor->flags.a_flag); + break; + case ECS_IOCTL_APP_SET_MVFLAG: + atomic_set(&sensor->flags.mv_flag, flag); + break; + case ECS_IOCTL_APP_GET_MVFLAG: + flag = atomic_read(&sensor->flags.mv_flag); + break; + case ECS_IOCTL_APP_SET_DELAY: + sensor->flags.delay = flag; + break; + case ECS_IOCTL_APP_GET_DELAY: + flag = sensor->flags.delay; + break; + default: + return -ENOTTY; + } + + switch (cmd) { + case ECS_IOCTL_APP_GET_MFLAG: + case ECS_IOCTL_APP_GET_AFLAG: + case ECS_IOCTL_APP_GET_MVFLAG: + case ECS_IOCTL_APP_GET_DELAY: + if (copy_to_user(argp, &flag, sizeof(flag))) + return -EFAULT; + break; + default: + break; + } + + return result; +} + + +static int light_dev_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int light_dev_release(struct inode *inode, struct file *file) +{ + return 0; +} + +#ifdef CONFIG_COMPAT +static long light_dev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + long ret = 0; + void __user *arg64 = compat_ptr(arg); + + if (!file->f_op || !file->f_op->unlocked_ioctl) { + printk(KERN_ERR "[DEBUG] file->f_op or file->f_op->unlocked_ioctl is null\n"); + return -ENOTTY; + } + + switch (cmd) { + case COMPAT_LIGHTSENSOR_IOCTL_GET_ENABLED: + if (file->f_op->unlocked_ioctl) + ret = file->f_op->unlocked_ioctl(file, LIGHTSENSOR_IOCTL_GET_ENABLED, (unsigned long)arg64); + break; + case COMPAT_LIGHTSENSOR_IOCTL_ENABLE: + if (file->f_op->unlocked_ioctl) + ret = file->f_op->unlocked_ioctl(file, LIGHTSENSOR_IOCTL_ENABLE, (unsigned long)arg64); + break; + case COMPAT_LIGHTSENSOR_IOCTL_SET_RATE: + if (file->f_op->unlocked_ioctl) + ret = file->f_op->unlocked_ioctl(file, LIGHTSENSOR_IOCTL_SET_RATE, (unsigned long)arg64); + break; + default: + break; + } + + return ret; +} +#endif + +/* ioctl - I/O control */ +static long light_dev_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct sensor_private_data *sensor = g_sensor[SENSOR_TYPE_LIGHT]; + struct i2c_client *client = sensor->client; + void __user *argp = (void __user *)arg; + int result = 0; + short rate; + + switch (cmd) { + case LIGHTSENSOR_IOCTL_SET_RATE: + if (copy_from_user(&rate, argp, sizeof(rate))) { + dev_err(&client->dev, "%s:failed to copy light sensor rate from user space.\n", __func__); + return -EFAULT; + } + mutex_lock(&sensor->operation_mutex); + result = sensor_reset_rate(client, rate); + if (result < 0) { + mutex_unlock(&sensor->operation_mutex); + goto error; + } + mutex_unlock(&sensor->operation_mutex); + break; + case LIGHTSENSOR_IOCTL_GET_ENABLED: + result = sensor->status_cur; + if (copy_to_user(argp, &result, sizeof(result))) { + dev_err(&client->dev, "%s:failed to copy light sensor status to user space.\n", __func__); + return -EFAULT; + } + break; + case LIGHTSENSOR_IOCTL_ENABLE: + if (copy_from_user(&result, argp, sizeof(result))) { + dev_err(&client->dev, "%s:failed to copy light sensor status from user space.\n", __func__); + return -EFAULT; + } + + mutex_lock(&sensor->operation_mutex); + if (result) { + if (sensor->status_cur == SENSOR_OFF) + sensor_enable(sensor, SENSOR_ON); + } else { + if (sensor->status_cur == SENSOR_ON) + sensor_enable(sensor, SENSOR_OFF); + } + mutex_unlock(&sensor->operation_mutex); + break; + + default: + break; + } + +error: + return result; +} + +static int proximity_dev_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int proximity_dev_release(struct inode *inode, struct file *file) +{ + return 0; +} + +#ifdef CONFIG_COMPAT +static long proximity_dev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + long ret = 0; + void __user *arg64 = compat_ptr(arg); + + if (!file->f_op || !file->f_op->unlocked_ioctl) { + printk(KERN_ERR "file->f_op or file->f_op->unlocked_ioctl is null\n"); + return -ENOTTY; + } + + switch (cmd) { + case COMPAT_PSENSOR_IOCTL_GET_ENABLED: + if (file->f_op->unlocked_ioctl) + ret = file->f_op->unlocked_ioctl(file, PSENSOR_IOCTL_GET_ENABLED, (unsigned long)arg64); + break; + case COMPAT_PSENSOR_IOCTL_ENABLE: + if (file->f_op->unlocked_ioctl) + ret = file->f_op->unlocked_ioctl(file, PSENSOR_IOCTL_ENABLE, (unsigned long)arg64); + break; + default: + break; + } + + return ret; +} +#endif + +/* ioctl - I/O control */ +static long proximity_dev_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct sensor_private_data *sensor = g_sensor[SENSOR_TYPE_PROXIMITY]; + void __user *argp = (void __user *)arg; + int result = 0; + + switch (cmd) { + case PSENSOR_IOCTL_GET_ENABLED: + result = sensor->status_cur; + if (copy_to_user(argp, &result, sizeof(result))) { + dev_err(&sensor->client->dev, "%s:failed to copy psensor status to user space.\n", __func__); + return -EFAULT; + } + break; + case PSENSOR_IOCTL_ENABLE: + if (copy_from_user(&result, argp, sizeof(result))) { + dev_err(&sensor->client->dev, "%s:failed to copy psensor status from user space.\n", __func__); + return -EFAULT; + } + mutex_lock(&sensor->operation_mutex); + if (result) { + if (sensor->status_cur == SENSOR_OFF) + sensor_enable(sensor, SENSOR_ON); + } else { + if (sensor->status_cur == SENSOR_ON) + sensor_enable(sensor, SENSOR_OFF); + } + mutex_unlock(&sensor->operation_mutex); + break; + + default: + break; + } + + return result; +} + +static int temperature_dev_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int temperature_dev_release(struct inode *inode, struct file *file) +{ + return 0; +} + +/* ioctl - I/O control */ +static long temperature_dev_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct sensor_private_data *sensor = g_sensor[SENSOR_TYPE_TEMPERATURE]; + void __user *argp = (void __user *)arg; + int result = 0; + + switch (cmd) { + case TEMPERATURE_IOCTL_GET_ENABLED: + result = sensor->status_cur; + if (copy_to_user(argp, &result, sizeof(result))) { + dev_err(&sensor->client->dev, "%s:failed to copy temperature sensor status to user space.\n", __func__); + return -EFAULT; + } + break; + case TEMPERATURE_IOCTL_ENABLE: + if (copy_from_user(&result, argp, sizeof(result))) { + dev_err(&sensor->client->dev, "%s:failed to copy temperature sensor status from user space.\n", __func__); + return -EFAULT; + } + mutex_lock(&sensor->operation_mutex); + if (result) { + if (sensor->status_cur == SENSOR_OFF) + sensor_enable(sensor, SENSOR_ON); + } else { + if (sensor->status_cur == SENSOR_ON) + sensor_enable(sensor, SENSOR_OFF); + } + mutex_unlock(&sensor->operation_mutex); + break; + + default: + break; + } + + return result; +} + + +static int pressure_dev_open(struct inode *inode, struct file *file) +{ + return 0; +} + + +static int pressure_dev_release(struct inode *inode, struct file *file) +{ + return 0; +} + + +/* ioctl - I/O control */ +static long pressure_dev_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct sensor_private_data *sensor = g_sensor[SENSOR_TYPE_PRESSURE]; + void __user *argp = (void __user *)arg; + int result = 0; + + switch (cmd) { + case PRESSURE_IOCTL_GET_ENABLED: + result = sensor->status_cur; + if (copy_to_user(argp, &result, sizeof(result))) { + dev_err(&sensor->client->dev, "%s:failed to copy pressure sensor status to user space.\n", __func__); + return -EFAULT; + } + break; + case PRESSURE_IOCTL_ENABLE: + if (copy_from_user(&result, argp, sizeof(result))) { + dev_err(&sensor->client->dev, "%s:failed to copy pressure sensor status from user space.\n", __func__); + return -EFAULT; + } + mutex_lock(&sensor->operation_mutex); + if (result) { + if (sensor->status_cur == SENSOR_OFF) + sensor_enable(sensor, SENSOR_ON); + } else { + if (sensor->status_cur == SENSOR_ON) + sensor_enable(sensor, SENSOR_OFF); + } + mutex_unlock(&sensor->operation_mutex); + break; + + default: + break; + } + + return result; +} + +static int sensor_misc_device_register(struct sensor_private_data *sensor, int type) +{ + int result = 0; + + switch (type) { + case SENSOR_TYPE_ANGLE: + if (!sensor->ops->misc_dev) { + sensor->fops.owner = THIS_MODULE; + sensor->fops.unlocked_ioctl = angle_dev_ioctl; + sensor->fops.open = angle_dev_open; + sensor->fops.release = angle_dev_release; + + sensor->miscdev.minor = MISC_DYNAMIC_MINOR; + sensor->miscdev.name = "angle"; + sensor->miscdev.fops = &sensor->fops; + } else { + memcpy(&sensor->miscdev, sensor->ops->misc_dev, sizeof(*sensor->ops->misc_dev)); + } + break; + + case SENSOR_TYPE_ACCEL: + if (!sensor->ops->misc_dev) { + sensor->fops.owner = THIS_MODULE; + sensor->fops.unlocked_ioctl = gsensor_dev_ioctl; + #ifdef CONFIG_COMPAT + sensor->fops.compat_ioctl = gsensor_dev_ioctl; + #endif + sensor->fops.open = gsensor_dev_open; + sensor->fops.release = gsensor_dev_release; + + sensor->miscdev.minor = MISC_DYNAMIC_MINOR; + sensor->miscdev.name = "accel"; + sensor->miscdev.fops = &sensor->fops; + } else { + memcpy(&sensor->miscdev, sensor->ops->misc_dev, sizeof(*sensor->ops->misc_dev)); + } + break; + + case SENSOR_TYPE_COMPASS: + if (!sensor->ops->misc_dev) { + sensor->fops.owner = THIS_MODULE; + sensor->fops.unlocked_ioctl = compass_dev_ioctl; + #ifdef CONFIG_COMPAT + sensor->fops.compat_ioctl = compass_dev_compat_ioctl; + #endif + sensor->fops.open = compass_dev_open; + sensor->fops.release = compass_dev_release; + + sensor->miscdev.minor = MISC_DYNAMIC_MINOR; + sensor->miscdev.name = "compass"; + sensor->miscdev.fops = &sensor->fops; + } else { + memcpy(&sensor->miscdev, sensor->ops->misc_dev, sizeof(*sensor->ops->misc_dev)); + } + break; + + case SENSOR_TYPE_LIGHT: + if (!sensor->ops->misc_dev) { + sensor->fops.owner = THIS_MODULE; + sensor->fops.unlocked_ioctl = light_dev_ioctl; + #ifdef CONFIG_COMPAT + sensor->fops.compat_ioctl = light_dev_compat_ioctl; + #endif + sensor->fops.open = light_dev_open; + sensor->fops.release = light_dev_release; + + sensor->miscdev.minor = MISC_DYNAMIC_MINOR; + sensor->miscdev.name = "lightsensor"; + sensor->miscdev.fops = &sensor->fops; + } else { + memcpy(&sensor->miscdev, sensor->ops->misc_dev, sizeof(*sensor->ops->misc_dev)); + } + break; + + case SENSOR_TYPE_PROXIMITY: + if (!sensor->ops->misc_dev) { + sensor->fops.owner = THIS_MODULE; + sensor->fops.unlocked_ioctl = proximity_dev_ioctl; + #ifdef CONFIG_COMPAT + sensor->fops.compat_ioctl = proximity_dev_compat_ioctl; + #endif + sensor->fops.open = proximity_dev_open; + sensor->fops.release = proximity_dev_release; + + sensor->miscdev.minor = MISC_DYNAMIC_MINOR; + sensor->miscdev.name = "psensor"; + sensor->miscdev.fops = &sensor->fops; + } else { + memcpy(&sensor->miscdev, sensor->ops->misc_dev, sizeof(*sensor->ops->misc_dev)); + } + break; + + case SENSOR_TYPE_TEMPERATURE: + if (!sensor->ops->misc_dev) { + sensor->fops.owner = THIS_MODULE; + sensor->fops.unlocked_ioctl = temperature_dev_ioctl; + sensor->fops.open = temperature_dev_open; + sensor->fops.release = temperature_dev_release; + + sensor->miscdev.minor = MISC_DYNAMIC_MINOR; + sensor->miscdev.name = "temperature"; + sensor->miscdev.fops = &sensor->fops; + } else { + memcpy(&sensor->miscdev, sensor->ops->misc_dev, sizeof(*sensor->ops->misc_dev)); + } + break; + + case SENSOR_TYPE_PRESSURE: + if (!sensor->ops->misc_dev) { + sensor->fops.owner = THIS_MODULE; + sensor->fops.unlocked_ioctl = pressure_dev_ioctl; + sensor->fops.open = pressure_dev_open; + sensor->fops.release = pressure_dev_release; + + sensor->miscdev.minor = MISC_DYNAMIC_MINOR; + sensor->miscdev.name = "pressure"; + sensor->miscdev.fops = &sensor->fops; + } else { + memcpy(&sensor->miscdev, sensor->ops->misc_dev, sizeof(*sensor->ops->misc_dev)); + } + break; + + default: + dev_err(&sensor->client->dev, "%s:unknow sensor type=%d\n", __func__, type); + result = -1; + goto error; + } + + sensor->miscdev.parent = &sensor->client->dev; + result = misc_register(&sensor->miscdev); + if (result < 0) { + dev_err(&sensor->client->dev, + "fail to register misc device %s\n", sensor->miscdev.name); + goto error; + } + dev_info(&sensor->client->dev, "%s:miscdevice: %s\n", __func__, sensor->miscdev.name); + +error: + return result; +} + +int sensor_register_slave(int type, struct i2c_client *client, + struct sensor_platform_data *slave_pdata, + struct sensor_operate *(*get_sensor_ops)(void)) +{ + int result = 0; + struct sensor_operate *ops = get_sensor_ops(); + + if (ops->id_i2c >= SENSOR_NUM_ID) { + printk(KERN_ERR "%s:%s id is error %d\n", __func__, ops->name, ops->id_i2c); + return -1; + } + sensor_ops[ops->id_i2c] = ops; + sensor_probe_times[ops->id_i2c] = 0; + + printk(KERN_INFO "%s:%s,id=%d\n", __func__, sensor_ops[ops->id_i2c]->name, ops->id_i2c); + + return result; +} + +int sensor_unregister_slave(int type, struct i2c_client *client, + struct sensor_platform_data *slave_pdata, + struct sensor_operate *(*get_sensor_ops)(void)) +{ + int result = 0; + struct sensor_operate *ops = get_sensor_ops(); + + if (ops->id_i2c >= SENSOR_NUM_ID) { + printk(KERN_ERR "%s:%s id is error %d\n", __func__, ops->name, ops->id_i2c); + return -1; + } + printk(KERN_INFO "%s:%s,id=%d\n", __func__, sensor_ops[ops->id_i2c]->name, ops->id_i2c); + sensor_ops[ops->id_i2c] = NULL; + + return result; +} + +int sensor_probe(struct i2c_client *client, const struct i2c_device_id *devid) +{ + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + struct sensor_platform_data *pdata; + struct device_node *np = client->dev.of_node; + enum of_gpio_flags rst_flags, pwr_flags; + unsigned long irq_flags; + int result = 0; + int type = 0; + int reprobe_en = 0; + + dev_info(&client->adapter->dev, "%s: %s,%p\n", __func__, devid->name, client); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + result = -ENODEV; + goto out_no_free; + } + if (!np) { + dev_err(&client->dev, "no device tree\n"); + return -EINVAL; + } + pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + result = -ENOMEM; + goto out_no_free; + } + sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL); + if (!sensor) { + result = -ENOMEM; + goto out_no_free; + } + + of_property_read_u32(np, "type", &(pdata->type)); + + pdata->irq_pin = of_get_named_gpio_flags(np, "irq-gpio", 0, (enum of_gpio_flags *)&irq_flags); + pdata->reset_pin = of_get_named_gpio_flags(np, "reset-gpio", 0, &rst_flags); + pdata->power_pin = of_get_named_gpio_flags(np, "power-gpio", 0, &pwr_flags); + + of_property_read_u32(np, "irq_enable", &(pdata->irq_enable)); + of_property_read_u32(np, "poll_delay_ms", &(pdata->poll_delay_ms)); + + of_property_read_u32(np, "x_min", &(pdata->x_min)); + of_property_read_u32(np, "y_min", &(pdata->y_min)); + of_property_read_u32(np, "z_min", &(pdata->z_min)); + of_property_read_u32(np, "factory", &(pdata->factory)); + of_property_read_u32(np, "layout", &(pdata->layout)); + of_property_read_u32(np, "reprobe_en", &reprobe_en); + + of_property_read_u8(np, "address", &(pdata->address)); + of_get_property(np, "project_name", pdata->project_name); + + of_property_read_u32(np, "power-off-in-suspend", + &pdata->power_off_in_suspend); + + switch (pdata->layout) { + case 1: + pdata->orientation[0] = 1; + pdata->orientation[1] = 0; + pdata->orientation[2] = 0; + + pdata->orientation[3] = 0; + pdata->orientation[4] = 1; + pdata->orientation[5] = 0; + + pdata->orientation[6] = 0; + pdata->orientation[7] = 0; + pdata->orientation[8] = 1; + break; + + case 2: + pdata->orientation[0] = 0; + pdata->orientation[1] = -1; + pdata->orientation[2] = 0; + + pdata->orientation[3] = 1; + pdata->orientation[4] = 0; + pdata->orientation[5] = 0; + + pdata->orientation[6] = 0; + pdata->orientation[7] = 0; + pdata->orientation[8] = 1; + break; + + case 3: + pdata->orientation[0] = -1; + pdata->orientation[1] = 0; + pdata->orientation[2] = 0; + + pdata->orientation[3] = 0; + pdata->orientation[4] = -1; + pdata->orientation[5] = 0; + + pdata->orientation[6] = 0; + pdata->orientation[7] = 0; + pdata->orientation[8] = 1; + break; + + case 4: + pdata->orientation[0] = 0; + pdata->orientation[1] = 1; + pdata->orientation[2] = 0; + + pdata->orientation[3] = -1; + pdata->orientation[4] = 0; + pdata->orientation[5] = 0; + + pdata->orientation[6] = 0; + pdata->orientation[7] = 0; + pdata->orientation[8] = 1; + break; + + case 5: + pdata->orientation[0] = 1; + pdata->orientation[1] = 0; + pdata->orientation[2] = 0; + + pdata->orientation[3] = 0; + pdata->orientation[4] = -1; + pdata->orientation[5] = 0; + + pdata->orientation[6] = 0; + pdata->orientation[7] = 0; + pdata->orientation[8] = -1; + break; + + case 6: + pdata->orientation[0] = 0; + pdata->orientation[1] = -1; + pdata->orientation[2] = 0; + + pdata->orientation[3] = -1; + pdata->orientation[4] = 0; + pdata->orientation[5] = 0; + + pdata->orientation[6] = 0; + pdata->orientation[7] = 0; + pdata->orientation[8] = -1; + break; + + case 7: + pdata->orientation[0] = -1; + pdata->orientation[1] = 0; + pdata->orientation[2] = 0; + + pdata->orientation[3] = 0; + pdata->orientation[4] = 1; + pdata->orientation[5] = 0; + + pdata->orientation[6] = 0; + pdata->orientation[7] = 0; + pdata->orientation[8] = -1; + break; + + case 8: + pdata->orientation[0] = 0; + pdata->orientation[1] = 1; + pdata->orientation[2] = 0; + + pdata->orientation[3] = 1; + pdata->orientation[4] = 0; + pdata->orientation[5] = 0; + + pdata->orientation[6] = 0; + pdata->orientation[7] = 0; + pdata->orientation[8] = -1; + break; + case 9: + pdata->orientation[0] = 1; + pdata->orientation[1] = 0; + pdata->orientation[2] = 0; + + pdata->orientation[3] = 0; + pdata->orientation[4] = -1; + pdata->orientation[5] = 0; + + pdata->orientation[6] = 0; + pdata->orientation[7] = 0; + pdata->orientation[8] = 1; + break; + default: + pdata->orientation[0] = 1; + pdata->orientation[1] = 0; + pdata->orientation[2] = 0; + + pdata->orientation[3] = 0; + pdata->orientation[4] = 1; + pdata->orientation[5] = 0; + + pdata->orientation[6] = 0; + pdata->orientation[7] = 0; + pdata->orientation[8] = 1; + break; + } + + client->irq = pdata->irq_pin; + type = pdata->type; + pdata->irq_flags = irq_flags; + //pdata->poll_delay_ms = 30; + + if ((type >= SENSOR_NUM_TYPES) || (type <= SENSOR_TYPE_NULL)) { + dev_err(&client->adapter->dev, "sensor type is error %d\n", type); + result = -EFAULT; + goto out_no_free; + } + + i2c_set_clientdata(client, sensor); + sensor->client = client; + sensor->pdata = pdata; + sensor->type = type; + sensor->i2c_id = (struct i2c_device_id *)devid; + + memset(&(sensor->axis), 0, sizeof(struct sensor_axis)); + mutex_init(&sensor->data_mutex); + mutex_init(&sensor->operation_mutex); + mutex_init(&sensor->sensor_mutex); + mutex_init(&sensor->i2c_mutex); + + atomic_set(&sensor->is_factory, 0); + init_waitqueue_head(&sensor->is_factory_ok); + + /* As default, report all information */ + atomic_set(&sensor->flags.m_flag, 1); + atomic_set(&sensor->flags.a_flag, 1); + atomic_set(&sensor->flags.mv_flag, 1); + atomic_set(&sensor->flags.open_flag, 0); + atomic_set(&sensor->flags.debug_flag, 1); + init_waitqueue_head(&sensor->flags.open_wq); + sensor->flags.delay = 100; + + sensor->status_cur = SENSOR_OFF; + sensor->axis.x = 0; + sensor->axis.y = 0; + sensor->axis.z = 0; + + result = sensor_chip_init(sensor->client); + if (result < 0) { + if (reprobe_en && (result == -2)) { + sensor_probe_times[sensor->ops->id_i2c]++; + if (sensor_probe_times[sensor->ops->id_i2c] < 3) + result = -EPROBE_DEFER; + } + goto out_free_memory; + } + + sensor->input_dev = devm_input_allocate_device(&client->dev); + if (!sensor->input_dev) { + result = -ENOMEM; + dev_err(&client->dev, + "Failed to allocate input device\n"); + goto out_free_memory; + } + + switch (type) { + case SENSOR_TYPE_ANGLE: + sensor->input_dev->name = "angle"; + set_bit(EV_ABS, sensor->input_dev->evbit); + /* x-axis acceleration */ + input_set_abs_params(sensor->input_dev, ABS_X, sensor->ops->range[0], sensor->ops->range[1], 0, 0); + /* y-axis acceleration */ + input_set_abs_params(sensor->input_dev, ABS_Y, sensor->ops->range[0], sensor->ops->range[1], 0, 0); + /* z-axis acceleration */ + input_set_abs_params(sensor->input_dev, ABS_Z, sensor->ops->range[0], sensor->ops->range[1], 0, 0); + + case SENSOR_TYPE_ACCEL: + sensor->input_dev->name = "gsensor"; + set_bit(EV_ABS, sensor->input_dev->evbit); + /* x-axis acceleration */ + input_set_abs_params(sensor->input_dev, ABS_X, sensor->ops->range[0], sensor->ops->range[1], 0, 0); + /* y-axis acceleration */ + input_set_abs_params(sensor->input_dev, ABS_Y, sensor->ops->range[0], sensor->ops->range[1], 0, 0); + /* z-axis acceleration */ + input_set_abs_params(sensor->input_dev, ABS_Z, sensor->ops->range[0], sensor->ops->range[1], 0, 0); + break; + case SENSOR_TYPE_COMPASS: + sensor->input_dev->name = "compass"; + /* Setup input device */ + set_bit(EV_ABS, sensor->input_dev->evbit); + /* yaw (0, 360) */ + input_set_abs_params(sensor->input_dev, ABS_RX, 0, 23040, 0, 0); + /* pitch (-180, 180) */ + input_set_abs_params(sensor->input_dev, ABS_RY, -11520, 11520, 0, 0); + /* roll (-90, 90) */ + input_set_abs_params(sensor->input_dev, ABS_RZ, -5760, 5760, 0, 0); + /* x-axis acceleration (720 x 8G) */ + input_set_abs_params(sensor->input_dev, ABS_X, -5760, 5760, 0, 0); + /* y-axis acceleration (720 x 8G) */ + input_set_abs_params(sensor->input_dev, ABS_Y, -5760, 5760, 0, 0); + /* z-axis acceleration (720 x 8G) */ + input_set_abs_params(sensor->input_dev, ABS_Z, -5760, 5760, 0, 0); + /* status of magnetic sensor */ + input_set_abs_params(sensor->input_dev, ABS_RUDDER, -32768, 3, 0, 0); + /* status of acceleration sensor */ + input_set_abs_params(sensor->input_dev, ABS_WHEEL, -32768, 3, 0, 0); + /* x-axis of raw magnetic vector (-4096, 4095) */ + input_set_abs_params(sensor->input_dev, ABS_HAT0X, -20480, 20479, 0, 0); + /* y-axis of raw magnetic vector (-4096, 4095) */ + input_set_abs_params(sensor->input_dev, ABS_HAT0Y, -20480, 20479, 0, 0); + /* z-axis of raw magnetic vector (-4096, 4095) */ + input_set_abs_params(sensor->input_dev, ABS_BRAKE, -20480, 20479, 0, 0); + break; + case SENSOR_TYPE_GYROSCOPE: + sensor->input_dev->name = "gyro"; + /* x-axis acceleration */ + input_set_capability(sensor->input_dev, EV_REL, REL_RX); + input_set_abs_params(sensor->input_dev, ABS_RX, sensor->ops->range[0], sensor->ops->range[1], 0, 0); + /* y-axis acceleration */ + input_set_capability(sensor->input_dev, EV_REL, REL_RY); + input_set_abs_params(sensor->input_dev, ABS_RY, sensor->ops->range[0], sensor->ops->range[1], 0, 0); + /* z-axis acceleration */ + input_set_capability(sensor->input_dev, EV_REL, REL_RZ); + input_set_abs_params(sensor->input_dev, ABS_RZ, sensor->ops->range[0], sensor->ops->range[1], 0, 0); + break; + case SENSOR_TYPE_LIGHT: + sensor->input_dev->name = "lightsensor-level"; + set_bit(EV_ABS, sensor->input_dev->evbit); + input_set_abs_params(sensor->input_dev, ABS_MISC, sensor->ops->range[0], sensor->ops->range[1], 0, 0); + input_set_abs_params(sensor->input_dev, ABS_TOOL_WIDTH, sensor->ops->brightness[0], sensor->ops->brightness[1], 0, 0); + break; + case SENSOR_TYPE_PROXIMITY: + sensor->input_dev->name = "proximity"; + set_bit(EV_ABS, sensor->input_dev->evbit); + input_set_abs_params(sensor->input_dev, ABS_DISTANCE, sensor->ops->range[0], sensor->ops->range[1], 0, 0); + break; + case SENSOR_TYPE_TEMPERATURE: + sensor->input_dev->name = "temperature"; + set_bit(EV_ABS, sensor->input_dev->evbit); + input_set_abs_params(sensor->input_dev, ABS_THROTTLE, sensor->ops->range[0], sensor->ops->range[1], 0, 0); + break; + case SENSOR_TYPE_PRESSURE: + sensor->input_dev->name = "pressure"; + set_bit(EV_ABS, sensor->input_dev->evbit); + input_set_abs_params(sensor->input_dev, ABS_PRESSURE, sensor->ops->range[0], sensor->ops->range[1], 0, 0); + break; + default: + dev_err(&client->dev, "%s:unknow sensor type=%d\n", __func__, type); + break; + } + sensor->input_dev->dev.parent = &client->dev; + + result = input_register_device(sensor->input_dev); + if (result) { + dev_err(&client->dev, + "Unable to register input device %s\n", sensor->input_dev->name); + goto out_input_register_device_failed; + } + + result = sensor_irq_init(sensor->client); + if (result) { + dev_err(&client->dev, + "fail to init sensor irq,ret=%d\n", result); + goto out_input_register_device_failed; + } + + sensor->miscdev.parent = &client->dev; + result = sensor_misc_device_register(sensor, type); + if (result) { + dev_err(&client->dev, + "fail to register misc device %s\n", sensor->miscdev.name); + goto out_misc_device_register_device_failed; + } + + g_sensor[type] = sensor; + +#ifdef CONFIG_HAS_EARLYSUSPEND + if ((sensor->ops->suspend) && (sensor->ops->resume)) { + sensor->early_suspend.suspend = sensor_suspend; + sensor->early_suspend.resume = sensor_resume; + sensor->early_suspend.level = 0x02; + register_early_suspend(&sensor->early_suspend); + } +#endif + dev_info(&client->dev, "%s:initialized ok,sensor name:%s,type:%d,id=%d\n\n", __func__, sensor->ops->name, type, (int)sensor->i2c_id->driver_data); + + return result; + +out_misc_device_register_device_failed: +out_input_register_device_failed: +out_free_memory: +out_no_free: + dev_err(&client->adapter->dev, "%s failed %d\n\n", __func__, result); + return result; +} + +static void sensor_shut_down(struct i2c_client *client) +{ +#ifdef CONFIG_HAS_EARLYSUSPEND + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + + if ((sensor->ops->suspend) && (sensor->ops->resume)) + unregister_early_suspend(&sensor->early_suspend); +#endif +} + +static int sensor_remove(struct i2c_client *client) +{ + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + + sensor->stop_work = 1; + cancel_delayed_work_sync(&sensor->delaywork); + misc_deregister(&sensor->miscdev); +#ifdef CONFIG_HAS_EARLYSUSPEND + if ((sensor->ops->suspend) && (sensor->ops->resume)) + unregister_early_suspend(&sensor->early_suspend); +#endif + + return 0; +} + +static const struct i2c_device_id sensor_id[] = { + /*gsensor*/ + {"kxtj3", ACCEL_ID_KXTJ3}, + {}, +}; + +static struct of_device_id sensor_dt_ids[] = { + /*gsensor*/ + { .compatible = "kxtj3" }, + { } +}; + +static struct i2c_driver sensor_driver = { + .probe = sensor_probe, + .remove = sensor_remove, + .shutdown = sensor_shut_down, + .id_table = sensor_id, + .driver = { + .name = "sensors", + .of_match_table = of_match_ptr(sensor_dt_ids), + .pm = SENSOR_PM_OPS, + }, +}; + +static int __init sensor_init(void) +{ + sensor_class_init(); + return i2c_add_driver(&sensor_driver); +} + +static void __exit sensor_exit(void) +{ + i2c_del_driver(&sensor_driver); +} + +late_initcall(sensor_init); +module_exit(sensor_exit); + +MODULE_AUTHOR("Waylon waylon@khadas.com"); +MODULE_DESCRIPTION("User space character device interface for sensors"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/sensors/sensor-i2c.c b/drivers/input/sensors/sensor-i2c.c new file mode 100755 index 0000000..0e418a5 --- /dev/null +++ b/drivers/input/sensors/sensor-i2c.c @@ -0,0 +1,241 @@ +/* drivers/input/sensors/sensor-i2c.c - sensor i2c handle + * + * Copyright (C) 2019 Khadas. + * Author: Waylon + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif +#include + +#define SENSOR_I2C_RATE 200*1000 + + +static int sensor_i2c_write(struct i2c_adapter *i2c_adap, + unsigned char address, + unsigned int len, unsigned char const *data) +{ + struct i2c_msg msgs[1]; + int res; + + if (!data || !i2c_adap) { + printk("%s:line=%d,error\n",__func__,__LINE__); + return -EINVAL; + } + + msgs[0].addr = address; + msgs[0].flags = 0; /* write */ + msgs[0].buf = (unsigned char *)data; + msgs[0].len = len; + + res = i2c_transfer(i2c_adap, msgs, 1); + if (res == 1) + return 0; + else if(res == 0) + return -EBUSY; + else + return res; + +} + +static int senosr_i2c_read(struct i2c_adapter *i2c_adap, + unsigned char address, unsigned char reg, + unsigned int len, unsigned char *data) +{ + struct i2c_msg msgs[2]; + int res; + + if (!data || !i2c_adap) { + printk("%s:line=%d,error\n",__func__,__LINE__); + return -EINVAL; + } + + msgs[0].addr = address; + msgs[0].flags = 0; /* write */ + msgs[0].buf = ® + msgs[0].len = 1; + + msgs[1].addr = address; + msgs[1].flags = I2C_M_RD; + msgs[1].buf = data; + msgs[1].len = len; + + res = i2c_transfer(i2c_adap, msgs, 2); + if (res == 2) + return 0; + else if(res == 0) + return -EBUSY; + else + return res; + +} + + +int sensor_rx_data(struct i2c_client *client, char *rxData, int length) +{ + int i = 0; + int ret = 0; + char reg = rxData[0]; + ret = senosr_i2c_read(client->adapter, client->addr, reg, length, rxData); + + DBG("addr=0x%x,len=%d,rxdata:",reg,length); + for(i=0; iadapter, client->addr, length, txData); + return ret; + +} +EXPORT_SYMBOL(sensor_tx_data); + +int sensor_write_reg(struct i2c_client *client, int addr, int value) +{ + char buffer[2]; + int ret = 0; + struct sensor_private_data* sensor = + (struct sensor_private_data *)i2c_get_clientdata(client); + + mutex_lock(&sensor->i2c_mutex); + buffer[0] = addr; + buffer[1] = value; + ret = sensor_tx_data(client, &buffer[0], 2); + mutex_unlock(&sensor->i2c_mutex); + return ret; +} +EXPORT_SYMBOL(sensor_write_reg); + +int sensor_read_reg(struct i2c_client *client, int addr) +{ + char tmp[1] = {0}; + int ret = 0; + struct sensor_private_data* sensor = + (struct sensor_private_data *)i2c_get_clientdata(client); + + mutex_lock(&sensor->i2c_mutex); + tmp[0] = addr; + ret = sensor_rx_data(client, tmp, 1); + mutex_unlock(&sensor->i2c_mutex); + + return tmp[0]; +} + +EXPORT_SYMBOL(sensor_read_reg); + +static int i2c_master_normal_recv(const struct i2c_client *client, char *buf, int count, int scl_rate) + { + struct i2c_adapter *adap=client->adapter; + struct i2c_msg msg; + int ret; + + msg.addr = client->addr; + msg.flags = client->flags | I2C_M_RD; + msg.len = count; + msg.buf = (char *)buf; + ret = i2c_transfer(adap, &msg, 1); + + return (ret == 1) ? count : ret; +} + +static int i2c_master_normal_send(const struct i2c_client *client, const char *buf, int count, int scl_rate) +{ + int ret; + struct i2c_adapter *adap=client->adapter; + struct i2c_msg msg; + + msg.addr = client->addr; + msg.flags = client->flags; + msg.len = count; + msg.buf = (char *)buf; + + ret = i2c_transfer(adap, &msg, 1); + return (ret == 1) ? count : ret; +} + +int sensor_tx_data_normal(struct i2c_client *client, char *buf, int num) +{ + int ret = 0; + ret = i2c_master_normal_send(client, buf, num, SENSOR_I2C_RATE); + + return (ret == num) ? 0 : ret; +} +EXPORT_SYMBOL(sensor_tx_data_normal); + + +int sensor_rx_data_normal(struct i2c_client *client, char *buf, int num) +{ + int ret = 0; + ret = i2c_master_normal_recv(client, buf, num, SENSOR_I2C_RATE); + + return (ret == num) ? 0 : ret; +} + +EXPORT_SYMBOL(sensor_rx_data_normal); + + +int sensor_write_reg_normal(struct i2c_client *client, char value) +{ + char buffer[2]; + int ret = 0; + struct sensor_private_data* sensor = + (struct sensor_private_data *)i2c_get_clientdata(client); + + mutex_lock(&sensor->i2c_mutex); + buffer[0] = value; + ret = sensor_tx_data_normal(client, &buffer[0], 1); + mutex_unlock(&sensor->i2c_mutex); + return ret; +} +EXPORT_SYMBOL(sensor_write_reg_normal); + +int sensor_read_reg_normal(struct i2c_client *client) +{ + char tmp[1] = {0}; + int ret = 0; + struct sensor_private_data* sensor = + (struct sensor_private_data *)i2c_get_clientdata(client); + + mutex_lock(&sensor->i2c_mutex); + ret = sensor_rx_data_normal(client, tmp, 1); + mutex_unlock(&sensor->i2c_mutex); + + return tmp[0]; +} + +EXPORT_SYMBOL(sensor_read_reg_normal); + diff --git a/include/linux/sensor-dev.h b/include/linux/sensor-dev.h new file mode 100755 index 0000000..81ef7b0 --- /dev/null +++ b/include/linux/sensor-dev.h @@ -0,0 +1,263 @@ +/* include/linux/sensor-dev.h - sensor header file + * + * Copyright (C) 2012-2015 ROCKCHIP. + * Author: luowei + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif + +#define SENSOR_TYPE_NULL 0 +#define SENSOR_TYPE_ANGLE 1 +#define SENSOR_TYPE_ACCEL 2 +#define SENSOR_TYPE_COMPASS 3 +#define SENSOR_TYPE_GYROSCOPE 4 +#define SENSOR_TYPE_LIGHT 5 +#define SENSOR_TYPE_PROXIMITY 6 +#define SENSOR_TYPE_TEMPERATURE 7 +#define SENSOR_TYPE_PRESSURE 8 +#define SENSOR_TYPE_HALL 9 +#define SENSOR_NUM_TYPES 10 + +#define SENSOR_ON 1 +#define SENSOR_OFF 0 +#define SENSOR_UNKNOW_DATA -1 + +#define GPIO_HIGH 1 +#define GPIO_LOW 0 + +enum sensor_id { + ACCEL_ID_KXTJ3 = 0, + SENSOR_NUM_ID, +}; + +struct sensor_axis { + int x; + int y; + int z; +}; + +struct sensor_flag { + atomic_t a_flag; + atomic_t m_flag; + atomic_t mv_flag; + atomic_t open_flag; + atomic_t debug_flag; + long long delay; + wait_queue_head_t open_wq; +}; + + +struct sensor_operate { + char *name; + int type; + int id_i2c; + int range[2]; + int brightness[2]; + int read_reg; + int read_len; + int id_reg; + int id_data; + int precision; + int ctrl_reg; + int ctrl_data; + int int_ctrl_reg; + int int_status_reg; + int trig; + int (*active)(struct i2c_client *client, int enable, int rate); + int (*init)(struct i2c_client *client); + int (*report)(struct i2c_client *client); + int (*suspend)(struct i2c_client *client); + int (*resume)(struct i2c_client *client); + struct miscdevice *misc_dev; +}; + +/* Platform data for the sensor */ +struct sensor_private_data { + int type; + struct i2c_client *client; + struct input_dev *input_dev; + int stop_work; + struct delayed_work delaywork; + struct sensor_axis axis; + char sensor_data[40]; + atomic_t is_factory; + wait_queue_head_t is_factory_ok; + struct mutex data_mutex; + struct mutex operation_mutex; + struct mutex sensor_mutex; + struct mutex i2c_mutex; + int status_cur; + int start_count; + int devid; + struct sensor_flag flags; + struct i2c_device_id *i2c_id; + struct sensor_platform_data *pdata; + struct sensor_operate *ops; + struct file_operations fops; + struct miscdevice miscdev; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend; +#endif +}; + +struct sensor_platform_data { + int type; + int irq; + int irq_pin; + int power_pin; + int reset_pin; + int standby_pin; + int irq_enable; + int poll_delay_ms; + int x_min; + int y_min; + int z_min; + int factory; + int layout; + unsigned char address; + unsigned long irq_flags; + signed char orientation[9]; + short m_layout[4][3][3]; + int *project_name; + int power_off_in_suspend; +}; + +struct gsensor_platform_data { + u16 model; + u16 swap_xy; + u16 swap_xyz; + signed char orientation[9]; + int (*get_pendown_state)(void); + int (*init_platform_hw)(void); + int (*gsensor_platform_sleep)(void); + int (*gsensor_platform_wakeup)(void); + void (*exit_platform_hw)(void); +}; + +struct akm8975_platform_data { + short m_layout[4][3][3]; + char project_name[64]; + int gpio_DRDY; +}; + +struct akm_platform_data { + short m_layout[4][3][3]; + char project_name[64]; + char layout; + char outbit; + int gpio_DRDY; + int gpio_RST; +}; + +extern int sensor_register_slave(int type, struct i2c_client *client, + struct sensor_platform_data *slave_pdata, + struct sensor_operate *(*get_sensor_ops)(void)); + + +extern int sensor_unregister_slave(int type, struct i2c_client *client, + struct sensor_platform_data *slave_pdata, + struct sensor_operate *(*get_sensor_ops)(void)); + +#define DBG(x...) + +#define GSENSOR_IOCTL_MAGIC 'a' +#define GBUFF_SIZE 12 /* Rx buffer size */ + +/* IOCTLs for MMA8452 library */ +#define GSENSOR_IOCTL_INIT _IO(GSENSOR_IOCTL_MAGIC, 0x01) +#define GSENSOR_IOCTL_RESET _IO(GSENSOR_IOCTL_MAGIC, 0x04) +#define GSENSOR_IOCTL_CLOSE _IO(GSENSOR_IOCTL_MAGIC, 0x02) +#define GSENSOR_IOCTL_START _IO(GSENSOR_IOCTL_MAGIC, 0x03) +#define GSENSOR_IOCTL_GETDATA _IOR(GSENSOR_IOCTL_MAGIC, 0x08, char[GBUFF_SIZE+1]) +#define GSENSOR_IOCTL_APP_SET_RATE _IOW(GSENSOR_IOCTL_MAGIC, 0x10, short) +#define GSENSOR_IOCTL_GET_CALIBRATION _IOR(GSENSOR_IOCTL_MAGIC, 0x11, int[3]) + + +#define COMPASS_IOCTL_MAGIC 'c' +/* IOCTLs for APPs */ +#define ECS_IOCTL_APP_SET_MODE _IOW(COMPASS_IOCTL_MAGIC, 0x10, short) +#define ECS_IOCTL_APP_SET_MFLAG _IOW(COMPASS_IOCTL_MAGIC, 0x11, short) +#define ECS_IOCTL_APP_GET_MFLAG _IOW(COMPASS_IOCTL_MAGIC, 0x12, short) +#define ECS_IOCTL_APP_SET_AFLAG _IOW(COMPASS_IOCTL_MAGIC, 0x13, short) +#define ECS_IOCTL_APP_GET_AFLAG _IOR(COMPASS_IOCTL_MAGIC, 0x14, short) +#define ECS_IOCTL_APP_SET_TFLAG _IOR(COMPASS_IOCTL_MAGIC, 0x15, short)/* NOT use */ +#define ECS_IOCTL_APP_GET_TFLAG _IOR(COMPASS_IOCTL_MAGIC, 0x16, short)/* NOT use */ +#define ECS_IOCTL_APP_RESET_PEDOMETER _IOW(COMPASS_IOCTL_MAGIC, 0x17) /* NOT use */ +#define ECS_IOCTL_APP_SET_DELAY _IOW(COMPASS_IOCTL_MAGIC, 0x18, short) +#define ECS_IOCTL_APP_SET_MVFLAG _IOW(COMPASS_IOCTL_MAGIC, 0x19, short) +#define ECS_IOCTL_APP_GET_MVFLAG _IOR(COMPASS_IOCTL_MAGIC, 0x1A, short) +#define ECS_IOCTL_APP_GET_DELAY _IOR(COMPASS_IOCTL_MAGIC, 0x1B, short) + +#ifdef CONFIG_COMPAT +#define COMPAT_ECS_IOCTL_APP_SET_MODE _IOW(COMPASS_IOCTL_MAGIC, 0x10, compat_short_t) +#define COMPAT_ECS_IOCTL_APP_SET_MFLAG _IOW(COMPASS_IOCTL_MAGIC, 0x11, compat_short_t) +#define COMPAT_ECS_IOCTL_APP_GET_MFLAG _IOW(COMPASS_IOCTL_MAGIC, 0x12, compat_short_t) +#define COMPAT_ECS_IOCTL_APP_SET_AFLAG _IOW(COMPASS_IOCTL_MAGIC, 0x13, compat_short_t) +#define COMPAT_ECS_IOCTL_APP_GET_AFLAG _IOR(COMPASS_IOCTL_MAGIC, 0x14, compat_short_t) +#define COMPAT_ECS_IOCTL_APP_SET_TFLAG _IOR(COMPASS_IOCTL_MAGIC, 0x15, compat_short_t)/* NOT use */ +#define COMPAT_ECS_IOCTL_APP_GET_TFLAG _IOR(COMPASS_IOCTL_MAGIC, 0x16, compat_short_t)/* NOT use */ +#define COMPAT_ECS_IOCTL_APP_RESET_PEDOMETER _IOW(COMPASS_IOCTL_MAGIC, 0x17) /* NOT use */ +#define COMPAT_ECS_IOCTL_APP_SET_DELAY _IOW(COMPASS_IOCTL_MAGIC, 0x18, compat_short_t) +#define COMPAT_ECS_IOCTL_APP_SET_MVFLAG _IOW(COMPASS_IOCTL_MAGIC, 0x19, compat_short_t) +#define COMPAT_ECS_IOCTL_APP_GET_MVFLAG _IOR(COMPASS_IOCTL_MAGIC, 0x1A, compat_short_t) +#define COMPAT_ECS_IOCTL_APP_GET_DELAY _IOR(COMPASS_IOCTL_MAGIC, 0x1B, compat_short_t) +#endif + +#define LIGHTSENSOR_IOCTL_MAGIC 'l' +#define LIGHTSENSOR_IOCTL_GET_ENABLED _IOR(LIGHTSENSOR_IOCTL_MAGIC, 1, int *) +#define LIGHTSENSOR_IOCTL_ENABLE _IOW(LIGHTSENSOR_IOCTL_MAGIC, 2, int *) +#define LIGHTSENSOR_IOCTL_SET_RATE _IOW(LIGHTSENSOR_IOCTL_MAGIC, 3, short) + +#ifdef CONFIG_COMPAT +#define COMPAT_LIGHTSENSOR_IOCTL_GET_ENABLED _IOR(LIGHTSENSOR_IOCTL_MAGIC, 1, compat_uptr_t) +#define COMPAT_LIGHTSENSOR_IOCTL_ENABLE _IOW(LIGHTSENSOR_IOCTL_MAGIC, 2, compat_uptr_t) +#define COMPAT_LIGHTSENSOR_IOCTL_SET_RATE _IOW(LIGHTSENSOR_IOCTL_MAGIC, 3, compat_short_t) +#endif + +#define PSENSOR_IOCTL_MAGIC 'p' +#define PSENSOR_IOCTL_GET_ENABLED _IOR(PSENSOR_IOCTL_MAGIC, 1, int *) +#define PSENSOR_IOCTL_ENABLE _IOW(PSENSOR_IOCTL_MAGIC, 2, int *) +#define PSENSOR_IOCTL_DISABLE _IOW(PSENSOR_IOCTL_MAGIC, 3, int *) + +#ifdef CONFIG_COMPAT +#define COMPAT_PSENSOR_IOCTL_GET_ENABLED _IOR(PSENSOR_IOCTL_MAGIC, 1, compat_uptr_t) +#define COMPAT_PSENSOR_IOCTL_ENABLE _IOW(PSENSOR_IOCTL_MAGIC, 2, compat_uptr_t) +#define COMPAT_PSENSOR_IOCTL_DISABLE _IOW(PSENSOR_IOCTL_MAGIC, 3, compat_uptr_t) +#endif + +#define PRESSURE_IOCTL_MAGIC 'r' +#define PRESSURE_IOCTL_GET_ENABLED _IOR(PRESSURE_IOCTL_MAGIC, 1, int *) +#define PRESSURE_IOCTL_ENABLE _IOW(PRESSURE_IOCTL_MAGIC, 2, int *) +#define PRESSURE_IOCTL_DISABLE _IOW(PRESSURE_IOCTL_MAGIC, 3, int *) +#define PRESSURE_IOCTL_SET_DELAY _IOW(PRESSURE_IOCTL_MAGIC, 4, int *) + + +#define TEMPERATURE_IOCTL_MAGIC 't' +#define TEMPERATURE_IOCTL_GET_ENABLED _IOR(TEMPERATURE_IOCTL_MAGIC, 1, int *) +#define TEMPERATURE_IOCTL_ENABLE _IOW(TEMPERATURE_IOCTL_MAGIC, 2, int *) +#define TEMPERATURE_IOCTL_DISABLE _IOW(TEMPERATURE_IOCTL_MAGIC, 3, int *) +#define TEMPERATURE_IOCTL_SET_DELAY _IOW(TEMPERATURE_IOCTL_MAGIC, 4, int *) + + +extern int sensor_rx_data(struct i2c_client *client, char *rxData, int length); +extern int sensor_tx_data(struct i2c_client *client, char *txData, int length); +extern int sensor_write_reg(struct i2c_client *client, int addr, int value); +extern int sensor_read_reg(struct i2c_client *client, int addr); +extern int sensor_tx_data_normal(struct i2c_client *client, char *buf, int num); +extern int sensor_rx_data_normal(struct i2c_client *client, char *buf, int num); +extern int sensor_write_reg_normal(struct i2c_client *client, char value); +extern int sensor_read_reg_normal(struct i2c_client *client); + -- 2.7.4 From 193319274ae620807553f50b98faa91a3a0b462c Mon Sep 17 00:00:00 2001 From: Seung-Woo Kim Date: Thu, 3 Jun 2021 14:50:00 +0900 Subject: [PATCH 12/16] arm64: configs: tizen_kvims: Enable sensor device As like kvims_defconfig, enable SENSOR_DEVICE to support g-sensor. Change-Id: If747f690aada88d17e49ad69cdc5b88e635687c4 Ref: commit 9519fc2cb1da ("Gsensor: add kxtj3 driver support") Signed-off-by: Seung-Woo Kim --- arch/arm64/configs/tizen_kvims_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/configs/tizen_kvims_defconfig b/arch/arm64/configs/tizen_kvims_defconfig index 3351f0d..df2e683 100644 --- a/arch/arm64/configs/tizen_kvims_defconfig +++ b/arch/arm64/configs/tizen_kvims_defconfig @@ -477,6 +477,7 @@ CONFIG_JOYSTICK_XPAD=y CONFIG_JOYSTICK_XPAD_FF=y CONFIG_JOYSTICK_XPAD_LEDS=y CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_SENSOR_DEVICE=y CONFIG_INPUT_MISC=y CONFIG_INPUT_UINPUT=y # CONFIG_LEGACY_PTYS is not set -- 2.7.4 From b30a3f24362be8b8d6a9f9964f8c545399ce8c80 Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Tue, 8 Jun 2021 16:59:32 +0900 Subject: [PATCH 13/16] ARM64: tizen_kvim3_defconfig: enable CONFIG_BT_HCIUART_BCM Enable CONFIG_BT_HCIUART_BCM configuration. Change-Id: I13525bce2d01d55e32712faf730ddc57971b5816 Signed-off-by: Jaehoon Chung --- arch/arm64/configs/tizen_kvims_defconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/configs/tizen_kvims_defconfig b/arch/arm64/configs/tizen_kvims_defconfig index df2e683..ac53cee 100644 --- a/arch/arm64/configs/tizen_kvims_defconfig +++ b/arch/arm64/configs/tizen_kvims_defconfig @@ -225,7 +225,7 @@ CONFIG_BT_BNEP_PROTO_FILTER=y CONFIG_BT_HIDP=y CONFIG_BT_6LOWPAN=y CONFIG_BT_HCIUART=y -CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUART_BCM=y CONFIG_CFG80211=y CONFIG_NL80211_TESTMODE=y CONFIG_CFG80211_WEXT=y -- 2.7.4 From c86246189c6bf8ace607b5446080f1951375b163 Mon Sep 17 00:00:00 2001 From: Seung-Woo Kim Date: Wed, 9 Jun 2021 18:10:06 +0900 Subject: [PATCH 14/16] net: rtl88xx: use null nic_hdl for _init_timer() without checking adapter In linux case, _init_timer() does not use nic_hdl, the 2nd argument, from drivers/net/wireless/rtl8812au/include/osdep_service_linux.h and rlt88xx should register timer even there is null adapter, otherwise interface-up causes mod_timer crash. Change-Id: I30addc152c197e0243b9b58e8108799f0f94330c Fixes: commit 52ddac0afb83 ("net: rtl88xx: fix to check null adapter") Reported-by: Cheoleun Moon Signed-off-by: Seung-Woo Kim --- drivers/net/wireless/rtl8812au/os_dep/osdep_service.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/net/wireless/rtl8812au/os_dep/osdep_service.c b/drivers/net/wireless/rtl8812au/os_dep/osdep_service.c index 36d793d..bed0639 100644 --- a/drivers/net/wireless/rtl8812au/os_dep/osdep_service.c +++ b/drivers/net/wireless/rtl8812au/os_dep/osdep_service.c @@ -1183,11 +1183,8 @@ void rtw_init_timer(_timer *ptimer, void *padapter, void *pfunc, void *ctx) { _adapter *adapter = (_adapter *)padapter; - if (!adapter) - return; - #ifdef PLATFORM_LINUX - _init_timer(ptimer, adapter->pnetdev, pfunc, ctx); + _init_timer(ptimer, NULL, pfunc, ctx); #endif #ifdef PLATFORM_FREEBSD _init_timer(ptimer, adapter->pifp, pfunc, ctx); -- 2.7.4 From 1b6591ee8d654d0b5301123b4492efb242722139 Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Mon, 1 Jun 2020 21:45:50 -0700 Subject: [PATCH 15/16] slub: Remove userspace notifier for cache add/remove MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit I came across some unnecessary uevents once again which reminded me this. The patch seems to be lost in the leaves of the original discussion [1], so resending. [1] https://lore.kernel.org/r/alpine.DEB.2.21.2001281813130.745@www.lameter.com Kmem caches are internal kernel structures so it is strange that userspace notifiers would be needed. And I am not aware of any use of these notifiers. These notifiers may just exist because in the initial slub release the sysfs code was copied from another subsystem. Signed-off-by: Christoph Lameter Signed-off-by: Andrew Morton Acked-by: Vlastimil Babka Acked-by: Michal Koutný Acked-by: David Rientjes Cc: Pekka Enberg Cc: Joonsoo Kim Link: http://lkml.kernel.org/r/20200423115721.19821-1-mkoutny@suse.com Signed-off-by: Linus Torvalds [sw0312.kim: backport mainline Linux v5.8 commit d7660ce5914d to remove unnecessary uevent for amlogic targets] Reported-by: INSUN PYO Signed-off-by: Seung-Woo Kim Change-Id: Idb636d2fbff9fd7f77a8b90c2bbee3892c0c5e77 --- mm/slub.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/mm/slub.c b/mm/slub.c index 80f291c..eb00e15 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -5632,19 +5632,6 @@ static struct kobj_type slab_ktype = { .release = kmem_cache_release, }; -static int uevent_filter(struct kset *kset, struct kobject *kobj) -{ - struct kobj_type *ktype = get_ktype(kobj); - - if (ktype == &slab_ktype) - return 1; - return 0; -} - -static const struct kset_uevent_ops slab_uevent_ops = { - .filter = uevent_filter, -}; - static struct kset *slab_kset; static inline struct kset *cache_kset(struct kmem_cache *s) @@ -5738,7 +5725,6 @@ static int sysfs_slab_add(struct kmem_cache *s) } #endif - kobject_uevent(&s->kobj, KOBJ_ADD); if (!unmergeable) { /* Setup first alias */ sysfs_slab_alias(s, s->name); @@ -5764,7 +5750,6 @@ void sysfs_slab_remove(struct kmem_cache *s) #ifdef CONFIG_MEMCG kset_unregister(s->memcg_kset); #endif - kobject_uevent(&s->kobj, KOBJ_REMOVE); kobject_del(&s->kobj); kobject_put(&s->kobj); } @@ -5811,7 +5796,7 @@ static int __init slab_sysfs_init(void) mutex_lock(&slab_mutex); - slab_kset = kset_create_and_add("slab", &slab_uevent_ops, kernel_kobj); + slab_kset = kset_create_and_add("slab", NULL, kernel_kobj); if (!slab_kset) { mutex_unlock(&slab_mutex); pr_err("Cannot register slab subsystem.\n"); -- 2.7.4 From c0817143ef512b03bed51f7966f0c53645a7b531 Mon Sep 17 00:00:00 2001 From: Sudha Bheemanna Date: Tue, 23 Aug 2016 17:07:10 +0530 Subject: [PATCH 16/16] Bluetooth: Add "TIZEN_BT" flag Added the tizen specific flag for use in adding tizen patches. Change-Id: Ia391644fddbe600c8d845e0bf0808f587aa73e0c Signed-off-by: Sudha Bheemanna Signed-off-by: DoHyun Pyun Signed-off-by: Amit Purwar --- include/net/bluetooth/bluetooth.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 0a1e21d..ca445d6 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -37,6 +37,9 @@ #define PF_BLUETOOTH AF_BLUETOOTH #endif +/*To enable Tizen specific fixes */ +#define TIZEN_BT + /* Bluetooth versions */ #define BLUETOOTH_VER_1_1 1 #define BLUETOOTH_VER_1_2 2 -- 2.7.4