From bac9528888a28bcc7ce87f1336c4ec2612be362f Mon Sep 17 00:00:00 2001 From: SeokYeon Hwang Date: Sun, 24 Apr 2016 16:40:58 +0900 Subject: [PATCH] Tizen specific files are merged from "tizen" branch Tizen specific drivers are merged and prepared. "DRM_VIGS", "MARU_CAMERA", "MARU_BACKLIGHT" are excluded from default config because of compilation failure. They should be fixed soon. Change-Id: I63cf7fdf71b627e91fb0131ef34beb1b207ff2ec Signed-off-by: SeokYeon Hwang --- arch/x86/configs/tizen_emul_defconfig | 4150 +++++++++++++++++++++++++++ build-i386.sh | 6 + build-x86_64.sh | 6 + drivers/Kconfig | 3 + drivers/Makefile | 3 + drivers/gpu/Makefile | 3 + drivers/gpu/drm/Kconfig | 3 + drivers/gpu/drm/Makefile | 2 + drivers/gpu/drm/vigs/Kconfig | 24 + drivers/gpu/drm/vigs/Makefile | 26 + drivers/gpu/drm/vigs/main.c | 25 + drivers/gpu/drm/vigs/vigs_comm.c | 523 ++++ drivers/gpu/drm/vigs/vigs_comm.h | 102 + drivers/gpu/drm/vigs/vigs_crtc.c | 368 +++ drivers/gpu/drm/vigs/vigs_crtc.h | 20 + drivers/gpu/drm/vigs/vigs_device.c | 369 +++ drivers/gpu/drm/vigs/vigs_device.h | 107 + drivers/gpu/drm/vigs/vigs_dmabuf.c | 206 ++ drivers/gpu/drm/vigs/vigs_dmabuf.h | 29 + drivers/gpu/drm/vigs/vigs_dp.c | 228 ++ drivers/gpu/drm/vigs/vigs_dp.h | 72 + drivers/gpu/drm/vigs/vigs_driver.c | 269 ++ drivers/gpu/drm/vigs/vigs_driver.h | 10 + drivers/gpu/drm/vigs/vigs_execbuffer.c | 548 ++++ drivers/gpu/drm/vigs/vigs_execbuffer.h | 75 + drivers/gpu/drm/vigs/vigs_fbdev.c | 566 ++++ drivers/gpu/drm/vigs/vigs_fbdev.h | 30 + drivers/gpu/drm/vigs/vigs_fence.c | 305 ++ drivers/gpu/drm/vigs/vigs_fence.h | 123 + drivers/gpu/drm/vigs/vigs_fenceman.c | 65 + drivers/gpu/drm/vigs/vigs_fenceman.h | 47 + drivers/gpu/drm/vigs/vigs_file.c | 37 + drivers/gpu/drm/vigs/vigs_file.h | 19 + drivers/gpu/drm/vigs/vigs_framebuffer.c | 237 ++ drivers/gpu/drm/vigs/vigs_framebuffer.h | 43 + drivers/gpu/drm/vigs/vigs_gem.c | 447 +++ drivers/gpu/drm/vigs/vigs_gem.h | 218 ++ drivers/gpu/drm/vigs/vigs_irq.c | 127 + drivers/gpu/drm/vigs/vigs_irq.h | 12 + drivers/gpu/drm/vigs/vigs_mman.c | 716 +++++ drivers/gpu/drm/vigs/vigs_mman.h | 92 + drivers/gpu/drm/vigs/vigs_output.c | 297 ++ drivers/gpu/drm/vigs/vigs_output.h | 16 + drivers/gpu/drm/vigs/vigs_plane.c | 286 ++ drivers/gpu/drm/vigs/vigs_plane.h | 56 + drivers/gpu/drm/vigs/vigs_protocol.h | 380 +++ drivers/gpu/drm/vigs/vigs_regs.h | 15 + drivers/gpu/drm/vigs/vigs_surface.c | 472 +++ drivers/gpu/drm/vigs/vigs_surface.h | 128 + drivers/gpu/yagl/Kconfig | 19 + drivers/gpu/yagl/Makefile | 9 + drivers/gpu/yagl/debug.h | 14 + drivers/gpu/yagl/main.c | 31 + drivers/gpu/yagl/print.h | 11 + drivers/gpu/yagl/yagl.h | 9 + drivers/gpu/yagl/yagl_driver.c | 840 ++++++ drivers/gpu/yagl/yagl_driver.h | 10 + drivers/gpu/yagl/yagl_ioctl.h | 53 + drivers/maru/Kconfig | 71 + drivers/maru/Makefile | 18 + drivers/maru/maru_bl.c | 366 +++ drivers/maru/maru_brillcodec.c | 1227 ++++++++ drivers/maru/maru_camera.c | 1453 ++++++++++ drivers/maru/maru_jack.c | 514 ++++ drivers/maru/maru_power_supply.c | 410 +++ drivers/maru/maru_virtio_evdi.c | 666 +++++ drivers/maru/maru_virtio_hwkey.c | 281 ++ drivers/maru/maru_virtio_keyboard.c | 291 ++ drivers/maru/maru_virtio_nfc.c | 581 ++++ drivers/maru/maru_virtio_rotary.c | 312 ++ drivers/maru/maru_virtio_tablet.c | 277 ++ drivers/maru/maru_virtio_touchscreen.c | 482 ++++ drivers/maru/maru_virtio_vmodem.c | 554 ++++ drivers/maru/sensors/Makefile | 11 + drivers/maru/sensors/maru_accel.c | 443 +++ drivers/maru/sensors/maru_geo.c | 468 +++ drivers/maru/sensors/maru_gyro.c | 493 ++++ drivers/maru/sensors/maru_haptic.c | 161 ++ drivers/maru/sensors/maru_hrm.c | 364 +++ drivers/maru/sensors/maru_light.c | 458 +++ drivers/maru/sensors/maru_pressure.c | 389 +++ drivers/maru/sensors/maru_proxi.c | 453 +++ drivers/maru/sensors/maru_rotation_vector.c | 359 +++ drivers/maru/sensors/maru_uv.c | 360 +++ drivers/maru/sensors/maru_virtio_sensor.c | 553 ++++ drivers/maru/sensors/maru_virtio_sensor.h | 294 ++ drivers/video/Kconfig | 4 + include/drm/vigs_drm.h | 217 ++ include/linux/pci_ids.h | 7 + include/uapi/linux/virtio_ids.h | 17 + package/build.linux | 35 + package/changelog | 208 ++ package/pkginfo.manifest | 13 + ramfs/busybox.i386 | Bin 0 -> 1881656 bytes ramfs/busybox.x86_64 | Bin 0 -> 2406608 bytes ramfs/init | 114 + ramfs/initramfs.i386 | 17 + ramfs/initramfs.x86_64 | 12 + ramfs/v86d | Bin 0 -> 327400 bytes 99 files changed, 25860 insertions(+) create mode 100644 arch/x86/configs/tizen_emul_defconfig create mode 100755 build-i386.sh create mode 100755 build-x86_64.sh create mode 100644 drivers/gpu/drm/vigs/Kconfig create mode 100644 drivers/gpu/drm/vigs/Makefile create mode 100644 drivers/gpu/drm/vigs/main.c create mode 100644 drivers/gpu/drm/vigs/vigs_comm.c create mode 100644 drivers/gpu/drm/vigs/vigs_comm.h create mode 100644 drivers/gpu/drm/vigs/vigs_crtc.c create mode 100644 drivers/gpu/drm/vigs/vigs_crtc.h create mode 100644 drivers/gpu/drm/vigs/vigs_device.c create mode 100644 drivers/gpu/drm/vigs/vigs_device.h create mode 100644 drivers/gpu/drm/vigs/vigs_dmabuf.c create mode 100644 drivers/gpu/drm/vigs/vigs_dmabuf.h create mode 100644 drivers/gpu/drm/vigs/vigs_dp.c create mode 100644 drivers/gpu/drm/vigs/vigs_dp.h create mode 100644 drivers/gpu/drm/vigs/vigs_driver.c create mode 100644 drivers/gpu/drm/vigs/vigs_driver.h create mode 100644 drivers/gpu/drm/vigs/vigs_execbuffer.c create mode 100644 drivers/gpu/drm/vigs/vigs_execbuffer.h create mode 100644 drivers/gpu/drm/vigs/vigs_fbdev.c create mode 100644 drivers/gpu/drm/vigs/vigs_fbdev.h create mode 100644 drivers/gpu/drm/vigs/vigs_fence.c create mode 100644 drivers/gpu/drm/vigs/vigs_fence.h create mode 100644 drivers/gpu/drm/vigs/vigs_fenceman.c create mode 100644 drivers/gpu/drm/vigs/vigs_fenceman.h create mode 100644 drivers/gpu/drm/vigs/vigs_file.c create mode 100644 drivers/gpu/drm/vigs/vigs_file.h create mode 100644 drivers/gpu/drm/vigs/vigs_framebuffer.c create mode 100644 drivers/gpu/drm/vigs/vigs_framebuffer.h create mode 100644 drivers/gpu/drm/vigs/vigs_gem.c create mode 100644 drivers/gpu/drm/vigs/vigs_gem.h create mode 100644 drivers/gpu/drm/vigs/vigs_irq.c create mode 100644 drivers/gpu/drm/vigs/vigs_irq.h create mode 100644 drivers/gpu/drm/vigs/vigs_mman.c create mode 100644 drivers/gpu/drm/vigs/vigs_mman.h create mode 100644 drivers/gpu/drm/vigs/vigs_output.c create mode 100644 drivers/gpu/drm/vigs/vigs_output.h create mode 100644 drivers/gpu/drm/vigs/vigs_plane.c create mode 100644 drivers/gpu/drm/vigs/vigs_plane.h create mode 100644 drivers/gpu/drm/vigs/vigs_protocol.h create mode 100644 drivers/gpu/drm/vigs/vigs_regs.h create mode 100644 drivers/gpu/drm/vigs/vigs_surface.c create mode 100644 drivers/gpu/drm/vigs/vigs_surface.h create mode 100644 drivers/gpu/yagl/Kconfig create mode 100644 drivers/gpu/yagl/Makefile create mode 100644 drivers/gpu/yagl/debug.h create mode 100644 drivers/gpu/yagl/main.c create mode 100644 drivers/gpu/yagl/print.h create mode 100644 drivers/gpu/yagl/yagl.h create mode 100644 drivers/gpu/yagl/yagl_driver.c create mode 100644 drivers/gpu/yagl/yagl_driver.h create mode 100644 drivers/gpu/yagl/yagl_ioctl.h create mode 100644 drivers/maru/Kconfig create mode 100644 drivers/maru/Makefile create mode 100644 drivers/maru/maru_bl.c create mode 100644 drivers/maru/maru_brillcodec.c create mode 100644 drivers/maru/maru_camera.c create mode 100644 drivers/maru/maru_jack.c create mode 100644 drivers/maru/maru_power_supply.c create mode 100644 drivers/maru/maru_virtio_evdi.c create mode 100644 drivers/maru/maru_virtio_hwkey.c create mode 100644 drivers/maru/maru_virtio_keyboard.c create mode 100644 drivers/maru/maru_virtio_nfc.c create mode 100644 drivers/maru/maru_virtio_rotary.c create mode 100644 drivers/maru/maru_virtio_tablet.c create mode 100644 drivers/maru/maru_virtio_touchscreen.c create mode 100644 drivers/maru/maru_virtio_vmodem.c create mode 100644 drivers/maru/sensors/Makefile create mode 100644 drivers/maru/sensors/maru_accel.c create mode 100644 drivers/maru/sensors/maru_geo.c create mode 100644 drivers/maru/sensors/maru_gyro.c create mode 100644 drivers/maru/sensors/maru_haptic.c create mode 100644 drivers/maru/sensors/maru_hrm.c create mode 100644 drivers/maru/sensors/maru_light.c create mode 100644 drivers/maru/sensors/maru_pressure.c create mode 100644 drivers/maru/sensors/maru_proxi.c create mode 100644 drivers/maru/sensors/maru_rotation_vector.c create mode 100644 drivers/maru/sensors/maru_uv.c create mode 100644 drivers/maru/sensors/maru_virtio_sensor.c create mode 100644 drivers/maru/sensors/maru_virtio_sensor.h create mode 100644 include/drm/vigs_drm.h create mode 100755 package/build.linux create mode 100644 package/changelog create mode 100644 package/pkginfo.manifest create mode 100755 ramfs/busybox.i386 create mode 100755 ramfs/busybox.x86_64 create mode 100644 ramfs/init create mode 100755 ramfs/initramfs.i386 create mode 100755 ramfs/initramfs.x86_64 create mode 100755 ramfs/v86d diff --git a/arch/x86/configs/tizen_emul_defconfig b/arch/x86/configs/tizen_emul_defconfig new file mode 100644 index 0000000..b19624f --- /dev/null +++ b/arch/x86/configs/tizen_emul_defconfig @@ -0,0 +1,4150 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/x86 4.4.8 Kernel Configuration +# +# CONFIG_64BIT is not set +CONFIG_X86_32=y +CONFIG_X86=y +CONFIG_INSTRUCTION_DECODER=y +CONFIG_PERF_EVENTS_INTEL_UNCORE=y +CONFIG_OUTPUT_FORMAT="elf32-i386" +CONFIG_ARCH_DEFCONFIG="arch/x86/configs/i386_defconfig" +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_MMU=y +CONFIG_NEED_SG_DMA_LENGTH=y +CONFIG_GENERIC_ISA_DMA=y +CONFIG_GENERIC_BUG=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_ARCH_MAY_HAVE_PC_FDC=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_ARCH_HAS_CPU_RELAX=y +CONFIG_ARCH_HAS_CACHE_LINE_SIZE=y +CONFIG_HAVE_SETUP_PER_CPU_AREA=y +CONFIG_NEED_PER_CPU_EMBED_FIRST_CHUNK=y +CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK=y +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARCH_WANT_HUGE_PMD_SHARE=y +CONFIG_ARCH_WANT_GENERAL_HUGETLB=y +CONFIG_ARCH_SUPPORTS_OPTIMIZED_INLINING=y +CONFIG_ARCH_SUPPORTS_DEBUG_PAGEALLOC=y +CONFIG_X86_32_SMP=y +CONFIG_X86_32_LAZY_GS=y +CONFIG_ARCH_HWEIGHT_CFLAGS="-fcall-saved-ecx -fcall-saved-edx" +CONFIG_ARCH_SUPPORTS_UPROBES=y +CONFIG_FIX_EARLYCON_MEM=y +CONFIG_PGTABLE_LEVELS=2 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_IRQ_WORK=y +CONFIG_BUILDTIME_EXTABLE_SORT=y + +# +# General setup +# +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +# CONFIG_COMPILE_TEST is not set +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_BZIP2=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +CONFIG_HAVE_KERNEL_LZ4=y +CONFIG_KERNEL_GZIP=y +# CONFIG_KERNEL_BZIP2 is not set +# CONFIG_KERNEL_LZMA is not set +# CONFIG_KERNEL_XZ is not set +# CONFIG_KERNEL_LZO is not set +# CONFIG_KERNEL_LZ4 is not set +CONFIG_DEFAULT_HOSTNAME="(none)" +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +CONFIG_POSIX_MQUEUE=y +CONFIG_POSIX_MQUEUE_SYSCTL=y +CONFIG_CROSS_MEMORY_ATTACH=y +CONFIG_FHANDLE=y +CONFIG_USELIB=y +CONFIG_AUDIT=y +CONFIG_HAVE_ARCH_AUDITSYSCALL=y +CONFIG_AUDITSYSCALL=y +CONFIG_AUDIT_WATCH=y +CONFIG_AUDIT_TREE=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_PENDING_IRQ=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_DOMAIN_HIERARCHY=y +CONFIG_GENERIC_MSI_IRQ=y +CONFIG_GENERIC_MSI_IRQ_DOMAIN=y +# CONFIG_IRQ_DOMAIN_DEBUG is not set +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_SPARSE_IRQ=y +CONFIG_CLOCKSOURCE_WATCHDOG=y +CONFIG_ARCH_CLOCKSOURCE_DATA=y +CONFIG_CLOCKSOURCE_VALIDATE_LAST_CYCLE=y +CONFIG_GENERIC_TIME_VSYSCALL=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y +CONFIG_GENERIC_CLOCKEVENTS_MIN_ADJUST=y +CONFIG_GENERIC_CMOS_UPDATE=y + +# +# Timers subsystem +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ_COMMON=y +# CONFIG_HZ_PERIODIC is not set +CONFIG_NO_HZ_IDLE=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y + +# +# CPU/Task time and stats accounting +# +CONFIG_TICK_CPU_ACCOUNTING=y +# CONFIG_IRQ_TIME_ACCOUNTING is not set +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y + +# +# RCU Subsystem +# +CONFIG_PREEMPT_RCU=y +# CONFIG_RCU_EXPERT is not set +CONFIG_SRCU=y +# CONFIG_TASKS_RCU is not set +CONFIG_RCU_STALL_COMMON=y +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_RCU_EXPEDITE_BOOT is not set +# CONFIG_BUILD_BIN2C is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=18 +CONFIG_LOG_CPU_MAX_BUF_SHIFT=12 +CONFIG_HAVE_UNSTABLE_SCHED_CLOCK=y +CONFIG_ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH=y +CONFIG_CGROUPS=y +# CONFIG_CGROUP_DEBUG is not set +CONFIG_CGROUP_FREEZER=y +# CONFIG_CGROUP_PIDS is not set +CONFIG_CGROUP_DEVICE=y +CONFIG_CPUSETS=y +CONFIG_PROC_PID_CPUSET=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_PAGE_COUNTER=y +CONFIG_MEMCG=y +CONFIG_MEMCG_SWAP=y +CONFIG_MEMCG_SWAP_ENABLED=y +CONFIG_MEMCG_KMEM=y +# CONFIG_CGROUP_HUGETLB is not set +# CONFIG_CGROUP_PERF is not set +CONFIG_CGROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +# CONFIG_CFS_BANDWIDTH is not set +# CONFIG_RT_GROUP_SCHED is not set +CONFIG_BLK_CGROUP=y +# CONFIG_DEBUG_BLK_CGROUP is not set +CONFIG_CGROUP_WRITEBACK=y +# CONFIG_CHECKPOINT_RESTORE is not set +CONFIG_NAMESPACES=y +CONFIG_UTS_NS=y +CONFIG_IPC_NS=y +CONFIG_USER_NS=y +CONFIG_PID_NS=y +CONFIG_NET_NS=y +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SYSFS_DEPRECATED is not set +CONFIG_RELAY=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="ramfs/initramfs.i386" +CONFIG_INITRAMFS_ROOT_UID=0 +CONFIG_INITRAMFS_ROOT_GID=0 +CONFIG_RD_GZIP=y +CONFIG_RD_BZIP2=y +CONFIG_RD_LZMA=y +CONFIG_RD_XZ=y +CONFIG_RD_LZO=y +CONFIG_RD_LZ4=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_HAVE_UID16=y +CONFIG_SYSCTL_EXCEPTION_TRACE=y +CONFIG_HAVE_PCSPKR_PLATFORM=y +CONFIG_BPF=y +# CONFIG_EXPERT is not set +CONFIG_UID16=y +CONFIG_MULTIUSER=y +CONFIG_SGETMASK_SYSCALL=y +CONFIG_SYSFS_SYSCALL=y +# CONFIG_SYSCTL_SYSCALL is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_PCSPKR_PLATFORM=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +# CONFIG_BPF_SYSCALL is not set +CONFIG_SHMEM=y +CONFIG_AIO=y +CONFIG_ADVISE_SYSCALLS=y +# CONFIG_USERFAULTFD is not set +CONFIG_PCI_QUIRKS=y +CONFIG_MEMBARRIER=y +# CONFIG_EMBEDDED is not set +CONFIG_HAVE_PERF_EVENTS=y + +# +# Kernel Performance Events And Counters +# +CONFIG_PERF_EVENTS=y +# CONFIG_DEBUG_PERF_USE_VMALLOC is not set +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_SLUB_DEBUG=y +# CONFIG_COMPAT_BRK is not set +# CONFIG_SLAB is not set +CONFIG_SLUB=y +CONFIG_SLUB_CPU_PARTIAL=y +# CONFIG_SYSTEM_DATA_VERIFICATION is not set +CONFIG_PROFILING=y +CONFIG_TRACEPOINTS=y +CONFIG_KEXEC_CORE=y +CONFIG_OPROFILE=y +# CONFIG_OPROFILE_EVENT_MULTIPLEX is not set +CONFIG_HAVE_OPROFILE=y +CONFIG_OPROFILE_NMI_TIMER=y +CONFIG_KPROBES=y +# CONFIG_JUMP_LABEL is not set +CONFIG_KPROBES_ON_FTRACE=y +CONFIG_UPROBES=y +# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set +CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y +CONFIG_ARCH_USE_BUILTIN_BSWAP=y +CONFIG_KRETPROBES=y +CONFIG_HAVE_IOREMAP_PROT=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_OPTPROBES=y +CONFIG_HAVE_KPROBES_ON_FTRACE=y +CONFIG_HAVE_ARCH_TRACEHOOK=y +CONFIG_HAVE_DMA_ATTRS=y +CONFIG_HAVE_DMA_CONTIGUOUS=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_ARCH_WANTS_DYNAMIC_TASK_STRUCT=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_HW_BREAKPOINT=y +CONFIG_HAVE_MIXED_BREAKPOINTS_REGS=y +CONFIG_HAVE_USER_RETURN_NOTIFIER=y +CONFIG_HAVE_PERF_EVENTS_NMI=y +CONFIG_HAVE_PERF_REGS=y +CONFIG_HAVE_PERF_USER_STACK_DUMP=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y +CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG=y +CONFIG_HAVE_ALIGNED_STRUCT_PAGE=y +CONFIG_HAVE_CMPXCHG_LOCAL=y +CONFIG_HAVE_CMPXCHG_DOUBLE=y +CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y +CONFIG_HAVE_ARCH_SECCOMP_FILTER=y +CONFIG_HAVE_CC_STACKPROTECTOR=y +# CONFIG_CC_STACKPROTECTOR is not set +CONFIG_CC_STACKPROTECTOR_NONE=y +# CONFIG_CC_STACKPROTECTOR_REGULAR is not set +# CONFIG_CC_STACKPROTECTOR_STRONG is not set +CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y +CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE=y +CONFIG_MODULES_USE_ELF_REL=y +CONFIG_ARCH_HAS_ELF_RANDOMIZE=y +CONFIG_HAVE_COPY_THREAD_TLS=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_OLD_SIGSUSPEND3=y +CONFIG_OLD_SIGACTION=y + +# +# GCOV-based kernel profiling +# +# CONFIG_GCOV_KERNEL is not set +CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +# CONFIG_MODULE_SIG is not set +# CONFIG_MODULE_COMPRESS is not set +CONFIG_MODULES_TREE_LOOKUP=y +CONFIG_BLOCK=y +CONFIG_LBDAF=y +CONFIG_BLK_DEV_BSG=y +# CONFIG_BLK_DEV_BSGLIB is not set +# CONFIG_BLK_DEV_INTEGRITY is not set +CONFIG_BLK_DEV_THROTTLING=y +# CONFIG_BLK_CMDLINE_PARSER is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_AIX_PARTITION is not set +CONFIG_OSF_PARTITION=y +CONFIG_AMIGA_PARTITION=y +# CONFIG_ATARI_PARTITION is not set +CONFIG_MAC_PARTITION=y +CONFIG_MSDOS_PARTITION=y +CONFIG_BSD_DISKLABEL=y +CONFIG_MINIX_SUBPARTITION=y +CONFIG_SOLARIS_X86_PARTITION=y +CONFIG_UNIXWARE_DISKLABEL=y +# CONFIG_LDM_PARTITION is not set +CONFIG_SGI_PARTITION=y +# CONFIG_ULTRIX_PARTITION is not set +CONFIG_SUN_PARTITION=y +CONFIG_KARMA_PARTITION=y +CONFIG_EFI_PARTITION=y +# CONFIG_SYSV68_PARTITION is not set +# CONFIG_CMDLINE_PARTITION is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_CFQ_GROUP_IOSCHED=y +# CONFIG_DEFAULT_DEADLINE is not set +# CONFIG_DEFAULT_CFQ is not set +CONFIG_DEFAULT_NOOP=y +CONFIG_DEFAULT_IOSCHED="noop" +CONFIG_UNINLINE_SPIN_UNLOCK=y +CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y +CONFIG_MUTEX_SPIN_ON_OWNER=y +CONFIG_RWSEM_SPIN_ON_OWNER=y +CONFIG_LOCK_SPIN_ON_OWNER=y +CONFIG_ARCH_USE_QUEUED_SPINLOCKS=y +CONFIG_QUEUED_SPINLOCKS=y +CONFIG_ARCH_USE_QUEUED_RWLOCKS=y +CONFIG_QUEUED_RWLOCKS=y +CONFIG_FREEZER=y + +# +# Processor type and features +# +CONFIG_ZONE_DMA=y +CONFIG_SMP=y +CONFIG_X86_FEATURE_NAMES=y +CONFIG_X86_MPPARSE=y +# CONFIG_X86_BIGSMP is not set +CONFIG_X86_EXTENDED_PLATFORM=y +# CONFIG_X86_GOLDFISH is not set +# CONFIG_X86_INTEL_MID is not set +# CONFIG_X86_INTEL_QUARK is not set +# CONFIG_X86_INTEL_LPSS is not set +# CONFIG_X86_AMD_PLATFORM_DEVICE is not set +# CONFIG_IOSF_MBI is not set +# CONFIG_X86_RDC321X is not set +# CONFIG_X86_32_NON_STANDARD is not set +CONFIG_X86_SUPPORTS_MEMORY_FAILURE=y +# CONFIG_X86_32_IRIS is not set +CONFIG_SCHED_OMIT_FRAME_POINTER=y +# CONFIG_HYPERVISOR_GUEST is not set +CONFIG_NO_BOOTMEM=y +# CONFIG_M486 is not set +# CONFIG_M586 is not set +# CONFIG_M586TSC is not set +# CONFIG_M586MMX is not set +CONFIG_M686=y +# CONFIG_MPENTIUMII is not set +# CONFIG_MPENTIUMIII is not set +# CONFIG_MPENTIUMM is not set +# CONFIG_MPENTIUM4 is not set +# CONFIG_MK6 is not set +# CONFIG_MK7 is not set +# CONFIG_MK8 is not set +# CONFIG_MCRUSOE is not set +# CONFIG_MEFFICEON is not set +# CONFIG_MWINCHIPC6 is not set +# CONFIG_MWINCHIP3D is not set +# CONFIG_MELAN is not set +# CONFIG_MGEODEGX1 is not set +# CONFIG_MGEODE_LX is not set +# CONFIG_MCYRIXIII is not set +# CONFIG_MVIAC3_2 is not set +# CONFIG_MVIAC7 is not set +# CONFIG_MCORE2 is not set +# CONFIG_MATOM is not set +CONFIG_X86_GENERIC=y +CONFIG_X86_INTERNODE_CACHE_SHIFT=6 +CONFIG_X86_L1_CACHE_SHIFT=6 +# CONFIG_X86_PPRO_FENCE is not set +CONFIG_X86_INTEL_USERCOPY=y +CONFIG_X86_USE_PPRO_CHECKSUM=y +CONFIG_X86_TSC=y +CONFIG_X86_CMPXCHG64=y +CONFIG_X86_CMOV=y +CONFIG_X86_MINIMUM_CPU_FAMILY=5 +CONFIG_X86_DEBUGCTLMSR=y +CONFIG_CPU_SUP_INTEL=y +CONFIG_CPU_SUP_AMD=y +CONFIG_CPU_SUP_CENTAUR=y +CONFIG_CPU_SUP_TRANSMETA_32=y +# CONFIG_HPET_TIMER is not set +CONFIG_DMI=y +CONFIG_NR_CPUS=8 +# CONFIG_SCHED_SMT is not set +CONFIG_SCHED_MC=y +# CONFIG_PREEMPT_NONE is not set +# CONFIG_PREEMPT_VOLUNTARY is not set +CONFIG_PREEMPT=y +CONFIG_PREEMPT_COUNT=y +CONFIG_X86_LOCAL_APIC=y +CONFIG_X86_IO_APIC=y +# CONFIG_X86_REROUTE_FOR_BROKEN_BOOT_IRQS is not set +CONFIG_X86_MCE=y +CONFIG_X86_MCE_INTEL=y +CONFIG_X86_MCE_AMD=y +# CONFIG_X86_ANCIENT_MCE is not set +CONFIG_X86_MCE_THRESHOLD=y +# CONFIG_X86_MCE_INJECT is not set +CONFIG_X86_THERMAL_VECTOR=y +# CONFIG_X86_LEGACY_VM86 is not set +# CONFIG_VM86 is not set +CONFIG_X86_16BIT=y +CONFIG_X86_ESPFIX32=y +# CONFIG_TOSHIBA is not set +# CONFIG_I8K is not set +CONFIG_X86_REBOOTFIXUPS=y +CONFIG_MICROCODE=y +CONFIG_MICROCODE_INTEL=y +CONFIG_MICROCODE_AMD=y +CONFIG_MICROCODE_OLD_INTERFACE=y +CONFIG_X86_MSR=y +CONFIG_X86_CPUID=y +# CONFIG_NOHIGHMEM is not set +CONFIG_HIGHMEM4G=y +# CONFIG_HIGHMEM64G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_HIGHMEM=y +CONFIG_ARCH_FLATMEM_ENABLE=y +CONFIG_ARCH_SPARSEMEM_ENABLE=y +CONFIG_ARCH_SELECT_MEMORY_MODEL=y +CONFIG_ILLEGAL_POINTER_VALUE=0 +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_SPARSEMEM_STATIC=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_HAVE_MEMBLOCK_NODE_MAP=y +CONFIG_ARCH_DISCARD_MEMBLOCK=y +# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set +CONFIG_SPLIT_PTLOCK_CPUS=4 +CONFIG_MEMORY_BALLOON=y +# CONFIG_COMPACTION is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=1 +CONFIG_BOUNCE=y +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_ARCH_SUPPORTS_MEMORY_FAILURE=y +# CONFIG_MEMORY_FAILURE is not set +# CONFIG_TRANSPARENT_HUGEPAGE is not set +# CONFIG_CLEANCACHE is not set +# CONFIG_FRONTSWAP is not set +# CONFIG_CMA is not set +# CONFIG_ZPOOL is not set +# CONFIG_ZBUD is not set +# CONFIG_ZSMALLOC is not set +CONFIG_GENERIC_EARLY_IOREMAP=y +CONFIG_ARCH_SUPPORTS_DEFERRED_STRUCT_PAGE_INIT=y +# CONFIG_IDLE_PAGE_TRACKING is not set +CONFIG_HIGHPTE=y +CONFIG_X86_CHECK_BIOS_CORRUPTION=y +CONFIG_X86_BOOTPARAM_MEMORY_CORRUPTION_CHECK=y +CONFIG_X86_RESERVE_LOW=64 +# CONFIG_MATH_EMULATION is not set +CONFIG_MTRR=y +# CONFIG_MTRR_SANITIZER is not set +CONFIG_X86_PAT=y +CONFIG_ARCH_USES_PG_UNCACHED=y +CONFIG_ARCH_RANDOM=y +CONFIG_X86_SMAP=y +# CONFIG_X86_INTEL_MPX is not set +CONFIG_EFI=y +# CONFIG_EFI_STUB is not set +# CONFIG_SECCOMP is not set +CONFIG_HZ_100=y +# CONFIG_HZ_250 is not set +# CONFIG_HZ_300 is not set +# CONFIG_HZ_1000 is not set +CONFIG_HZ=100 +CONFIG_SCHED_HRTICK=y +CONFIG_KEXEC=y +CONFIG_CRASH_DUMP=y +# CONFIG_KEXEC_JUMP is not set +CONFIG_PHYSICAL_START=0x1000000 +CONFIG_RELOCATABLE=y +# CONFIG_RANDOMIZE_BASE is not set +CONFIG_X86_NEED_RELOCS=y +CONFIG_PHYSICAL_ALIGN=0x1000000 +CONFIG_HOTPLUG_CPU=y +# CONFIG_BOOTPARAM_HOTPLUG_CPU0 is not set +# CONFIG_DEBUG_HOTPLUG_CPU0 is not set +# CONFIG_COMPAT_VDSO is not set +# CONFIG_CMDLINE_BOOL is not set +CONFIG_MODIFY_LDT_SYSCALL=y +CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y + +# +# Power management and ACPI options +# +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HIBERNATE_CALLBACKS=y +CONFIG_HIBERNATION=y +CONFIG_PM_STD_PARTITION="" +CONFIG_PM_SLEEP=y +CONFIG_PM_SLEEP_SMP=y +# CONFIG_PM_AUTOSLEEP is not set +# CONFIG_PM_WAKELOCKS is not set +CONFIG_PM=y +CONFIG_PM_DEBUG=y +# CONFIG_PM_ADVANCED_DEBUG is not set +# CONFIG_PM_TEST_SUSPEND is not set +CONFIG_PM_SLEEP_DEBUG=y +CONFIG_PM_TRACE=y +CONFIG_PM_TRACE_RTC=y +# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set +CONFIG_ACPI=y +CONFIG_ACPI_LEGACY_TABLES_LOOKUP=y +CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC=y +CONFIG_ACPI_SYSTEM_POWER_STATES_SUPPORT=y +# CONFIG_ACPI_DEBUGGER is not set +CONFIG_ACPI_SLEEP=y +# CONFIG_ACPI_PROCFS_POWER is not set +CONFIG_ACPI_REV_OVERRIDE_POSSIBLE=y +# CONFIG_ACPI_EC_DEBUGFS is not set +# CONFIG_ACPI_AC is not set +# CONFIG_ACPI_BATTERY is not set +CONFIG_ACPI_BUTTON=y +CONFIG_ACPI_VIDEO=y +CONFIG_ACPI_FAN=y +CONFIG_ACPI_DOCK=y +CONFIG_ACPI_CPU_FREQ_PSS=y +CONFIG_ACPI_PROCESSOR_IDLE=y +CONFIG_ACPI_PROCESSOR=y +CONFIG_ACPI_HOTPLUG_CPU=y +# CONFIG_ACPI_PROCESSOR_AGGREGATOR is not set +CONFIG_ACPI_THERMAL=y +# CONFIG_ACPI_CUSTOM_DSDT is not set +# CONFIG_ACPI_INITRD_TABLE_OVERRIDE is not set +# CONFIG_ACPI_DEBUG is not set +CONFIG_ACPI_PCI_SLOT=y +CONFIG_X86_PM_TIMER=y +CONFIG_ACPI_CONTAINER=y +CONFIG_ACPI_HOTPLUG_IOAPIC=y +# CONFIG_ACPI_SBS is not set +# CONFIG_ACPI_HED is not set +# CONFIG_ACPI_CUSTOM_METHOD is not set +# CONFIG_ACPI_BGRT is not set +# CONFIG_ACPI_REDUCED_HARDWARE_ONLY is not set +CONFIG_HAVE_ACPI_APEI=y +CONFIG_HAVE_ACPI_APEI_NMI=y +# CONFIG_ACPI_APEI is not set +# CONFIG_ACPI_EXTLOG is not set +# CONFIG_PMIC_OPREGION is not set +# CONFIG_SFI is not set +# CONFIG_APM is not set + +# +# CPU Frequency scaling +# +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_GOV_COMMON=y +# CONFIG_CPU_FREQ_STAT is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set + +# +# CPU frequency scaling drivers +# +# CONFIG_X86_INTEL_PSTATE is not set +# CONFIG_X86_PCC_CPUFREQ is not set +CONFIG_X86_ACPI_CPUFREQ=y +CONFIG_X86_ACPI_CPUFREQ_CPB=y +# CONFIG_X86_POWERNOW_K6 is not set +# CONFIG_X86_POWERNOW_K7 is not set +# CONFIG_X86_POWERNOW_K8 is not set +# CONFIG_X86_AMD_FREQ_SENSITIVITY is not set +# CONFIG_X86_GX_SUSPMOD is not set +# CONFIG_X86_SPEEDSTEP_CENTRINO is not set +# CONFIG_X86_SPEEDSTEP_ICH is not set +# CONFIG_X86_SPEEDSTEP_SMI is not set +# CONFIG_X86_P4_CLOCKMOD is not set +# CONFIG_X86_CPUFREQ_NFORCE2 is not set +# CONFIG_X86_LONGRUN is not set +# CONFIG_X86_LONGHAUL is not set +# CONFIG_X86_E_POWERSAVER is not set + +# +# shared options +# +# CONFIG_X86_SPEEDSTEP_LIB is not set + +# +# CPU Idle +# +CONFIG_CPU_IDLE=y +CONFIG_CPU_IDLE_GOV_LADDER=y +CONFIG_CPU_IDLE_GOV_MENU=y +# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set +# CONFIG_INTEL_IDLE is not set + +# +# Bus options (PCI etc.) +# +CONFIG_PCI=y +# CONFIG_PCI_GOBIOS is not set +# CONFIG_PCI_GOMMCONFIG is not set +# CONFIG_PCI_GODIRECT is not set +CONFIG_PCI_GOANY=y +CONFIG_PCI_BIOS=y +CONFIG_PCI_DIRECT=y +CONFIG_PCI_MMCONFIG=y +CONFIG_PCI_DOMAINS=y +CONFIG_PCIEPORTBUS=y +# CONFIG_HOTPLUG_PCI_PCIE is not set +CONFIG_PCIEAER=y +# CONFIG_PCIE_ECRC is not set +# CONFIG_PCIEAER_INJECT is not set +CONFIG_PCIEASPM=y +# CONFIG_PCIEASPM_DEBUG is not set +CONFIG_PCIEASPM_DEFAULT=y +# CONFIG_PCIEASPM_POWERSAVE is not set +# CONFIG_PCIEASPM_PERFORMANCE is not set +CONFIG_PCIE_PME=y +CONFIG_PCI_MSI=y +CONFIG_PCI_MSI_IRQ_DOMAIN=y +# CONFIG_PCI_DEBUG is not set +# CONFIG_PCI_REALLOC_ENABLE_AUTO is not set +# CONFIG_PCI_STUB is not set +CONFIG_HT_IRQ=y +# CONFIG_PCI_IOV is not set +# CONFIG_PCI_PRI is not set +# CONFIG_PCI_PASID is not set +CONFIG_PCI_LABEL=y + +# +# PCI host controller drivers +# +CONFIG_ISA_DMA_API=y +# CONFIG_ISA is not set +# CONFIG_SCx200 is not set +# CONFIG_OLPC is not set +# CONFIG_ALIX is not set +# CONFIG_NET5501 is not set +# CONFIG_GEOS is not set +CONFIG_AMD_NB=y +CONFIG_PCCARD=y +CONFIG_PCMCIA=y +CONFIG_PCMCIA_LOAD_CIS=y +CONFIG_CARDBUS=y + +# +# PC-card bridges +# +CONFIG_YENTA=y +CONFIG_YENTA_O2=y +CONFIG_YENTA_RICOH=y +CONFIG_YENTA_TI=y +CONFIG_YENTA_ENE_TUNE=y +CONFIG_YENTA_TOSHIBA=y +# CONFIG_PD6729 is not set +# CONFIG_I82092 is not set +CONFIG_PCCARD_NONSTATIC=y +CONFIG_HOTPLUG_PCI=y +# CONFIG_HOTPLUG_PCI_COMPAQ is not set +# CONFIG_HOTPLUG_PCI_IBM is not set +CONFIG_HOTPLUG_PCI_ACPI=y +# CONFIG_HOTPLUG_PCI_ACPI_IBM is not set +# CONFIG_HOTPLUG_PCI_CPCI is not set +# CONFIG_HOTPLUG_PCI_SHPC is not set +# CONFIG_RAPIDIO is not set +# CONFIG_X86_SYSFB is not set + +# +# Executable file formats / Emulations +# +CONFIG_BINFMT_ELF=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +CONFIG_BINFMT_SCRIPT=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +CONFIG_BINFMT_MISC=y +CONFIG_COREDUMP=y +CONFIG_HAVE_ATOMIC_IOMAP=y +CONFIG_PMC_ATOM=y +CONFIG_NET=y +CONFIG_NET_INGRESS=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_DIAG is not set +CONFIG_UNIX=y +# CONFIG_UNIX_DIAG is not set +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +# CONFIG_IP_FIB_TRIE_STATS is not set +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +CONFIG_NET_IP_TUNNEL=y +CONFIG_IP_MROUTE=y +# CONFIG_IP_MROUTE_MULTIPLE_TABLES is not set +CONFIG_IP_PIMSM_V1=y +CONFIG_IP_PIMSM_V2=y +CONFIG_SYN_COOKIES=y +# CONFIG_NET_UDP_TUNNEL is not set +# CONFIG_NET_FOU is not set +# CONFIG_NET_FOU_IP_TUNNELS is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +CONFIG_INET_TUNNEL=y +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +CONFIG_INET_LRO=y +# CONFIG_INET_DIAG is not set +CONFIG_TCP_CONG_ADVANCED=y +# CONFIG_TCP_CONG_BIC is not set +CONFIG_TCP_CONG_CUBIC=y +# CONFIG_TCP_CONG_WESTWOOD is not set +# CONFIG_TCP_CONG_HTCP is not set +# CONFIG_TCP_CONG_HSTCP is not set +# CONFIG_TCP_CONG_HYBLA is not set +# CONFIG_TCP_CONG_VEGAS is not set +# CONFIG_TCP_CONG_SCALABLE is not set +# CONFIG_TCP_CONG_LP is not set +# CONFIG_TCP_CONG_VENO is not set +# CONFIG_TCP_CONG_YEAH is not set +# CONFIG_TCP_CONG_ILLINOIS is not set +# CONFIG_TCP_CONG_DCTCP is not set +# CONFIG_TCP_CONG_CDG is not set +CONFIG_DEFAULT_CUBIC=y +# CONFIG_DEFAULT_RENO is not set +CONFIG_DEFAULT_TCP_CONG="cubic" +CONFIG_TCP_MD5SIG=y +CONFIG_IPV6=y +# CONFIG_IPV6_ROUTER_PREF is not set +# CONFIG_IPV6_OPTIMISTIC_DAD is not set +# CONFIG_INET6_AH is not set +# CONFIG_INET6_ESP is not set +# CONFIG_INET6_IPCOMP is not set +# CONFIG_IPV6_MIP6 is not set +# CONFIG_IPV6_ILA is not set +# CONFIG_INET6_XFRM_TUNNEL is not set +# CONFIG_INET6_TUNNEL is not set +CONFIG_INET6_XFRM_MODE_TRANSPORT=y +CONFIG_INET6_XFRM_MODE_TUNNEL=y +CONFIG_INET6_XFRM_MODE_BEET=y +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +# CONFIG_IPV6_VTI is not set +CONFIG_IPV6_SIT=y +# CONFIG_IPV6_SIT_6RD is not set +CONFIG_IPV6_NDISC_NODETYPE=y +# CONFIG_IPV6_TUNNEL is not set +# CONFIG_IPV6_GRE is not set +# CONFIG_IPV6_MULTIPLE_TABLES is not set +# CONFIG_IPV6_MROUTE is not set +CONFIG_NETLABEL=y +CONFIG_NETWORK_SECMARK=y +CONFIG_NET_PTP_CLASSIFY=y +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y +CONFIG_BRIDGE_NETFILTER=y + +# +# Core Netfilter Configuration +# +CONFIG_NETFILTER_INGRESS=y +CONFIG_NETFILTER_NETLINK=y +CONFIG_NETFILTER_NETLINK_ACCT=y +# CONFIG_NETFILTER_NETLINK_QUEUE is not set +CONFIG_NETFILTER_NETLINK_LOG=y +CONFIG_NF_CONNTRACK=y +# CONFIG_NF_CONNTRACK_MARK is not set +CONFIG_NF_CONNTRACK_SECMARK=y +CONFIG_NF_CONNTRACK_PROCFS=y +# CONFIG_NF_CONNTRACK_EVENTS is not set +# CONFIG_NF_CONNTRACK_TIMEOUT is not set +# CONFIG_NF_CONNTRACK_TIMESTAMP is not set +# CONFIG_NF_CT_PROTO_DCCP is not set +# CONFIG_NF_CT_PROTO_SCTP is not set +# CONFIG_NF_CT_PROTO_UDPLITE is not set +# CONFIG_NF_CONNTRACK_AMANDA is not set +CONFIG_NF_CONNTRACK_FTP=y +# CONFIG_NF_CONNTRACK_H323 is not set +CONFIG_NF_CONNTRACK_IRC=y +# CONFIG_NF_CONNTRACK_NETBIOS_NS is not set +# CONFIG_NF_CONNTRACK_SNMP is not set +# CONFIG_NF_CONNTRACK_PPTP is not set +# CONFIG_NF_CONNTRACK_SANE is not set +CONFIG_NF_CONNTRACK_SIP=y +# CONFIG_NF_CONNTRACK_TFTP is not set +CONFIG_NF_CT_NETLINK=y +# CONFIG_NF_CT_NETLINK_TIMEOUT is not set +# CONFIG_NETFILTER_NETLINK_GLUE_CT is not set +CONFIG_NF_NAT=y +CONFIG_NF_NAT_NEEDED=y +# CONFIG_NF_NAT_AMANDA is not set +CONFIG_NF_NAT_FTP=y +CONFIG_NF_NAT_IRC=y +CONFIG_NF_NAT_SIP=y +# CONFIG_NF_NAT_TFTP is not set +# CONFIG_NF_NAT_REDIRECT is not set +# CONFIG_NF_TABLES is not set +CONFIG_NETFILTER_XTABLES=y + +# +# Xtables combined modules +# +# CONFIG_NETFILTER_XT_MARK is not set +# CONFIG_NETFILTER_XT_CONNMARK is not set + +# +# Xtables targets +# +# CONFIG_NETFILTER_XT_TARGET_AUDIT is not set +# CONFIG_NETFILTER_XT_TARGET_CHECKSUM is not set +# CONFIG_NETFILTER_XT_TARGET_CLASSIFY is not set +# CONFIG_NETFILTER_XT_TARGET_CONNMARK is not set +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y +# CONFIG_NETFILTER_XT_TARGET_DSCP is not set +# CONFIG_NETFILTER_XT_TARGET_HL is not set +# CONFIG_NETFILTER_XT_TARGET_HMARK is not set +# CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set +# CONFIG_NETFILTER_XT_TARGET_LED is not set +# CONFIG_NETFILTER_XT_TARGET_LOG is not set +# CONFIG_NETFILTER_XT_TARGET_MARK is not set +# CONFIG_NETFILTER_XT_NAT is not set +# CONFIG_NETFILTER_XT_TARGET_NETMAP is not set +# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set +# CONFIG_NETFILTER_XT_TARGET_NFQUEUE is not set +# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set +# CONFIG_NETFILTER_XT_TARGET_REDIRECT is not set +# CONFIG_NETFILTER_XT_TARGET_TEE is not set +# CONFIG_NETFILTER_XT_TARGET_TPROXY is not set +CONFIG_NETFILTER_XT_TARGET_SECMARK=y +CONFIG_NETFILTER_XT_TARGET_TCPMSS=y +# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set + +# +# Xtables matches +# +# CONFIG_NETFILTER_XT_MATCH_ADDRTYPE is not set +# CONFIG_NETFILTER_XT_MATCH_BPF is not set +# CONFIG_NETFILTER_XT_MATCH_CGROUP is not set +# CONFIG_NETFILTER_XT_MATCH_CLUSTER is not set +# CONFIG_NETFILTER_XT_MATCH_COMMENT is not set +# CONFIG_NETFILTER_XT_MATCH_CONNBYTES is not set +# CONFIG_NETFILTER_XT_MATCH_CONNLABEL is not set +# CONFIG_NETFILTER_XT_MATCH_CONNLIMIT is not set +# CONFIG_NETFILTER_XT_MATCH_CONNMARK is not set +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +# CONFIG_NETFILTER_XT_MATCH_CPU is not set +# CONFIG_NETFILTER_XT_MATCH_DCCP is not set +# CONFIG_NETFILTER_XT_MATCH_DEVGROUP is not set +# CONFIG_NETFILTER_XT_MATCH_DSCP is not set +# CONFIG_NETFILTER_XT_MATCH_ECN is not set +# CONFIG_NETFILTER_XT_MATCH_ESP is not set +# CONFIG_NETFILTER_XT_MATCH_HASHLIMIT is not set +# CONFIG_NETFILTER_XT_MATCH_HELPER is not set +# CONFIG_NETFILTER_XT_MATCH_HL is not set +# CONFIG_NETFILTER_XT_MATCH_IPCOMP is not set +# CONFIG_NETFILTER_XT_MATCH_IPRANGE is not set +# CONFIG_NETFILTER_XT_MATCH_L2TP is not set +# CONFIG_NETFILTER_XT_MATCH_LENGTH is not set +# CONFIG_NETFILTER_XT_MATCH_LIMIT is not set +# CONFIG_NETFILTER_XT_MATCH_MAC is not set +# CONFIG_NETFILTER_XT_MATCH_MARK is not set +# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set +CONFIG_NETFILTER_XT_MATCH_NFACCT=y +# CONFIG_NETFILTER_XT_MATCH_OSF is not set +# CONFIG_NETFILTER_XT_MATCH_OWNER is not set +# CONFIG_NETFILTER_XT_MATCH_POLICY is not set +# CONFIG_NETFILTER_XT_MATCH_PHYSDEV is not set +# CONFIG_NETFILTER_XT_MATCH_PKTTYPE is not set +# CONFIG_NETFILTER_XT_MATCH_QUOTA is not set +# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set +# CONFIG_NETFILTER_XT_MATCH_REALM is not set +# CONFIG_NETFILTER_XT_MATCH_RECENT is not set +# CONFIG_NETFILTER_XT_MATCH_SCTP is not set +# CONFIG_NETFILTER_XT_MATCH_SOCKET is not set +CONFIG_NETFILTER_XT_MATCH_STATE=y +# CONFIG_NETFILTER_XT_MATCH_STATISTIC is not set +# CONFIG_NETFILTER_XT_MATCH_STRING is not set +# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set +# CONFIG_NETFILTER_XT_MATCH_TIME is not set +# CONFIG_NETFILTER_XT_MATCH_U32 is not set +# CONFIG_IP_SET is not set +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV4=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_NF_CONNTRACK_PROC_COMPAT=y +# CONFIG_NF_DUP_IPV4 is not set +# CONFIG_NF_LOG_ARP is not set +# CONFIG_NF_LOG_IPV4 is not set +CONFIG_NF_REJECT_IPV4=y +CONFIG_NF_NAT_IPV4=y +# CONFIG_NF_NAT_MASQUERADE_IPV4 is not set +# CONFIG_NF_NAT_PPTP is not set +# CONFIG_NF_NAT_H323 is not set +CONFIG_IP_NF_IPTABLES=y +# CONFIG_IP_NF_MATCH_AH is not set +# CONFIG_IP_NF_MATCH_ECN is not set +# CONFIG_IP_NF_MATCH_RPFILTER is not set +# CONFIG_IP_NF_MATCH_TTL is not set +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +# CONFIG_IP_NF_TARGET_SYNPROXY is not set +# CONFIG_IP_NF_NAT is not set +CONFIG_IP_NF_MANGLE=y +# CONFIG_IP_NF_TARGET_CLUSTERIP is not set +# CONFIG_IP_NF_TARGET_ECN is not set +# CONFIG_IP_NF_TARGET_TTL is not set +# CONFIG_IP_NF_RAW is not set +# CONFIG_IP_NF_SECURITY is not set +# CONFIG_IP_NF_ARPTABLES is not set + +# +# IPv6: Netfilter Configuration +# +# CONFIG_NF_DEFRAG_IPV6 is not set +# CONFIG_NF_CONNTRACK_IPV6 is not set +# CONFIG_NF_DUP_IPV6 is not set +# CONFIG_NF_REJECT_IPV6 is not set +# CONFIG_NF_LOG_IPV6 is not set +# CONFIG_IP6_NF_IPTABLES is not set +# CONFIG_BRIDGE_NF_EBTABLES is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +CONFIG_STP=y +CONFIG_BRIDGE=y +CONFIG_BRIDGE_IGMP_SNOOPING=y +CONFIG_HAVE_NET_DSA=y +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +CONFIG_LLC=y +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_PHONET is not set +# CONFIG_6LOWPAN is not set +# CONFIG_IEEE802154 is not set +CONFIG_NET_SCHED=y + +# +# Queueing/Scheduling +# +# CONFIG_NET_SCH_CBQ is not set +# CONFIG_NET_SCH_HTB is not set +# CONFIG_NET_SCH_HFSC is not set +# CONFIG_NET_SCH_PRIO is not set +# CONFIG_NET_SCH_MULTIQ is not set +# CONFIG_NET_SCH_RED is not set +# CONFIG_NET_SCH_SFB is not set +# CONFIG_NET_SCH_SFQ is not set +# CONFIG_NET_SCH_TEQL is not set +# CONFIG_NET_SCH_TBF is not set +# CONFIG_NET_SCH_GRED is not set +# CONFIG_NET_SCH_DSMARK is not set +# CONFIG_NET_SCH_NETEM is not set +# CONFIG_NET_SCH_DRR is not set +# CONFIG_NET_SCH_MQPRIO is not set +# CONFIG_NET_SCH_CHOKE is not set +# CONFIG_NET_SCH_QFQ is not set +# CONFIG_NET_SCH_CODEL is not set +# CONFIG_NET_SCH_FQ_CODEL is not set +# CONFIG_NET_SCH_FQ is not set +# CONFIG_NET_SCH_HHF is not set +# CONFIG_NET_SCH_PIE is not set +# CONFIG_NET_SCH_INGRESS is not set +# CONFIG_NET_SCH_PLUG is not set + +# +# Classification +# +CONFIG_NET_CLS=y +# CONFIG_NET_CLS_BASIC is not set +# CONFIG_NET_CLS_TCINDEX is not set +# CONFIG_NET_CLS_ROUTE4 is not set +# CONFIG_NET_CLS_FW is not set +# CONFIG_NET_CLS_U32 is not set +# CONFIG_NET_CLS_RSVP is not set +# CONFIG_NET_CLS_RSVP6 is not set +# CONFIG_NET_CLS_FLOW is not set +CONFIG_NET_CLS_CGROUP=y +# CONFIG_NET_CLS_BPF is not set +# CONFIG_NET_CLS_FLOWER is not set +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_STACK=32 +# CONFIG_NET_EMATCH_CMP is not set +# CONFIG_NET_EMATCH_NBYTE is not set +# CONFIG_NET_EMATCH_U32 is not set +# CONFIG_NET_EMATCH_META is not set +# CONFIG_NET_EMATCH_TEXT is not set +# CONFIG_NET_EMATCH_CANID is not set +CONFIG_NET_CLS_ACT=y +# CONFIG_NET_ACT_POLICE is not set +# CONFIG_NET_ACT_GACT is not set +# CONFIG_NET_ACT_MIRRED is not set +# CONFIG_NET_ACT_IPT is not set +# CONFIG_NET_ACT_NAT is not set +# CONFIG_NET_ACT_PEDIT is not set +# CONFIG_NET_ACT_SIMP is not set +# CONFIG_NET_ACT_SKBEDIT is not set +# CONFIG_NET_ACT_CSUM is not set +# CONFIG_NET_ACT_VLAN is not set +# CONFIG_NET_ACT_BPF is not set +CONFIG_NET_SCH_FIFO=y +# CONFIG_DCB is not set +CONFIG_DNS_RESOLVER=y +# CONFIG_BATMAN_ADV is not set +# CONFIG_OPENVSWITCH is not set +# CONFIG_VSOCKETS is not set +# CONFIG_NETLINK_MMAP is not set +# CONFIG_NETLINK_DIAG is not set +# CONFIG_MPLS is not set +# CONFIG_HSR is not set +# CONFIG_NET_SWITCHDEV is not set +# CONFIG_NET_L3_MASTER_DEV is not set +CONFIG_RPS=y +CONFIG_RFS_ACCEL=y +CONFIG_XPS=y +# CONFIG_CGROUP_NET_PRIO is not set +CONFIG_CGROUP_NET_CLASSID=y +CONFIG_NET_RX_BUSY_POLL=y +CONFIG_BQL=y +CONFIG_NET_FLOW_LIMIT=y + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NET_TCPPROBE is not set +# CONFIG_NET_DROP_MONITOR is not set +CONFIG_HAMRADIO=y + +# +# Packet Radio protocols +# +# CONFIG_AX25 is not set +CONFIG_CAN=y +CONFIG_CAN_RAW=y +CONFIG_CAN_BCM=y +CONFIG_CAN_GW=y + +# +# CAN Device Drivers +# +CONFIG_CAN_VCAN=y +# CONFIG_CAN_SLCAN is not set +CONFIG_CAN_DEV=y +CONFIG_CAN_CALC_BITTIMING=y +# CONFIG_CAN_LEDS is not set +# CONFIG_PCH_CAN is not set +# CONFIG_CAN_SJA1000 is not set +# CONFIG_CAN_C_CAN is not set +# CONFIG_CAN_M_CAN is not set +# CONFIG_CAN_CC770 is not set + +# +# CAN USB interfaces +# +# CONFIG_CAN_EMS_USB is not set +# CONFIG_CAN_ESD_USB2 is not set +# CONFIG_CAN_GS_USB is not set +# CONFIG_CAN_KVASER_USB is not set +CONFIG_CAN_PEAK_USB=y +# CONFIG_CAN_8DEV_USB is not set +# CONFIG_CAN_SOFTING is not set +# CONFIG_CAN_DEBUG_DEVICES is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_AF_RXRPC is not set +CONFIG_FIB_RULES=y +CONFIG_WIRELESS=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_CFG80211=y +# CONFIG_NL80211_TESTMODE is not set +# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +# CONFIG_CFG80211_REG_DEBUG is not set +CONFIG_CFG80211_DEFAULT_PS=y +# CONFIG_CFG80211_DEBUGFS is not set +# CONFIG_CFG80211_INTERNAL_REGDB is not set +CONFIG_CFG80211_CRDA_SUPPORT=y +CONFIG_CFG80211_WEXT=y +# CONFIG_LIB80211 is not set +CONFIG_MAC80211=y +CONFIG_MAC80211_HAS_RC=y +CONFIG_MAC80211_RC_MINSTREL=y +CONFIG_MAC80211_RC_MINSTREL_HT=y +# CONFIG_MAC80211_RC_MINSTREL_VHT is not set +CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y +CONFIG_MAC80211_RC_DEFAULT="minstrel_ht" +# CONFIG_MAC80211_MESH is not set +CONFIG_MAC80211_LEDS=y +# CONFIG_MAC80211_DEBUGFS is not set +# CONFIG_MAC80211_MESSAGE_TRACING is not set +# CONFIG_MAC80211_DEBUG_MENU is not set +CONFIG_MAC80211_STA_HASH_MAX_SIZE=0 +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +CONFIG_RFKILL_LEDS=y +CONFIG_RFKILL_INPUT=y +CONFIG_NET_9P=y +CONFIG_NET_9P_VIRTIO=y +# CONFIG_NET_9P_DEBUG is not set +# CONFIG_CAIF is not set +# CONFIG_CEPH_LIB is not set +# CONFIG_NFC is not set +# CONFIG_LWTUNNEL is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER=y +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_FW_LOADER_USER_HELPER_FALLBACK is not set +CONFIG_ALLOW_DEV_COREDUMP=y +# CONFIG_DEBUG_DRIVER is not set +CONFIG_DEBUG_DEVRES=y +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_GENERIC_CPU_DEVICES is not set +CONFIG_GENERIC_CPU_AUTOPROBE=y +CONFIG_REGMAP=y +CONFIG_DMA_SHARED_BUFFER=y +# CONFIG_FENCE_TRACE is not set + +# +# Bus devices +# +CONFIG_CONNECTOR=y +CONFIG_PROC_EVENTS=y +# CONFIG_MTD is not set +# CONFIG_OF is not set +CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y +# CONFIG_PARPORT is not set +CONFIG_PNP=y +CONFIG_PNP_DEBUG_MESSAGES=y + +# +# Protocols +# +CONFIG_PNPACPI=y +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_NULL_BLK is not set +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_PCIESSD_MTIP32XX is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_UMEM is not set +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_LOOP_MIN_COUNT=8 +CONFIG_BLK_DEV_CRYPTOLOOP=y +# CONFIG_BLK_DEV_DRBD is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_SX8 is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=16384 +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +CONFIG_VIRTIO_BLK=y +# CONFIG_BLK_DEV_HD is not set +# CONFIG_BLK_DEV_RBD is not set +# CONFIG_BLK_DEV_RSXX is not set +# CONFIG_BLK_DEV_NVME is not set + +# +# Misc devices +# +# CONFIG_SENSORS_LIS3LV02D is not set +# CONFIG_AD525X_DPOT is not set +# CONFIG_DUMMY_IRQ is not set +# CONFIG_IBM_ASM is not set +# CONFIG_PHANTOM is not set +# CONFIG_SGI_IOC4 is not set +# CONFIG_TIFM_CORE is not set +# CONFIG_ICS932S401 is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_HP_ILO is not set +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_DS1682 is not set +# CONFIG_BMP085_I2C is not set +# CONFIG_PCH_PHUB is not set +# CONFIG_USB_SWITCH_FSA9480 is not set +# CONFIG_SRAM is not set +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_MAX6875 is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_CB710_CORE is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_SENSORS_LIS3_I2C is not set + +# +# Altera FPGA firmware download module +# +# CONFIG_ALTERA_STAPL is not set +# CONFIG_VMWARE_VMCI is not set + +# +# Intel MIC Bus Driver +# + +# +# SCIF Bus Driver +# + +# +# Intel MIC Host Driver +# + +# +# Intel MIC Card Driver +# + +# +# SCIF Driver +# + +# +# Intel MIC Coprocessor State Management (COSM) Drivers +# +# CONFIG_ECHO is not set +# CONFIG_CXL_BASE is not set +# CONFIG_CXL_KERNEL_API is not set +# CONFIG_CXL_EEH is not set +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +CONFIG_SCSI=y +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_NETLINK is not set +# CONFIG_SCSI_MQ_DEFAULT is not set +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +CONFIG_BLK_DEV_SR=y +CONFIG_BLK_DEV_SR_VENDOR=y +CONFIG_CHR_DEV_SG=y +# CONFIG_CHR_DEV_SCH is not set +CONFIG_SCSI_CONSTANTS=y +# CONFIG_SCSI_LOGGING is not set +# CONFIG_SCSI_SCAN_ASYNC is not set + +# +# SCSI Transports +# +CONFIG_SCSI_SPI_ATTRS=y +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_ISCSI_ATTRS is not set +# CONFIG_SCSI_SAS_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SRP_ATTRS is not set +# CONFIG_SCSI_LOWLEVEL is not set +# CONFIG_SCSI_LOWLEVEL_PCMCIA is not set +# CONFIG_SCSI_DH is not set +# CONFIG_SCSI_OSD_INITIATOR is not set +CONFIG_ATA=y +# CONFIG_ATA_NONSTANDARD is not set +CONFIG_ATA_VERBOSE_ERROR=y +CONFIG_ATA_ACPI=y +# CONFIG_SATA_ZPODD is not set +CONFIG_SATA_PMP=y + +# +# Controllers with non-SFF native interface +# +CONFIG_SATA_AHCI=y +# CONFIG_SATA_AHCI_PLATFORM is not set +# CONFIG_SATA_INIC162X is not set +# CONFIG_SATA_ACARD_AHCI is not set +# CONFIG_SATA_SIL24 is not set +CONFIG_ATA_SFF=y + +# +# SFF controllers with custom DMA interface +# +# CONFIG_PDC_ADMA is not set +# CONFIG_SATA_QSTOR is not set +# CONFIG_SATA_SX4 is not set +CONFIG_ATA_BMDMA=y + +# +# SATA SFF controllers with BMDMA +# +CONFIG_ATA_PIIX=y +# CONFIG_SATA_MV is not set +# CONFIG_SATA_NV is not set +# CONFIG_SATA_PROMISE is not set +# CONFIG_SATA_SIL is not set +# CONFIG_SATA_SIS is not set +# CONFIG_SATA_SVW is not set +# CONFIG_SATA_ULI is not set +# CONFIG_SATA_VIA is not set +# CONFIG_SATA_VITESSE is not set + +# +# PATA SFF controllers with BMDMA +# +# CONFIG_PATA_ALI is not set +CONFIG_PATA_AMD=y +# CONFIG_PATA_ARTOP is not set +# CONFIG_PATA_ATIIXP is not set +# CONFIG_PATA_ATP867X is not set +# CONFIG_PATA_CMD64X is not set +# CONFIG_PATA_CS5520 is not set +# CONFIG_PATA_CS5530 is not set +# CONFIG_PATA_CS5535 is not set +# CONFIG_PATA_CS5536 is not set +# CONFIG_PATA_CYPRESS is not set +# CONFIG_PATA_EFAR is not set +# CONFIG_PATA_HPT366 is not set +# CONFIG_PATA_HPT37X is not set +# CONFIG_PATA_HPT3X2N is not set +# CONFIG_PATA_HPT3X3 is not set +# CONFIG_PATA_IT8213 is not set +# CONFIG_PATA_IT821X is not set +# CONFIG_PATA_JMICRON is not set +# CONFIG_PATA_MARVELL is not set +# CONFIG_PATA_NETCELL is not set +# CONFIG_PATA_NINJA32 is not set +# CONFIG_PATA_NS87415 is not set +CONFIG_PATA_OLDPIIX=y +# CONFIG_PATA_OPTIDMA is not set +# CONFIG_PATA_PDC2027X is not set +# CONFIG_PATA_PDC_OLD is not set +# CONFIG_PATA_RADISYS is not set +# CONFIG_PATA_RDC is not set +# CONFIG_PATA_SC1200 is not set +CONFIG_PATA_SCH=y +# CONFIG_PATA_SERVERWORKS is not set +# CONFIG_PATA_SIL680 is not set +# CONFIG_PATA_SIS is not set +# CONFIG_PATA_TOSHIBA is not set +# CONFIG_PATA_TRIFLEX is not set +# CONFIG_PATA_VIA is not set +# CONFIG_PATA_WINBOND is not set + +# +# PIO-only SFF controllers +# +# CONFIG_PATA_CMD640_PCI is not set +CONFIG_PATA_MPIIX=y +# CONFIG_PATA_NS87410 is not set +# CONFIG_PATA_OPTI is not set +# CONFIG_PATA_PCMCIA is not set +# CONFIG_PATA_RZ1000 is not set + +# +# Generic fallback / legacy drivers +# +# CONFIG_PATA_ACPI is not set +CONFIG_ATA_GENERIC=y +# CONFIG_PATA_LEGACY is not set +# CONFIG_MD is not set +# CONFIG_TARGET_CORE is not set +# CONFIG_FUSION is not set + +# +# IEEE 1394 (FireWire) support +# +# CONFIG_FIREWIRE is not set +# CONFIG_FIREWIRE_NOSY is not set +CONFIG_MACINTOSH_DRIVERS=y +CONFIG_MAC_EMUMOUSEBTN=y +CONFIG_NETDEVICES=y +CONFIG_MII=y +CONFIG_NET_CORE=y +# CONFIG_BONDING is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +# CONFIG_NET_FC is not set +# CONFIG_IFB is not set +# CONFIG_NET_TEAM is not set +CONFIG_MACVLAN=y +# CONFIG_MACVTAP is not set +# CONFIG_IPVLAN is not set +# CONFIG_VXLAN is not set +CONFIG_NETCONSOLE=y +CONFIG_NETPOLL=y +CONFIG_NET_POLL_CONTROLLER=y +# CONFIG_TUN is not set +# CONFIG_TUN_VNET_CROSS_LE is not set +CONFIG_VETH=y +CONFIG_VIRTIO_NET=y +# CONFIG_NLMON is not set +# CONFIG_ARCNET is not set + +# +# CAIF transport drivers +# +# CONFIG_VHOST_NET is not set +# CONFIG_VHOST_CROSS_ENDIAN_LEGACY is not set + +# +# Distributed Switch Architecture drivers +# +# CONFIG_NET_DSA_MV88E6XXX is not set +# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set +CONFIG_ETHERNET=y +CONFIG_NET_VENDOR_3COM=y +# CONFIG_PCMCIA_3C574 is not set +# CONFIG_PCMCIA_3C589 is not set +# CONFIG_VORTEX is not set +# CONFIG_TYPHOON is not set +CONFIG_NET_VENDOR_ADAPTEC=y +# CONFIG_ADAPTEC_STARFIRE is not set +CONFIG_NET_VENDOR_AGERE=y +# CONFIG_ET131X is not set +CONFIG_NET_VENDOR_ALTEON=y +# CONFIG_ACENIC is not set +# CONFIG_ALTERA_TSE is not set +CONFIG_NET_VENDOR_AMD=y +# CONFIG_AMD8111_ETH is not set +# CONFIG_PCNET32 is not set +# CONFIG_PCMCIA_NMCLAN is not set +CONFIG_NET_VENDOR_ARC=y +CONFIG_NET_VENDOR_ATHEROS=y +# CONFIG_ATL2 is not set +# CONFIG_ATL1 is not set +# CONFIG_ATL1E is not set +# CONFIG_ATL1C is not set +# CONFIG_ALX is not set +# CONFIG_NET_VENDOR_AURORA is not set +CONFIG_NET_CADENCE=y +# CONFIG_MACB is not set +CONFIG_NET_VENDOR_BROADCOM=y +# CONFIG_B44 is not set +# CONFIG_BCMGENET is not set +CONFIG_BNX2=y +# CONFIG_CNIC is not set +CONFIG_TIGON3=y +# CONFIG_BNX2X is not set +# CONFIG_BNXT is not set +CONFIG_NET_VENDOR_BROCADE=y +# CONFIG_BNA is not set +CONFIG_NET_VENDOR_CAVIUM=y +CONFIG_NET_VENDOR_CHELSIO=y +# CONFIG_CHELSIO_T1 is not set +# CONFIG_CHELSIO_T3 is not set +# CONFIG_CHELSIO_T4 is not set +# CONFIG_CHELSIO_T4VF is not set +CONFIG_NET_VENDOR_CISCO=y +# CONFIG_ENIC is not set +# CONFIG_CX_ECAT is not set +# CONFIG_DNET is not set +CONFIG_NET_VENDOR_DEC=y +CONFIG_NET_TULIP=y +# CONFIG_DE2104X is not set +# CONFIG_TULIP is not set +# CONFIG_DE4X5 is not set +# CONFIG_WINBOND_840 is not set +# CONFIG_DM9102 is not set +# CONFIG_ULI526X is not set +# CONFIG_PCMCIA_XIRCOM is not set +CONFIG_NET_VENDOR_DLINK=y +# CONFIG_DL2K is not set +# CONFIG_SUNDANCE is not set +CONFIG_NET_VENDOR_EMULEX=y +# CONFIG_BE2NET is not set +CONFIG_NET_VENDOR_EZCHIP=y +CONFIG_NET_VENDOR_EXAR=y +# CONFIG_S2IO is not set +# CONFIG_VXGE is not set +CONFIG_NET_VENDOR_FUJITSU=y +# CONFIG_PCMCIA_FMVJ18X is not set +CONFIG_NET_VENDOR_HP=y +# CONFIG_HP100 is not set +CONFIG_NET_VENDOR_INTEL=y +CONFIG_E100=y +CONFIG_E1000=y +CONFIG_E1000E=y +# CONFIG_IGB is not set +# CONFIG_IGBVF is not set +# CONFIG_IXGB is not set +# CONFIG_IXGBE is not set +# CONFIG_IXGBEVF is not set +# CONFIG_I40E is not set +# CONFIG_I40EVF is not set +# CONFIG_FM10K is not set +CONFIG_NET_VENDOR_I825XX=y +# CONFIG_JME is not set +CONFIG_NET_VENDOR_MARVELL=y +# CONFIG_MVMDIO is not set +# CONFIG_SKGE is not set +CONFIG_SKY2=y +# CONFIG_SKY2_DEBUG is not set +CONFIG_NET_VENDOR_MELLANOX=y +# CONFIG_MLX4_EN is not set +# CONFIG_MLX4_CORE is not set +# CONFIG_MLX5_CORE is not set +# CONFIG_MLXSW_CORE is not set +CONFIG_NET_VENDOR_MICREL=y +# CONFIG_KS8851_MLL is not set +# CONFIG_KSZ884X_PCI is not set +CONFIG_NET_VENDOR_MYRI=y +# CONFIG_MYRI10GE is not set +# CONFIG_FEALNX is not set +CONFIG_NET_VENDOR_NATSEMI=y +# CONFIG_NATSEMI is not set +# CONFIG_NS83820 is not set +CONFIG_NET_VENDOR_8390=y +# CONFIG_PCMCIA_AXNET is not set +CONFIG_NE2K_PCI=y +# CONFIG_PCMCIA_PCNET is not set +CONFIG_NET_VENDOR_NVIDIA=y +CONFIG_FORCEDETH=y +CONFIG_NET_VENDOR_OKI=y +# CONFIG_PCH_GBE is not set +# CONFIG_ETHOC is not set +CONFIG_NET_PACKET_ENGINE=y +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +CONFIG_NET_VENDOR_QLOGIC=y +# CONFIG_QLA3XXX is not set +# CONFIG_QLCNIC is not set +# CONFIG_QLGE is not set +# CONFIG_NETXEN_NIC is not set +# CONFIG_QED is not set +CONFIG_NET_VENDOR_QUALCOMM=y +CONFIG_NET_VENDOR_REALTEK=y +# CONFIG_8139CP is not set +CONFIG_8139TOO=y +# CONFIG_8139TOO_PIO is not set +# CONFIG_8139TOO_TUNE_TWISTER is not set +# CONFIG_8139TOO_8129 is not set +# CONFIG_8139_OLD_RX_RESET is not set +CONFIG_R8169=y +CONFIG_NET_VENDOR_RENESAS=y +CONFIG_NET_VENDOR_RDC=y +# CONFIG_R6040 is not set +CONFIG_NET_VENDOR_ROCKER=y +CONFIG_NET_VENDOR_SAMSUNG=y +# CONFIG_SXGBE_ETH is not set +CONFIG_NET_VENDOR_SEEQ=y +CONFIG_NET_VENDOR_SILAN=y +# CONFIG_SC92031 is not set +CONFIG_NET_VENDOR_SIS=y +# CONFIG_SIS900 is not set +# CONFIG_SIS190 is not set +# CONFIG_SFC is not set +CONFIG_NET_VENDOR_SMSC=y +# CONFIG_PCMCIA_SMC91C92 is not set +# CONFIG_EPIC100 is not set +# CONFIG_SMSC911X is not set +# CONFIG_SMSC9420 is not set +CONFIG_NET_VENDOR_STMICRO=y +# CONFIG_STMMAC_ETH is not set +CONFIG_NET_VENDOR_SUN=y +# CONFIG_HAPPYMEAL is not set +# CONFIG_SUNGEM is not set +# CONFIG_CASSINI is not set +# CONFIG_NIU is not set +CONFIG_NET_VENDOR_SYNOPSYS=y +CONFIG_NET_VENDOR_TEHUTI=y +# CONFIG_TEHUTI is not set +CONFIG_NET_VENDOR_TI=y +# CONFIG_TI_CPSW_ALE is not set +# CONFIG_TLAN is not set +CONFIG_NET_VENDOR_VIA=y +# CONFIG_VIA_RHINE is not set +# CONFIG_VIA_VELOCITY is not set +CONFIG_NET_VENDOR_WIZNET=y +# CONFIG_WIZNET_W5100 is not set +# CONFIG_WIZNET_W5300 is not set +CONFIG_NET_VENDOR_XIRCOM=y +# CONFIG_PCMCIA_XIRC2PS is not set +CONFIG_FDDI=y +# CONFIG_DEFXX is not set +# CONFIG_SKFP is not set +# CONFIG_HIPPI is not set +# CONFIG_NET_SB1000 is not set +CONFIG_PHYLIB=y + +# +# MII PHY device drivers +# +# CONFIG_AQUANTIA_PHY is not set +# CONFIG_AT803X_PHY is not set +# CONFIG_AMD_PHY is not set +# CONFIG_MARVELL_PHY is not set +# CONFIG_DAVICOM_PHY is not set +# CONFIG_QSEMI_PHY is not set +# CONFIG_LXT_PHY is not set +# CONFIG_CICADA_PHY is not set +# CONFIG_VITESSE_PHY is not set +# CONFIG_TERANETICS_PHY is not set +# CONFIG_SMSC_PHY is not set +# CONFIG_BROADCOM_PHY is not set +# CONFIG_BCM7XXX_PHY is not set +# CONFIG_BCM87XX_PHY is not set +# CONFIG_ICPLUS_PHY is not set +# CONFIG_REALTEK_PHY is not set +# CONFIG_NATIONAL_PHY is not set +# CONFIG_STE10XP is not set +# CONFIG_LSI_ET1011C_PHY is not set +# CONFIG_MICREL_PHY is not set +# CONFIG_DP83848_PHY is not set +# CONFIG_DP83867_PHY is not set +# CONFIG_MICROCHIP_PHY is not set +# CONFIG_FIXED_PHY is not set +# CONFIG_MDIO_BITBANG is not set +# CONFIG_MDIO_BCM_UNIMAC is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +CONFIG_USB_NET_DRIVERS=y +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +# CONFIG_USB_RTL8152 is not set +# CONFIG_USB_LAN78XX is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_HSO is not set +# CONFIG_USB_IPHETH is not set +CONFIG_WLAN=y +# CONFIG_PCMCIA_RAYCS is not set +# CONFIG_LIBERTAS_THINFIRM is not set +# CONFIG_AIRO is not set +# CONFIG_ATMEL is not set +# CONFIG_AT76C50X_USB is not set +# CONFIG_AIRO_CS is not set +# CONFIG_PCMCIA_WL3501 is not set +# CONFIG_PRISM54 is not set +# CONFIG_USB_ZD1201 is not set +# CONFIG_USB_NET_RNDIS_WLAN is not set +# CONFIG_ADM8211 is not set +# CONFIG_RTL8180 is not set +# CONFIG_RTL8187 is not set +# CONFIG_MAC80211_HWSIM is not set +# CONFIG_MWL8K is not set +# CONFIG_ATH_CARDS is not set +# CONFIG_B43 is not set +# CONFIG_B43LEGACY is not set +# CONFIG_BRCMSMAC is not set +# CONFIG_BRCMFMAC is not set +# CONFIG_HOSTAP is not set +# CONFIG_IPW2100 is not set +# CONFIG_IPW2200 is not set +# CONFIG_IWLWIFI is not set +# CONFIG_IWL4965 is not set +# CONFIG_IWL3945 is not set +# CONFIG_LIBERTAS is not set +# CONFIG_HERMES is not set +# CONFIG_P54_COMMON is not set +# CONFIG_RT2X00 is not set +# CONFIG_WL_MEDIATEK is not set +CONFIG_RTL_CARDS=y +# CONFIG_RTL8192CE is not set +# CONFIG_RTL8192SE is not set +# CONFIG_RTL8192DE is not set +# CONFIG_RTL8723AE is not set +# CONFIG_RTL8723BE is not set +# CONFIG_RTL8188EE is not set +# CONFIG_RTL8192EE is not set +# CONFIG_RTL8821AE is not set +# CONFIG_RTL8192CU is not set +# CONFIG_RTL8XXXU is not set +# CONFIG_WL_TI is not set +# CONFIG_ZD1211RW is not set +# CONFIG_MWIFIEX is not set +# CONFIG_CW1200 is not set +# CONFIG_RSI_91X is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set +# CONFIG_VMXNET3 is not set +# CONFIG_FUJITSU_ES is not set +# CONFIG_ISDN is not set +# CONFIG_NVM is not set + +# +# Input device support +# +CONFIG_INPUT=y +CONFIG_INPUT_LEDS=y +CONFIG_INPUT_FF_MEMLESS=y +CONFIG_INPUT_POLLDEV=y +CONFIG_INPUT_SPARSEKMAP=y +# CONFIG_INPUT_MATRIXKMAP is not set + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +# CONFIG_INPUT_MOUSEDEV_PSAUX is not set +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ADP5588 is not set +# CONFIG_KEYBOARD_ADP5589 is not set +CONFIG_KEYBOARD_ATKBD=y +# CONFIG_KEYBOARD_QT1070 is not set +# CONFIG_KEYBOARD_QT2160 is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_TCA6416 is not set +# CONFIG_KEYBOARD_TCA8418 is not set +# CONFIG_KEYBOARD_LM8323 is not set +# CONFIG_KEYBOARD_LM8333 is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_MCS is not set +# CONFIG_KEYBOARD_MPR121 is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +CONFIG_INPUT_MOUSE=y +CONFIG_MOUSE_PS2=y +CONFIG_MOUSE_PS2_ALPS=y +CONFIG_MOUSE_PS2_LOGIPS2PP=y +CONFIG_MOUSE_PS2_SYNAPTICS=y +CONFIG_MOUSE_PS2_CYPRESS=y +CONFIG_MOUSE_PS2_LIFEBOOK=y +CONFIG_MOUSE_PS2_TRACKPOINT=y +# CONFIG_MOUSE_PS2_ELANTECH is not set +# CONFIG_MOUSE_PS2_SENTELIC is not set +# CONFIG_MOUSE_PS2_TOUCHKIT is not set +CONFIG_MOUSE_PS2_FOCALTECH=y +# CONFIG_MOUSE_SERIAL is not set +# CONFIG_MOUSE_APPLETOUCH is not set +# CONFIG_MOUSE_BCM5974 is not set +# CONFIG_MOUSE_CYAPA is not set +# CONFIG_MOUSE_ELAN_I2C is not set +# CONFIG_MOUSE_VSXXXAA is not set +# CONFIG_MOUSE_SYNAPTICS_I2C is not set +# CONFIG_MOUSE_SYNAPTICS_USB is not set +CONFIG_INPUT_JOYSTICK=y +# CONFIG_JOYSTICK_ANALOG is not set +# CONFIG_JOYSTICK_A3D is not set +# CONFIG_JOYSTICK_ADI is not set +# CONFIG_JOYSTICK_COBRA is not set +# CONFIG_JOYSTICK_GF2K is not set +# CONFIG_JOYSTICK_GRIP is not set +# CONFIG_JOYSTICK_GRIP_MP is not set +# CONFIG_JOYSTICK_GUILLEMOT is not set +# CONFIG_JOYSTICK_INTERACT is not set +# CONFIG_JOYSTICK_SIDEWINDER is not set +# CONFIG_JOYSTICK_TMDC is not set +# CONFIG_JOYSTICK_IFORCE is not set +# CONFIG_JOYSTICK_WARRIOR is not set +# CONFIG_JOYSTICK_MAGELLAN is not set +# CONFIG_JOYSTICK_SPACEORB is not set +# CONFIG_JOYSTICK_SPACEBALL is not set +# CONFIG_JOYSTICK_STINGER is not set +# CONFIG_JOYSTICK_TWIDJOY is not set +# CONFIG_JOYSTICK_ZHENHUA is not set +# CONFIG_JOYSTICK_AS5011 is not set +# CONFIG_JOYSTICK_JOYDUMP is not set +# CONFIG_JOYSTICK_XPAD is not set +CONFIG_INPUT_TABLET=y +# CONFIG_TABLET_USB_ACECAD is not set +# CONFIG_TABLET_USB_AIPTEK is not set +# CONFIG_TABLET_USB_GTCO is not set +# CONFIG_TABLET_USB_HANWANG is not set +# CONFIG_TABLET_USB_KBTAB is not set +# CONFIG_TABLET_SERIAL_WACOM4 is not set +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_PROPERTIES=y +# CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_ATMEL_MXT is not set +# CONFIG_TOUCHSCREEN_BU21013 is not set +# CONFIG_TOUCHSCREEN_CYTTSP_CORE is not set +# CONFIG_TOUCHSCREEN_CYTTSP4_CORE is not set +# CONFIG_TOUCHSCREEN_DYNAPRO is not set +# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set +# CONFIG_TOUCHSCREEN_EETI is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GOODIX is not set +# CONFIG_TOUCHSCREEN_ILI210X is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELAN is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_WACOM_I2C is not set +# CONFIG_TOUCHSCREEN_MAX11801 is not set +# CONFIG_TOUCHSCREEN_MCS5000 is not set +# CONFIG_TOUCHSCREEN_MMS114 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_EDT_FT5X06 is not set +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_PIXCIR is not set +# CONFIG_TOUCHSCREEN_WDT87XX_I2C is not set +# CONFIG_TOUCHSCREEN_WM97XX is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_TOUCHSCREEN_TSC_SERIO is not set +# CONFIG_TOUCHSCREEN_TSC2004 is not set +# CONFIG_TOUCHSCREEN_TSC2007 is not set +# CONFIG_TOUCHSCREEN_ST1232 is not set +# CONFIG_TOUCHSCREEN_SX8654 is not set +# CONFIG_TOUCHSCREEN_TPS6507X is not set +# CONFIG_TOUCHSCREEN_ROHM_BU21023 is not set +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_AD714X is not set +# CONFIG_INPUT_BMA150 is not set +# CONFIG_INPUT_E3X0_BUTTON is not set +# CONFIG_INPUT_PCSPKR is not set +# CONFIG_INPUT_MMA8450 is not set +# CONFIG_INPUT_MPU3050 is not set +# CONFIG_INPUT_APANEL is not set +# CONFIG_INPUT_WISTRON_BTNS is not set +# CONFIG_INPUT_ATLAS_BTNS is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_KXTJ9 is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INPUT_CM109 is not set +CONFIG_INPUT_UINPUT=y +# CONFIG_INPUT_PCF8574 is not set +# CONFIG_INPUT_ADXL34X is not set +# CONFIG_INPUT_IMS_PCU is not set +# CONFIG_INPUT_CMA3000 is not set +# CONFIG_INPUT_IDEAPAD_SLIDEBAR is not set +# CONFIG_INPUT_DRV2665_HAPTICS is not set +# CONFIG_INPUT_DRV2667_HAPTICS is not set + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +CONFIG_ARCH_MIGHT_HAVE_PC_SERIO=y +CONFIG_SERIO_I8042=y +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_CT82C710 is not set +# CONFIG_SERIO_PCIPS2 is not set +CONFIG_SERIO_LIBPS2=y +# CONFIG_SERIO_RAW is not set +# CONFIG_SERIO_ALTERA_PS2 is not set +# CONFIG_SERIO_PS2MULT is not set +# CONFIG_SERIO_ARC_PS2 is not set +# CONFIG_USERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_TTY=y +CONFIG_VT=y +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_VT_CONSOLE=y +CONFIG_VT_CONSOLE_SLEEP=y +CONFIG_HW_CONSOLE=y +CONFIG_VT_HW_CONSOLE_BINDING=y +CONFIG_UNIX98_PTYS=y +CONFIG_DEVPTS_MULTIPLE_INSTANCES=y +# CONFIG_LEGACY_PTYS is not set +CONFIG_SERIAL_NONSTANDARD=y +# CONFIG_ROCKETPORT is not set +# CONFIG_CYCLADES is not set +# CONFIG_MOXA_INTELLIO is not set +# CONFIG_MOXA_SMARTIO is not set +# CONFIG_SYNCLINK is not set +# CONFIG_SYNCLINKMP is not set +# CONFIG_SYNCLINK_GT is not set +# CONFIG_NOZOMI is not set +# CONFIG_ISI is not set +# CONFIG_N_HDLC is not set +# CONFIG_N_GSM is not set +# CONFIG_TRACE_SINK is not set +CONFIG_DEVMEM=y +CONFIG_DEVKMEM=y + +# +# Serial drivers +# +CONFIG_SERIAL_EARLYCON=y +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_DEPRECATED_OPTIONS=y +CONFIG_SERIAL_8250_PNP=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_DMA=y +CONFIG_SERIAL_8250_PCI=y +# CONFIG_SERIAL_8250_CS is not set +CONFIG_SERIAL_8250_NR_UARTS=32 +CONFIG_SERIAL_8250_RUNTIME_UARTS=4 +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_MANY_PORTS=y +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_SERIAL_8250_DETECT_IRQ=y +CONFIG_SERIAL_8250_RSA=y +# CONFIG_SERIAL_8250_FSL is not set +# CONFIG_SERIAL_8250_DW is not set +# CONFIG_SERIAL_8250_RT288X is not set +# CONFIG_SERIAL_8250_FINTEK is not set +# CONFIG_SERIAL_8250_MID is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_UARTLITE is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_JSM is not set +# CONFIG_SERIAL_SCCNXP is not set +# CONFIG_SERIAL_SC16IS7XX is not set +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_PCH_UART is not set +# CONFIG_SERIAL_ARC is not set +# CONFIG_SERIAL_RP2 is not set +# CONFIG_SERIAL_FSL_LPUART is not set +CONFIG_HVC_DRIVER=y +CONFIG_VIRTIO_CONSOLE=y +# CONFIG_IPMI_HANDLER is not set +CONFIG_HW_RANDOM=y +# CONFIG_HW_RANDOM_TIMERIOMEM is not set +CONFIG_HW_RANDOM_INTEL=y +CONFIG_HW_RANDOM_AMD=y +CONFIG_HW_RANDOM_GEODE=y +CONFIG_HW_RANDOM_VIA=y +# CONFIG_HW_RANDOM_VIRTIO is not set +CONFIG_NVRAM=y +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set +# CONFIG_SONYPI is not set + +# +# PCMCIA character devices +# +# CONFIG_SYNCLINK_CS is not set +# CONFIG_CARDMAN_4000 is not set +# CONFIG_CARDMAN_4040 is not set +# CONFIG_IPWIRELESS is not set +# CONFIG_MWAVE is not set +# CONFIG_PC8736x_GPIO is not set +# CONFIG_NSC_GPIO is not set +# CONFIG_RAW_DRIVER is not set +CONFIG_HPET=y +# CONFIG_HPET_MMAP is not set +# CONFIG_HANGCHECK_TIMER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_TELCLOCK is not set +CONFIG_DEVPORT=y +# CONFIG_XILLYBUS is not set + +# +# I2C support +# +CONFIG_I2C=y +CONFIG_ACPI_I2C_OPREGION=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +# CONFIG_I2C_CHARDEV is not set +CONFIG_I2C_MUX=y + +# +# Multiplexer I2C Chip support +# +# CONFIG_I2C_MUX_PCA9541 is not set +# CONFIG_I2C_MUX_REG is not set +CONFIG_I2C_HELPER_AUTO=y +CONFIG_I2C_ALGOBIT=y + +# +# I2C Hardware Bus support +# + +# +# PC SMBus host controller drivers +# +# CONFIG_I2C_ALI1535 is not set +# CONFIG_I2C_ALI1563 is not set +# CONFIG_I2C_ALI15X3 is not set +# CONFIG_I2C_AMD756 is not set +# CONFIG_I2C_AMD8111 is not set +CONFIG_I2C_I801=y +# CONFIG_I2C_ISCH is not set +# CONFIG_I2C_ISMT is not set +# CONFIG_I2C_PIIX4 is not set +# CONFIG_I2C_NFORCE2 is not set +# CONFIG_I2C_SIS5595 is not set +# CONFIG_I2C_SIS630 is not set +# CONFIG_I2C_SIS96X is not set +# CONFIG_I2C_VIA is not set +# CONFIG_I2C_VIAPRO is not set + +# +# ACPI drivers +# +# CONFIG_I2C_SCMI is not set + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE_PCI is not set +# CONFIG_I2C_EG20T is not set +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_PXA_PCI is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_XILINX is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_DIOLAN_U2C is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_ROBOTFUZZ_OSIF is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_SCx200_ACB is not set +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_SLAVE is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_SPI is not set +# CONFIG_SPMI is not set +# CONFIG_HSI is not set + +# +# PPS support +# +CONFIG_PPS=y +# CONFIG_PPS_DEBUG is not set + +# +# PPS clients support +# +# CONFIG_PPS_CLIENT_KTIMER is not set +# CONFIG_PPS_CLIENT_LDISC is not set +# CONFIG_PPS_CLIENT_GPIO is not set + +# +# PPS generators support +# + +# +# PTP clock support +# +CONFIG_PTP_1588_CLOCK=y + +# +# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. +# +# CONFIG_PTP_1588_CLOCK_PCH is not set +CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y +# CONFIG_GPIOLIB is not set +# CONFIG_W1 is not set +CONFIG_POWER_SUPPLY=y +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_PDA_POWER is not set +# CONFIG_TEST_POWER is not set +# CONFIG_BATTERY_DS2780 is not set +# CONFIG_BATTERY_DS2781 is not set +# CONFIG_BATTERY_DS2782 is not set +# CONFIG_BATTERY_SBS is not set +# CONFIG_BATTERY_BQ27XXX is not set +# CONFIG_BATTERY_MAX17040 is not set +# CONFIG_BATTERY_MAX17042 is not set +# CONFIG_CHARGER_MAX8903 is not set +# CONFIG_CHARGER_LP8727 is not set +# CONFIG_CHARGER_BQ2415X is not set +# CONFIG_CHARGER_SMB347 is not set +# CONFIG_BATTERY_GAUGE_LTC2941 is not set +# CONFIG_POWER_RESET is not set +# CONFIG_POWER_AVS is not set +CONFIG_HWMON=y +# CONFIG_HWMON_VID is not set +# CONFIG_HWMON_DEBUG_CHIP is not set + +# +# Native drivers +# +# CONFIG_SENSORS_ABITUGURU is not set +# CONFIG_SENSORS_ABITUGURU3 is not set +# CONFIG_SENSORS_AD7414 is not set +# CONFIG_SENSORS_AD7418 is not set +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1026 is not set +# CONFIG_SENSORS_ADM1029 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ADM9240 is not set +# CONFIG_SENSORS_ADT7410 is not set +# CONFIG_SENSORS_ADT7411 is not set +# CONFIG_SENSORS_ADT7462 is not set +# CONFIG_SENSORS_ADT7470 is not set +# CONFIG_SENSORS_ADT7475 is not set +# CONFIG_SENSORS_ASC7621 is not set +# CONFIG_SENSORS_K8TEMP is not set +# CONFIG_SENSORS_K10TEMP is not set +# CONFIG_SENSORS_FAM15H_POWER is not set +# CONFIG_SENSORS_APPLESMC is not set +# CONFIG_SENSORS_ASB100 is not set +# CONFIG_SENSORS_ATXP1 is not set +# CONFIG_SENSORS_DS620 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_DELL_SMM is not set +# CONFIG_SENSORS_I5K_AMB is not set +# CONFIG_SENSORS_F71805F is not set +# CONFIG_SENSORS_F71882FG is not set +# CONFIG_SENSORS_F75375S is not set +# CONFIG_SENSORS_FSCHMD is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_GL520SM is not set +# CONFIG_SENSORS_G760A is not set +# CONFIG_SENSORS_G762 is not set +# CONFIG_SENSORS_HIH6130 is not set +# CONFIG_SENSORS_I5500 is not set +# CONFIG_SENSORS_CORETEMP is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_JC42 is not set +# CONFIG_SENSORS_POWR1220 is not set +# CONFIG_SENSORS_LINEAGE is not set +# CONFIG_SENSORS_LTC2945 is not set +# CONFIG_SENSORS_LTC4151 is not set +# CONFIG_SENSORS_LTC4215 is not set +# CONFIG_SENSORS_LTC4222 is not set +# CONFIG_SENSORS_LTC4245 is not set +# CONFIG_SENSORS_LTC4260 is not set +# CONFIG_SENSORS_LTC4261 is not set +# CONFIG_SENSORS_MAX16065 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_MAX1668 is not set +# CONFIG_SENSORS_MAX197 is not set +# CONFIG_SENSORS_MAX6639 is not set +# CONFIG_SENSORS_MAX6642 is not set +# CONFIG_SENSORS_MAX6650 is not set +# CONFIG_SENSORS_MAX6697 is not set +# CONFIG_SENSORS_MAX31790 is not set +# CONFIG_SENSORS_HTU21 is not set +# CONFIG_SENSORS_MCP3021 is not set +# CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM73 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_LM92 is not set +# CONFIG_SENSORS_LM93 is not set +# CONFIG_SENSORS_LM95234 is not set +# CONFIG_SENSORS_LM95241 is not set +# CONFIG_SENSORS_LM95245 is not set +# CONFIG_SENSORS_PC87360 is not set +# CONFIG_SENSORS_PC87427 is not set +# CONFIG_SENSORS_NTC_THERMISTOR is not set +# CONFIG_SENSORS_NCT6683 is not set +# CONFIG_SENSORS_NCT6775 is not set +# CONFIG_SENSORS_NCT7802 is not set +# CONFIG_SENSORS_NCT7904 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_PMBUS is not set +# CONFIG_SENSORS_SHT21 is not set +# CONFIG_SENSORS_SHTC1 is not set +# CONFIG_SENSORS_SIS5595 is not set +# CONFIG_SENSORS_DME1737 is not set +# CONFIG_SENSORS_EMC1403 is not set +# CONFIG_SENSORS_EMC2103 is not set +# CONFIG_SENSORS_EMC6W201 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_SMSC47M192 is not set +# CONFIG_SENSORS_SMSC47B397 is not set +# CONFIG_SENSORS_SCH56XX_COMMON is not set +# CONFIG_SENSORS_SCH5627 is not set +# CONFIG_SENSORS_SCH5636 is not set +# CONFIG_SENSORS_SMM665 is not set +# CONFIG_SENSORS_ADC128D818 is not set +# CONFIG_SENSORS_ADS1015 is not set +# CONFIG_SENSORS_ADS7828 is not set +# CONFIG_SENSORS_AMC6821 is not set +# CONFIG_SENSORS_INA209 is not set +# CONFIG_SENSORS_INA2XX is not set +# CONFIG_SENSORS_TC74 is not set +# CONFIG_SENSORS_THMC50 is not set +# CONFIG_SENSORS_TMP102 is not set +# CONFIG_SENSORS_TMP103 is not set +# CONFIG_SENSORS_TMP401 is not set +# CONFIG_SENSORS_TMP421 is not set +# CONFIG_SENSORS_VIA_CPUTEMP is not set +# CONFIG_SENSORS_VIA686A is not set +# CONFIG_SENSORS_VT1211 is not set +# CONFIG_SENSORS_VT8231 is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83791D is not set +# CONFIG_SENSORS_W83792D is not set +# CONFIG_SENSORS_W83793 is not set +# CONFIG_SENSORS_W83795 is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83L786NG is not set +# CONFIG_SENSORS_W83627HF is not set +# CONFIG_SENSORS_W83627EHF is not set + +# +# ACPI drivers +# +# CONFIG_SENSORS_ACPI_POWER is not set +# CONFIG_SENSORS_ATK0110 is not set +CONFIG_THERMAL=y +CONFIG_THERMAL_HWMON=y +CONFIG_THERMAL_WRITABLE_TRIPS=y +CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y +# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set +# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set +# CONFIG_THERMAL_DEFAULT_GOV_POWER_ALLOCATOR is not set +# CONFIG_THERMAL_GOV_FAIR_SHARE is not set +CONFIG_THERMAL_GOV_STEP_WISE=y +# CONFIG_THERMAL_GOV_BANG_BANG is not set +# CONFIG_THERMAL_GOV_USER_SPACE is not set +# CONFIG_THERMAL_GOV_POWER_ALLOCATOR is not set +# CONFIG_THERMAL_EMULATION is not set +# CONFIG_INTEL_POWERCLAMP is not set +# CONFIG_X86_PKG_TEMP_THERMAL is not set +# CONFIG_INTEL_SOC_DTS_THERMAL is not set +# CONFIG_INT340X_THERMAL is not set +# CONFIG_INTEL_PCH_THERMAL is not set +CONFIG_WATCHDOG=y +# CONFIG_WATCHDOG_CORE is not set +# CONFIG_WATCHDOG_NOWAYOUT is not set + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set +# CONFIG_XILINX_WATCHDOG is not set +# CONFIG_CADENCE_WATCHDOG is not set +# CONFIG_DW_WATCHDOG is not set +# CONFIG_MAX63XX_WATCHDOG is not set +# CONFIG_ACQUIRE_WDT is not set +# CONFIG_ADVANTECH_WDT is not set +# CONFIG_ALIM1535_WDT is not set +# CONFIG_ALIM7101_WDT is not set +# CONFIG_F71808E_WDT is not set +# CONFIG_SP5100_TCO is not set +# CONFIG_SBC_FITPC2_WATCHDOG is not set +# CONFIG_EUROTECH_WDT is not set +# CONFIG_IB700_WDT is not set +# CONFIG_IBMASR is not set +# CONFIG_WAFER_WDT is not set +# CONFIG_I6300ESB_WDT is not set +# CONFIG_IE6XX_WDT is not set +# CONFIG_ITCO_WDT is not set +# CONFIG_IT8712F_WDT is not set +# CONFIG_IT87_WDT is not set +# CONFIG_HP_WATCHDOG is not set +# CONFIG_SC1200_WDT is not set +# CONFIG_PC87413_WDT is not set +# CONFIG_NV_TCO is not set +# CONFIG_60XX_WDT is not set +# CONFIG_SBC8360_WDT is not set +# CONFIG_SBC7240_WDT is not set +# CONFIG_CPU5_WDT is not set +# CONFIG_SMSC_SCH311X_WDT is not set +# CONFIG_SMSC37B787_WDT is not set +# CONFIG_VIA_WDT is not set +# CONFIG_W83627HF_WDT is not set +# CONFIG_W83877F_WDT is not set +# CONFIG_W83977F_WDT is not set +# CONFIG_MACHZ_WDT is not set +# CONFIG_SBC_EPX_C3_WATCHDOG is not set +# CONFIG_BCM7038_WDT is not set + +# +# PCI-based Watchdog Cards +# +# CONFIG_PCIPCWATCHDOG is not set +# CONFIG_WDTPCI is not set + +# +# USB-based Watchdog Cards +# +# CONFIG_USBPCWATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_BCMA_POSSIBLE=y + +# +# Broadcom specific AMBA +# +# CONFIG_BCMA is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_CS5535 is not set +# CONFIG_MFD_AS3711 is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_BCM590XX is not set +# CONFIG_MFD_AXP20X is not set +# CONFIG_MFD_CROS_EC is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_MFD_DA9052_I2C is not set +# CONFIG_MFD_DA9055 is not set +# CONFIG_MFD_DA9062 is not set +# CONFIG_MFD_DA9063 is not set +# CONFIG_MFD_DA9150 is not set +# CONFIG_MFD_DLN2 is not set +# CONFIG_MFD_MC13XXX_I2C is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_LPC_ICH is not set +# CONFIG_LPC_SCH is not set +# CONFIG_MFD_INTEL_LPSS_ACPI is not set +# CONFIG_MFD_INTEL_LPSS_PCI is not set +# CONFIG_MFD_JANZ_CMODIO is not set +# CONFIG_MFD_KEMPLD is not set +# CONFIG_MFD_88PM800 is not set +# CONFIG_MFD_88PM805 is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_MAX14577 is not set +# CONFIG_MFD_MAX77693 is not set +# CONFIG_MFD_MAX77843 is not set +# CONFIG_MFD_MAX8907 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8997 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_MT6397 is not set +# CONFIG_MFD_MENF21BMC is not set +# CONFIG_MFD_VIPERBOARD is not set +# CONFIG_MFD_RETU is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_RDC321X is not set +# CONFIG_MFD_RTSX_PCI is not set +# CONFIG_MFD_RT5033 is not set +# CONFIG_MFD_RTSX_USB is not set +# CONFIG_MFD_RC5T583 is not set +# CONFIG_MFD_RN5T618 is not set +# CONFIG_MFD_SEC_CORE is not set +# CONFIG_MFD_SI476X_CORE is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_SKY81452 is not set +# CONFIG_MFD_SMSC is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_MFD_SYSCON is not set +# CONFIG_MFD_TI_AM335X_TSCADC is not set +# CONFIG_MFD_LP3943 is not set +# CONFIG_MFD_LP8788 is not set +# CONFIG_MFD_PALMAS is not set +# CONFIG_TPS6105X is not set +# CONFIG_TPS6507X is not set +# CONFIG_MFD_TPS65090 is not set +# CONFIG_MFD_TPS65217 is not set +# CONFIG_MFD_TPS65218 is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_TPS80031 is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_TWL6040_CORE is not set +# CONFIG_MFD_WL1273_CORE is not set +# CONFIG_MFD_LM3533 is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_VX855 is not set +# CONFIG_MFD_ARIZONA_I2C is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_REGULATOR is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +CONFIG_MEDIA_CAMERA_SUPPORT=y +# CONFIG_MEDIA_ANALOG_TV_SUPPORT is not set +# CONFIG_MEDIA_DIGITAL_TV_SUPPORT is not set +# CONFIG_MEDIA_RADIO_SUPPORT is not set +# CONFIG_MEDIA_SDR_SUPPORT is not set +# CONFIG_MEDIA_RC_SUPPORT is not set +# CONFIG_MEDIA_CONTROLLER is not set +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_V4L2=y +# CONFIG_VIDEO_ADV_DEBUG is not set +# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set +# CONFIG_TTPCI_EEPROM is not set + +# +# Media drivers +# +# CONFIG_MEDIA_USB_SUPPORT is not set +CONFIG_MEDIA_PCI_SUPPORT=y + +# +# Media capture support +# +# CONFIG_VIDEO_SOLO6X10 is not set +# CONFIG_VIDEO_TW68 is not set +# CONFIG_VIDEO_ZORAN is not set +CONFIG_V4L_PLATFORM_DRIVERS=y +# CONFIG_VIDEO_CAFE_CCIC is not set +# CONFIG_SOC_CAMERA is not set +# CONFIG_V4L_MEM2MEM_DRIVERS is not set +# CONFIG_V4L_TEST_DRIVERS is not set + +# +# Supported MMC/SDIO adapters +# +# CONFIG_CYPRESS_FIRMWARE is not set + +# +# Media ancillary drivers (tuners, sensors, i2c, frontends) +# +CONFIG_MEDIA_SUBDRV_AUTOSELECT=y + +# +# Audio decoders, processors and mixers +# + +# +# RDS decoders +# + +# +# Video decoders +# + +# +# Video and audio decoders +# + +# +# Video encoders +# + +# +# Camera sensor devices +# + +# +# Flash devices +# + +# +# Video improvement chips +# + +# +# Audio/Video compression chips +# + +# +# Miscellaneous helper chips +# + +# +# Sensors used on soc_camera driver +# + +# +# Tools to develop new frontends +# +# CONFIG_DVB_DUMMY_FE is not set + +# +# Graphics support +# +CONFIG_AGP=y +# CONFIG_AGP_ALI is not set +# CONFIG_AGP_ATI is not set +# CONFIG_AGP_AMD is not set +CONFIG_AGP_AMD64=y +CONFIG_AGP_INTEL=y +# CONFIG_AGP_NVIDIA is not set +# CONFIG_AGP_SIS is not set +# CONFIG_AGP_SWORKS is not set +# CONFIG_AGP_VIA is not set +# CONFIG_AGP_EFFICEON is not set +CONFIG_INTEL_GTT=y +CONFIG_VGA_ARB=y +CONFIG_VGA_ARB_MAX_GPUS=16 +# CONFIG_VGA_SWITCHEROO is not set +CONFIG_DRM=y +CONFIG_DRM_MIPI_DSI=y +CONFIG_DRM_KMS_HELPER=y +CONFIG_DRM_KMS_FB_HELPER=y +CONFIG_DRM_FBDEV_EMULATION=y +# CONFIG_DRM_LOAD_EDID_FIRMWARE is not set + +# +# I2C encoder or helper chips +# +# CONFIG_DRM_I2C_ADV7511 is not set +# CONFIG_DRM_I2C_CH7006 is not set +# CONFIG_DRM_I2C_SIL164 is not set +# CONFIG_DRM_I2C_NXP_TDA998X is not set +# CONFIG_DRM_TDFX is not set +# CONFIG_DRM_R128 is not set +# CONFIG_DRM_RADEON is not set +# CONFIG_DRM_AMDGPU is not set +# CONFIG_DRM_NOUVEAU is not set +CONFIG_DRM_I915=y +# CONFIG_DRM_I915_PRELIMINARY_HW_SUPPORT is not set +# CONFIG_DRM_MGA is not set +# CONFIG_DRM_SIS is not set +# CONFIG_DRM_VIA is not set +# CONFIG_DRM_SAVAGE is not set +# CONFIG_DRM_VGEM is not set +# CONFIG_DRM_VMWGFX is not set +# CONFIG_DRM_GMA500 is not set +# CONFIG_DRM_UDL is not set +# CONFIG_DRM_AST is not set +# CONFIG_DRM_MGAG200 is not set +# CONFIG_DRM_CIRRUS_QEMU is not set +# CONFIG_DRM_QXL is not set +# CONFIG_DRM_BOCHS is not set +# CONFIG_DRM_VIRTIO_GPU is not set +CONFIG_DRM_PANEL=y + +# +# Display Panels +# +CONFIG_DRM_BRIDGE=y + +# +# Display Interface Bridges +# +# CONFIG_DRM_VIGS is not set +CONFIG_YAGL=y +CONFIG_YAGL_DEBUG=y + +# +# Frame buffer Devices +# +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +CONFIG_FB_CMDLINE=y +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +CONFIG_FB_SYS_FILLRECT=y +CONFIG_FB_SYS_COPYAREA=y +CONFIG_FB_SYS_IMAGEBLIT=y +# CONFIG_FB_FOREIGN_ENDIAN is not set +CONFIG_FB_SYS_FOPS=y +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +CONFIG_FB_MODE_HELPERS=y +CONFIG_FB_TILEBLITTING=y + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_CIRRUS is not set +# CONFIG_FB_PM2 is not set +# CONFIG_FB_CYBER2000 is not set +# CONFIG_FB_ARC is not set +# CONFIG_FB_ASILIANT is not set +# CONFIG_FB_IMSTT is not set +# CONFIG_FB_VGA16 is not set +# CONFIG_FB_UVESA is not set +# CONFIG_FB_VESA is not set +# CONFIG_FB_EFI is not set +# CONFIG_FB_N411 is not set +# CONFIG_FB_HGA is not set +# CONFIG_FB_OPENCORES is not set +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_NVIDIA is not set +# CONFIG_FB_RIVA is not set +# CONFIG_FB_I740 is not set +# CONFIG_FB_I810 is not set +# CONFIG_FB_LE80578 is not set +# CONFIG_FB_MATROX is not set +# CONFIG_FB_RADEON is not set +# CONFIG_FB_ATY128 is not set +# CONFIG_FB_ATY is not set +# CONFIG_FB_S3 is not set +# CONFIG_FB_SAVAGE is not set +# CONFIG_FB_SIS is not set +# CONFIG_FB_NEOMAGIC is not set +# CONFIG_FB_KYRO is not set +# CONFIG_FB_3DFX is not set +# CONFIG_FB_VOODOO1 is not set +# CONFIG_FB_VT8623 is not set +# CONFIG_FB_TRIDENT is not set +# CONFIG_FB_ARK is not set +# CONFIG_FB_PM3 is not set +# CONFIG_FB_CARMINE is not set +# CONFIG_FB_GEODE is not set +# CONFIG_FB_SMSCUFX is not set +# CONFIG_FB_UDL is not set +# CONFIG_FB_IBM_GXT4500 is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_MB862XX is not set +# CONFIG_FB_BROADSHEET is not set +# CONFIG_FB_AUO_K190X is not set +# CONFIG_FB_SIMPLE is not set +# CONFIG_FB_SM712 is not set +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_LCD_CLASS_DEVICE=y +CONFIG_LCD_PLATFORM=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_GENERIC=y +# CONFIG_BACKLIGHT_APPLE is not set +# CONFIG_BACKLIGHT_PM8941_WLED is not set +# CONFIG_BACKLIGHT_SAHARA is not set +# CONFIG_BACKLIGHT_ADP8860 is not set +# CONFIG_BACKLIGHT_ADP8870 is not set +# CONFIG_BACKLIGHT_LM3639 is not set +# CONFIG_BACKLIGHT_LV5207LP is not set +# CONFIG_BACKLIGHT_BD6107 is not set +# CONFIG_VGASTATE is not set +CONFIG_HDMI=y + +# +# Console display driver support +# +CONFIG_VGA_CONSOLE=y +CONFIG_VGACON_SOFT_SCROLLBACK=y +CONFIG_VGACON_SOFT_SCROLLBACK_SIZE=64 +CONFIG_DUMMY_CONSOLE=y +CONFIG_DUMMY_CONSOLE_COLUMNS=80 +CONFIG_DUMMY_CONSOLE_ROWS=25 +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y +# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set +# CONFIG_LOGO is not set +CONFIG_SOUND=y +CONFIG_SOUND_OSS_CORE=y +CONFIG_SOUND_OSS_CORE_PRECLAIM=y +CONFIG_SND=y +CONFIG_SND_TIMER=y +CONFIG_SND_PCM=y +CONFIG_SND_HWDEP=y +CONFIG_SND_JACK=y +CONFIG_SND_SEQUENCER=y +CONFIG_SND_SEQ_DUMMY=y +CONFIG_SND_OSSEMUL=y +CONFIG_SND_MIXER_OSS=y +CONFIG_SND_PCM_OSS=y +CONFIG_SND_PCM_OSS_PLUGINS=y +CONFIG_SND_PCM_TIMER=y +CONFIG_SND_SEQUENCER_OSS=y +CONFIG_SND_HRTIMER=y +CONFIG_SND_SEQ_HRTIMER_DEFAULT=y +CONFIG_SND_DYNAMIC_MINORS=y +CONFIG_SND_MAX_CARDS=32 +CONFIG_SND_SUPPORT_OLD_API=y +CONFIG_SND_PROC_FS=y +CONFIG_SND_VERBOSE_PROCFS=y +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set +CONFIG_SND_VMASTER=y +CONFIG_SND_DMA_SGBUF=y +# CONFIG_SND_RAWMIDI_SEQ is not set +# CONFIG_SND_OPL3_LIB_SEQ is not set +# CONFIG_SND_OPL4_LIB_SEQ is not set +# CONFIG_SND_SBAWE_SEQ is not set +# CONFIG_SND_EMU10K1_SEQ is not set +CONFIG_SND_AC97_CODEC=y +CONFIG_SND_DRIVERS=y +# CONFIG_SND_PCSP is not set +# CONFIG_SND_DUMMY is not set +# CONFIG_SND_ALOOP is not set +# CONFIG_SND_VIRMIDI is not set +# CONFIG_SND_MTPAV is not set +# CONFIG_SND_SERIAL_U16550 is not set +# CONFIG_SND_MPU401 is not set +# CONFIG_SND_AC97_POWER_SAVE is not set +CONFIG_SND_PCI=y +# CONFIG_SND_AD1889 is not set +# CONFIG_SND_ALS300 is not set +# CONFIG_SND_ALS4000 is not set +# CONFIG_SND_ALI5451 is not set +# CONFIG_SND_ASIHPI is not set +# CONFIG_SND_ATIIXP is not set +# CONFIG_SND_ATIIXP_MODEM is not set +# CONFIG_SND_AU8810 is not set +# CONFIG_SND_AU8820 is not set +# CONFIG_SND_AU8830 is not set +# CONFIG_SND_AW2 is not set +# CONFIG_SND_AZT3328 is not set +# CONFIG_SND_BT87X is not set +# CONFIG_SND_CA0106 is not set +# CONFIG_SND_CMIPCI is not set +# CONFIG_SND_OXYGEN is not set +# CONFIG_SND_CS4281 is not set +# CONFIG_SND_CS46XX is not set +# CONFIG_SND_CS5530 is not set +# CONFIG_SND_CS5535AUDIO is not set +# CONFIG_SND_CTXFI is not set +# CONFIG_SND_DARLA20 is not set +# CONFIG_SND_GINA20 is not set +# CONFIG_SND_LAYLA20 is not set +# CONFIG_SND_DARLA24 is not set +# CONFIG_SND_GINA24 is not set +# CONFIG_SND_LAYLA24 is not set +# CONFIG_SND_MONA is not set +# CONFIG_SND_MIA is not set +# CONFIG_SND_ECHO3G is not set +# CONFIG_SND_INDIGO is not set +# CONFIG_SND_INDIGOIO is not set +# CONFIG_SND_INDIGODJ is not set +# CONFIG_SND_INDIGOIOX is not set +# CONFIG_SND_INDIGODJX is not set +# CONFIG_SND_EMU10K1 is not set +# CONFIG_SND_EMU10K1X is not set +# CONFIG_SND_ENS1370 is not set +# CONFIG_SND_ENS1371 is not set +# CONFIG_SND_ES1938 is not set +# CONFIG_SND_ES1968 is not set +# CONFIG_SND_FM801 is not set +# CONFIG_SND_HDSP is not set +# CONFIG_SND_HDSPM is not set +# CONFIG_SND_ICE1712 is not set +# CONFIG_SND_ICE1724 is not set +CONFIG_SND_INTEL8X0=y +# CONFIG_SND_INTEL8X0M is not set +# CONFIG_SND_KORG1212 is not set +# CONFIG_SND_LOLA is not set +# CONFIG_SND_LX6464ES is not set +# CONFIG_SND_MAESTRO3 is not set +# CONFIG_SND_MIXART is not set +# CONFIG_SND_NM256 is not set +# CONFIG_SND_PCXHR is not set +# CONFIG_SND_RIPTIDE is not set +# CONFIG_SND_RME32 is not set +# CONFIG_SND_RME96 is not set +# CONFIG_SND_RME9652 is not set +# CONFIG_SND_SE6X is not set +# CONFIG_SND_SIS7019 is not set +# CONFIG_SND_SONICVIBES is not set +# CONFIG_SND_TRIDENT is not set +# CONFIG_SND_VIA82XX is not set +# CONFIG_SND_VIA82XX_MODEM is not set +# CONFIG_SND_VIRTUOSO is not set +# CONFIG_SND_VX222 is not set +# CONFIG_SND_YMFPCI is not set + +# +# HD-Audio +# +CONFIG_SND_HDA=y +CONFIG_SND_HDA_INTEL=y +CONFIG_SND_HDA_HWDEP=y +# CONFIG_SND_HDA_RECONFIG is not set +# CONFIG_SND_HDA_INPUT_BEEP is not set +# CONFIG_SND_HDA_PATCH_LOADER is not set +CONFIG_SND_HDA_CODEC_REALTEK=y +CONFIG_SND_HDA_CODEC_ANALOG=y +CONFIG_SND_HDA_CODEC_SIGMATEL=y +CONFIG_SND_HDA_CODEC_VIA=y +CONFIG_SND_HDA_CODEC_HDMI=y +CONFIG_SND_HDA_CODEC_CIRRUS=y +CONFIG_SND_HDA_CODEC_CONEXANT=y +CONFIG_SND_HDA_CODEC_CA0110=y +CONFIG_SND_HDA_CODEC_CA0132=y +# CONFIG_SND_HDA_CODEC_CA0132_DSP is not set +CONFIG_SND_HDA_CODEC_CMEDIA=y +CONFIG_SND_HDA_CODEC_SI3054=y +CONFIG_SND_HDA_GENERIC=y +CONFIG_SND_HDA_POWER_SAVE_DEFAULT=0 +CONFIG_SND_HDA_CORE=y +CONFIG_SND_HDA_I915=y +CONFIG_SND_HDA_PREALLOC_SIZE=64 +CONFIG_SND_USB=y +# CONFIG_SND_USB_AUDIO is not set +# CONFIG_SND_USB_UA101 is not set +# CONFIG_SND_USB_USX2Y is not set +# CONFIG_SND_USB_CAIAQ is not set +# CONFIG_SND_USB_US122L is not set +# CONFIG_SND_USB_6FIRE is not set +# CONFIG_SND_USB_HIFACE is not set +# CONFIG_SND_BCD2000 is not set +# CONFIG_SND_USB_POD is not set +# CONFIG_SND_USB_PODHD is not set +# CONFIG_SND_USB_TONEPORT is not set +# CONFIG_SND_USB_VARIAX is not set +CONFIG_SND_PCMCIA=y +# CONFIG_SND_VXPOCKET is not set +# CONFIG_SND_PDAUDIOCF is not set +# CONFIG_SND_SOC is not set +# CONFIG_SOUND_PRIME is not set +CONFIG_AC97_BUS=y + +# +# HID support +# +CONFIG_HID=y +# CONFIG_HID_BATTERY_STRENGTH is not set +CONFIG_HIDRAW=y +# CONFIG_UHID is not set +CONFIG_HID_GENERIC=y + +# +# Special HID drivers +# +# CONFIG_HID_A4TECH is not set +# CONFIG_HID_ACRUX is not set +# CONFIG_HID_APPLE is not set +# CONFIG_HID_APPLEIR is not set +# CONFIG_HID_AUREAL is not set +# CONFIG_HID_BELKIN is not set +# CONFIG_HID_BETOP_FF is not set +# CONFIG_HID_CHERRY is not set +# CONFIG_HID_CHICONY is not set +# CONFIG_HID_CORSAIR is not set +# CONFIG_HID_PRODIKEYS is not set +# CONFIG_HID_CYPRESS is not set +# CONFIG_HID_DRAGONRISE is not set +# CONFIG_HID_EMS_FF is not set +# CONFIG_HID_ELECOM is not set +# CONFIG_HID_ELO is not set +# CONFIG_HID_EZKEY is not set +# CONFIG_HID_GEMBIRD is not set +# CONFIG_HID_GFRM is not set +# CONFIG_HID_HOLTEK is not set +# CONFIG_HID_GT683R is not set +# CONFIG_HID_KEYTOUCH is not set +# CONFIG_HID_KYE is not set +# CONFIG_HID_UCLOGIC is not set +# CONFIG_HID_WALTOP is not set +# CONFIG_HID_GYRATION is not set +# CONFIG_HID_ICADE is not set +# CONFIG_HID_TWINHAN is not set +# CONFIG_HID_KENSINGTON is not set +# CONFIG_HID_LCPOWER is not set +# CONFIG_HID_LENOVO is not set +# CONFIG_HID_LOGITECH is not set +# CONFIG_HID_MAGICMOUSE is not set +# CONFIG_HID_MICROSOFT is not set +# CONFIG_HID_MONTEREY is not set +# CONFIG_HID_MULTITOUCH is not set +# CONFIG_HID_NTRIG is not set +# CONFIG_HID_ORTEK is not set +# CONFIG_HID_PANTHERLORD is not set +# CONFIG_HID_PENMOUNT is not set +# CONFIG_HID_PETALYNX is not set +# CONFIG_HID_PICOLCD is not set +# CONFIG_HID_PLANTRONICS is not set +# CONFIG_HID_PRIMAX is not set +# CONFIG_HID_ROCCAT is not set +# CONFIG_HID_SAITEK is not set +# CONFIG_HID_SAMSUNG is not set +# CONFIG_HID_SONY is not set +# CONFIG_HID_SPEEDLINK is not set +# CONFIG_HID_STEELSERIES is not set +# CONFIG_HID_SUNPLUS is not set +# CONFIG_HID_RMI is not set +# CONFIG_HID_GREENASIA is not set +# CONFIG_HID_SMARTJOYPLUS is not set +# CONFIG_HID_TIVO is not set +# CONFIG_HID_TOPSEED is not set +# CONFIG_HID_THINGM is not set +# CONFIG_HID_THRUSTMASTER is not set +# CONFIG_HID_WACOM is not set +# CONFIG_HID_WIIMOTE is not set +# CONFIG_HID_XINMO is not set +# CONFIG_HID_ZEROPLUS is not set +# CONFIG_HID_ZYDACRON is not set +# CONFIG_HID_SENSOR_HUB is not set + +# +# USB HID support +# +CONFIG_USB_HID=y +CONFIG_HID_PID=y +CONFIG_USB_HIDDEV=y + +# +# I2C HID support +# +# CONFIG_I2C_HID is not set +CONFIG_USB_OHCI_LITTLE_ENDIAN=y +CONFIG_USB_SUPPORT=y +CONFIG_USB_COMMON=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y + +# +# Miscellaneous USB options +# +CONFIG_USB_DEFAULT_PERSIST=y +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_OTG is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_ULPI_BUS is not set +CONFIG_USB_MON=y +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +# CONFIG_USB_XHCI_HCD is not set +CONFIG_USB_EHCI_HCD=y +# CONFIG_USB_EHCI_ROOT_HUB_TT is not set +# CONFIG_USB_EHCI_TT_NEWSCHED is not set +CONFIG_USB_EHCI_PCI=y +# CONFIG_USB_EHCI_HCD_PLATFORM is not set +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_FOTG210_HCD is not set +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_HCD_PCI=y +# CONFIG_USB_OHCI_HCD_PLATFORM is not set +CONFIG_USB_UHCI_HCD=y +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +# CONFIG_USB_HCD_TEST_MODE is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +CONFIG_USB_PRINTER=y +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# +CONFIG_USB_STORAGE=y +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_REALTEK is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_USBAT is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_ALAUDA is not set +# CONFIG_USB_STORAGE_ONETOUCH is not set +# CONFIG_USB_STORAGE_KARMA is not set +# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set +# CONFIG_USB_STORAGE_ENE_UB6250 is not set +# CONFIG_USB_UAS is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USBIP_CORE is not set +# CONFIG_USB_MUSB_HDRC is not set +# CONFIG_USB_DWC3 is not set +# CONFIG_USB_DWC2 is not set +# CONFIG_USB_CHIPIDEA is not set +# CONFIG_USB_ISP1760 is not set + +# +# USB port drivers +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_SISUSBVGA is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_EHSET_TEST_FIXTURE is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_YUREX is not set +# CONFIG_USB_EZUSB_FX2 is not set +# CONFIG_USB_HSIC_USB3503 is not set +# CONFIG_USB_LINK_LAYER_TEST is not set +# CONFIG_USB_CHAOSKEY is not set + +# +# USB Physical Layer drivers +# +# CONFIG_USB_PHY is not set +# CONFIG_NOP_USB_XCEIV is not set +# CONFIG_USB_ISP1301 is not set +# CONFIG_USB_GADGET is not set +# CONFIG_USB_LED_TRIG is not set +# CONFIG_UWB is not set +# CONFIG_MMC is not set +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +# CONFIG_LEDS_CLASS_FLASH is not set + +# +# LED drivers +# +# CONFIG_LEDS_LM3530 is not set +# CONFIG_LEDS_LM3642 is not set +# CONFIG_LEDS_PCA9532 is not set +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +# CONFIG_LEDS_LP5562 is not set +# CONFIG_LEDS_LP8501 is not set +# CONFIG_LEDS_LP8860 is not set +# CONFIG_LEDS_CLEVO_MAIL is not set +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_PCA963X is not set +# CONFIG_LEDS_BD2802 is not set +# CONFIG_LEDS_INTEL_SS4200 is not set +# CONFIG_LEDS_TCA6507 is not set +# CONFIG_LEDS_TLC591XX is not set +# CONFIG_LEDS_LM355x is not set +# CONFIG_LEDS_OT200 is not set + +# +# LED driver for blink(1) USB RGB LED is under Special HID drivers (HID_THINGM) +# +# CONFIG_LEDS_BLINKM is not set + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGERS=y +# CONFIG_LEDS_TRIGGER_TIMER is not set +# CONFIG_LEDS_TRIGGER_ONESHOT is not set +# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_CPU is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set + +# +# iptables trigger is under Netfilter config (LED target) +# +# CONFIG_LEDS_TRIGGER_TRANSIENT is not set +# CONFIG_LEDS_TRIGGER_CAMERA is not set +# CONFIG_ACCESSIBILITY is not set +# CONFIG_INFINIBAND is not set +CONFIG_EDAC_ATOMIC_SCRUB=y +CONFIG_EDAC_SUPPORT=y +CONFIG_EDAC=y +CONFIG_EDAC_LEGACY_SYSFS=y +# CONFIG_EDAC_DEBUG is not set +CONFIG_EDAC_DECODE_MCE=y +# CONFIG_EDAC_MM_EDAC is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +# CONFIG_RTC_HCTOSYS is not set +CONFIG_RTC_SYSTOHC=y +CONFIG_RTC_SYSTOHC_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_ABB5ZES3 is not set +# CONFIG_RTC_DRV_ABX80X is not set +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_ISL12057 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF2127 is not set +# CONFIG_RTC_DRV_PCF8523 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF85063 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set +# CONFIG_RTC_DRV_EM3027 is not set +# CONFIG_RTC_DRV_RV3029C2 is not set +# CONFIG_RTC_DRV_RV8803 is not set + +# +# SPI RTC drivers +# + +# +# Platform RTC drivers +# +CONFIG_RTC_DRV_CMOS=y +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1685_FAMILY is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_DS2404 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# + +# +# HID Sensor RTC drivers +# +# CONFIG_RTC_DRV_HID_SENSOR_TIME is not set +CONFIG_DMADEVICES=y +# CONFIG_DMADEVICES_DEBUG is not set + +# +# DMA Devices +# +CONFIG_DMA_ACPI=y +# CONFIG_INTEL_IDMA64 is not set +# CONFIG_PCH_DMA is not set +# CONFIG_DW_DMAC is not set +# CONFIG_DW_DMAC_PCI is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set +# CONFIG_VIRT_DRIVERS is not set +CONFIG_VIRTIO=y + +# +# Virtio drivers +# +CONFIG_VIRTIO_PCI=y +CONFIG_VIRTIO_PCI_LEGACY=y +CONFIG_VIRTIO_BALLOON=y +# CONFIG_VIRTIO_INPUT is not set +CONFIG_VIRTIO_MMIO=y +# CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES is not set + +# +# Microsoft Hyper-V guest support +# +CONFIG_STAGING=y +# CONFIG_SLICOSS is not set +# CONFIG_PRISM2_USB is not set +# CONFIG_COMEDI is not set +# CONFIG_RTL8192U is not set +# CONFIG_RTLLIB is not set +# CONFIG_R8712U is not set +# CONFIG_R8188EU is not set +# CONFIG_R8723AU is not set +# CONFIG_RTS5208 is not set +# CONFIG_VT6655 is not set +# CONFIG_VT6656 is not set +# CONFIG_FB_SM750 is not set +# CONFIG_FB_XGI is not set + +# +# Speakup console speech +# +# CONFIG_SPEAKUP is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4 is not set +# CONFIG_STAGING_MEDIA is not set + +# +# Android +# +# CONFIG_ASHMEM is not set +CONFIG_ANDROID_TIMED_OUTPUT=y +# CONFIG_ANDROID_LOW_MEMORY_KILLER is not set +# CONFIG_SYNC is not set +# CONFIG_ION is not set +# CONFIG_WIMAX_GDM72XX is not set +# CONFIG_LTE_GDM724X is not set +# CONFIG_LUSTRE_FS is not set +# CONFIG_DGNC is not set +# CONFIG_DGAP is not set +# CONFIG_GS_FPGABOOT is not set +# CONFIG_WILC1000_DRIVER is not set +# CONFIG_MOST is not set +CONFIG_X86_PLATFORM_DEVICES=y +# CONFIG_ACERHDF is not set +# CONFIG_ASUS_LAPTOP is not set +# CONFIG_DELL_SMO8800 is not set +# CONFIG_DELL_RBTN is not set +# CONFIG_FUJITSU_LAPTOP is not set +# CONFIG_FUJITSU_TABLET is not set +# CONFIG_AMILO_RFKILL is not set +# CONFIG_HP_ACCEL is not set +# CONFIG_HP_WIRELESS is not set +# CONFIG_MSI_LAPTOP is not set +# CONFIG_PANASONIC_LAPTOP is not set +# CONFIG_COMPAL_LAPTOP is not set +# CONFIG_SONY_LAPTOP is not set +# CONFIG_IDEAPAD_LAPTOP is not set +# CONFIG_THINKPAD_ACPI is not set +# CONFIG_SENSORS_HDAPS is not set +# CONFIG_INTEL_MENLOW is not set +CONFIG_EEEPC_LAPTOP=y +# CONFIG_ACPI_WMI is not set +# CONFIG_TOPSTAR_LAPTOP is not set +# CONFIG_TOSHIBA_BT_RFKILL is not set +# CONFIG_TOSHIBA_HAPS is not set +# CONFIG_ACPI_CMPC is not set +# CONFIG_INTEL_IPS is not set +# CONFIG_IBM_RTL is not set +# CONFIG_SAMSUNG_LAPTOP is not set +# CONFIG_INTEL_OAKTRAIL is not set +# CONFIG_SAMSUNG_Q10 is not set +# CONFIG_APPLE_GMUX is not set +# CONFIG_INTEL_RST is not set +# CONFIG_INTEL_SMARTCONNECT is not set +# CONFIG_PVPANIC is not set +# CONFIG_INTEL_PMC_IPC is not set +# CONFIG_SURFACE_PRO3_BUTTON is not set +# CONFIG_CHROME_PLATFORMS is not set + +# +# Hardware Spinlock drivers +# + +# +# Clock Source drivers +# +CONFIG_CLKSRC_I8253=y +CONFIG_CLKEVT_I8253=y +CONFIG_I8253_LOCK=y +CONFIG_CLKBLD_I8253=y +# CONFIG_ATMEL_PIT is not set +# CONFIG_SH_TIMER_CMT is not set +# CONFIG_SH_TIMER_MTU2 is not set +# CONFIG_SH_TIMER_TMU is not set +# CONFIG_EM_TIMER_STI is not set +# CONFIG_MAILBOX is not set +CONFIG_IOMMU_SUPPORT=y + +# +# Generic IOMMU Pagetable Support +# +# CONFIG_INTEL_IOMMU is not set + +# +# Remoteproc drivers +# +# CONFIG_STE_MODEM_RPROC is not set + +# +# Rpmsg drivers +# + +# +# SOC (System On Chip) specific Drivers +# +# CONFIG_SUNXI_SRAM is not set +# CONFIG_SOC_TI is not set +# CONFIG_PM_DEVFREQ is not set +# CONFIG_EXTCON is not set +# CONFIG_MEMORY is not set +# CONFIG_IIO is not set +# CONFIG_NTB is not set +# CONFIG_VME_BUS is not set +# CONFIG_PWM is not set +# CONFIG_IPACK_BUS is not set +# CONFIG_RESET_CONTROLLER is not set +# CONFIG_FMC is not set + +# +# PHY Subsystem +# +# CONFIG_GENERIC_PHY is not set +# CONFIG_PHY_PXA_28NM_HSIC is not set +# CONFIG_PHY_PXA_28NM_USB2 is not set +# CONFIG_BCM_KONA_USB2_PHY is not set +# CONFIG_POWERCAP is not set +# CONFIG_MCB is not set + +# +# Performance monitor support +# +CONFIG_RAS=y +# CONFIG_AMD_MCE_INJ is not set +# CONFIG_THUNDERBOLT is not set + +# +# Android +# +CONFIG_ANDROID=y +# CONFIG_ANDROID_BINDER_IPC is not set +# CONFIG_NVMEM is not set +# CONFIG_STM is not set +# CONFIG_STM_DUMMY is not set +# CONFIG_STM_SOURCE_CONSOLE is not set +# CONFIG_INTEL_TH is not set + +# +# FPGA Configuration Support +# +# CONFIG_FPGA is not set +CONFIG_MARU=y +CONFIG_MARU_VIRTIO_TOUCHSCREEN=y +# CONFIG_MARU_CAMERA is not set +# CONFIG_MARU_BACKLIGHT is not set +CONFIG_MARU_JACK=y +CONFIG_MARU_VIRTIO_HWKEY=y +CONFIG_MARU_VIRTIO_TABLET=y +CONFIG_MARU_VIRTIO_KEYBOARD=y +CONFIG_MARU_VIRTIO_EVDI=y +CONFIG_MARU_VIRTIO_SENSOR=y +CONFIG_MARU_VIRTIO_NFC=y +CONFIG_MARU_BRILLCODEC=y +CONFIG_MARU_VIRTIO_VMODEM=y +CONFIG_MARU_VIRTIO_ROTARY=y +# CONFIG_MARU_EXTENSION_SOURCE is not set + +# +# Firmware Drivers +# +# CONFIG_EDD is not set +CONFIG_FIRMWARE_MEMMAP=y +# CONFIG_DELL_RBU is not set +# CONFIG_DCDBAS is not set +CONFIG_DMIID=y +# CONFIG_DMI_SYSFS is not set +CONFIG_DMI_SCAN_MACHINE_NON_EFI_FALLBACK=y +# CONFIG_ISCSI_IBFT_FIND is not set +# CONFIG_GOOGLE_FIRMWARE is not set + +# +# EFI (Extensible Firmware Interface) Support +# +CONFIG_EFI_VARS=y +CONFIG_EFI_ESRT=y +CONFIG_EFI_RUNTIME_MAP=y +# CONFIG_EFI_FAKE_MEMMAP is not set +CONFIG_EFI_RUNTIME_WRAPPERS=y + +# +# File systems +# +CONFIG_DCACHE_WORD_ACCESS=y +# CONFIG_EXT2_FS is not set +CONFIG_EXT3_FS=y +CONFIG_EXT3_FS_POSIX_ACL=y +CONFIG_EXT3_FS_SECURITY=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_USE_FOR_EXT2=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +# CONFIG_EXT4_ENCRYPTION is not set +# CONFIG_EXT4_DEBUG is not set +CONFIG_JBD2=y +# CONFIG_JBD2_DEBUG is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +# CONFIG_F2FS_FS is not set +# CONFIG_FS_DAX is not set +CONFIG_FS_POSIX_ACL=y +CONFIG_EXPORTFS=y +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_FANOTIFY is not set +CONFIG_QUOTA=y +CONFIG_QUOTA_NETLINK_INTERFACE=y +# CONFIG_PRINT_QUOTA_WARNING is not set +# CONFIG_QUOTA_DEBUG is not set +CONFIG_QUOTA_TREE=y +# CONFIG_QFMT_V1 is not set +CONFIG_QFMT_V2=y +CONFIG_QUOTACTL=y +CONFIG_AUTOFS4_FS=y +CONFIG_FUSE_FS=y +# CONFIG_CUSE is not set +# CONFIG_OVERLAY_FS is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=y +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_PROC_VMCORE=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +# CONFIG_PROC_CHILDREN is not set +CONFIG_KERNFS=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_TMPFS_XATTR=y +CONFIG_HUGETLBFS=y +CONFIG_HUGETLB_PAGE=y +# CONFIG_CONFIGFS_FS is not set +# CONFIG_EFIVAR_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_ECRYPT_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_LOGFS is not set +# CONFIG_CRAMFS is not set +# CONFIG_SQUASHFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX6FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_PSTORE is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V2=y +CONFIG_NFS_V3=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +# CONFIG_NFS_SWAP is not set +# CONFIG_NFS_V4_1 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFS_USE_LEGACY_DNS is not set +CONFIG_NFS_USE_KERNEL_DNS=y +# CONFIG_NFSD is not set +CONFIG_GRACE_PERIOD=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_ACL_SUPPORT=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +CONFIG_SUNRPC_GSS=y +# CONFIG_SUNRPC_DEBUG is not set +# CONFIG_CEPH_FS is not set +CONFIG_CIFS=y +# CONFIG_CIFS_STATS is not set +# CONFIG_CIFS_WEAK_PW_HASH is not set +# CONFIG_CIFS_UPCALL is not set +CONFIG_CIFS_XATTR=y +# CONFIG_CIFS_POSIX is not set +# CONFIG_CIFS_ACL is not set +CONFIG_CIFS_DEBUG=y +# CONFIG_CIFS_DEBUG2 is not set +# CONFIG_CIFS_DFS_UPCALL is not set +# CONFIG_CIFS_SMB2 is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set +CONFIG_9P_FS=y +# CONFIG_9P_FS_POSIX_ACL is not set +# CONFIG_9P_FS_SECURITY is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="utf8" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +CONFIG_NLS_CODEPAGE_949=y +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_MAC_ROMAN is not set +# CONFIG_NLS_MAC_CELTIC is not set +# CONFIG_NLS_MAC_CENTEURO is not set +# CONFIG_NLS_MAC_CROATIAN is not set +# CONFIG_NLS_MAC_CYRILLIC is not set +# CONFIG_NLS_MAC_GAELIC is not set +# CONFIG_NLS_MAC_GREEK is not set +# CONFIG_NLS_MAC_ICELAND is not set +# CONFIG_NLS_MAC_INUIT is not set +# CONFIG_NLS_MAC_ROMANIAN is not set +# CONFIG_NLS_MAC_TURKISH is not set +CONFIG_NLS_UTF8=y + +# +# Kernel hacking +# +CONFIG_TRACE_IRQFLAGS_SUPPORT=y + +# +# printk and dmesg options +# +CONFIG_PRINTK_TIME=y +CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_DYNAMIC_DEBUG is not set + +# +# Compile-time checks and compiler options +# +CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_INFO_REDUCED is not set +# CONFIG_DEBUG_INFO_SPLIT is not set +# CONFIG_DEBUG_INFO_DWARF4 is not set +# CONFIG_GDB_SCRIPTS is not set +# CONFIG_ENABLE_WARN_DEPRECATED is not set +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=2048 +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_READABLE_ASM is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_PAGE_OWNER is not set +CONFIG_DEBUG_FS=y +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_SECTION_MISMATCH is not set +CONFIG_SECTION_MISMATCH_WARN_ONLY=y +CONFIG_ARCH_WANT_FRAME_POINTERS=y +CONFIG_FRAME_POINTER=y +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +CONFIG_MAGIC_SYSRQ=y +CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x1 +CONFIG_DEBUG_KERNEL=y + +# +# Memory Debugging +# +# CONFIG_PAGE_EXTENSION is not set +# CONFIG_DEBUG_PAGEALLOC is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_DEBUG_ON is not set +# CONFIG_SLUB_STATS is not set +CONFIG_HAVE_DEBUG_KMEMLEAK=y +# CONFIG_DEBUG_KMEMLEAK is not set +CONFIG_DEBUG_STACK_USAGE=y +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_VIRTUAL is not set +CONFIG_DEBUG_MEMORY_INIT=y +# CONFIG_DEBUG_PER_CPU_MAPS is not set +# CONFIG_DEBUG_HIGHMEM is not set +CONFIG_HAVE_DEBUG_STACKOVERFLOW=y +CONFIG_DEBUG_STACKOVERFLOW=y +CONFIG_HAVE_ARCH_KMEMCHECK=y +# CONFIG_DEBUG_SHIRQ is not set + +# +# Debug Lockups and Hangs +# +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_DETECT_HUNG_TASK is not set +# CONFIG_PANIC_ON_OOPS is not set +CONFIG_PANIC_ON_OOPS_VALUE=0 +CONFIG_PANIC_TIMEOUT=0 +# CONFIG_SCHED_DEBUG is not set +CONFIG_SCHED_INFO=y +CONFIG_SCHEDSTATS=y +# CONFIG_SCHED_STACK_END_CHECK is not set +# CONFIG_DEBUG_TIMEKEEPING is not set +CONFIG_TIMER_STATS=y +CONFIG_DEBUG_PREEMPT=y + +# +# Lock Debugging (spinlocks, mutexes, etc...) +# +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_ATOMIC_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_LOCK_TORTURE_TEST is not set +CONFIG_TRACE_IRQFLAGS=y +CONFIG_STACKTRACE=y +# CONFIG_DEBUG_KOBJECT is not set +CONFIG_DEBUG_BUGVERBOSE=y +# CONFIG_DEBUG_LIST is not set +# CONFIG_DEBUG_PI_LIST is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set + +# +# RCU Debugging +# +# CONFIG_PROVE_RCU is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_TORTURE_TEST is not set +# CONFIG_RCU_TORTURE_TEST is not set +CONFIG_RCU_CPU_STALL_TIMEOUT=21 +# CONFIG_RCU_TRACE is not set +# CONFIG_RCU_EQS_DEBUG is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_NOTIFIER_ERROR_INJECTION is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +CONFIG_ARCH_HAS_DEBUG_STRICT_USER_COPY_CHECKS=y +# CONFIG_DEBUG_STRICT_USER_COPY_CHECKS is not set +CONFIG_USER_STACKTRACE_SUPPORT=y +CONFIG_NOP_TRACER=y +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_FP_TEST=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_DYNAMIC_FTRACE_WITH_REGS=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_SYSCALL_TRACEPOINTS=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACER_MAX_TRACE=y +CONFIG_TRACE_CLOCK=y +CONFIG_RING_BUFFER=y +CONFIG_EVENT_TRACING=y +CONFIG_CONTEXT_SWITCH_TRACER=y +CONFIG_RING_BUFFER_ALLOW_SWAP=y +CONFIG_TRACING=y +CONFIG_GENERIC_TRACER=y +CONFIG_TRACING_SUPPORT=y +CONFIG_FTRACE=y +CONFIG_FUNCTION_TRACER=y +CONFIG_IRQSOFF_TRACER=y +CONFIG_PREEMPT_TRACER=y +CONFIG_SCHED_TRACER=y +CONFIG_FTRACE_SYSCALLS=y +CONFIG_TRACER_SNAPSHOT=y +CONFIG_TRACER_SNAPSHOT_PER_CPU_SWAP=y +CONFIG_BRANCH_PROFILE_NONE=y +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +# CONFIG_PROFILE_ALL_BRANCHES is not set +CONFIG_STACK_TRACER=y +CONFIG_BLK_DEV_IO_TRACE=y +CONFIG_KPROBE_EVENT=y +CONFIG_UPROBE_EVENT=y +CONFIG_PROBE_EVENTS=y +CONFIG_DYNAMIC_FTRACE=y +CONFIG_DYNAMIC_FTRACE_WITH_REGS=y +CONFIG_FUNCTION_PROFILER=y +CONFIG_FTRACE_MCOUNT_RECORD=y +# CONFIG_FTRACE_STARTUP_TEST is not set +CONFIG_MMIOTRACE=y +# CONFIG_MMIOTRACE_TEST is not set +# CONFIG_TRACEPOINT_BENCHMARK is not set +# CONFIG_RING_BUFFER_BENCHMARK is not set +# CONFIG_RING_BUFFER_STARTUP_TEST is not set +# CONFIG_TRACE_ENUM_MAP_FILE is not set + +# +# Runtime Testing +# +# CONFIG_LKDTM is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_KPROBES_SANITY_TEST is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_RBTREE_TEST is not set +# CONFIG_INTERVAL_TREE_TEST is not set +# CONFIG_PERCPU_TEST is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_TEST_HEXDUMP is not set +# CONFIG_TEST_STRING_HELPERS is not set +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_TEST_PRINTF is not set +# CONFIG_TEST_RHASHTABLE is not set +CONFIG_PROVIDE_OHCI1394_DMA_INIT=y +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_TEST_LKM is not set +# CONFIG_TEST_USER_COPY is not set +# CONFIG_TEST_BPF is not set +# CONFIG_TEST_FIRMWARE is not set +# CONFIG_TEST_UDELAY is not set +# CONFIG_MEMTEST is not set +# CONFIG_TEST_STATIC_KEYS is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +# CONFIG_STRICT_DEVMEM is not set +CONFIG_X86_VERBOSE_BOOTUP=y +CONFIG_EARLY_PRINTK=y +CONFIG_EARLY_PRINTK_DBGP=y +# CONFIG_EARLY_PRINTK_EFI is not set +# CONFIG_X86_PTDUMP_CORE is not set +# CONFIG_X86_PTDUMP is not set +# CONFIG_EFI_PGT_DUMP is not set +CONFIG_DEBUG_RODATA=y +# CONFIG_DEBUG_RODATA_TEST is not set +# CONFIG_DEBUG_WX is not set +# CONFIG_DEBUG_SET_MODULE_RONX is not set +# CONFIG_DEBUG_NX_TEST is not set +CONFIG_DOUBLEFAULT=y +# CONFIG_DEBUG_TLBFLUSH is not set +# CONFIG_IOMMU_STRESS is not set +CONFIG_HAVE_MMIOTRACE_SUPPORT=y +# CONFIG_X86_DECODER_SELFTEST is not set +CONFIG_IO_DELAY_TYPE_0X80=0 +CONFIG_IO_DELAY_TYPE_0XED=1 +CONFIG_IO_DELAY_TYPE_UDELAY=2 +CONFIG_IO_DELAY_TYPE_NONE=3 +CONFIG_IO_DELAY_0X80=y +# CONFIG_IO_DELAY_0XED is not set +# CONFIG_IO_DELAY_UDELAY is not set +# CONFIG_IO_DELAY_NONE is not set +CONFIG_DEFAULT_IO_DELAY_TYPE=0 +CONFIG_DEBUG_BOOT_PARAMS=y +# CONFIG_CPA_DEBUG is not set +CONFIG_OPTIMIZE_INLINING=y +# CONFIG_DEBUG_ENTRY is not set +# CONFIG_DEBUG_NMI_SELFTEST is not set +# CONFIG_X86_DEBUG_STATIC_CPU_HAS is not set +CONFIG_X86_DEBUG_FPU=y +# CONFIG_PUNIT_ATOM_DEBUG is not set + +# +# Security options +# +CONFIG_KEYS=y +# CONFIG_PERSISTENT_KEYRINGS is not set +# CONFIG_BIG_KEYS is not set +# CONFIG_ENCRYPTED_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +CONFIG_SECURITY=y +# CONFIG_SECURITYFS is not set +CONFIG_SECURITY_NETWORK=y +# CONFIG_SECURITY_NETWORK_XFRM is not set +# CONFIG_SECURITY_PATH is not set +# CONFIG_SECURITY_SELINUX is not set +CONFIG_SECURITY_SMACK=y +CONFIG_SECURITY_SMACK_BRINGUP=y +# CONFIG_SECURITY_SMACK_NETFILTER is not set +# CONFIG_SECURITY_TOMOYO is not set +# CONFIG_SECURITY_APPARMOR is not set +# CONFIG_SECURITY_YAMA is not set +CONFIG_INTEGRITY=y +# CONFIG_INTEGRITY_SIGNATURE is not set +CONFIG_INTEGRITY_AUDIT=y +# CONFIG_IMA is not set +# CONFIG_EVM is not set +CONFIG_DEFAULT_SECURITY_SMACK=y +# CONFIG_DEFAULT_SECURITY_DAC is not set +CONFIG_DEFAULT_SECURITY="smack" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_RNG_DEFAULT=y +CONFIG_CRYPTO_PCOMP2=y +CONFIG_CRYPTO_AKCIPHER2=y +# CONFIG_CRYPTO_RSA is not set +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +# CONFIG_CRYPTO_USER is not set +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y +CONFIG_CRYPTO_GF128MUL=y +CONFIG_CRYPTO_NULL=y +CONFIG_CRYPTO_NULL2=y +# CONFIG_CRYPTO_PCRYPT is not set +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_MCRYPTD is not set +CONFIG_CRYPTO_AUTHENC=y +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +CONFIG_CRYPTO_CCM=y +CONFIG_CRYPTO_GCM=y +# CONFIG_CRYPTO_CHACHA20POLY1305 is not set +CONFIG_CRYPTO_SEQIV=y +CONFIG_CRYPTO_ECHAINIV=y + +# +# Block modes +# +CONFIG_CRYPTO_CBC=y +CONFIG_CRYPTO_CTR=y +# CONFIG_CRYPTO_CTS is not set +CONFIG_CRYPTO_ECB=y +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set +# CONFIG_CRYPTO_KEYWRAP is not set + +# +# Hash modes +# +CONFIG_CRYPTO_CMAC=y +CONFIG_CRYPTO_HMAC=y +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +# CONFIG_CRYPTO_CRC32C_INTEL is not set +# CONFIG_CRYPTO_CRC32 is not set +# CONFIG_CRYPTO_CRC32_PCLMUL is not set +CONFIG_CRYPTO_CRCT10DIF=y +CONFIG_CRYPTO_GHASH=y +# CONFIG_CRYPTO_POLY1305 is not set +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_MD5=y +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +CONFIG_CRYPTO_SHA1=y +CONFIG_CRYPTO_SHA256=y +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +CONFIG_CRYPTO_AES_586=y +# CONFIG_CRYPTO_AES_NI_INTEL is not set +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SALSA20_586 is not set +# CONFIG_CRYPTO_CHACHA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_SERPENT_SSE2_586 is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set +# CONFIG_CRYPTO_TWOFISH_586 is not set + +# +# Compression +# +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set +# CONFIG_CRYPTO_842 is not set +# CONFIG_CRYPTO_LZ4 is not set +# CONFIG_CRYPTO_LZ4HC is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +CONFIG_CRYPTO_DRBG_MENU=y +CONFIG_CRYPTO_DRBG_HMAC=y +# CONFIG_CRYPTO_DRBG_HASH is not set +# CONFIG_CRYPTO_DRBG_CTR is not set +CONFIG_CRYPTO_DRBG=y +CONFIG_CRYPTO_JITTERENTROPY=y +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +# CONFIG_CRYPTO_USER_API_RNG is not set +# CONFIG_CRYPTO_USER_API_AEAD is not set +CONFIG_CRYPTO_HW=y +# CONFIG_CRYPTO_DEV_PADLOCK is not set +# CONFIG_CRYPTO_DEV_GEODE is not set +# CONFIG_CRYPTO_DEV_HIFN_795X is not set +# CONFIG_CRYPTO_DEV_CCP is not set +# CONFIG_CRYPTO_DEV_QAT_DH895xCC is not set +# CONFIG_CRYPTO_DEV_QAT_DH895xCCVF is not set +# CONFIG_ASYMMETRIC_KEY_TYPE is not set + +# +# Certificates for signature checking +# +# CONFIG_SYSTEM_TRUSTED_KEYRING is not set +CONFIG_HAVE_KVM=y +CONFIG_VIRTUALIZATION=y +# CONFIG_KVM is not set +# CONFIG_LGUEST is not set +CONFIG_BINARY_PRINTF=y + +# +# Library routines +# +CONFIG_BITREVERSE=y +# CONFIG_HAVE_ARCH_BITREVERSE is not set +CONFIG_RATIONAL=y +CONFIG_GENERIC_STRNCPY_FROM_USER=y +CONFIG_GENERIC_STRNLEN_USER=y +CONFIG_GENERIC_NET_UTILS=y +CONFIG_GENERIC_FIND_FIRST_BIT=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_IOMAP=y +CONFIG_GENERIC_IO=y +CONFIG_ARCH_HAS_FAST_MULTIPLIER=y +# CONFIG_CRC_CCITT is not set +CONFIG_CRC16=y +CONFIG_CRC_T10DIF=y +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC32_SELFTEST is not set +CONFIG_CRC32_SLICEBY8=y +# CONFIG_CRC32_SLICEBY4 is not set +# CONFIG_CRC32_SARWATE is not set +# CONFIG_CRC32_BIT is not set +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +# CONFIG_CRC8 is not set +CONFIG_AUDIT_GENERIC=y +# CONFIG_AUDIT_ARCH_COMPAT_GENERIC is not set +# CONFIG_RANDOM32_SELFTEST is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_LZ4_DECOMPRESS=y +CONFIG_XZ_DEC=y +CONFIG_XZ_DEC_X86=y +CONFIG_XZ_DEC_POWERPC=y +CONFIG_XZ_DEC_IA64=y +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_ARMTHUMB=y +CONFIG_XZ_DEC_SPARC=y +CONFIG_XZ_DEC_BCJ=y +# CONFIG_XZ_DEC_TEST is not set +CONFIG_DECOMPRESS_GZIP=y +CONFIG_DECOMPRESS_BZIP2=y +CONFIG_DECOMPRESS_LZMA=y +CONFIG_DECOMPRESS_XZ=y +CONFIG_DECOMPRESS_LZO=y +CONFIG_DECOMPRESS_LZ4=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_INTERVAL_TREE=y +CONFIG_ASSOCIATIVE_ARRAY=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT_MAP=y +CONFIG_HAS_DMA=y +CONFIG_CHECK_SIGNATURE=y +CONFIG_CPU_RMAP=y +CONFIG_DQL=y +CONFIG_GLOB=y +# CONFIG_GLOB_SELFTEST is not set +CONFIG_NLATTR=y +CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y +# CONFIG_CORDIC is not set +# CONFIG_DDR is not set +CONFIG_OID_REGISTRY=y +CONFIG_UCS2_STRING=y +CONFIG_FONT_SUPPORT=y +# CONFIG_FONTS is not set +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y +# CONFIG_SG_SPLIT is not set +CONFIG_ARCH_HAS_SG_CHAIN=y +CONFIG_ARCH_HAS_MMIO_FLUSH=y diff --git a/build-i386.sh b/build-i386.sh new file mode 100755 index 0000000..6d2b499 --- /dev/null +++ b/build-i386.sh @@ -0,0 +1,6 @@ +#!/bin/sh +# Build x86 emulator kernel image + +ARCH=i386 make tizen_emul_defconfig +./scripts/config --set-str CONFIG_INITRAMFS_SOURCE ramfs/initramfs.i386 +ARCH=i386 CROSS_COMPILE='' make -j8 diff --git a/build-x86_64.sh b/build-x86_64.sh new file mode 100755 index 0000000..d1fe896b --- /dev/null +++ b/build-x86_64.sh @@ -0,0 +1,6 @@ +#!/bin/sh +# Build x86 emulator kernel image + +ARCH=x86_64 make tizen_emul_defconfig +./scripts/config --set-str CONFIG_INITRAMFS_SOURCE ramfs/initramfs.x86_64 +ARCH=x86_64 CROSS_COMPILE='' make -j8 diff --git a/drivers/Kconfig b/drivers/Kconfig index d2ac339..cf83bbc 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -198,4 +198,7 @@ source "drivers/hwtracing/intel_th/Kconfig" source "drivers/fpga/Kconfig" +# for maru board +source "drivers/maru/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 795d0ca..ac25eb1 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -172,3 +172,6 @@ obj-$(CONFIG_STM) += hwtracing/stm/ obj-$(CONFIG_ANDROID) += android/ obj-$(CONFIG_NVMEM) += nvmem/ obj-$(CONFIG_FPGA) += fpga/ + +# for maru board +obj-$(CONFIG_MARU) += maru/ diff --git a/drivers/gpu/Makefile b/drivers/gpu/Makefile index e9ed439..6a9c3d6 100644 --- a/drivers/gpu/Makefile +++ b/drivers/gpu/Makefile @@ -4,3 +4,6 @@ obj-$(CONFIG_TEGRA_HOST1X) += host1x/ obj-y += drm/ vga/ obj-$(CONFIG_IMX_IPUV3_CORE) += ipu-v3/ + +# for maru board +obj-y += yagl/ diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index c4bf9a1..62bdfbe 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -266,3 +266,6 @@ source "drivers/gpu/drm/amd/amdkfd/Kconfig" source "drivers/gpu/drm/imx/Kconfig" source "drivers/gpu/drm/vc4/Kconfig" + +# for maru board +source "drivers/gpu/drm/vigs/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 1e9ff4c..1b47972 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -75,3 +75,5 @@ obj-y += i2c/ obj-y += panel/ obj-y += bridge/ obj-$(CONFIG_DRM_FSL_DCU) += fsl-dcu/ +# for maru board +obj-$(CONFIG_DRM_VIGS) += vigs/ diff --git a/drivers/gpu/drm/vigs/Kconfig b/drivers/gpu/drm/vigs/Kconfig new file mode 100644 index 0000000..f3a1826 --- /dev/null +++ b/drivers/gpu/drm/vigs/Kconfig @@ -0,0 +1,24 @@ +# +# VIGS configuration +# + +config DRM_VIGS + tristate "DRM Support for VIGS" + depends on DRM && PCI + default n + select DRM_KMS_HELPER + select DRM_TTM + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE + help + This module enables VIGS passthrough from emulated system + to hypervisor (for example, QEMU). + +config DRM_VIGS_DEBUG + bool "VIGS debug messages" + depends on DRM_VIGS + default no + help + Enable VIGS debug messages. diff --git a/drivers/gpu/drm/vigs/Makefile b/drivers/gpu/drm/vigs/Makefile new file mode 100644 index 0000000..a05f999 --- /dev/null +++ b/drivers/gpu/drm/vigs/Makefile @@ -0,0 +1,26 @@ +# +# Makefile for the drm device driver. This driver provides support for the +# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. + +ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/vigs -Werror +vigs_drm-y := main.o \ + vigs_driver.o \ + vigs_gem.o \ + vigs_surface.o \ + vigs_execbuffer.o \ + vigs_device.o \ + vigs_mman.o \ + vigs_crtc.o \ + vigs_output.o \ + vigs_framebuffer.o \ + vigs_comm.o \ + vigs_fbdev.o \ + vigs_irq.o \ + vigs_fence.o \ + vigs_fenceman.o \ + vigs_file.o \ + vigs_plane.o \ + vigs_dp.o \ + vigs_dmabuf.o + +obj-$(CONFIG_DRM_VIGS) += vigs_drm.o diff --git a/drivers/gpu/drm/vigs/main.c b/drivers/gpu/drm/vigs/main.c new file mode 100644 index 0000000..2ff1911 --- /dev/null +++ b/drivers/gpu/drm/vigs/main.c @@ -0,0 +1,25 @@ +#include "vigs_driver.h" +#include +#include + +MODULE_AUTHOR("Stanislav Vorobiov"); +MODULE_LICENSE("Dual BSD/GPL"); + +int vigs_init(void) +{ + int ret = vigs_driver_register(); + + if (ret != 0) { + return ret; + } + + return 0; +} + +void vigs_cleanup(void) +{ + vigs_driver_unregister(); +} + +module_init(vigs_init); +module_exit(vigs_cleanup); diff --git a/drivers/gpu/drm/vigs/vigs_comm.c b/drivers/gpu/drm/vigs/vigs_comm.c new file mode 100644 index 0000000..1171cfe --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_comm.c @@ -0,0 +1,523 @@ +#include "vigs_comm.h" +#include "vigs_device.h" +#include "vigs_execbuffer.h" +#include "vigs_regs.h" +#include "vigs_fence.h" +#include + +static int vigs_comm_alloc(struct vigs_comm *comm, + unsigned long size, + void **ptr) +{ + int ret; + + if (!comm->execbuffer || + (vigs_gem_size(&comm->execbuffer->gem) < size)) { + if (comm->execbuffer) { + drm_gem_object_unreference_unlocked(&comm->execbuffer->gem.base); + comm->execbuffer = NULL; + } + + ret = vigs_execbuffer_create(comm->vigs_dev, + size, + true, + &comm->execbuffer); + + if (ret != 0) { + DRM_ERROR("unable to create execbuffer\n"); + return ret; + } + + vigs_gem_reserve(&comm->execbuffer->gem); + + ret = vigs_gem_kmap(&comm->execbuffer->gem); + + vigs_gem_unreserve(&comm->execbuffer->gem); + + if (ret != 0) { + DRM_ERROR("unable to kmap execbuffer\n"); + + drm_gem_object_unreference_unlocked(&comm->execbuffer->gem.base); + comm->execbuffer = NULL; + + return ret; + } + } + + *ptr = comm->execbuffer->gem.kptr; + + return 0; +} + +static int vigs_comm_prepare(struct vigs_comm *comm, + vigsp_cmd cmd, + unsigned long request_size, + void **request) +{ + int ret; + void *ptr; + struct vigsp_cmd_batch_header *batch_header; + struct vigsp_cmd_request_header *request_header; + + ret = vigs_comm_alloc(comm, + sizeof(*batch_header) + + sizeof(*request_header) + + request_size, + &ptr); + + if (ret != 0) { + return ret; + } + + batch_header = ptr; + request_header = (struct vigsp_cmd_request_header*)(batch_header + 1); + + batch_header->fence_seq = 0; + batch_header->size = sizeof(*request_header) + request_size; + + request_header->cmd = cmd; + request_header->size = request_size; + + if (request) { + *request = (request_header + 1); + } + + return 0; +} + +static void vigs_comm_exec_internal(struct vigs_comm *comm, + struct vigs_execbuffer *execbuffer) +{ + writel(vigs_gem_offset(&execbuffer->gem), comm->io_ptr + VIGS_REG_EXEC); +} + +static int vigs_comm_init(struct vigs_comm *comm) +{ + int ret; + struct vigsp_cmd_init_request *request; + + ret = vigs_comm_prepare(comm, + vigsp_cmd_init, + sizeof(*request), + (void**)&request); + + if (ret != 0) { + return ret; + } + + request->client_version = VIGS_PROTOCOL_VERSION; + request->server_version = 0; + + vigs_comm_exec_internal(comm, comm->execbuffer); + + if (request->server_version != VIGS_PROTOCOL_VERSION) { + DRM_ERROR("protocol version mismatch, expected %u, actual %u\n", + VIGS_PROTOCOL_VERSION, + request->server_version); + return -ENODEV; + } + + return 0; +} + +static void vigs_comm_exit(struct vigs_comm *comm) +{ + int ret; + + ret = vigs_comm_prepare(comm, vigsp_cmd_exit, 0, NULL); + + if (ret != 0) { + return; + } + + vigs_comm_exec_internal(comm, comm->execbuffer); +} + +int vigs_comm_create(struct vigs_device *vigs_dev, + struct vigs_comm **comm) +{ + int ret = 0; + + DRM_DEBUG_DRIVER("enter\n"); + + *comm = kzalloc(sizeof(**comm), GFP_KERNEL); + + if (!*comm) { + ret = -ENOMEM; + goto fail1; + } + + (*comm)->vigs_dev = vigs_dev; + (*comm)->io_ptr = vigs_dev->io_map->handle; + + ret = vigs_comm_init(*comm); + + if (ret != 0) { + goto fail2; + } + + mutex_init(&(*comm)->mutex); + + return 0; + +fail2: + if ((*comm)->execbuffer) { + drm_gem_object_unreference_unlocked(&(*comm)->execbuffer->gem.base); + } + kfree(*comm); +fail1: + *comm = NULL; + + return ret; +} + +void vigs_comm_destroy(struct vigs_comm *comm) +{ + DRM_DEBUG_DRIVER("enter\n"); + + mutex_destroy(&comm->mutex); + vigs_comm_exit(comm); + if (comm->execbuffer) { + drm_gem_object_unreference_unlocked(&comm->execbuffer->gem.base); + } + kfree(comm); +} + +void vigs_comm_exec(struct vigs_comm *comm, + struct vigs_execbuffer *execbuffer) +{ + vigs_comm_exec_internal(comm, execbuffer); +} + +int vigs_comm_reset(struct vigs_comm *comm) +{ + int ret; + + mutex_lock(&comm->mutex); + + ret = vigs_comm_prepare(comm, vigsp_cmd_reset, 0, NULL); + + if (ret == 0) { + vigs_comm_exec_internal(comm, comm->execbuffer); + } + + mutex_unlock(&comm->mutex); + + return ret; +} + +int vigs_comm_create_surface(struct vigs_comm *comm, + u32 width, + u32 height, + u32 stride, + vigsp_surface_format format, + vigsp_surface_id id) +{ + int ret; + struct vigsp_cmd_create_surface_request *request; + + DRM_DEBUG_DRIVER("width = %u, height = %u, stride = %u, fmt = %d, id = %u\n", + width, + height, + stride, + format, + id); + + mutex_lock(&comm->mutex); + + ret = vigs_comm_prepare(comm, + vigsp_cmd_create_surface, + sizeof(*request), + (void**)&request); + + if (ret == 0) { + request->width = width; + request->height = height; + request->stride = stride; + request->format = format; + request->id = id; + + vigs_comm_exec_internal(comm, comm->execbuffer); + } + + mutex_unlock(&comm->mutex); + + return ret; +} + +int vigs_comm_destroy_surface(struct vigs_comm *comm, vigsp_surface_id id) +{ + int ret; + struct vigsp_cmd_destroy_surface_request *request; + + DRM_DEBUG_DRIVER("id = %u\n", id); + + mutex_lock(&comm->mutex); + + ret = vigs_comm_prepare(comm, + vigsp_cmd_destroy_surface, + sizeof(*request), + (void**)&request); + + if (ret == 0) { + request->id = id; + + vigs_comm_exec_internal(comm, comm->execbuffer); + } + + mutex_unlock(&comm->mutex); + + return ret; +} + +int vigs_comm_set_root_surface(struct vigs_comm *comm, + vigsp_surface_id id, + bool scanout, + vigsp_offset offset) +{ + int ret; + struct vigs_fence *fence = NULL; + struct vigsp_cmd_set_root_surface_request *request; + + DRM_DEBUG_DRIVER("id = %u, scanout = %d, offset = %u\n", + id, scanout, offset); + + if (scanout) { + /* + * We only need to fence this if surface is + * scanout, this is in order not to display garbage + * on page flip. + */ + + ret = vigs_fence_create(comm->vigs_dev->fenceman, &fence); + + if (ret != 0) { + return ret; + } + } + + mutex_lock(&comm->mutex); + + ret = vigs_comm_prepare(comm, + vigsp_cmd_set_root_surface, + sizeof(*request), + (void**)&request); + + if (ret == 0) { + request->id = id; + request->scanout = scanout; + request->offset = offset; + + if (fence) { + vigs_execbuffer_fence(comm->execbuffer, fence); + } + + vigs_comm_exec_internal(comm, comm->execbuffer); + } + + mutex_unlock(&comm->mutex); + + if ((ret == 0) && fence) { + vigs_fence_wait(fence, false); + } + + vigs_fence_unref(fence); + + return ret; +} + +int vigs_comm_update_vram(struct vigs_comm *comm, + vigsp_surface_id id, + vigsp_offset offset) +{ + int ret; + struct vigs_fence *fence; + struct vigsp_cmd_update_vram_request *request; + + DRM_DEBUG_DRIVER("id = %u, offset = %u\n", id, offset); + + ret = vigs_fence_create(comm->vigs_dev->fenceman, &fence); + + if (ret != 0) { + return ret; + } + + mutex_lock(&comm->mutex); + + ret = vigs_comm_prepare(comm, + vigsp_cmd_update_vram, + sizeof(*request), + (void**)&request); + + if (ret == 0) { + request->sfc_id = id; + request->offset = offset; + + vigs_execbuffer_fence(comm->execbuffer, fence); + + vigs_comm_exec_internal(comm, comm->execbuffer); + } + + mutex_unlock(&comm->mutex); + + if (ret == 0) { + vigs_fence_wait(fence, false); + } + + vigs_fence_unref(fence); + + return ret; +} + +int vigs_comm_update_gpu(struct vigs_comm *comm, + vigsp_surface_id id, + u32 width, + u32 height, + vigsp_offset offset) +{ + int ret; + struct vigs_fence *fence; + struct vigsp_cmd_update_gpu_request *request; + + DRM_DEBUG_DRIVER("id = %u, offset = %u\n", id, offset); + + ret = vigs_fence_create(comm->vigs_dev->fenceman, &fence); + + if (ret != 0) { + return ret; + } + + mutex_lock(&comm->mutex); + + ret = vigs_comm_prepare(comm, + vigsp_cmd_update_gpu, + sizeof(*request) + sizeof(struct vigsp_rect), + (void**)&request); + + if (ret == 0) { + request->sfc_id = id; + request->offset = offset; + request->num_entries = 1; + request->entries[0].pos.x = 0; + request->entries[0].pos.y = 0; + request->entries[0].size.w = width; + request->entries[0].size.h = height; + + vigs_execbuffer_fence(comm->execbuffer, fence); + + vigs_comm_exec_internal(comm, comm->execbuffer); + } + + mutex_unlock(&comm->mutex); + + if (ret == 0) { + vigs_fence_wait(fence, false); + } + + vigs_fence_unref(fence); + + return ret; +} + +int vigs_comm_set_plane(struct vigs_comm *comm, + u32 plane, + u32 width, + u32 height, + vigsp_plane_format format, + vigsp_surface_id surfaces[4], + unsigned int src_x, + unsigned int src_y, + unsigned int src_w, + unsigned int src_h, + int dst_x, + int dst_y, + unsigned int dst_w, + unsigned int dst_h, + int z_pos, + int hflip, + int vflip, + int rotation) +{ + int ret; + struct vigsp_cmd_set_plane_request *request; + + DRM_DEBUG_DRIVER("plane = %u, src_x = %u, src_y = %u, src_w = %u, src_h = %u, dst_x = %d, dst_y = %d, dst_w = %u, dst_h = %u, z_pos = %d, hflip = %d, vflip = %d, rotation = %d\n", + plane, src_x, src_y, src_w, src_h, + dst_x, dst_y, dst_w, dst_h, z_pos, hflip, vflip, + rotation); + + mutex_lock(&comm->mutex); + + ret = vigs_comm_prepare(comm, + vigsp_cmd_set_plane, + sizeof(*request), + (void**)&request); + + if (ret == 0) { + request->plane = plane; + request->width = width; + request->height = height; + request->format = format; + memcpy(request->surfaces, surfaces, sizeof(request->surfaces)); + request->src_rect.pos.x = src_x; + request->src_rect.pos.y = src_y; + request->src_rect.size.w = src_w; + request->src_rect.size.h = src_h; + request->dst_x = dst_x; + request->dst_y = dst_y; + request->dst_size.w = dst_w; + request->dst_size.h = dst_h; + request->z_pos = z_pos; + request->hflip = hflip; + request->vflip = vflip; + request->rotation = rotation; + + vigs_comm_exec_internal(comm, comm->execbuffer); + } + + mutex_unlock(&comm->mutex); + + return ret; +} + +int vigs_comm_fence(struct vigs_comm *comm, struct vigs_fence *fence) +{ + struct vigsp_cmd_batch_header *batch_header; + int ret; + + DRM_DEBUG_DRIVER("seq = %u\n", fence->seq); + + mutex_lock(&comm->mutex); + + ret = vigs_comm_alloc(comm, + sizeof(*batch_header), + (void**)&batch_header); + + if (ret != 0) { + mutex_unlock(&comm->mutex); + + return ret; + } + + batch_header->fence_seq = 0; + batch_header->size = 0; + + vigs_execbuffer_fence(comm->execbuffer, fence); + + vigs_comm_exec_internal(comm, comm->execbuffer); + + mutex_unlock(&comm->mutex); + + return 0; +} + +int vigs_comm_get_protocol_version_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv) +{ + struct drm_vigs_get_protocol_version *args = data; + + args->version = VIGS_PROTOCOL_VERSION; + + return 0; +} diff --git a/drivers/gpu/drm/vigs/vigs_comm.h b/drivers/gpu/drm/vigs/vigs_comm.h new file mode 100644 index 0000000..9b87e94 --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_comm.h @@ -0,0 +1,102 @@ +#ifndef _VIGS_COMM_H_ +#define _VIGS_COMM_H_ + +#include +#include +#include "vigs_protocol.h" + +struct drm_device; +struct drm_file; +struct vigs_device; +struct vigs_execbuffer; +struct vigs_fence; + +struct vigs_comm +{ + struct vigs_device *vigs_dev; + + /* + * From vigs_device::io_map::handle for speed. + */ + void __iomem *io_ptr; + + /* + * For synchronizing all calls. + */ + struct mutex mutex; + + /* + * For internal use. + */ + struct vigs_execbuffer *execbuffer; +}; + +int vigs_comm_create(struct vigs_device *vigs_dev, + struct vigs_comm **comm); + +void vigs_comm_destroy(struct vigs_comm *comm); + +void vigs_comm_exec(struct vigs_comm *comm, + struct vigs_execbuffer *execbuffer); + +int vigs_comm_reset(struct vigs_comm *comm); + +int vigs_comm_create_surface(struct vigs_comm *comm, + u32 width, + u32 height, + u32 stride, + vigsp_surface_format format, + vigsp_surface_id id); + +int vigs_comm_destroy_surface(struct vigs_comm *comm, vigsp_surface_id id); + +int vigs_comm_set_root_surface(struct vigs_comm *comm, + vigsp_surface_id id, + bool scanout, + vigsp_offset offset); + +int vigs_comm_update_vram(struct vigs_comm *comm, + vigsp_surface_id id, + vigsp_offset offset); + +int vigs_comm_update_gpu(struct vigs_comm *comm, + vigsp_surface_id id, + u32 width, + u32 height, + vigsp_offset offset); + +int vigs_comm_set_plane(struct vigs_comm *comm, + u32 plane, + u32 width, + u32 height, + vigsp_plane_format format, + vigsp_surface_id surfaces[4], + unsigned int src_x, + unsigned int src_y, + unsigned int src_w, + unsigned int src_h, + int dst_x, + int dst_y, + unsigned int dst_w, + unsigned int dst_h, + int z_pos, + int hflip, + int vflip, + int rotation); + +int vigs_comm_fence(struct vigs_comm *comm, struct vigs_fence *fence); + +/* + * IOCTLs + * @{ + */ + +int vigs_comm_get_protocol_version_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv); + +/* + * @} + */ + +#endif diff --git a/drivers/gpu/drm/vigs/vigs_crtc.c b/drivers/gpu/drm/vigs/vigs_crtc.c new file mode 100644 index 0000000..cb06587 --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_crtc.c @@ -0,0 +1,368 @@ +#include "vigs_crtc.h" +#include "vigs_device.h" +#include "vigs_framebuffer.h" +#include "vigs_surface.h" +#include "vigs_comm.h" +#include "vigs_fbdev.h" +#include "drm_crtc_helper.h" +#include + +static int vigs_crtc_update(struct drm_crtc *crtc, + struct drm_framebuffer *old_fb) +{ + struct vigs_device *vigs_dev = crtc->dev->dev_private; + struct vigs_framebuffer *vigs_old_fb = NULL; + struct vigs_framebuffer *vigs_fb; + int ret; + + /* + * New framebuffer has been attached, notify the host that + * root surface has been updated. + */ + + if (!crtc->fb) { + DRM_ERROR("crtc->fb is NULL\n"); + return -EINVAL; + } + + if (old_fb) { + vigs_old_fb = fb_to_vigs_fb(old_fb); + } + + vigs_fb = fb_to_vigs_fb(crtc->fb); + + if (vigs_fb->surfaces[0]->scanout) { +retry: + ret = vigs_framebuffer_pin(vigs_fb); + + if (ret != 0) { + /* + * In condition of very intense GEM operations + * and with small amount of VRAM memory it's possible that + * GEM pin will be failing for some time, thus, framebuffer pin + * will be failing. This is unavoidable with current TTM design, + * thus, if someone is intensively + * reserves/unreserves GEMs then ttm_bo_validate can fail even if there + * is free space in a placement. Even worse, ttm_bo_validate fails with + * ENOMEM so it's not possible to tell if it's a temporary failure due + * to reserve/unreserve pressure or constant one due to memory shortage. + * We assume here that it's temporary and retry framebuffer pin. This + * is relatively safe since we only pin GEMs on pageflip and user + * should have started the VM with VRAM size equal to at least 3 frames, + * thus, 2 frame will always be free and we can always pin 1 frame. + */ + cpu_relax(); + goto retry; + } + + vigs_gem_reserve(&vigs_fb->surfaces[0]->gem); + + ret = vigs_comm_set_root_surface(vigs_dev->comm, + vigs_fb->surfaces[0]->id, + 1, + vigs_gem_offset(&vigs_fb->surfaces[0]->gem)); + + vigs_gem_unreserve(&vigs_fb->surfaces[0]->gem); + + if (ret != 0) { + vigs_framebuffer_unpin(vigs_fb); + + return ret; + } + } else { + ret = vigs_comm_set_root_surface(vigs_dev->comm, + vigs_fb->surfaces[0]->id, + 0, + 0); + + if (ret != 0) { + return ret; + } + } + + if (vigs_old_fb && vigs_old_fb->surfaces[0]->scanout) { + vigs_framebuffer_unpin(vigs_old_fb); + } + + return 0; +} + +static void vigs_crtc_destroy(struct drm_crtc *crtc) +{ + struct vigs_crtc *vigs_crtc = crtc_to_vigs_crtc(crtc); + + DRM_DEBUG_KMS("enter"); + + drm_crtc_cleanup(crtc); + + kfree(vigs_crtc); +} + +static void vigs_crtc_dpms(struct drm_crtc *crtc, int mode) +{ + struct vigs_device *vigs_dev = crtc->dev->dev_private; + int blank, i; + struct fb_event event; + + DRM_DEBUG_KMS("enter: in_dpms = %d, mode = %d\n", + vigs_dev->in_dpms, + mode); + + if (vigs_dev->in_dpms) { + return; + } + + switch (mode) { + case DRM_MODE_DPMS_ON: + blank = FB_BLANK_UNBLANK; + break; + case DRM_MODE_DPMS_STANDBY: + blank = FB_BLANK_NORMAL; + break; + case DRM_MODE_DPMS_SUSPEND: + blank = FB_BLANK_VSYNC_SUSPEND; + break; + case DRM_MODE_DPMS_OFF: + blank = FB_BLANK_POWERDOWN; + break; + default: + DRM_ERROR("unspecified mode %d\n", mode); + return; + } + + event.info = vigs_dev->fbdev->base.fbdev; + event.data = ␣ + + /* + * We can't just 'console_lock' here, since + * this may result in deadlock: + * fb func: + * console_lock(); + * mutex_lock(&dev->mode_config.mutex); + * DRM func: + * mutex_lock(&dev->mode_config.mutex); + * console_lock(); + * + * So we just try to acquire it for 5 times with a delay + * and then just skip. + * + * This code is here only because pm is currently done via + * backlight which is bad, we need to make proper pm via + * kernel support. + */ + for (i = 0; i < 5; ++i) { + if (console_trylock()) { + /* + * We must set in_dpms to true while walking + * fb call chain because a callback inside the + * call chain might do FB_BLANK on its own, i.e. + * 'vigs_fbdev_dpms' might get called from here. To avoid + * this we set in_dpms to true and 'vigs_fbdev_dpms' + * checks this and returns. + */ + vigs_dev->in_dpms = true; + + fb_notifier_call_chain(FB_EVENT_BLANK, &event); + + vigs_dev->in_dpms = false; + + console_unlock(); + return; + } + msleep(100); + DRM_ERROR("unable to lock console, trying again\n"); + } + + DRM_ERROR("unable to lock console, skipping fb call chain\n"); +} + +static bool vigs_crtc_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + DRM_DEBUG_KMS("enter\n"); + + return true; +} + +static int vigs_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, + struct drm_framebuffer *old_fb) +{ + DRM_DEBUG_KMS("enter: x = %d, y = %d\n", x, y); + + return vigs_crtc_update(crtc, old_fb); +} + +static int vigs_crtc_mode_set(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, + int x, int y, + struct drm_framebuffer *old_fb) +{ + DRM_DEBUG_KMS("enter: x = %d, y = %d\n", x, y); + + return vigs_crtc_mode_set_base(crtc, x, y, old_fb); +} + +static void vigs_crtc_prepare(struct drm_crtc *crtc) +{ + DRM_DEBUG_KMS("enter\n"); +} + +static void vigs_crtc_commit(struct drm_crtc *crtc) +{ + DRM_DEBUG_KMS("enter\n"); +} + +static void vigs_crtc_load_lut(struct drm_crtc *crtc) +{ +} + +static int vigs_crtc_cursor_set(struct drm_crtc *crtc, + struct drm_file *file_priv, + uint32_t handle, + uint32_t width, + uint32_t height) +{ + /* + * Not supported. + */ + + return 0; +} + +static int vigs_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) +{ + /* + * Not supported. + */ + + return 0; +} + +static int vigs_crtc_page_flip(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct drm_pending_vblank_event *event, + uint32_t page_flip_flags) +{ + unsigned long flags; + struct vigs_device *vigs_dev = crtc->dev->dev_private; + struct drm_framebuffer *old_fb = crtc->fb; + int ret = -EINVAL; + + mutex_lock(&vigs_dev->drm_dev->struct_mutex); + + if (event) { + event->pipe = 0; + + ret = drm_vblank_get(vigs_dev->drm_dev, 0); + + if (ret != 0) { + DRM_ERROR("failed to acquire vblank counter\n"); + goto out; + } + + spin_lock_irqsave(&vigs_dev->drm_dev->event_lock, flags); + list_add_tail(&event->base.link, + &vigs_dev->pageflip_event_list); + spin_unlock_irqrestore(&vigs_dev->drm_dev->event_lock, flags); + + crtc->fb = fb; + ret = vigs_crtc_update(crtc, old_fb); + if (ret != 0) { + crtc->fb = old_fb; + spin_lock_irqsave(&vigs_dev->drm_dev->event_lock, flags); + if (atomic_read(&vigs_dev->drm_dev->vblank[0].refcount) > 0) { + /* + * Only do this if event wasn't already processed. + */ + drm_vblank_put(vigs_dev->drm_dev, 0); + list_del(&event->base.link); + } + spin_unlock_irqrestore(&vigs_dev->drm_dev->event_lock, flags); + goto out; + } + } + +out: + mutex_unlock(&vigs_dev->drm_dev->struct_mutex); + + return ret; +} + +static void vigs_crtc_disable(struct drm_crtc *crtc) +{ + struct vigs_device *vigs_dev = crtc->dev->dev_private; + struct vigs_framebuffer *vigs_fb; + + /* + * Framebuffer has been detached, notify the host that + * root surface is gone. + */ + + DRM_DEBUG_KMS("enter\n"); + + if (!crtc->fb) { + /* + * No current framebuffer, no need to notify the host. + */ + + return; + } + + vigs_fb = fb_to_vigs_fb(crtc->fb); + + vigs_comm_set_root_surface(vigs_dev->comm, 0, 0, 0); + + if (vigs_fb->surfaces[0]->scanout) { + vigs_framebuffer_unpin(vigs_fb); + } +} + +static const struct drm_crtc_funcs vigs_crtc_funcs = +{ + .cursor_set = vigs_crtc_cursor_set, + .cursor_move = vigs_crtc_cursor_move, + .set_config = drm_crtc_helper_set_config, + .page_flip = vigs_crtc_page_flip, + .destroy = vigs_crtc_destroy, +}; + +static const struct drm_crtc_helper_funcs vigs_crtc_helper_funcs = +{ + .dpms = vigs_crtc_dpms, + .mode_fixup = vigs_crtc_mode_fixup, + .mode_set = vigs_crtc_mode_set, + .mode_set_base = vigs_crtc_mode_set_base, + .prepare = vigs_crtc_prepare, + .commit = vigs_crtc_commit, + .load_lut = vigs_crtc_load_lut, + .disable = vigs_crtc_disable, +}; + +int vigs_crtc_init(struct vigs_device *vigs_dev) +{ + struct vigs_crtc *vigs_crtc; + int ret; + + DRM_DEBUG_KMS("enter\n"); + + vigs_crtc = kzalloc(sizeof(*vigs_crtc), GFP_KERNEL); + + if (!vigs_crtc) { + return -ENOMEM; + } + + ret = drm_crtc_init(vigs_dev->drm_dev, + &vigs_crtc->base, + &vigs_crtc_funcs); + + if (ret != 0) { + kfree(vigs_crtc); + return ret; + } + + drm_crtc_helper_add(&vigs_crtc->base, &vigs_crtc_helper_funcs); + + return 0; +} diff --git a/drivers/gpu/drm/vigs/vigs_crtc.h b/drivers/gpu/drm/vigs/vigs_crtc.h new file mode 100644 index 0000000..b6e6feb --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_crtc.h @@ -0,0 +1,20 @@ +#ifndef _VIGS_CRTC_H_ +#define _VIGS_CRTC_H_ + +#include "drmP.h" + +struct vigs_device; + +struct vigs_crtc +{ + struct drm_crtc base; +}; + +static inline struct vigs_crtc *crtc_to_vigs_crtc(struct drm_crtc *crtc) +{ + return container_of(crtc, struct vigs_crtc, base); +} + +int vigs_crtc_init(struct vigs_device *vigs_dev); + +#endif diff --git a/drivers/gpu/drm/vigs/vigs_device.c b/drivers/gpu/drm/vigs/vigs_device.c new file mode 100644 index 0000000..1e97673 --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_device.c @@ -0,0 +1,369 @@ +#include "vigs_device.h" +#include "vigs_mman.h" +#include "vigs_fenceman.h" +#include "vigs_crtc.h" +#include "vigs_output.h" +#include "vigs_plane.h" +#include "vigs_framebuffer.h" +#include "vigs_comm.h" +#include "vigs_fbdev.h" +#include "vigs_execbuffer.h" +#include "vigs_surface.h" +#include "vigs_dp.h" +#include + +extern const struct dma_buf_ops vigs_dmabuf_ops; + +static void vigs_device_mman_vram_to_gpu(void *user_data, + struct ttm_buffer_object *bo) +{ + struct vigs_device *vigs_dev = user_data; + struct vigs_gem_object *vigs_gem = bo_to_vigs_gem(bo); + struct vigs_surface *vigs_sfc = vigs_gem_to_vigs_surface(vigs_gem); + bool need_gpu_update = vigs_surface_need_gpu_update(vigs_sfc); + + if (!vigs_sfc->is_gpu_dirty && need_gpu_update) { + DRM_INFO("vram_to_gpu: 0x%llX\n", + drm_vma_node_offset_addr(&bo->vma_node)); + vigs_comm_update_gpu(vigs_dev->comm, + vigs_sfc->id, + vigs_sfc->width, + vigs_sfc->height, + vigs_gem_offset(vigs_gem)); + } else { + DRM_INFO("vram_to_gpu: 0x%llX (no-op)\n", + drm_vma_node_offset_addr(&bo->vma_node)); + } + + vigs_sfc->is_gpu_dirty = false; +} + +static void vigs_device_mman_gpu_to_vram(void *user_data, + struct ttm_buffer_object *bo, + unsigned long new_offset) +{ + struct vigs_device *vigs_dev = user_data; + struct vigs_gem_object *vigs_gem = bo_to_vigs_gem(bo); + struct vigs_surface *vigs_sfc = vigs_gem_to_vigs_surface(vigs_gem); + + if (vigs_surface_need_vram_update(vigs_sfc)) { + DRM_DEBUG_DRIVER("0x%llX\n", + drm_vma_node_offset_addr(&bo->vma_node)); + vigs_comm_update_vram(vigs_dev->comm, + vigs_sfc->id, + new_offset); + } else { + DRM_DEBUG_DRIVER("0x%llX (no-op)\n", + drm_vma_node_offset_addr(&bo->vma_node)); + } +} + +static void vigs_device_mman_init_vma(void *user_data, + void *vma_data_opaque, + struct ttm_buffer_object *bo, + bool track_access) +{ + struct vigs_vma_data *vma_data = vma_data_opaque; + struct vigs_gem_object *vigs_gem = bo_to_vigs_gem(bo); + + if (vigs_gem->type != VIGS_GEM_TYPE_SURFACE) { + vma_data->sfc = NULL; + return; + } + + vigs_vma_data_init(vma_data, + vigs_gem_to_vigs_surface(vigs_gem), + track_access); +} + +static void vigs_device_mman_cleanup_vma(void *user_data, + void *vma_data_opaque) +{ + struct vigs_vma_data *vma_data = vma_data_opaque; + + if (!vma_data->sfc) { + return; + } + + vigs_vma_data_cleanup(vma_data); +} + +static struct vigs_mman_ops mman_ops = +{ + .vram_to_gpu = &vigs_device_mman_vram_to_gpu, + .gpu_to_vram = &vigs_device_mman_gpu_to_vram, + .init_vma = &vigs_device_mman_init_vma, + .cleanup_vma = &vigs_device_mman_cleanup_vma +}; + +int vigs_device_init(struct vigs_device *vigs_dev, + struct drm_device *drm_dev, + struct pci_dev *pci_dev, + unsigned long flags) +{ + int ret; + u32 i; + + DRM_DEBUG_DRIVER("enter\n"); + + vigs_dev->dev = &pci_dev->dev; + vigs_dev->drm_dev = drm_dev; + vigs_dev->pci_dev = pci_dev; + + INIT_LIST_HEAD(&vigs_dev->pageflip_event_list); + + vigs_dev->vram_base = pci_resource_start(pci_dev, 0); + vigs_dev->vram_size = pci_resource_len(pci_dev, 0); + + vigs_dev->ram_base = pci_resource_start(pci_dev, 1); + vigs_dev->ram_size = pci_resource_len(pci_dev, 1); + + vigs_dev->io_base = pci_resource_start(pci_dev, 2); + vigs_dev->io_size = pci_resource_len(pci_dev, 2); + + idr_init(&vigs_dev->surface_idr); + mutex_init(&vigs_dev->surface_idr_mutex); + + if (!vigs_dev->vram_base || !vigs_dev->ram_base || !vigs_dev->io_base) { + DRM_ERROR("VRAM, RAM or IO bar not found on device\n"); + ret = -ENODEV; + goto fail1; + } + + if ((vigs_dev->io_size < sizeof(void*)) || + ((vigs_dev->io_size % sizeof(void*)) != 0)) { + DRM_ERROR("IO bar has bad size: %pa bytes\n", &vigs_dev->io_size); + ret = -ENODEV; + goto fail1; + } + + ret = drm_addmap(vigs_dev->drm_dev, + vigs_dev->io_base, + vigs_dev->io_size, + _DRM_REGISTERS, + 0, + &vigs_dev->io_map); + if (ret != 0) { + goto fail1; + } + + ret = vigs_mman_create(vigs_dev->vram_base, vigs_dev->vram_size, + vigs_dev->ram_base, vigs_dev->ram_size, + sizeof(struct vigs_vma_data), + &mman_ops, + vigs_dev, + &vigs_dev->mman); + + if (ret != 0) { + goto fail2; + } + + vigs_dev->obj_dev = ttm_object_device_init(vigs_dev->mman->mem_global_ref.object, + 12, &vigs_dmabuf_ops); + + if (!vigs_dev->obj_dev) { + DRM_ERROR("Unable to initialize obj_dev\n"); + ret = -ENOMEM; + goto fail3; + } + + ret = vigs_fenceman_create(&vigs_dev->fenceman); + + if (ret != 0) { + goto fail4; + } + + ret = vigs_dp_create(vigs_dev, &vigs_dev->dp); + + if (ret != 0) { + goto fail5; + } + + ret = vigs_comm_create(vigs_dev, &vigs_dev->comm); + + if (ret != 0) { + goto fail6; + } + + spin_lock_init(&vigs_dev->irq_lock); + + drm_mode_config_init(vigs_dev->drm_dev); + + vigs_framebuffer_config_init(vigs_dev); + + ret = vigs_crtc_init(vigs_dev); + + if (ret != 0) { + goto fail7; + } + + ret = vigs_output_init(vigs_dev); + + if (ret != 0) { + goto fail7; + } + + for (i = 0; i < VIGS_MAX_PLANES; ++i) { + ret = vigs_plane_init(vigs_dev, i); + + if (ret != 0) { + goto fail7; + } + } + + ret = drm_vblank_init(drm_dev, 1); + + if (ret != 0) { + goto fail7; + } + + /* + * We allow VBLANK interrupt disabling right from the start. There's + * no point in "waiting until first modeset". + */ + drm_dev->vblank_disable_allowed = 1; + + ret = drm_irq_install(drm_dev); + + if (ret != 0) { + goto fail8; + } + + ret = vigs_fbdev_create(vigs_dev, &vigs_dev->fbdev); + + if (ret != 0) { + goto fail9; + } + + return 0; + +fail9: + drm_irq_uninstall(drm_dev); +fail8: + drm_vblank_cleanup(drm_dev); +fail7: + drm_mode_config_cleanup(vigs_dev->drm_dev); + vigs_comm_destroy(vigs_dev->comm); +fail6: + vigs_dp_destroy(vigs_dev->dp); +fail5: + vigs_fenceman_destroy(vigs_dev->fenceman); +fail4: + ttm_object_device_release(&vigs_dev->obj_dev); +fail3: + vigs_mman_destroy(vigs_dev->mman); +fail2: + drm_rmmap(vigs_dev->drm_dev, vigs_dev->io_map); +fail1: + idr_destroy(&vigs_dev->surface_idr); + mutex_destroy(&vigs_dev->surface_idr_mutex); + + return ret; +} + +void vigs_device_cleanup(struct vigs_device *vigs_dev) +{ + DRM_DEBUG_DRIVER("enter\n"); + + vigs_fbdev_destroy(vigs_dev->fbdev); + drm_irq_uninstall(vigs_dev->drm_dev); + drm_vblank_cleanup(vigs_dev->drm_dev); + drm_mode_config_cleanup(vigs_dev->drm_dev); + vigs_comm_destroy(vigs_dev->comm); + vigs_dp_destroy(vigs_dev->dp); + vigs_fenceman_destroy(vigs_dev->fenceman); + ttm_object_device_release(&vigs_dev->obj_dev); + vigs_mman_destroy(vigs_dev->mman); + drm_rmmap(vigs_dev->drm_dev, vigs_dev->io_map); + idr_destroy(&vigs_dev->surface_idr); + mutex_destroy(&vigs_dev->surface_idr_mutex); +} + +int vigs_device_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct drm_file *file_priv = filp->private_data; + struct vigs_device *vigs_dev = file_priv->minor->dev->dev_private; + + if (vigs_dev == NULL) { + DRM_ERROR("no device\n"); + return -EINVAL; + } + + return vigs_mman_mmap(vigs_dev->mman, + filp, + vma, + vigs_dev->track_gem_access); +} + +int vigs_device_add_surface(struct vigs_device *vigs_dev, + struct vigs_surface *sfc, + vigsp_surface_id* id) +{ + int ret; + + mutex_lock(&vigs_dev->surface_idr_mutex); + + ret = idr_alloc(&vigs_dev->surface_idr, sfc, 1, 0, GFP_KERNEL); + + mutex_unlock(&vigs_dev->surface_idr_mutex); + + if (ret < 0) { + return ret; + } + + *id = ret; + + return 0; +} + +void vigs_device_remove_surface(struct vigs_device *vigs_dev, + vigsp_surface_id sfc_id) +{ + mutex_lock(&vigs_dev->surface_idr_mutex); + idr_remove(&vigs_dev->surface_idr, sfc_id); + mutex_unlock(&vigs_dev->surface_idr_mutex); +} + +struct vigs_surface + *vigs_device_reference_surface(struct vigs_device *vigs_dev, + vigsp_surface_id sfc_id) +{ + struct vigs_surface *sfc; + + mutex_lock(&vigs_dev->surface_idr_mutex); + + sfc = idr_find(&vigs_dev->surface_idr, sfc_id); + + if (sfc) { + if (vigs_gem_freed(&sfc->gem)) { + sfc = NULL; + } else { + drm_gem_object_reference(&sfc->gem.base); + } + } + + mutex_unlock(&vigs_dev->surface_idr_mutex); + + return sfc; +} + +int vigs_device_add_surface_unlocked(struct vigs_device *vigs_dev, + struct vigs_surface *sfc, + vigsp_surface_id* id) +{ + int ret; + + mutex_lock(&vigs_dev->drm_dev->struct_mutex); + ret = vigs_device_add_surface(vigs_dev, sfc, id); + mutex_unlock(&vigs_dev->drm_dev->struct_mutex); + + return ret; +} + +void vigs_device_remove_surface_unlocked(struct vigs_device *vigs_dev, + vigsp_surface_id sfc_id) +{ + mutex_lock(&vigs_dev->drm_dev->struct_mutex); + vigs_device_remove_surface(vigs_dev, sfc_id); + mutex_unlock(&vigs_dev->drm_dev->struct_mutex); +} diff --git a/drivers/gpu/drm/vigs/vigs_device.h b/drivers/gpu/drm/vigs/vigs_device.h new file mode 100644 index 0000000..d75f61c --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_device.h @@ -0,0 +1,107 @@ +#ifndef _VIGS_DEVICE_H_ +#define _VIGS_DEVICE_H_ + +#include "drmP.h" +#include "vigs_protocol.h" + +struct vigs_mman; +struct vigs_fenceman; +struct vigs_dp; +struct vigs_comm; +struct vigs_fbdev; +struct vigs_surface; + +struct vigs_device +{ + struct device *dev; + struct drm_device *drm_dev; + struct pci_dev *pci_dev; + + struct list_head pageflip_event_list; + + resource_size_t vram_base; + resource_size_t vram_size; + + resource_size_t ram_base; + resource_size_t ram_size; + + resource_size_t io_base; + resource_size_t io_size; + + struct idr surface_idr; + struct mutex surface_idr_mutex; + + /* Map of IO BAR. */ + drm_local_map_t *io_map; + + struct vigs_mman *mman; + + struct ttm_object_device *obj_dev; + + struct vigs_fenceman *fenceman; + + struct vigs_dp *dp; + + struct vigs_comm *comm; + + struct vigs_fbdev *fbdev; + + /* + * We need this because it's essential to read 'lower' and 'upper' + * fence acks atomically in IRQ handler and on SMP systems IRQ handler + * can be run on several CPUs concurrently. + */ + spinlock_t irq_lock; + + /* + * A hack we're forced to have in order to tell if we + * need to track GEM access or not in 'vigs_device_mmap'. + * current's 'mmap_sem' is write-locked while this is true, + * so no race will occur. + */ + bool track_gem_access; + + /* + * A hack to tell if DPMS callback is called from inside + * 'fb_blank' or vice-versa. + */ + bool in_dpms; +}; + +int vigs_device_init(struct vigs_device *vigs_dev, + struct drm_device *drm_dev, + struct pci_dev *pci_dev, + unsigned long flags); + +void vigs_device_cleanup(struct vigs_device *vigs_dev); + +int vigs_device_mmap(struct file *filp, struct vm_area_struct *vma); + +int vigs_device_add_surface(struct vigs_device *vigs_dev, + struct vigs_surface *sfc, + vigsp_surface_id* id); + +void vigs_device_remove_surface(struct vigs_device *vigs_dev, + vigsp_surface_id sfc_id); + +struct vigs_surface + *vigs_device_reference_surface(struct vigs_device *vigs_dev, + vigsp_surface_id sfc_id); + +/* + * Locks drm_device::struct_mutex. + * @{ + */ + +int vigs_device_add_surface_unlocked(struct vigs_device *vigs_dev, + struct vigs_surface *sfc, + vigsp_surface_id* id); + +void vigs_device_remove_surface_unlocked(struct vigs_device *vigs_dev, + vigsp_surface_id sfc_id); + +/* + * @} + */ + +#endif diff --git a/drivers/gpu/drm/vigs/vigs_dmabuf.c b/drivers/gpu/drm/vigs/vigs_dmabuf.c new file mode 100644 index 0000000..ba47bca --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_dmabuf.c @@ -0,0 +1,206 @@ +/************************************************************************** + * + * Based on drivers/gpu/drm/vmwgfx/vmwgfx_prime.c + * + * Copyright © 2013 VMware, Inc., Palo Alto, CA., USA + * Copyright © 2014 Samsung Electronics Co., Ltd. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#include "vigs_gem.h" +#include + +static int vigs_dmabuf_attach(struct dma_buf *dma_buf, + struct device *target_dev, + struct dma_buf_attachment *attach) +{ + DRM_DEBUG_PRIME("enter"); + + return -ENOSYS; +} + +static void vigs_dmabuf_detach(struct dma_buf *dma_buf, + struct dma_buf_attachment *attach) +{ + DRM_DEBUG_PRIME("enter"); +} + +static struct sg_table *vigs_dmabuf_map(struct dma_buf_attachment *attach, + enum dma_data_direction dir) +{ + DRM_DEBUG_PRIME("enter"); + + return ERR_PTR(-ENOSYS); +} + +static void vigs_dmabuf_unmap(struct dma_buf_attachment *attach, + struct sg_table *sgb, + enum dma_data_direction dir) +{ + DRM_DEBUG_PRIME("enter"); +} + +static void *vigs_dmabuf_kmap(struct dma_buf *dma_buf, + unsigned long page_num) +{ + DRM_DEBUG_PRIME("enter"); + + return NULL; +} + +static void *vigs_dmabuf_kmap_atomic(struct dma_buf *dma_buf, + unsigned long page_num) +{ + DRM_DEBUG_PRIME("enter"); + + return NULL; +} + +static void vigs_dmabuf_kunmap(struct dma_buf *dma_buf, + unsigned long page_num, + void *addr) +{ + DRM_DEBUG_PRIME("enter"); +} + +static void vigs_dmabuf_kunmap_atomic(struct dma_buf *dma_buf, + unsigned long page_num, + void *addr) +{ + DRM_DEBUG_PRIME("enter"); +} + +static int vigs_dmabuf_mmap(struct dma_buf *dma_buf, + struct vm_area_struct *vma) +{ + DRM_DEBUG_PRIME("enter"); + + return -ENOSYS; +} + +static void *vigs_dmabuf_vmap(struct dma_buf *dma_buf) +{ + DRM_DEBUG_PRIME("enter"); + + return NULL; +} + +static void vigs_dmabuf_vunmap(struct dma_buf *dma_buf, + void *vaddr) +{ + DRM_DEBUG_PRIME("enter"); +} + +static int vigs_dmabuf_begin_cpu_access(struct dma_buf *dma_buf, + size_t start, + size_t length, + enum dma_data_direction direction) +{ + DRM_DEBUG_PRIME("enter"); + + return 0; +} + +static void vigs_dmabuf_end_cpu_access(struct dma_buf *dma_buf, + size_t start, + size_t length, + enum dma_data_direction direction) +{ + DRM_DEBUG_PRIME("enter"); +} + +const struct dma_buf_ops vigs_dmabuf_ops = { + .attach = vigs_dmabuf_attach, + .detach = vigs_dmabuf_detach, + .map_dma_buf = vigs_dmabuf_map, + .unmap_dma_buf = vigs_dmabuf_unmap, + .release = drm_gem_dmabuf_release, + .kmap = vigs_dmabuf_kmap, + .kmap_atomic = vigs_dmabuf_kmap_atomic, + .kunmap = vigs_dmabuf_kunmap, + .kunmap_atomic = vigs_dmabuf_kunmap_atomic, + .mmap = vigs_dmabuf_mmap, + .vmap = vigs_dmabuf_vmap, + .vunmap = vigs_dmabuf_vunmap, + .begin_cpu_access = vigs_dmabuf_begin_cpu_access, + .end_cpu_access = vigs_dmabuf_end_cpu_access, +}; + +int vigs_prime_handle_to_fd(struct drm_device *dev, + struct drm_file *file_priv, + uint32_t handle, + uint32_t flags, + int *prime_fd) +{ + DRM_DEBUG_PRIME("enter"); + + return drm_gem_prime_handle_to_fd(dev, file_priv, handle, flags, prime_fd); +} + +int vigs_prime_fd_to_handle(struct drm_device *dev, + struct drm_file *file_priv, + int fd, + uint32_t *handle) +{ + DRM_DEBUG_PRIME("enter"); + + return drm_gem_prime_fd_to_handle(dev, file_priv, fd, handle); +} + +struct dma_buf *vigs_dmabuf_prime_export(struct drm_device *dev, + struct drm_gem_object *gem_obj, + int flags) +{ + struct vigs_gem_object *vigs_gem = gem_to_vigs_gem(gem_obj); + + DRM_DEBUG_PRIME("enter"); + + return dma_buf_export(gem_obj, + &vigs_dmabuf_ops, + vigs_gem_size(vigs_gem), + flags); +} + +struct drm_gem_object *vigs_dmabuf_prime_import(struct drm_device *dev, + struct dma_buf *dma_buf) +{ + struct drm_gem_object *obj; + + DRM_DEBUG_PRIME("enter"); + + if (dma_buf->ops == &vigs_dmabuf_ops) { + obj = dma_buf->priv; + + if (obj->dev == dev) { + /* + * Importing dmabuf exported from our own gem increases + * refcount on gem itself instead of f_count of dmabuf. + */ + drm_gem_object_reference(obj); + return obj; + } + } + + return ERR_PTR(-ENOSYS); +} diff --git a/drivers/gpu/drm/vigs/vigs_dmabuf.h b/drivers/gpu/drm/vigs/vigs_dmabuf.h new file mode 100644 index 0000000..6e0f819 --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_dmabuf.h @@ -0,0 +1,29 @@ +#ifndef _VIGS_DMABUF_H_ +#define _VIGS_DMABUF_H_ + +#include + +struct drm_device; +struct drm_file; +struct dma_buf; +struct drm_gem_object; + +int vigs_prime_handle_to_fd(struct drm_device *dev, + struct drm_file *file_priv, + uint32_t handle, + uint32_t flags, + int *prime_fd); + +int vigs_prime_fd_to_handle(struct drm_device *dev, + struct drm_file *file_priv, + int fd, + uint32_t *handle); + +struct dma_buf *vigs_dmabuf_prime_export(struct drm_device *dev, + struct drm_gem_object *gem_obj, + int flags); + +struct drm_gem_object *vigs_dmabuf_prime_import(struct drm_device *dev, + struct dma_buf *dma_buf); + +#endif /* _VIGS_DMABUF_H_ */ diff --git a/drivers/gpu/drm/vigs/vigs_dp.c b/drivers/gpu/drm/vigs/vigs_dp.c new file mode 100644 index 0000000..7680230 --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_dp.c @@ -0,0 +1,228 @@ +#include "vigs_dp.h" +#include "vigs_surface.h" +#include "vigs_device.h" + +int vigs_dp_create(struct vigs_device *vigs_dev, + struct vigs_dp **dp) +{ + int ret = 0; + + DRM_DEBUG_DRIVER("enter\n"); + + *dp = kzalloc(sizeof(**dp), GFP_KERNEL); + + if (!*dp) { + ret = -ENOMEM; + goto fail1; + } + + return 0; + +fail1: + *dp = NULL; + + return ret; +} + +void vigs_dp_destroy(struct vigs_dp *dp) +{ + DRM_DEBUG_DRIVER("enter\n"); + + kfree(dp); +} + +void vigs_dp_remove_surface(struct vigs_dp *dp, struct vigs_surface *sfc) +{ + int i, j; + + for (i = 0; i < VIGS_MAX_PLANES; ++i) { + for (j = 0; j < DRM_VIGS_NUM_DP_FB_BUF; ++j) { + if (dp->planes[i].fb_bufs[j].y == sfc) { + dp->planes[i].fb_bufs[j].y = NULL; + } + if (dp->planes[i].fb_bufs[j].c == sfc) { + dp->planes[i].fb_bufs[j].c = NULL; + } + } + } +} + +int vigs_dp_surface_create_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv) +{ + struct vigs_device *vigs_dev = drm_dev->dev_private; + struct vigs_dp *dp = vigs_dev->dp; + struct drm_vigs_dp_create_surface *args = data; + struct vigs_surface *sfc = NULL; + bool busy; + uint32_t handle; + int ret; + + if (args->dp_plane >= VIGS_MAX_PLANES) { + DRM_ERROR("bad DP plane = %u\n", args->dp_plane); + return -ENOMEM; + } + + if (args->dp_fb_buf >= DRM_VIGS_NUM_DP_FB_BUF) { + DRM_ERROR("bad DP fb buf = %u\n", args->dp_fb_buf); + return -ENOMEM; + } + + mutex_lock(&vigs_dev->drm_dev->struct_mutex); + + switch (args->dp_mem_flag) { + case DRM_VIGS_DP_FB_Y: + busy = dp->planes[args->dp_plane].fb_bufs[args->dp_fb_buf].y != NULL; + break; + case DRM_VIGS_DP_FB_C: + busy = dp->planes[args->dp_plane].fb_bufs[args->dp_fb_buf].c != NULL; + break; + default: + mutex_unlock(&vigs_dev->drm_dev->struct_mutex); + DRM_ERROR("bad DP mem flag = %u\n", args->dp_mem_flag); + return -ENOMEM; + } + + mutex_unlock(&vigs_dev->drm_dev->struct_mutex); + + if (busy) { + DRM_INFO("DP mem %u:%u:%u is busy\n", args->dp_plane, + args->dp_fb_buf, + args->dp_mem_flag); + return -ENOMEM; + } + + ret = vigs_surface_create(vigs_dev, + args->width, + args->height, + args->stride, + args->format, + false, + &sfc); + + if (ret != 0) { + return ret; + } + + /* + * Check busy again since DP mem might + * gotten busy while we were creating our surface. + * If it's not busy then occupy it. + */ + + mutex_lock(&vigs_dev->drm_dev->struct_mutex); + + switch (args->dp_mem_flag) { + case DRM_VIGS_DP_FB_Y: + if (dp->planes[args->dp_plane].fb_bufs[args->dp_fb_buf].y) { + busy = true; + } else { + dp->planes[args->dp_plane].fb_bufs[args->dp_fb_buf].y = sfc; + } + break; + case DRM_VIGS_DP_FB_C: + if (dp->planes[args->dp_plane].fb_bufs[args->dp_fb_buf].c) { + busy = true; + } else { + dp->planes[args->dp_plane].fb_bufs[args->dp_fb_buf].c = sfc; + } + break; + default: + drm_gem_object_unreference(&sfc->gem.base); + mutex_unlock(&vigs_dev->drm_dev->struct_mutex); + BUG(); + return -ENOMEM; + } + + mutex_unlock(&vigs_dev->drm_dev->struct_mutex); + + if (busy) { + drm_gem_object_unreference_unlocked(&sfc->gem.base); + + DRM_INFO("DP mem %u:%u:%u is busy\n", args->dp_plane, + args->dp_fb_buf, + args->dp_mem_flag); + return -ENOMEM; + } + + ret = drm_gem_handle_create(file_priv, + &sfc->gem.base, + &handle); + + if (ret == 0) { + args->handle = handle; + args->size = vigs_gem_size(&sfc->gem); + args->id = sfc->id; + } else { + /* + * Don't bother setting DP mem slot to NULL here, DRM + * will do this for us once the GEM is freed. + */ + } + + drm_gem_object_unreference_unlocked(&sfc->gem.base); + + return ret; +} + +int vigs_dp_surface_open_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv) +{ + struct vigs_device *vigs_dev = drm_dev->dev_private; + struct vigs_dp *dp = vigs_dev->dp; + struct drm_vigs_dp_open_surface *args = data; + struct vigs_surface *sfc = NULL; + uint32_t handle; + int ret; + + if (args->dp_plane >= VIGS_MAX_PLANES) { + DRM_ERROR("bad DP plane = %u\n", args->dp_plane); + return -ENOMEM; + } + + if (args->dp_fb_buf >= DRM_VIGS_NUM_DP_FB_BUF) { + DRM_ERROR("bad DP fb buf = %u\n", args->dp_fb_buf); + return -ENOMEM; + } + + mutex_lock(&vigs_dev->drm_dev->struct_mutex); + + switch (args->dp_mem_flag) { + case DRM_VIGS_DP_FB_Y: + sfc = dp->planes[args->dp_plane].fb_bufs[args->dp_fb_buf].y; + break; + case DRM_VIGS_DP_FB_C: + sfc = dp->planes[args->dp_plane].fb_bufs[args->dp_fb_buf].c; + break; + default: + mutex_unlock(&vigs_dev->drm_dev->struct_mutex); + DRM_ERROR("bad DP mem flag = %u\n", args->dp_mem_flag); + return -ENOMEM; + } + + if (sfc) { + drm_gem_object_reference(&sfc->gem.base); + } else { + mutex_unlock(&vigs_dev->drm_dev->struct_mutex); + DRM_INFO("DP mem %u:%u:%u is empty\n", args->dp_plane, + args->dp_fb_buf, + args->dp_mem_flag); + return -ENOMEM; + } + + mutex_unlock(&vigs_dev->drm_dev->struct_mutex); + + ret = drm_gem_handle_create(file_priv, + &sfc->gem.base, + &handle); + + if (ret == 0) { + args->handle = handle; + } + + drm_gem_object_unreference_unlocked(&sfc->gem.base); + + return ret; +} diff --git a/drivers/gpu/drm/vigs/vigs_dp.h b/drivers/gpu/drm/vigs/vigs_dp.h new file mode 100644 index 0000000..46093ec --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_dp.h @@ -0,0 +1,72 @@ +#ifndef _VIGS_DP_H_ +#define _VIGS_DP_H_ + +#include "drmP.h" +#include "vigs_protocol.h" +#include + +struct vigs_device; +struct vigs_surface; + +struct vigs_dp_fb_buf +{ + /* + * These are weak pointers, no reference is kept + * for them. When surface is destroyed they're + * automatically reset to NULL. Must be + * accessed only with drm_device::struct_mutex held. + * @{ + */ + + struct vigs_surface *y; + struct vigs_surface *c; + + /* + * @} + */ +}; + +struct vigs_dp_plane +{ + struct vigs_dp_fb_buf fb_bufs[DRM_VIGS_NUM_DP_FB_BUF]; +}; + +struct vigs_dp +{ + struct vigs_dp_plane planes[VIGS_MAX_PLANES]; +}; + +int vigs_dp_create(struct vigs_device *vigs_dev, + struct vigs_dp **dp); + +void vigs_dp_destroy(struct vigs_dp *dp); + +/* + * Must be called with drm_device::struct_mutex held. + * @{ + */ + +void vigs_dp_remove_surface(struct vigs_dp *dp, struct vigs_surface *sfc); + +/* + * @} + */ + +/* + * IOCTLs + * @{ + */ + +int vigs_dp_surface_create_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv); + +int vigs_dp_surface_open_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv); + +/* + * @} + */ + +#endif diff --git a/drivers/gpu/drm/vigs/vigs_driver.c b/drivers/gpu/drm/vigs/vigs_driver.c new file mode 100644 index 0000000..4b10f7c --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_driver.c @@ -0,0 +1,269 @@ +#include "vigs_driver.h" +#include "vigs_gem.h" +#include "vigs_device.h" +#include "vigs_fbdev.h" +#include "vigs_comm.h" +#include "vigs_surface.h" +#include "vigs_execbuffer.h" +#include "vigs_irq.h" +#include "vigs_fence.h" +#include "vigs_file.h" +#include "vigs_plane.h" +#include "vigs_mman.h" +#include "vigs_dp.h" +#include "vigs_dmabuf.h" +#include +#include +#include + +#define PCI_VENDOR_ID_VIGS 0x19B2 +#define PCI_DEVICE_ID_VIGS 0x1011 + +#define DRIVER_NAME "vigs" +#define DRIVER_DESC "VIGS DRM" +#define DRIVER_DATE "20121102" +#define DRIVER_MAJOR DRM_VIGS_DRIVER_VERSION +#define DRIVER_MINOR 0 + +static struct pci_device_id vigs_pci_table[] = +{ + { + .vendor = PCI_VENDOR_ID_VIGS, + .device = PCI_DEVICE_ID_VIGS, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { 0 } +}; +MODULE_DEVICE_TABLE(pci, vigs_pci_table); + +static struct drm_ioctl_desc vigs_drm_ioctls[] = +{ + DRM_IOCTL_DEF_DRV(VIGS_GET_PROTOCOL_VERSION, vigs_comm_get_protocol_version_ioctl, + DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VIGS_CREATE_SURFACE, vigs_surface_create_ioctl, + DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VIGS_CREATE_EXECBUFFER, vigs_execbuffer_create_ioctl, + DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VIGS_GEM_MAP, vigs_gem_map_ioctl, + DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VIGS_GEM_WAIT, vigs_gem_wait_ioctl, + DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VIGS_SURFACE_INFO, vigs_surface_info_ioctl, + DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VIGS_EXEC, vigs_execbuffer_exec_ioctl, + DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VIGS_SURFACE_SET_GPU_DIRTY, vigs_surface_set_gpu_dirty_ioctl, + DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VIGS_SURFACE_START_ACCESS, vigs_surface_start_access_ioctl, + DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VIGS_SURFACE_END_ACCESS, vigs_surface_end_access_ioctl, + DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VIGS_CREATE_FENCE, vigs_fence_create_ioctl, + DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VIGS_FENCE_WAIT, vigs_fence_wait_ioctl, + DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VIGS_FENCE_SIGNALED, vigs_fence_signaled_ioctl, + DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VIGS_FENCE_UNREF, vigs_fence_unref_ioctl, + DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VIGS_PLANE_SET_ZPOS, vigs_plane_set_zpos_ioctl, + DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VIGS_PLANE_SET_TRANSFORM, vigs_plane_set_transform_ioctl, + DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VIGS_DP_CREATE_SURFACE, vigs_dp_surface_create_ioctl, + DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VIGS_DP_OPEN_SURFACE, vigs_dp_surface_open_ioctl, + DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), +}; + +static const struct file_operations vigs_drm_driver_fops = +{ + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = drm_ioctl, + .poll = drm_poll, + .mmap = vigs_device_mmap, + .read = drm_read +}; + +static int vigs_drm_load(struct drm_device *dev, unsigned long flags) +{ + int ret = 0; + struct vigs_device *vigs_dev = NULL; + + DRM_DEBUG_DRIVER("enter\n"); + + vigs_dev = kzalloc(sizeof(*vigs_dev), GFP_KERNEL); + + if (vigs_dev == NULL) { + DRM_ERROR("failed to allocate VIGS device\n"); + return -ENOMEM; + } + + dev->dev_private = vigs_dev; + + ret = vigs_device_init(vigs_dev, dev, dev->pdev, flags); + + if (ret != 0) { + goto fail; + } + + return 0; + +fail: + kfree(vigs_dev); + + return ret; +} + +static int vigs_drm_unload(struct drm_device *dev) +{ + struct vigs_device *vigs_dev = dev->dev_private; + + DRM_DEBUG_DRIVER("enter\n"); + + vigs_device_cleanup(vigs_dev); + + kfree(dev->dev_private); + dev->dev_private = NULL; + + return 0; +} + +static int vigs_drm_open(struct drm_device *dev, struct drm_file *file_priv) +{ + int ret = 0; + struct vigs_device *vigs_dev = dev->dev_private; + struct vigs_file *vigs_file; + + DRM_DEBUG_DRIVER("enter\n"); + + ret = vigs_file_create(vigs_dev, &vigs_file); + + if (ret != 0) { + return ret; + } + + file_priv->driver_priv = vigs_file; + + vigs_dev->mman->bo_dev.dev_mapping = dev->dev_mapping; + + return 0; +} + +static void vigs_drm_preclose(struct drm_device *dev, + struct drm_file *file_priv) +{ + struct vigs_device *vigs_dev = dev->dev_private; + struct drm_pending_vblank_event *event, *tmp; + unsigned long flags; + + DRM_DEBUG_DRIVER("enter\n"); + + spin_lock_irqsave(&dev->event_lock, flags); + + list_for_each_entry_safe(event, tmp, + &vigs_dev->pageflip_event_list, + base.link) { + if (event->base.file_priv == file_priv) { + list_del(&event->base.link); + event->base.destroy(&event->base); + } + } + + spin_unlock_irqrestore(&dev->event_lock, flags); +} + +static void vigs_drm_postclose(struct drm_device *dev, + struct drm_file *file_priv) +{ + struct vigs_file *vigs_file = file_priv->driver_priv; + + DRM_DEBUG_DRIVER("enter\n"); + + vigs_file_destroy(vigs_file); + + file_priv->driver_priv = NULL; +} + +static void vigs_drm_lastclose(struct drm_device *dev) +{ + struct vigs_device *vigs_dev = dev->dev_private; + + DRM_DEBUG_DRIVER("enter\n"); + + if (vigs_dev->fbdev) { + vigs_fbdev_restore_mode(vigs_dev->fbdev); + } + + vigs_comm_reset(vigs_dev->comm); +} + +static struct drm_driver vigs_drm_driver = +{ + .driver_features = DRIVER_GEM | DRIVER_MODESET | + DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | + DRIVER_RENDER | DRIVER_PRIME, + .load = vigs_drm_load, + .unload = vigs_drm_unload, + .open = vigs_drm_open, + .preclose = vigs_drm_preclose, + .postclose = vigs_drm_postclose, + .lastclose = vigs_drm_lastclose, + .get_vblank_counter = drm_vblank_count, + .enable_vblank = vigs_enable_vblank, + .disable_vblank = vigs_disable_vblank, + .irq_handler = vigs_irq_handler, + .gem_free_object = vigs_gem_free_object, + .gem_open_object = vigs_gem_open_object, + .gem_close_object = vigs_gem_close_object, + + .prime_handle_to_fd = vigs_prime_handle_to_fd, + .prime_fd_to_handle = vigs_prime_fd_to_handle, + .gem_prime_export = vigs_dmabuf_prime_export, + .gem_prime_import = vigs_dmabuf_prime_import, + + .dumb_create = vigs_gem_dumb_create, + .dumb_map_offset = vigs_gem_dumb_map_offset, + .dumb_destroy = vigs_gem_dumb_destroy, + .ioctls = vigs_drm_ioctls, + .num_ioctls = DRM_ARRAY_SIZE(vigs_drm_ioctls), + .fops = &vigs_drm_driver_fops, + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, +}; + +static int vigs_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + return drm_get_pci_dev(pdev, ent, &vigs_drm_driver); +} + +static void vigs_pci_remove(struct pci_dev *pdev) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + + drm_put_dev(dev); +} + +static struct pci_driver vigs_pci_driver = +{ + .name = DRIVER_NAME, + .id_table = vigs_pci_table, + .probe = vigs_pci_probe, + .remove = vigs_pci_remove, +}; + +int vigs_driver_register(void) +{ + return drm_pci_init(&vigs_drm_driver, &vigs_pci_driver); +} + +void vigs_driver_unregister(void) +{ + drm_pci_exit(&vigs_drm_driver, &vigs_pci_driver); +} diff --git a/drivers/gpu/drm/vigs/vigs_driver.h b/drivers/gpu/drm/vigs/vigs_driver.h new file mode 100644 index 0000000..4cd8374 --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_driver.h @@ -0,0 +1,10 @@ +#ifndef _VIGS_DRIVER_H_ +#define _VIGS_DRIVER_H_ + +#include + +int vigs_driver_register(void); + +void vigs_driver_unregister(void); + +#endif diff --git a/drivers/gpu/drm/vigs/vigs_execbuffer.c b/drivers/gpu/drm/vigs/vigs_execbuffer.c new file mode 100644 index 0000000..d609448 --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_execbuffer.c @@ -0,0 +1,548 @@ +#include "vigs_execbuffer.h" +#include "vigs_device.h" +#include "vigs_surface.h" +#include "vigs_comm.h" +#include "vigs_fence.h" +#include + +union vigs_request +{ + struct vigsp_cmd_update_vram_request *update_vram; + struct vigsp_cmd_update_gpu_request *update_gpu; + struct vigsp_cmd_copy_request *copy; + struct vigsp_cmd_solid_fill_request *solid_fill; + struct vigsp_cmd_ga_copy_request *ga_copy; + void *data; +}; + +static int vigs_execbuffer_validate_buffer(struct vigs_device *vigs_dev, + struct vigs_validate_buffer *buffer, + struct list_head* list, + vigsp_surface_id sfc_id, + vigsp_cmd cmd, + int which, + void *data) +{ + struct vigs_surface *sfc = vigs_device_reference_surface(vigs_dev, sfc_id); + struct vigs_validate_buffer *tmp; + + if (!sfc) { + DRM_ERROR("Surface %u not found\n", sfc_id); + return -EINVAL; + } + + buffer->base.bo = &sfc->gem.bo; + buffer->cmd = cmd; + buffer->which = which; + buffer->data = data; + + list_for_each_entry(tmp, list, base.head) { + if (tmp->base.bo == buffer->base.bo) { + /* + * Already on the list, we're done. + */ + return 0; + } + } + + list_add_tail(&buffer->base.head, list); + + return 0; +} + +static void vigs_execbuffer_clear_validation(struct vigs_validate_buffer *buffer) +{ + struct vigs_gem_object *gem = bo_to_vigs_gem(buffer->base.bo); + + drm_gem_object_unreference(&gem->base); +} + +static void vigs_execbuffer_destroy(struct vigs_gem_object *gem) +{ +} + +int vigs_execbuffer_create(struct vigs_device *vigs_dev, + unsigned long size, + bool kernel, + struct vigs_execbuffer **execbuffer) +{ + int ret = 0; + + *execbuffer = kzalloc(sizeof(**execbuffer), GFP_KERNEL); + + if (!*execbuffer) { + ret = -ENOMEM; + goto fail1; + } + + ret = vigs_gem_init(&(*execbuffer)->gem, + vigs_dev, + VIGS_GEM_TYPE_EXECBUFFER, + size, + kernel, + &vigs_execbuffer_destroy); + + if (ret != 0) { + goto fail1; + } + + return 0; + +fail1: + *execbuffer = NULL; + + return ret; +} + +int vigs_execbuffer_validate_buffers(struct vigs_execbuffer *execbuffer, + struct list_head* list, + struct vigs_validate_buffer **buffers, + int *num_buffers, + bool *sync) +{ + struct vigs_device *vigs_dev = execbuffer->gem.base.dev->dev_private; + void *data = execbuffer->gem.kptr; + u32 data_size = vigs_gem_size(&execbuffer->gem); + struct vigsp_cmd_batch_header *batch_header = data; + struct vigsp_cmd_request_header *request_header = + (struct vigsp_cmd_request_header*)(batch_header + 1); + union vigs_request request; + int num_commands = 0, ret = 0; + + *num_buffers = 0; + *sync = false; + + /* + * GEM is always at least PAGE_SIZE long, so don't check + * if batch header is out of bounds. + */ + + while ((void*)request_header < + ((void*)(batch_header + 1) + batch_header->size)) { + if (((void*)(request_header) + sizeof(*request_header)) > + (data + data_size)) { + DRM_ERROR("request header outside of GEM\n"); + ret = -EINVAL; + goto fail1; + } + + if (((void*)(request_header + 1) + request_header->size) > + (data + data_size)) { + DRM_ERROR("request data outside of GEM\n"); + ret = -EINVAL; + goto fail1; + } + + request.data = (request_header + 1); + + switch (request_header->cmd) { + case vigsp_cmd_update_vram: + case vigsp_cmd_update_gpu: + *sync = true; + *num_buffers += 1; + break; + case vigsp_cmd_copy: + *num_buffers += 2; + break; + case vigsp_cmd_solid_fill: + *num_buffers += 1; + break; + case vigsp_cmd_ga_copy: + *num_buffers += 2; + break; + default: + break; + } + + request_header = + (struct vigsp_cmd_request_header*)(request.data + + request_header->size); + + ++num_commands; + } + + *buffers = kmalloc(*num_buffers * sizeof(**buffers), GFP_KERNEL); + + if (!*buffers) { + ret = -ENOMEM; + goto fail1; + } + + request_header = (struct vigsp_cmd_request_header*)(batch_header + 1); + + mutex_lock(&vigs_dev->drm_dev->struct_mutex); + + *num_buffers = 0; + + while (--num_commands >= 0) { + request.data = (request_header + 1); + + switch (request_header->cmd) { + case vigsp_cmd_update_vram: + ret = vigs_execbuffer_validate_buffer(vigs_dev, + &(*buffers)[*num_buffers], + list, + request.update_vram->sfc_id, + request_header->cmd, + 0, + request.data); + + if (ret != 0) { + goto fail2; + } + + ++*num_buffers; + + break; + case vigsp_cmd_update_gpu: + ret = vigs_execbuffer_validate_buffer(vigs_dev, + &(*buffers)[*num_buffers], + list, + request.update_gpu->sfc_id, + request_header->cmd, + 0, + request.data); + + if (ret != 0) { + goto fail2; + } + + ++*num_buffers; + + break; + case vigsp_cmd_copy: + ret = vigs_execbuffer_validate_buffer(vigs_dev, + &(*buffers)[*num_buffers], + list, + request.copy->src_id, + request_header->cmd, + 0, + request.data); + + if (ret != 0) { + goto fail2; + } + + ++*num_buffers; + + ret = vigs_execbuffer_validate_buffer(vigs_dev, + &(*buffers)[*num_buffers], + list, + request.copy->dst_id, + request_header->cmd, + 1, + request.data); + + if (ret != 0) { + goto fail2; + } + + ++*num_buffers; + + break; + case vigsp_cmd_solid_fill: + ret = vigs_execbuffer_validate_buffer(vigs_dev, + &(*buffers)[*num_buffers], + list, + request.solid_fill->sfc_id, + request_header->cmd, + 0, + request.data); + + if (ret != 0) { + goto fail2; + } + + ++*num_buffers; + + break; + case vigsp_cmd_ga_copy: + ret = vigs_execbuffer_validate_buffer(vigs_dev, + &(*buffers)[*num_buffers], + list, + request.ga_copy->src_id, + request_header->cmd, + 0, + request.data); + + if (ret != 0) { + goto fail2; + } + + ++*num_buffers; + + ret = vigs_execbuffer_validate_buffer(vigs_dev, + &(*buffers)[*num_buffers], + list, + request.ga_copy->dst_id, + request_header->cmd, + 1, + request.data); + + if (ret != 0) { + goto fail2; + } + + ++*num_buffers; + + break; + default: + break; + } + + request_header = + (struct vigsp_cmd_request_header*)(request.data + + request_header->size); + } + + mutex_unlock(&vigs_dev->drm_dev->struct_mutex); + + return 0; + +fail2: + while (--*num_buffers >= 0) { + vigs_execbuffer_clear_validation(&(*buffers)[*num_buffers]); + } + mutex_unlock(&vigs_dev->drm_dev->struct_mutex); + kfree(*buffers); +fail1: + *buffers = NULL; + + return ret; +} + +void vigs_execbuffer_process_buffers(struct vigs_execbuffer *execbuffer, + struct vigs_validate_buffer *buffers, + int num_buffers, + bool *sync) +{ + union vigs_request request; + struct vigs_gem_object *gem; + struct vigs_surface *sfc; + int i; + + for (i = 0; i < num_buffers; ++i) { + request.data = buffers[i].data; + gem = bo_to_vigs_gem(buffers[i].base.bo); + sfc = vigs_gem_to_vigs_surface(gem); + + switch (buffers[i].cmd) { + case vigsp_cmd_update_vram: + if (vigs_gem_in_vram(&sfc->gem)) { + if (vigs_surface_need_vram_update(sfc)) { + request.update_vram->offset = vigs_gem_offset(&sfc->gem); + sfc->is_gpu_dirty = false; + } else { + DRM_DEBUG_DRIVER("Surface %u doesn't need to be updated, ignoring update_vram\n", + request.update_vram->sfc_id); + request.update_vram->sfc_id = 0; + } + } else { + DRM_DEBUG_DRIVER("Surface %u not in VRAM, ignoring update_vram\n", + request.update_vram->sfc_id); + request.update_vram->sfc_id = 0; + } + break; + case vigsp_cmd_update_gpu: + if (vigs_gem_in_vram(&sfc->gem)) { + if (vigs_surface_need_gpu_update(sfc)) { + request.update_gpu->offset = vigs_gem_offset(&sfc->gem); + sfc->is_gpu_dirty = false; + } else { + DRM_DEBUG_DRIVER("Surface %u doesn't need to be updated, ignoring update_gpu\n", + request.update_gpu->sfc_id); + request.update_gpu->sfc_id = 0; + } + } else { + DRM_DEBUG_DRIVER("Surface %u not in VRAM, ignoring update_gpu\n", + request.update_gpu->sfc_id); + request.update_gpu->sfc_id = 0; + } + break; + case vigsp_cmd_copy: + if (buffers[i].which && vigs_gem_in_vram(&sfc->gem)) { + sfc->is_gpu_dirty = true; + } + break; + case vigsp_cmd_solid_fill: + if (vigs_gem_in_vram(&sfc->gem)) { + sfc->is_gpu_dirty = true; + } + break; + case vigsp_cmd_ga_copy: + if (buffers[i].which && vigs_gem_in_vram(&sfc->gem)) { + sfc->is_gpu_dirty = true; + } else if (buffers[i].which == 0) { + if (vigs_gem_in_vram(&sfc->gem)) { + request.ga_copy->src_scanout = true; + request.ga_copy->src_offset = vigs_gem_offset(&sfc->gem); + *sync = true; + } else { + request.ga_copy->src_scanout = false; + request.ga_copy->src_offset = 0; + } + } + break; + default: + break; + } + } +} + +void vigs_execbuffer_fence(struct vigs_execbuffer *execbuffer, + struct vigs_fence *fence) +{ + struct vigsp_cmd_batch_header *batch_header = execbuffer->gem.kptr; + + batch_header->fence_seq = fence->seq; +} + +void vigs_execbuffer_clear_validations(struct vigs_execbuffer *execbuffer, + struct vigs_validate_buffer *buffers, + int num_buffers) +{ + struct vigs_device *vigs_dev = execbuffer->gem.base.dev->dev_private; + int i; + + mutex_lock(&vigs_dev->drm_dev->struct_mutex); + + for (i = 0; i < num_buffers; ++i) { + vigs_execbuffer_clear_validation(&buffers[i]); + } + + mutex_unlock(&vigs_dev->drm_dev->struct_mutex); + + kfree(buffers); +} + +int vigs_execbuffer_create_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv) +{ + struct vigs_device *vigs_dev = drm_dev->dev_private; + struct drm_vigs_create_execbuffer *args = data; + struct vigs_execbuffer *execbuffer = NULL; + uint32_t handle; + int ret; + + ret = vigs_execbuffer_create(vigs_dev, + args->size, + false, + &execbuffer); + + if (ret != 0) { + return ret; + } + + ret = drm_gem_handle_create(file_priv, + &execbuffer->gem.base, + &handle); + + drm_gem_object_unreference_unlocked(&execbuffer->gem.base); + + if (ret == 0) { + args->size = vigs_gem_size(&execbuffer->gem); + args->handle = handle; + } + + return ret; +} + +int vigs_execbuffer_exec_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv) +{ + struct vigs_device *vigs_dev = drm_dev->dev_private; + struct drm_vigs_exec *args = data; + struct drm_gem_object *gem; + struct vigs_gem_object *vigs_gem; + struct vigs_execbuffer *execbuffer; + struct ww_acquire_ctx ticket; + struct list_head list; + struct vigs_validate_buffer *buffers; + int num_buffers = 0; + struct vigs_fence *fence = NULL; + bool sync = false; + int ret = 0; + + INIT_LIST_HEAD(&list); + + gem = drm_gem_object_lookup(drm_dev, file_priv, args->handle); + + if (gem == NULL) { + ret = -ENOENT; + goto out1; + } + + vigs_gem = gem_to_vigs_gem(gem); + + if (vigs_gem->type != VIGS_GEM_TYPE_EXECBUFFER) { + ret = -ENOENT; + goto out2; + } + + execbuffer = vigs_gem_to_vigs_execbuffer(vigs_gem); + + vigs_gem_reserve(vigs_gem); + + /* + * Never unmap for optimization, but we got to be careful, + * worst case scenario is when whole RAM BAR is mapped into kernel. + */ + ret = vigs_gem_kmap(vigs_gem); + + if (ret != 0) { + vigs_gem_unreserve(vigs_gem); + goto out2; + } + + vigs_gem_unreserve(vigs_gem); + + ret = vigs_execbuffer_validate_buffers(execbuffer, + &list, + &buffers, + &num_buffers, + &sync); + + if (ret != 0) { + goto out2; + } + + if (list_empty(&list)) { + vigs_comm_exec(vigs_dev->comm, execbuffer); + } else { + ret = ttm_eu_reserve_buffers(&ticket, &list); + + if (ret != 0) { + goto out3; + } + + ret = vigs_fence_create(vigs_dev->fenceman, &fence); + + if (ret != 0) { + ttm_eu_backoff_reservation(&ticket, &list); + goto out3; + } + + vigs_execbuffer_process_buffers(execbuffer, buffers, num_buffers, &sync); + + vigs_execbuffer_fence(execbuffer, fence); + + vigs_comm_exec(vigs_dev->comm, execbuffer); + + ttm_eu_fence_buffer_objects(&ticket, &list, fence); + + if (sync) { + vigs_fence_wait(fence, false); + } + + vigs_fence_unref(fence); + } + +out3: + vigs_execbuffer_clear_validations(execbuffer, buffers, num_buffers); +out2: + drm_gem_object_unreference_unlocked(gem); +out1: + return ret; +} diff --git a/drivers/gpu/drm/vigs/vigs_execbuffer.h b/drivers/gpu/drm/vigs/vigs_execbuffer.h new file mode 100644 index 0000000..0564954 --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_execbuffer.h @@ -0,0 +1,75 @@ +#ifndef _VIGS_EXECBUFFER_H_ +#define _VIGS_EXECBUFFER_H_ + +#include "drmP.h" +#include "vigs_gem.h" +#include "vigs_protocol.h" +#include + +struct vigs_fence; + +struct vigs_validate_buffer +{ + struct ttm_validate_buffer base; + + vigsp_cmd cmd; + + int which; + + void *data; +}; + +struct vigs_execbuffer +{ + /* + * Must be first member! + */ + struct vigs_gem_object gem; +}; + +static inline struct vigs_execbuffer *vigs_gem_to_vigs_execbuffer(struct vigs_gem_object *vigs_gem) +{ + return container_of(vigs_gem, struct vigs_execbuffer, gem); +} + +int vigs_execbuffer_create(struct vigs_device *vigs_dev, + unsigned long size, + bool kernel, + struct vigs_execbuffer **execbuffer); + +int vigs_execbuffer_validate_buffers(struct vigs_execbuffer *execbuffer, + struct list_head* list, + struct vigs_validate_buffer **buffers, + int *num_buffers, + bool *sync); + +void vigs_execbuffer_process_buffers(struct vigs_execbuffer *execbuffer, + struct vigs_validate_buffer *buffers, + int num_buffers, + bool *sync); + +void vigs_execbuffer_fence(struct vigs_execbuffer *execbuffer, + struct vigs_fence *fence); + +void vigs_execbuffer_clear_validations(struct vigs_execbuffer *execbuffer, + struct vigs_validate_buffer *buffers, + int num_buffers); + +/* + * IOCTLs + * @{ + */ + +int vigs_execbuffer_create_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv); + +int vigs_execbuffer_exec_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv); + +/* + * @} + */ + +#endif diff --git a/drivers/gpu/drm/vigs/vigs_fbdev.c b/drivers/gpu/drm/vigs/vigs_fbdev.c new file mode 100644 index 0000000..20d1a8f --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_fbdev.c @@ -0,0 +1,566 @@ +#include "vigs_fbdev.h" +#include "vigs_device.h" +#include "vigs_surface.h" +#include "vigs_framebuffer.h" +#include "vigs_output.h" +#include "vigs_crtc.h" +#include "drm_crtc_helper.h" +#include + +/* + * From drm_fb_helper.c, modified to work with 'regno' > 16. + * @{ + */ + +static bool vigs_fbdev_helper_is_bound(struct drm_fb_helper *fb_helper) +{ + struct drm_device *dev = fb_helper->dev; + struct drm_crtc *crtc; + int bound = 0, crtcs_bound = 0; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + if (crtc->fb) { + crtcs_bound++; + } + + if (crtc->fb == fb_helper->fb) { + bound++; + } + } + + if (bound < crtcs_bound) { + return false; + } + + return true; +} + +static int vigs_fbdev_setcolreg(struct drm_crtc *crtc, u16 red, u16 green, + u16 blue, u16 regno, struct fb_info *fbi) +{ + struct drm_fb_helper *fb_helper = fbi->par; + struct drm_framebuffer *fb = fb_helper->fb; + int pindex; + + if (fbi->fix.visual == FB_VISUAL_TRUECOLOR) { + u32 *palette; + u32 value; + /* place color in psuedopalette */ + if (regno <= 16) { + palette = (u32*)fbi->pseudo_palette; + red >>= (16 - fbi->var.red.length); + green >>= (16 - fbi->var.green.length); + blue >>= (16 - fbi->var.blue.length); + value = (red << fbi->var.red.offset) | + (green << fbi->var.green.offset) | + (blue << fbi->var.blue.offset); + if (fbi->var.transp.length > 0) { + u32 mask = (1 << fbi->var.transp.length) - 1; + mask <<= fbi->var.transp.offset; + value |= mask; + } + palette[regno] = value; + } + return 0; + } + + /* + * The driver really shouldn't advertise pseudo/directcolor + * visuals if it can't deal with the palette. + */ + if (WARN_ON(!fb_helper->funcs->gamma_set || + !fb_helper->funcs->gamma_get)) { + return -EINVAL; + } + + pindex = regno; + + if (fb->bits_per_pixel == 16) { + pindex = regno << 3; + + if ((fb->depth == 16) && (regno > 63)) { + return -EINVAL; + } + + if ((fb->depth == 15) && (regno > 31)) { + return -EINVAL; + } + + if (fb->depth == 16) { + u16 r, g, b; + int i; + + if (regno < 32) { + for (i = 0; i < 8; i++) { + fb_helper->funcs->gamma_set(crtc, red, + green, blue, pindex + i); + } + } + + fb_helper->funcs->gamma_get(crtc, &r, + &g, &b, + (pindex >> 1)); + + for (i = 0; i < 4; i++) { + fb_helper->funcs->gamma_set(crtc, r, + green, b, + (pindex >> 1) + i); + } + } + } + + if (fb->depth != 16) { + fb_helper->funcs->gamma_set(crtc, red, green, blue, pindex); + } + + return 0; +} + +static int vigs_fbdev_setcmap(struct fb_cmap *cmap, struct fb_info *fbi) +{ + struct drm_fb_helper *fb_helper = fbi->par; + struct drm_device *dev = fb_helper->dev; + struct drm_crtc_helper_funcs *crtc_funcs; + u16 *red, *green, *blue, *transp; + struct drm_crtc *crtc; + int i, j, ret = 0; + int start; + + drm_modeset_lock_all(dev); + if (!vigs_fbdev_helper_is_bound(fb_helper)) { + drm_modeset_unlock_all(dev); + return -EBUSY; + } + + for (i = 0; i < fb_helper->crtc_count; i++) { + crtc = fb_helper->crtc_info[i].mode_set.crtc; + crtc_funcs = crtc->helper_private; + + red = cmap->red; + green = cmap->green; + blue = cmap->blue; + transp = cmap->transp; + start = cmap->start; + + for (j = 0; j < cmap->len; j++) { + u16 hred, hgreen, hblue, htransp = 0xffff; + + hred = *red++; + hgreen = *green++; + hblue = *blue++; + + if (transp) { + htransp = *transp++; + } + + ret = vigs_fbdev_setcolreg(crtc, hred, hgreen, hblue, start++, fbi); + + if (ret != 0) { + goto out; + } + } + + if (crtc_funcs->load_lut) { + crtc_funcs->load_lut(crtc); + } + } + + out: + drm_modeset_unlock_all(dev); + return ret; +} + +/* + * @} + */ + +static int vigs_fbdev_set_par(struct fb_info *fbi) +{ + DRM_DEBUG_KMS("enter\n"); + + return drm_fb_helper_set_par(fbi); +} + +/* + * This is 'drm_fb_helper_dpms' modified to set 'in_dpms' + * flag inside drm_modeset_lock_all. + */ +static void vigs_fbdev_dpms(struct fb_info *fbi, int dpms_mode) +{ + struct drm_fb_helper *fb_helper = fbi->par; + struct drm_device *dev = fb_helper->dev; + struct vigs_device *vigs_dev = dev->dev_private; + struct drm_crtc *crtc; + struct vigs_crtc *vigs_crtc; + struct drm_connector *connector; + int i, j; + + /* + * fbdev->blank can be called from irq context in case of a panic. + * Since we already have our own special panic handler which will + * restore the fbdev console mode completely, just bail out early. + */ + if (oops_in_progress) { + return; + } + + if (vigs_dev->in_dpms) { + /* + * If this is called from 'vigs_crtc_dpms' then we just + * return in order to not deadlock. Note that it's + * correct to check this flag here without any locks + * being held since 'fb_blank' callback is already called with + * console lock being held and 'vigs_crtc_dpms' only sets in_dpms + * inside the console lock. + */ + return; + } + + /* + * For each CRTC in this fb, turn the connectors on/off. + */ + drm_modeset_lock_all(dev); + if (!vigs_fbdev_helper_is_bound(fb_helper)) { + drm_modeset_unlock_all(dev); + return; + } + + for (i = 0; i < fb_helper->crtc_count; i++) { + crtc = fb_helper->crtc_info[i].mode_set.crtc; + vigs_crtc = crtc_to_vigs_crtc(crtc); + + if (!crtc->enabled) { + continue; + } + + vigs_dev->in_dpms = true; + + /* Walk the connectors & encoders on this fb turning them on/off */ + for (j = 0; j < fb_helper->connector_count; j++) { + connector = fb_helper->connector_info[j]->connector; + connector->funcs->dpms(connector, dpms_mode); + drm_object_property_set_value(&connector->base, + dev->mode_config.dpms_property, dpms_mode); + } + + vigs_dev->in_dpms = false; + } + + drm_modeset_unlock_all(dev); +} + +/* + * This is 'drm_fb_helper_blank' modified to use + * 'vigs_fbdev_dpms'. + */ +static int vigs_fbdev_blank(int blank, struct fb_info *fbi) +{ + switch (blank) { + /* Display: On; HSync: On, VSync: On */ + case FB_BLANK_UNBLANK: + vigs_fbdev_dpms(fbi, DRM_MODE_DPMS_ON); + break; + /* Display: Off; HSync: On, VSync: On */ + case FB_BLANK_NORMAL: + vigs_fbdev_dpms(fbi, DRM_MODE_DPMS_STANDBY); + break; + /* Display: Off; HSync: Off, VSync: On */ + case FB_BLANK_HSYNC_SUSPEND: + vigs_fbdev_dpms(fbi, DRM_MODE_DPMS_STANDBY); + break; + /* Display: Off; HSync: On, VSync: Off */ + case FB_BLANK_VSYNC_SUSPEND: + vigs_fbdev_dpms(fbi, DRM_MODE_DPMS_SUSPEND); + break; + /* Display: Off; HSync: Off, VSync: Off */ + case FB_BLANK_POWERDOWN: + vigs_fbdev_dpms(fbi, DRM_MODE_DPMS_OFF); + break; + } + + return 0; +} + +static struct fb_ops vigs_fbdev_ops = +{ + .owner = THIS_MODULE, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_check_var = drm_fb_helper_check_var, + .fb_set_par = vigs_fbdev_set_par, + .fb_blank = vigs_fbdev_blank, + .fb_pan_display = drm_fb_helper_pan_display, + .fb_setcmap = vigs_fbdev_setcmap, + .fb_debug_enter = drm_fb_helper_debug_enter, + .fb_debug_leave = drm_fb_helper_debug_leave, +}; + +static int vigs_fbdev_probe_once(struct drm_fb_helper *helper, + struct drm_fb_helper_surface_size *sizes) +{ + struct vigs_fbdev *vigs_fbdev = fbdev_to_vigs_fbdev(helper); + struct vigs_device *vigs_dev = helper->dev->dev_private; + struct vigs_surface *fb_sfc; + struct vigs_framebuffer *vigs_fb; + struct fb_info *fbi; + struct drm_mode_fb_cmd2 mode_cmd = { 0 }; + vigsp_surface_format format; + unsigned long offset; + int dpi; + int ret; + struct drm_connector *connector; + + DRM_DEBUG_KMS("%dx%dx%d\n", + sizes->surface_width, + sizes->surface_height, + sizes->surface_bpp); + + mode_cmd.width = sizes->surface_width; + mode_cmd.height = sizes->surface_height; + mode_cmd.pitches[0] = sizes->surface_width * (sizes->surface_bpp >> 3); + mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, + sizes->surface_depth); + + switch (mode_cmd.pixel_format) { + case DRM_FORMAT_XRGB8888: + format = vigsp_surface_bgrx8888; + break; + case DRM_FORMAT_ARGB8888: + format = vigsp_surface_bgra8888; + break; + default: + DRM_DEBUG_KMS("unsupported pixel format: %u\n", mode_cmd.pixel_format); + ret = -EINVAL; + goto fail1; + } + + fbi = framebuffer_alloc(0, &vigs_dev->pci_dev->dev); + + if (!fbi) { + DRM_ERROR("failed to allocate fb info\n"); + ret = -ENOMEM; + goto fail1; + } + + ret = vigs_surface_create(vigs_dev, + mode_cmd.width, + mode_cmd.height, + mode_cmd.pitches[0], + format, + true, + &fb_sfc); + + if (ret != 0) { + goto fail2; + } + + ret = vigs_framebuffer_create(vigs_dev, + &mode_cmd, + fb_sfc, + &vigs_fb); + + drm_gem_object_unreference_unlocked(&fb_sfc->gem.base); + + if (ret != 0) { + goto fail2; + } + + helper->fb = &vigs_fb->base; + helper->fbdev = fbi; + + fbi->par = helper; + fbi->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT; + fbi->fbops = &vigs_fbdev_ops; + + ret = fb_alloc_cmap(&fbi->cmap, 256, 0); + + if (ret != 0) { + DRM_ERROR("failed to allocate cmap\n"); + goto fail3; + } + + /* + * This is a hack to make fbdev work without calling + * 'vigs_framebuffer_pin'. VRAM is precious resource and we + * don't want to give it away to fbdev just to show + * that "kernel loading" thing. Here we assume that + * GEM zero is always located at offset 0 in VRAM and just map + * it and give it to fbdev. If later, when X starts for example, + * one will attempt to write to /dev/fb0 then he'll probably + * write to some GEM's memory, but we don't care. + */ + vigs_fbdev->kptr = ioremap_wc(vigs_dev->vram_base, + vigs_gem_size(&fb_sfc->gem)); + + if (!vigs_fbdev->kptr) { + goto fail4; + } + + strcpy(fbi->fix.id, "VIGS"); + + drm_fb_helper_fill_fix(fbi, vigs_fb->base.pitches[0], vigs_fb->base.depth); + drm_fb_helper_fill_var(fbi, helper, vigs_fb->base.width, vigs_fb->base.height); + + /* + * Setup DPI. + * @{ + */ + + dpi = vigs_output_get_dpi(); + fbi->var.height = vigs_output_get_phys_height(dpi, fbi->var.yres); + fbi->var.width = vigs_output_get_phys_width(dpi, fbi->var.xres); + + /* + * Walk all connectors and set display_info. + */ + + list_for_each_entry(connector, &vigs_dev->drm_dev->mode_config.connector_list, head) { + connector->display_info.width_mm = fbi->var.width; + connector->display_info.height_mm = fbi->var.height; + } + + /* + * @} + */ + + /* + * TODO: Play around with xoffset/yoffset, make sure this code works. + */ + + offset = fbi->var.xoffset * (vigs_fb->base.bits_per_pixel >> 3); + offset += fbi->var.yoffset * vigs_fb->base.pitches[0]; + + /* + * TODO: "vram_base + ..." - not nice, make a function for this. + */ + fbi->fix.smem_start = vigs_dev->vram_base + + 0 + + offset; + fbi->screen_base = vigs_fbdev->kptr + offset; + fbi->screen_size = fbi->fix.smem_len = vigs_fb->base.width * + vigs_fb->base.height * + (vigs_fb->base.bits_per_pixel >> 3); + + return 0; + +fail4: + fb_dealloc_cmap(&fbi->cmap); +fail3: + helper->fb = NULL; + helper->fbdev = NULL; +fail2: + framebuffer_release(fbi); +fail1: + + return ret; +} + +static int vigs_fbdev_probe(struct drm_fb_helper *helper, + struct drm_fb_helper_surface_size *sizes) +{ + int ret = 0; + + DRM_DEBUG_KMS("enter\n"); + + /* + * With !helper->fb, it means that this function is called first time + * and after that, the helper->fb would be used as clone mode. + */ + + if (!helper->fb) { + ret = vigs_fbdev_probe_once(helper, sizes); + + if (ret >= 0) { + ret = 1; + } + } + + return ret; +} + +static struct drm_fb_helper_funcs vigs_fbdev_funcs = +{ + .fb_probe = vigs_fbdev_probe, +}; + +int vigs_fbdev_create(struct vigs_device *vigs_dev, + struct vigs_fbdev **vigs_fbdev) +{ + int ret = 0; + + DRM_DEBUG_KMS("enter\n"); + + *vigs_fbdev = kzalloc(sizeof(**vigs_fbdev), GFP_KERNEL); + + if (!*vigs_fbdev) { + ret = -ENOMEM; + goto fail1; + } + + (*vigs_fbdev)->base.funcs = &vigs_fbdev_funcs; + + ret = drm_fb_helper_init(vigs_dev->drm_dev, + &(*vigs_fbdev)->base, + 1, 1); + + if (ret != 0) { + DRM_ERROR("unable to init fb_helper: %d\n", ret); + goto fail2; + } + + drm_fb_helper_single_add_all_connectors(&(*vigs_fbdev)->base); + drm_fb_helper_initial_config(&(*vigs_fbdev)->base, 32); + + return 0; + +fail2: + kfree(*vigs_fbdev); +fail1: + *vigs_fbdev = NULL; + + return ret; +} + +void vigs_fbdev_destroy(struct vigs_fbdev *vigs_fbdev) +{ + struct fb_info *fbi = vigs_fbdev->base.fbdev; + struct drm_framebuffer *fb; + + DRM_DEBUG_KMS("enter\n"); + + if (fbi) { + unregister_framebuffer(fbi); + fb_dealloc_cmap(&fbi->cmap); + framebuffer_release(fbi); + } + + fb = vigs_fbdev->base.fb; + + drm_fb_helper_fini(&vigs_fbdev->base); + + if (vigs_fbdev->kptr) { + iounmap(vigs_fbdev->kptr); + } + + drm_framebuffer_unregister_private(fb); + drm_framebuffer_remove(fb); + + kfree(vigs_fbdev); +} + +void vigs_fbdev_output_poll_changed(struct vigs_fbdev *vigs_fbdev) +{ + DRM_DEBUG_KMS("enter\n"); + + drm_fb_helper_hotplug_event(&vigs_fbdev->base); +} + +void vigs_fbdev_restore_mode(struct vigs_fbdev *vigs_fbdev) +{ + DRM_DEBUG_KMS("enter\n"); + + drm_modeset_lock_all(vigs_fbdev->base.dev); + drm_fb_helper_restore_fbdev_mode(&vigs_fbdev->base); + drm_modeset_unlock_all(vigs_fbdev->base.dev); +} diff --git a/drivers/gpu/drm/vigs/vigs_fbdev.h b/drivers/gpu/drm/vigs/vigs_fbdev.h new file mode 100644 index 0000000..81a997e --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_fbdev.h @@ -0,0 +1,30 @@ +#ifndef _VIGS_FBDEV_H_ +#define _VIGS_FBDEV_H_ + +#include "drmP.h" +#include "drm_fb_helper.h" + +struct vigs_device; + +struct vigs_fbdev +{ + struct drm_fb_helper base; + + void __iomem *kptr; +}; + +static inline struct vigs_fbdev *fbdev_to_vigs_fbdev(struct drm_fb_helper *fbdev) +{ + return container_of(fbdev, struct vigs_fbdev, base); +} + +int vigs_fbdev_create(struct vigs_device *vigs_dev, + struct vigs_fbdev **vigs_fbdev); + +void vigs_fbdev_destroy(struct vigs_fbdev *vigs_fbdev); + +void vigs_fbdev_output_poll_changed(struct vigs_fbdev *vigs_fbdev); + +void vigs_fbdev_restore_mode(struct vigs_fbdev *vigs_fbdev); + +#endif diff --git a/drivers/gpu/drm/vigs/vigs_fence.c b/drivers/gpu/drm/vigs/vigs_fence.c new file mode 100644 index 0000000..8a9b349 --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_fence.c @@ -0,0 +1,305 @@ +#include "vigs_fence.h" +#include "vigs_fenceman.h" +#include "vigs_file.h" +#include "vigs_device.h" +#include "vigs_comm.h" +#include + +static void vigs_fence_cleanup(struct vigs_fence *fence) +{ +} + +static void vigs_fence_destroy(struct vigs_fence *fence) +{ + vigs_fence_cleanup(fence); + kfree(fence); +} + +static void vigs_user_fence_destroy(struct vigs_fence *fence) +{ + struct vigs_user_fence *user_fence = vigs_fence_to_vigs_user_fence(fence); + + vigs_fence_cleanup(&user_fence->fence); + ttm_base_object_kfree(user_fence, base); +} + +static void vigs_fence_release_locked(struct kref *kref) +{ + struct vigs_fence *fence = kref_to_vigs_fence(kref); + + DRM_DEBUG_DRIVER("Fence destroyed (seq = %u, signaled = %u)\n", + fence->seq, + fence->signaled); + + list_del_init(&fence->list); + fence->destroy(fence); +} + +static void vigs_user_fence_refcount_release(struct ttm_base_object **base) +{ + struct ttm_base_object *tmp = *base; + struct vigs_user_fence *user_fence = base_to_vigs_user_fence(tmp); + + vigs_fence_unref(&user_fence->fence); + *base = NULL; +} + +static void vigs_fence_init(struct vigs_fence *fence, + struct vigs_fenceman *fenceman, + void (*destroy)(struct vigs_fence*)) +{ + unsigned long flags; + + kref_init(&fence->kref); + INIT_LIST_HEAD(&fence->list); + fence->fenceman = fenceman; + fence->signaled = false; + init_waitqueue_head(&fence->wait); + fence->destroy = destroy; + + spin_lock_irqsave(&fenceman->lock, flags); + + fence->seq = vigs_fence_seq_next(fenceman->seq); + fenceman->seq = fence->seq; + + list_add_tail(&fence->list, &fenceman->fence_list); + + spin_unlock_irqrestore(&fenceman->lock, flags); + + DRM_DEBUG_DRIVER("Fence created (seq = %u)\n", fence->seq); +} + +int vigs_fence_create(struct vigs_fenceman *fenceman, + struct vigs_fence **fence) +{ + int ret = 0; + + *fence = kzalloc(sizeof(**fence), GFP_KERNEL); + + if (!*fence) { + ret = -ENOMEM; + goto fail1; + } + + vigs_fence_init(*fence, fenceman, &vigs_fence_destroy); + + return 0; + +fail1: + *fence = NULL; + + return ret; +} + +int vigs_user_fence_create(struct vigs_fenceman *fenceman, + struct drm_file *file_priv, + struct vigs_user_fence **user_fence, + uint32_t *handle) +{ + struct vigs_file *vigs_file = file_priv->driver_priv; + int ret = 0; + + *user_fence = kzalloc(sizeof(**user_fence), GFP_KERNEL); + + if (!*user_fence) { + ret = -ENOMEM; + goto fail1; + } + + vigs_fence_init(&(*user_fence)->fence, fenceman, &vigs_user_fence_destroy); + + ret = ttm_base_object_init(vigs_file->obj_file, + &(*user_fence)->base, false, + VIGS_FENCE_TYPE, + &vigs_user_fence_refcount_release, + NULL); + + if (ret != 0) { + goto fail2; + } + + /* + * For ttm_base_object. + */ + vigs_fence_ref(&(*user_fence)->fence); + + *handle = (*user_fence)->base.hash.key; + + return 0; + +fail2: + vigs_fence_cleanup(&(*user_fence)->fence); + kfree(*user_fence); +fail1: + *user_fence = NULL; + + return ret; +} + +int vigs_fence_wait(struct vigs_fence *fence, bool interruptible) +{ + long ret = 0; + + if (vigs_fence_signaled(fence)) { + DRM_DEBUG_DRIVER("Fence wait (seq = %u, signaled = %u)\n", + fence->seq, + fence->signaled); + return 0; + } + + DRM_DEBUG_DRIVER("Fence wait (seq = %u)\n", fence->seq); + + if (interruptible) { + ret = wait_event_interruptible(fence->wait, vigs_fence_signaled(fence)); + } else { + wait_event(fence->wait, vigs_fence_signaled(fence)); + } + + if (ret != 0) { + DRM_INFO("Fence wait interrupted (seq = %u) = %ld\n", fence->seq, ret); + } else { + DRM_DEBUG_DRIVER("Fence wait done (seq = %u)\n", fence->seq); + } + + return ret; +} + +bool vigs_fence_signaled(struct vigs_fence *fence) +{ + unsigned long flags; + bool signaled; + + spin_lock_irqsave(&fence->fenceman->lock, flags); + + signaled = fence->signaled; + + spin_unlock_irqrestore(&fence->fenceman->lock, flags); + + return signaled; +} + +void vigs_fence_ref(struct vigs_fence *fence) +{ + if (unlikely(!fence)) { + return; + } + + kref_get(&fence->kref); +} + +void vigs_fence_unref(struct vigs_fence *fence) +{ + struct vigs_fenceman *fenceman; + + if (unlikely(!fence)) { + return; + } + + fenceman = fence->fenceman; + + spin_lock_irq(&fenceman->lock); + BUG_ON(atomic_read(&fence->kref.refcount) == 0); + kref_put(&fence->kref, vigs_fence_release_locked); + spin_unlock_irq(&fenceman->lock); +} + +int vigs_fence_create_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv) +{ + struct vigs_device *vigs_dev = drm_dev->dev_private; + struct vigs_file *vigs_file = file_priv->driver_priv; + struct drm_vigs_create_fence *args = data; + struct vigs_user_fence *user_fence; + uint32_t handle; + int ret; + + ret = vigs_user_fence_create(vigs_dev->fenceman, + file_priv, + &user_fence, + &handle); + + if (ret != 0) { + goto out; + } + + if (args->send) { + ret = vigs_comm_fence(vigs_dev->comm, &user_fence->fence); + + if (ret != 0) { + ttm_ref_object_base_unref(vigs_file->obj_file, + handle, + TTM_REF_USAGE); + goto out; + } + } + + args->handle = handle; + args->seq = user_fence->fence.seq; + +out: + vigs_fence_unref(&user_fence->fence); + + return ret; +} + +int vigs_fence_wait_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv) +{ + struct vigs_file *vigs_file = file_priv->driver_priv; + struct drm_vigs_fence_wait *args = data; + struct ttm_base_object *base; + struct vigs_user_fence *user_fence; + int ret; + + base = ttm_base_object_lookup(vigs_file->obj_file, args->handle); + + if (!base) { + return -ENOENT; + } + + user_fence = base_to_vigs_user_fence(base); + + ret = vigs_fence_wait(&user_fence->fence, true); + + ttm_base_object_unref(&base); + + return ret; +} + +int vigs_fence_signaled_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv) +{ + struct vigs_file *vigs_file = file_priv->driver_priv; + struct drm_vigs_fence_signaled *args = data; + struct ttm_base_object *base; + struct vigs_user_fence *user_fence; + + base = ttm_base_object_lookup(vigs_file->obj_file, args->handle); + + if (!base) { + return -ENOENT; + } + + user_fence = base_to_vigs_user_fence(base); + + args->signaled = vigs_fence_signaled(&user_fence->fence); + + ttm_base_object_unref(&base); + + return 0; +} + +int vigs_fence_unref_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv) +{ + struct vigs_file *vigs_file = file_priv->driver_priv; + struct drm_vigs_fence_unref *args = data; + + return ttm_ref_object_base_unref(vigs_file->obj_file, + args->handle, + TTM_REF_USAGE); +} diff --git a/drivers/gpu/drm/vigs/vigs_fence.h b/drivers/gpu/drm/vigs/vigs_fence.h new file mode 100644 index 0000000..c0c41be --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_fence.h @@ -0,0 +1,123 @@ +#ifndef _VIGS_FENCE_H_ +#define _VIGS_FENCE_H_ + +#include "drmP.h" +#include + +#define VIGS_FENCE_TYPE ttm_driver_type2 + +struct vigs_fenceman; + +struct vigs_fence +{ + struct kref kref; + + struct list_head list; + + struct vigs_fenceman *fenceman; + + uint32_t seq; + + bool signaled; + + wait_queue_head_t wait; + + void (*destroy)(struct vigs_fence *fence); +}; + +/* + * Users can access fences via TTM base object mechanism, + * thus, we need to wrap vigs_fence into vigs_user_fence because + * not every fence object needs to be referenced from user space. + * So no point in always having struct ttm_base_object inside vigs_fence. + */ + +struct vigs_user_fence +{ + struct ttm_base_object base; + + struct vigs_fence fence; +}; + +static inline struct vigs_fence *kref_to_vigs_fence(struct kref *kref) +{ + return container_of(kref, struct vigs_fence, kref); +} + +static inline struct vigs_user_fence *vigs_fence_to_vigs_user_fence(struct vigs_fence *fence) +{ + return container_of(fence, struct vigs_user_fence, fence); +} + +static inline struct vigs_user_fence *base_to_vigs_user_fence(struct ttm_base_object *base) +{ + return container_of(base, struct vigs_user_fence, base); +} + +static inline uint32_t vigs_fence_seq_next(uint32_t seq) +{ + if (++seq == 0) { + ++seq; + } + return seq; +} + +#define vigs_fence_seq_num_after(a, b) \ + (typecheck(u32, a) && typecheck(u32, b) && ((s32)(b) - (s32)(a) < 0)) + +#define vigs_fence_seq_num_before(a, b) vigs_fence_seq_num_after(b, a) + +#define vigs_fence_seq_num_after_eq(a, b) \ + ( typecheck(u32, a) && typecheck(u32, b) && \ + ((s32)(a) - (s32)(b) >= 0) ) + +#define vigs_fence_seq_num_before_eq(a, b) vigs_fence_seq_num_after_eq(b, a) + +int vigs_fence_create(struct vigs_fenceman *fenceman, + struct vigs_fence **fence); + +int vigs_user_fence_create(struct vigs_fenceman *fenceman, + struct drm_file *file_priv, + struct vigs_user_fence **user_fence, + uint32_t *handle); + +int vigs_fence_wait(struct vigs_fence *fence, bool interruptible); + +bool vigs_fence_signaled(struct vigs_fence *fence); + +/* + * Passing NULL won't hurt, this is for convenience. + */ +void vigs_fence_ref(struct vigs_fence *fence); + +/* + * Passing NULL won't hurt, this is for convenience. + */ +void vigs_fence_unref(struct vigs_fence *fence); + +/* + * IOCTLs + * @{ + */ + +int vigs_fence_create_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv); + +int vigs_fence_wait_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv); + +int vigs_fence_signaled_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv); + +int vigs_fence_unref_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv); + +/* + * @} + */ + +#endif diff --git a/drivers/gpu/drm/vigs/vigs_fenceman.c b/drivers/gpu/drm/vigs/vigs_fenceman.c new file mode 100644 index 0000000..c551852c --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_fenceman.c @@ -0,0 +1,65 @@ +#include "vigs_fenceman.h" +#include "vigs_fence.h" + +int vigs_fenceman_create(struct vigs_fenceman **fenceman) +{ + int ret = 0; + + DRM_DEBUG_DRIVER("enter\n"); + + *fenceman = kzalloc(sizeof(**fenceman), GFP_KERNEL); + + if (!*fenceman) { + ret = -ENOMEM; + goto fail1; + } + + spin_lock_init(&(*fenceman)->lock); + INIT_LIST_HEAD(&(*fenceman)->fence_list); + (*fenceman)->seq = UINT_MAX; + + return 0; + +fail1: + *fenceman = NULL; + + return ret; +} + +void vigs_fenceman_destroy(struct vigs_fenceman *fenceman) +{ + unsigned long flags; + bool fence_list_empty; + + DRM_DEBUG_DRIVER("enter\n"); + + spin_lock_irqsave(&fenceman->lock, flags); + fence_list_empty = list_empty(&fenceman->fence_list); + spin_unlock_irqrestore(&fenceman->lock, flags); + + BUG_ON(!fence_list_empty); + + kfree(fenceman); +} + +void vigs_fenceman_ack(struct vigs_fenceman *fenceman, + uint32_t lower, uint32_t upper) +{ + unsigned long flags; + struct vigs_fence *fence, *tmp; + + spin_lock_irqsave(&fenceman->lock, flags); + + list_for_each_entry_safe(fence, tmp, &fenceman->fence_list, list) { + if (vigs_fence_seq_num_after_eq(fence->seq, lower) && + vigs_fence_seq_num_before_eq(fence->seq, upper)) { + DRM_DEBUG_DRIVER("Fence signaled (seq = %u)\n", + fence->seq); + list_del_init(&fence->list); + fence->signaled = true; + wake_up_all(&fence->wait); + } + } + + spin_unlock_irqrestore(&fenceman->lock, flags); +} diff --git a/drivers/gpu/drm/vigs/vigs_fenceman.h b/drivers/gpu/drm/vigs/vigs_fenceman.h new file mode 100644 index 0000000..e6e1028 --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_fenceman.h @@ -0,0 +1,47 @@ +#ifndef _VIGS_FENCEMAN_H_ +#define _VIGS_FENCEMAN_H_ + +#include "drmP.h" + +/* + * This is fence manager for VIGS. It's responsible for the following: + * + Fence bookkeeping. + * + Fence sequence number management and IRQ processing. + */ + +struct vigs_fenceman +{ + /* + * Lock that's used to guard all data inside + * fence manager and fence objects. Don't confuse it + * with struct ttm_bo_device::fence_lock, that lock + * is used to work with TTM sync objects, i.e. it's more + * "high level". + */ + spinlock_t lock; + + /* + * List of currently pending fences. + */ + struct list_head fence_list; + + /* + * Current sequence number, new fence should be + * assigned (seq + 1). + * Note! Sequence numbers are always non-0, 0 is + * a special value that tells GPU not to fence things. + */ + uint32_t seq; +}; + +int vigs_fenceman_create(struct vigs_fenceman **fenceman); + +void vigs_fenceman_destroy(struct vigs_fenceman *fenceman); + +/* + * Can be called from IRQ handler. + */ +void vigs_fenceman_ack(struct vigs_fenceman *fenceman, + uint32_t lower, uint32_t upper); + +#endif diff --git a/drivers/gpu/drm/vigs/vigs_file.c b/drivers/gpu/drm/vigs/vigs_file.c new file mode 100644 index 0000000..eef78de --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_file.c @@ -0,0 +1,37 @@ +#include "vigs_file.h" +#include "vigs_device.h" + +int vigs_file_create(struct vigs_device *vigs_dev, + struct vigs_file **vigs_file) +{ + int ret = 0; + + *vigs_file = kzalloc(sizeof(**vigs_file), GFP_KERNEL); + + if (!*vigs_file) { + ret = -ENOMEM; + goto fail1; + } + + (*vigs_file)->obj_file = ttm_object_file_init(vigs_dev->obj_dev, 10); + + if (!(*vigs_file)->obj_file) { + ret = -ENOMEM; + goto fail2; + } + + return 0; + +fail2: + kfree(*vigs_file); +fail1: + *vigs_file = NULL; + + return ret; +} + +void vigs_file_destroy(struct vigs_file *vigs_file) +{ + ttm_object_file_release(&vigs_file->obj_file); + kfree(vigs_file); +} diff --git a/drivers/gpu/drm/vigs/vigs_file.h b/drivers/gpu/drm/vigs/vigs_file.h new file mode 100644 index 0000000..45f16f8 --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_file.h @@ -0,0 +1,19 @@ +#ifndef _VIGS_FILE_H_ +#define _VIGS_FILE_H_ + +#include "drmP.h" +#include + +struct vigs_device; + +struct vigs_file +{ + struct ttm_object_file *obj_file; +}; + +int vigs_file_create(struct vigs_device *vigs_dev, + struct vigs_file **vigs_file); + +void vigs_file_destroy(struct vigs_file *vigs_file); + +#endif diff --git a/drivers/gpu/drm/vigs/vigs_framebuffer.c b/drivers/gpu/drm/vigs/vigs_framebuffer.c new file mode 100644 index 0000000..65c78f5 --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_framebuffer.c @@ -0,0 +1,237 @@ +#include "vigs_framebuffer.h" +#include "vigs_device.h" +#include "vigs_surface.h" +#include "vigs_fbdev.h" +#include "vigs_comm.h" +#include "drm_crtc_helper.h" +#include + +static void vigs_framebuffer_destroy(struct drm_framebuffer *fb) +{ + struct vigs_framebuffer *vigs_fb = fb_to_vigs_fb(fb); + int i; + + DRM_DEBUG_KMS("enter\n"); + + /* + * First, we need to call 'drm_framebuffer_cleanup', this'll + * automatically call 'vigs_crtc_disable' if needed, thus, notifying + * the host that root surface is gone. + */ + + drm_framebuffer_cleanup(fb); + + /* + * And we can finally free the GEMs. + */ + + for (i = 0; i < 4; ++i) { + if (vigs_fb->surfaces[i]) { + drm_gem_object_unreference_unlocked(&vigs_fb->surfaces[i]->gem.base); + } + } + kfree(vigs_fb); +} + +static int vigs_framebuffer_create_handle(struct drm_framebuffer *fb, + struct drm_file *file_priv, + unsigned int *handle) +{ + struct vigs_framebuffer *vigs_fb = fb_to_vigs_fb(fb); + + DRM_DEBUG_KMS("enter\n"); + + return drm_gem_handle_create(file_priv, &vigs_fb->surfaces[0]->gem.base, handle); +} + +static int vigs_framebuffer_dirty(struct drm_framebuffer *fb, + struct drm_file *file_priv, + unsigned flags, unsigned color, + struct drm_clip_rect *clips, + unsigned num_clips) +{ + DRM_DEBUG_KMS("enter\n"); + + return 0; +} + +static struct drm_framebuffer_funcs vigs_framebuffer_funcs = +{ + .destroy = vigs_framebuffer_destroy, + .create_handle = vigs_framebuffer_create_handle, + .dirty = vigs_framebuffer_dirty, +}; + +static struct drm_framebuffer *vigs_fb_create(struct drm_device *drm_dev, + struct drm_file *file_priv, + struct drm_mode_fb_cmd2 *mode_cmd) +{ + struct vigs_device *vigs_dev = drm_dev->dev_private; + struct vigs_surface *surfaces[4]; + int ret, i; + int num_planes = drm_format_num_planes(mode_cmd->pixel_format); + struct vigs_framebuffer *vigs_fb; + + DRM_DEBUG_KMS("enter\n"); + + for (i = 0; i < num_planes; ++i) { + struct drm_gem_object *gem; + struct vigs_gem_object *vigs_gem; + + gem = drm_gem_object_lookup(drm_dev, file_priv, mode_cmd->handles[i]); + + if (!gem) { + DRM_ERROR("GEM lookup failed, handle = %u\n", mode_cmd->handles[i]); + ret = -ENOENT; + goto fail; + } + + vigs_gem = gem_to_vigs_gem(gem); + + if (vigs_gem->type != VIGS_GEM_TYPE_SURFACE) { + DRM_ERROR("GEM is not a surface, handle = %u\n", mode_cmd->handles[i]); + drm_gem_object_unreference_unlocked(gem); + ret = -ENOENT; + goto fail; + } + + surfaces[i] = vigs_gem_to_vigs_surface(vigs_gem); + } + + vigs_fb = kzalloc(sizeof(*vigs_fb), GFP_KERNEL); + + if (!vigs_fb) { + ret = -ENOMEM; + goto fail; + } + + vigs_fb->comm = vigs_dev->comm; + + for (i = 0; i < num_planes; ++i) { + vigs_fb->surfaces[i] = surfaces[i]; + } + + ret = drm_framebuffer_init(vigs_dev->drm_dev, + &vigs_fb->base, + &vigs_framebuffer_funcs); + + if (ret != 0) { + DRM_ERROR("unable to create the framebuffer: %d\n", ret); + kfree(vigs_fb); + goto fail; + } + + drm_helper_mode_fill_fb_struct(&vigs_fb->base, mode_cmd); + + return &vigs_fb->base; + +fail: + for (i--; i >= 0; i--) { + drm_gem_object_unreference_unlocked(&surfaces[i]->gem.base); + } + + return ERR_PTR(ret); +} + +static void vigs_output_poll_changed(struct drm_device *drm_dev) +{ + struct vigs_device *vigs_dev = drm_dev->dev_private; + + DRM_DEBUG_KMS("enter\n"); + + if (vigs_dev->fbdev) { + vigs_fbdev_output_poll_changed(vigs_dev->fbdev); + } +} + +static struct drm_mode_config_funcs vigs_mode_config_funcs = +{ + .fb_create = vigs_fb_create, + .output_poll_changed = vigs_output_poll_changed +}; + +void vigs_framebuffer_config_init(struct vigs_device *vigs_dev) +{ + DRM_DEBUG_KMS("enter\n"); + + vigs_dev->drm_dev->mode_config.min_width = 0; + vigs_dev->drm_dev->mode_config.min_height = 0; + + vigs_dev->drm_dev->mode_config.max_width = 4096; + vigs_dev->drm_dev->mode_config.max_height = 4096; + + vigs_dev->drm_dev->mode_config.funcs = &vigs_mode_config_funcs; +} + +int vigs_framebuffer_create(struct vigs_device *vigs_dev, + struct drm_mode_fb_cmd2 *mode_cmd, + struct vigs_surface *fb_sfc, + struct vigs_framebuffer **vigs_fb) +{ + int ret = 0; + + DRM_DEBUG_KMS("enter\n"); + + *vigs_fb = kzalloc(sizeof(**vigs_fb), GFP_KERNEL); + + if (!*vigs_fb) { + ret = -ENOMEM; + goto fail1; + } + + if ((fb_sfc->width != mode_cmd->width) || + (fb_sfc->height != mode_cmd->height) || + (fb_sfc->stride != mode_cmd->pitches[0])) { + DRM_DEBUG_KMS("surface format mismatch, sfc - (%u,%u,%u), mode - (%u,%u,%u)\n", + fb_sfc->width, fb_sfc->height, fb_sfc->stride, + mode_cmd->width, mode_cmd->height, mode_cmd->pitches[0]); + ret = -EINVAL; + goto fail2; + } + + (*vigs_fb)->comm = vigs_dev->comm; + (*vigs_fb)->surfaces[0] = fb_sfc; + + ret = drm_framebuffer_init(vigs_dev->drm_dev, + &(*vigs_fb)->base, + &vigs_framebuffer_funcs); + + if (ret != 0) { + goto fail2; + } + + drm_helper_mode_fill_fb_struct(&(*vigs_fb)->base, mode_cmd); + + drm_gem_object_reference(&fb_sfc->gem.base); + + return 0; + +fail2: + kfree(*vigs_fb); +fail1: + *vigs_fb = NULL; + + return ret; +} + +int vigs_framebuffer_pin(struct vigs_framebuffer *vigs_fb) +{ + int ret; + + vigs_gem_reserve(&vigs_fb->surfaces[0]->gem); + + ret = vigs_gem_pin(&vigs_fb->surfaces[0]->gem); + + vigs_gem_unreserve(&vigs_fb->surfaces[0]->gem); + + return ret; +} + +void vigs_framebuffer_unpin(struct vigs_framebuffer *vigs_fb) +{ + vigs_gem_reserve(&vigs_fb->surfaces[0]->gem); + + vigs_gem_unpin(&vigs_fb->surfaces[0]->gem); + + vigs_gem_unreserve(&vigs_fb->surfaces[0]->gem); +} diff --git a/drivers/gpu/drm/vigs/vigs_framebuffer.h b/drivers/gpu/drm/vigs/vigs_framebuffer.h new file mode 100644 index 0000000..f95728ef --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_framebuffer.h @@ -0,0 +1,43 @@ +#ifndef _VIGS_FRAMEBUFFER_H_ +#define _VIGS_FRAMEBUFFER_H_ + +#include "drmP.h" +#include "vigs_protocol.h" + +struct vigs_device; +struct vigs_comm; +struct vigs_surface; + +struct vigs_framebuffer +{ + struct drm_framebuffer base; + + /* + * Cached from 'vigs_device' for speed. + */ + struct vigs_comm *comm; + + struct vigs_surface *surfaces[4]; +}; + +static inline struct vigs_framebuffer *fb_to_vigs_fb(struct drm_framebuffer *fb) +{ + return container_of(fb, struct vigs_framebuffer, base); +} + +void vigs_framebuffer_config_init(struct vigs_device *vigs_dev); + +/* + * Creates a framebuffer object. + * Note that it also gets a reference to 'fb_gem' (in case of success), so + * don't forget to unreference it in the calling code. + */ +int vigs_framebuffer_create(struct vigs_device *vigs_dev, + struct drm_mode_fb_cmd2 *mode_cmd, + struct vigs_surface *fb_sfc, + struct vigs_framebuffer **vigs_fb); + +int vigs_framebuffer_pin(struct vigs_framebuffer *vigs_fb); +void vigs_framebuffer_unpin(struct vigs_framebuffer *vigs_fb); + +#endif diff --git a/drivers/gpu/drm/vigs/vigs_gem.c b/drivers/gpu/drm/vigs/vigs_gem.c new file mode 100644 index 0000000..1dfc1dd --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_gem.c @@ -0,0 +1,447 @@ +#include "vigs_gem.h" +#include "vigs_device.h" +#include "vigs_mman.h" +#include "vigs_surface.h" +#include "vigs_dp.h" +#include +#include + +static void vigs_gem_bo_destroy(struct ttm_buffer_object *bo) +{ + struct vigs_gem_object *vigs_gem = bo_to_vigs_gem(bo); + + if (vigs_gem->destroy) { + vigs_gem->destroy(vigs_gem); + } + + drm_gem_object_release(&vigs_gem->base); + kfree(vigs_gem); +} + +int vigs_gem_init(struct vigs_gem_object *vigs_gem, + struct vigs_device *vigs_dev, + enum ttm_object_type type, + unsigned long size, + bool kernel, + vigs_gem_destroy_func destroy) +{ + u32 placements[1]; + struct ttm_placement placement; + enum ttm_bo_type bo_type; + int ret = 0; + + size = roundup(size, PAGE_SIZE); + + if (size == 0) { + kfree(vigs_gem); + return -EINVAL; + } + + if (type == VIGS_GEM_TYPE_SURFACE) { + placements[0] = + TTM_PL_FLAG_CACHED | TTM_PL_FLAG_TT | TTM_PL_FLAG_NO_EVICT; + } else if (type == VIGS_GEM_TYPE_EXECBUFFER) { + placements[0] = + TTM_PL_FLAG_WC | TTM_PL_FLAG_PRIV0 | TTM_PL_FLAG_NO_EVICT; + } else { + kfree(vigs_gem); + return -EINVAL; + } + + memset(&placement, 0, sizeof(placement)); + + placement.placement = placements; + placement.busy_placement = placements; + placement.num_placement = 1; + placement.num_busy_placement = 1; + + if (kernel) { + bo_type = ttm_bo_type_kernel; + } else { + bo_type = ttm_bo_type_device; + } + + if (unlikely(vigs_dev->mman->bo_dev.dev_mapping == NULL)) { + vigs_dev->mman->bo_dev.dev_mapping = vigs_dev->drm_dev->dev_mapping; + } + + ret = drm_gem_object_init(vigs_dev->drm_dev, &vigs_gem->base, size); + + if (ret != 0) { + kfree(vigs_gem); + return ret; + } + + ret = ttm_bo_init(&vigs_dev->mman->bo_dev, &vigs_gem->bo, size, bo_type, + &placement, 0, + false, NULL, 0, NULL, + &vigs_gem_bo_destroy); + + if (ret != 0) { + return ret; + } + + vigs_gem->type = type; + vigs_gem->pin_count = 0; + vigs_gem->destroy = destroy; + + DRM_DEBUG_DRIVER("GEM created (type = %u, off = 0x%llX, sz = %lu)\n", + type, + vigs_gem_mmap_offset(vigs_gem), + vigs_gem_size(vigs_gem)); + + return 0; +} + +void vigs_gem_cleanup(struct vigs_gem_object *vigs_gem) +{ + struct ttm_buffer_object *bo = &vigs_gem->bo; + + ttm_bo_unref(&bo); +} + +int vigs_gem_pin(struct vigs_gem_object *vigs_gem) +{ + u32 placements[1]; + struct ttm_placement placement; + int ret; + + if (vigs_gem->pin_count) { + ++vigs_gem->pin_count; + return 0; + } + + if (vigs_gem->type == VIGS_GEM_TYPE_EXECBUFFER) { + vigs_gem->pin_count = 1; + + return 0; + } + + placements[0] = + TTM_PL_FLAG_WC | TTM_PL_FLAG_VRAM | TTM_PL_FLAG_NO_EVICT; + + memset(&placement, 0, sizeof(placement)); + + placement.placement = placements; + placement.busy_placement = placements; + placement.num_placement = 1; + placement.num_busy_placement = 1; + + ret = ttm_bo_validate(&vigs_gem->bo, &placement, false, false); + + if (ret != 0) { + DRM_ERROR("GEM pin failed (type = %u, off = 0x%llX, sz = %lu)\n", + vigs_gem->type, + vigs_gem_mmap_offset(vigs_gem), + vigs_gem_size(vigs_gem)); + return ret; + } + + vigs_gem->pin_count = 1; + + DRM_DEBUG_DRIVER("GEM pinned (type = %u, off = 0x%llX, sz = %lu)\n", + vigs_gem->type, + vigs_gem_mmap_offset(vigs_gem), + vigs_gem_size(vigs_gem)); + + return 0; +} + +void vigs_gem_unpin(struct vigs_gem_object *vigs_gem) +{ + u32 placements[2]; + struct ttm_placement placement; + int ret; + + BUG_ON(vigs_gem->pin_count == 0); + + if (--vigs_gem->pin_count > 0) { + return; + } + + if (vigs_gem->type == VIGS_GEM_TYPE_EXECBUFFER) { + return; + } + + vigs_gem_kunmap(vigs_gem); + + placements[0] = + TTM_PL_FLAG_WC | TTM_PL_FLAG_VRAM; + placements[1] = + TTM_PL_FLAG_CACHED | TTM_PL_FLAG_TT | TTM_PL_FLAG_NO_EVICT; + + memset(&placement, 0, sizeof(placement)); + + placement.placement = placements; + placement.busy_placement = placements; + placement.num_placement = 2; + placement.num_busy_placement = 2; + + ret = ttm_bo_validate(&vigs_gem->bo, &placement, false, false); + + if (ret != 0) { + DRM_ERROR("GEM unpin failed (type = %u, off = 0x%llX, sz = %lu)\n", + vigs_gem->type, + vigs_gem_mmap_offset(vigs_gem), + vigs_gem_size(vigs_gem)); + } else { + DRM_DEBUG_DRIVER("GEM unpinned (type = %u, off = 0x%llX, sz = %lu)\n", + vigs_gem->type, + vigs_gem_mmap_offset(vigs_gem), + vigs_gem_size(vigs_gem)); + } +} + +int vigs_gem_kmap(struct vigs_gem_object *vigs_gem) +{ + bool is_iomem; + int ret; + + BUG_ON((vigs_gem->type == VIGS_GEM_TYPE_SURFACE) && + (vigs_gem->pin_count == 0)); + + if (vigs_gem->kptr) { + return 0; + } + + ret = ttm_bo_kmap(&vigs_gem->bo, + 0, + vigs_gem->bo.num_pages, + &vigs_gem->kmap); + + if (ret != 0) { + return ret; + } + + vigs_gem->kptr = ttm_kmap_obj_virtual(&vigs_gem->kmap, &is_iomem); + + DRM_DEBUG_DRIVER("GEM (type = %u, off = 0x%llX, sz = %lu) mapped to 0x%p\n", + vigs_gem->type, + vigs_gem_mmap_offset(vigs_gem), + vigs_gem_size(vigs_gem), + vigs_gem->kptr); + + return 0; +} + +void vigs_gem_kunmap(struct vigs_gem_object *vigs_gem) +{ + if (vigs_gem->kptr == NULL) { + return; + } + + vigs_gem->kptr = NULL; + + ttm_bo_kunmap(&vigs_gem->kmap); + + DRM_DEBUG_DRIVER("GEM (type = %u, off = 0x%llX, sz = %lu) unmapped\n", + vigs_gem->type, + vigs_gem_mmap_offset(vigs_gem), + vigs_gem_size(vigs_gem)); +} + +int vigs_gem_in_vram(struct vigs_gem_object *vigs_gem) +{ + return vigs_gem->bo.mem.mem_type == TTM_PL_VRAM; +} + +int vigs_gem_wait(struct vigs_gem_object *vigs_gem) +{ + int ret; + + spin_lock(&vigs_gem->bo.bdev->fence_lock); + + ret = ttm_bo_wait(&vigs_gem->bo, true, false, false); + + spin_unlock(&vigs_gem->bo.bdev->fence_lock); + + return ret; +} + +void vigs_gem_free_object(struct drm_gem_object *gem) +{ + struct vigs_gem_object *vigs_gem = gem_to_vigs_gem(gem); + + if (vigs_gem->type == VIGS_GEM_TYPE_SURFACE) { + struct vigs_device *vigs_dev = gem->dev->dev_private; + + vigs_dp_remove_surface(vigs_dev->dp, + vigs_gem_to_vigs_surface(vigs_gem)); + } + + vigs_gem_reserve(vigs_gem); + + vigs_gem_kunmap(vigs_gem); + + vigs_gem_unreserve(vigs_gem); + + vigs_gem->freed = true; + + DRM_DEBUG_DRIVER("GEM free (type = %u, off = 0x%llX, sz = %lu)\n", + vigs_gem->type, + vigs_gem_mmap_offset(vigs_gem), + vigs_gem_size(vigs_gem)); + + vigs_gem_cleanup(vigs_gem); +} + +int vigs_gem_open_object(struct drm_gem_object *gem, + struct drm_file *file_priv) +{ + return 0; +} + +void vigs_gem_close_object(struct drm_gem_object *gem, + struct drm_file *file_priv) +{ +} + +int vigs_gem_map_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv) +{ + struct vigs_device *vigs_dev = drm_dev->dev_private; + struct drm_vigs_gem_map *args = data; + struct drm_gem_object *gem; + struct vigs_gem_object *vigs_gem; + struct mm_struct *mm = current->mm; + unsigned long address, unused; + + gem = drm_gem_object_lookup(drm_dev, file_priv, args->handle); + + if (gem == NULL) { + return -ENOENT; + } + + vigs_gem = gem_to_vigs_gem(gem); + + down_write(&mm->mmap_sem); + + /* + * We can't use 'do_mmap' here (like in i915, exynos and others) because + * 'do_mmap' takes an offset in bytes and our + * offset is 64-bit (since it's TTM offset) and it can't fit into 32-bit + * variable. + */ + vigs_dev->track_gem_access = args->track_access; + address = do_mmap_pgoff(file_priv->filp, 0, vigs_gem_size(vigs_gem), + PROT_READ | PROT_WRITE, + MAP_SHARED, + vigs_gem_mmap_offset(vigs_gem) >> PAGE_SHIFT, + &unused); + vigs_dev->track_gem_access = false; + + up_write(&mm->mmap_sem); + + drm_gem_object_unreference_unlocked(gem); + + if (IS_ERR((void*)address)) { + return PTR_ERR((void*)address); + } + + args->address = address; + + return 0; +} + +int vigs_gem_wait_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv) +{ + struct drm_vigs_gem_wait *args = data; + struct drm_gem_object *gem; + struct vigs_gem_object *vigs_gem; + int ret; + + gem = drm_gem_object_lookup(drm_dev, file_priv, args->handle); + + if (gem == NULL) { + return -ENOENT; + } + + vigs_gem = gem_to_vigs_gem(gem); + + vigs_gem_reserve(vigs_gem); + + ret = vigs_gem_wait(vigs_gem); + + vigs_gem_unreserve(vigs_gem); + + drm_gem_object_unreference_unlocked(gem); + + return ret; +} + +int vigs_gem_dumb_create(struct drm_file *file_priv, + struct drm_device *drm_dev, + struct drm_mode_create_dumb *args) +{ + struct vigs_device *vigs_dev = drm_dev->dev_private; + struct vigs_surface *sfc = NULL; + uint32_t handle; + int ret; + + if (args->bpp != 32) { + DRM_ERROR("Only 32 bpp surfaces are supported for now\n"); + return -EINVAL; + } + + args->pitch = args->width * ((args->bpp + 7) / 8); + + ret = vigs_surface_create(vigs_dev, + args->width, + args->height, + args->pitch, + vigsp_surface_bgrx8888, + true, + &sfc); + + if (ret != 0) { + return ret; + } + + args->size = vigs_gem_size(&sfc->gem); + + ret = drm_gem_handle_create(file_priv, + &sfc->gem.base, + &handle); + + drm_gem_object_unreference_unlocked(&sfc->gem.base); + + if (ret == 0) { + args->handle = handle; + } + + return 0; +} + +int vigs_gem_dumb_destroy(struct drm_file *file_priv, + struct drm_device *drm_dev, + uint32_t handle) +{ + return drm_gem_handle_delete(file_priv, handle); +} + +int vigs_gem_dumb_map_offset(struct drm_file *file_priv, + struct drm_device *drm_dev, + uint32_t handle, uint64_t *offset_p) +{ + struct drm_gem_object *gem; + struct vigs_gem_object *vigs_gem; + + BUG_ON(!offset_p); + + gem = drm_gem_object_lookup(drm_dev, file_priv, handle); + + if (gem == NULL) { + return -ENOENT; + } + + vigs_gem = gem_to_vigs_gem(gem); + + *offset_p = vigs_gem_mmap_offset(vigs_gem); + + drm_gem_object_unreference_unlocked(gem); + + return 0; +} diff --git a/drivers/gpu/drm/vigs/vigs_gem.h b/drivers/gpu/drm/vigs/vigs_gem.h new file mode 100644 index 0000000..7a8646d --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_gem.h @@ -0,0 +1,218 @@ +#ifndef _VIGS_GEM_H_ +#define _VIGS_GEM_H_ + +#include "drmP.h" +#include +#include + +#define VIGS_GEM_TYPE_SURFACE ttm_driver_type0 +#define VIGS_GEM_TYPE_EXECBUFFER ttm_driver_type1 + +struct vigs_device; +struct vigs_gem_object; + +typedef void (*vigs_gem_destroy_func)(struct vigs_gem_object *vigs_gem); + +struct vigs_gem_object +{ + struct drm_gem_object base; + + struct ttm_buffer_object bo; + + /* + * Indicates that drm_driver::gem_free_object was called. + */ + bool freed; + + enum ttm_object_type type; + + /* + * Valid only after successful call to 'vigs_gem_kmap'. + * @{ + */ + + struct ttm_bo_kmap_obj kmap; + void *kptr; /* Kernel pointer to buffer data. */ + + /* + * @} + */ + + volatile unsigned pin_count; + + vigs_gem_destroy_func destroy; +}; + +static inline struct vigs_gem_object *gem_to_vigs_gem(struct drm_gem_object *gem) +{ + return container_of(gem, struct vigs_gem_object, base); +} + +static inline struct vigs_gem_object *bo_to_vigs_gem(struct ttm_buffer_object *bo) +{ + return container_of(bo, struct vigs_gem_object, bo); +} + +/* + * Must be called with drm_device::struct_mutex held. + * @{ + */ + +static inline bool vigs_gem_freed(struct vigs_gem_object *vigs_gem) +{ + return vigs_gem->freed; +} + +/* + * @} + */ + +/* + * Initializes a gem object. 'size' is automatically rounded up to page size. + * 'vigs_gem' is kfree'd on failure. + */ +int vigs_gem_init(struct vigs_gem_object *vigs_gem, + struct vigs_device *vigs_dev, + enum ttm_object_type type, + unsigned long size, + bool kernel, + vigs_gem_destroy_func destroy); + +void vigs_gem_cleanup(struct vigs_gem_object *vigs_gem); + +/* + * Buffer size. + */ +static inline unsigned long vigs_gem_size(struct vigs_gem_object *vigs_gem) +{ + return vigs_gem->bo.num_pages << PAGE_SHIFT; +} + +/* + * GEM offset in a placement. In case of execbuffer always the same. + * In case of surface only valid when GEM is in VRAM. + */ +static inline unsigned long vigs_gem_offset(struct vigs_gem_object *vigs_gem) +{ + return vigs_gem->bo.offset; +} + +/* + * GEM offset relative to DRM_FILE_OFFSET. For kernel buffers it's always 0. + */ +static inline u64 vigs_gem_mmap_offset(struct vigs_gem_object *vigs_gem) +{ + return drm_vma_node_offset_addr(&vigs_gem->bo.vma_node); +} + +static inline void vigs_gem_reserve(struct vigs_gem_object *vigs_gem) +{ + int ret; + + ret = ttm_bo_reserve(&vigs_gem->bo, false, false, false, 0); + + BUG_ON(ret != 0); +} + +static inline void vigs_gem_unreserve(struct vigs_gem_object *vigs_gem) +{ + ttm_bo_unreserve(&vigs_gem->bo); +} + +/* + * Functions below MUST be called between + * vigs_gem_reserve/vigs_gem_unreserve. + * @{ + */ + +/* + * Pin/unpin GEM. For execbuffers this is a no-op, since they're always + * in RAM placement. For surfaces this pins the GEM into VRAM. The + * operation can fail if there's no room in VRAM and all GEMs currently + * in VRAM are pinned. + * @{ + */ +int vigs_gem_pin(struct vigs_gem_object *vigs_gem); +void vigs_gem_unpin(struct vigs_gem_object *vigs_gem); +/* + * @} + */ + +/* + * Surface GEMs must be pinned before calling these. + * @{ + */ +int vigs_gem_kmap(struct vigs_gem_object *vigs_gem); +void vigs_gem_kunmap(struct vigs_gem_object *vigs_gem); +/* + * @} + */ + +/* + * true if GEM is currently in VRAM. Note that this doesn't + * necessarily mean that it's pinned. + */ +int vigs_gem_in_vram(struct vigs_gem_object *vigs_gem); + +int vigs_gem_wait(struct vigs_gem_object *vigs_gem); + +/* + * @} + */ + +/* + * Driver hooks. + * @{ + */ + +void vigs_gem_free_object(struct drm_gem_object *gem); + +int vigs_gem_open_object(struct drm_gem_object *gem, + struct drm_file *file_priv); + +void vigs_gem_close_object(struct drm_gem_object *gem, + struct drm_file *file_priv); + +/* + * @} + */ + +/* + * IOCTLs + * @{ + */ + +int vigs_gem_map_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv); + +int vigs_gem_wait_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv); + +/* + * @} + */ + +/* + * Dumb + * @{ + */ + +int vigs_gem_dumb_create(struct drm_file *file_priv, + struct drm_device *drm_dev, + struct drm_mode_create_dumb *args); + +int vigs_gem_dumb_destroy(struct drm_file *file_priv, + struct drm_device *drm_dev, + uint32_t handle); + +int vigs_gem_dumb_map_offset(struct drm_file *file_priv, + struct drm_device *drm_dev, + uint32_t handle, uint64_t *offset_p); + +/* + * @} + */ + +#endif diff --git a/drivers/gpu/drm/vigs/vigs_irq.c b/drivers/gpu/drm/vigs/vigs_irq.c new file mode 100644 index 0000000..0bab268 --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_irq.c @@ -0,0 +1,127 @@ +#include "vigs_irq.h" +#include "vigs_device.h" +#include "vigs_regs.h" +#include "vigs_fenceman.h" + +static void vigs_finish_pageflips(struct vigs_device *vigs_dev) +{ + struct drm_pending_vblank_event *event, *tmp; + struct timeval now; + unsigned long flags; + bool is_checked = false; + + spin_lock_irqsave(&vigs_dev->drm_dev->event_lock, flags); + + list_for_each_entry_safe(event, tmp, + &vigs_dev->pageflip_event_list, + base.link) { + if (event->pipe != 0) { + continue; + } + + is_checked = true; + + do_gettimeofday(&now); + event->event.sequence = 0; + event->event.tv_sec = now.tv_sec; + event->event.tv_usec = now.tv_usec; + + list_move_tail(&event->base.link, &event->base.file_priv->event_list); + wake_up_interruptible(&event->base.file_priv->event_wait); + } + + if (is_checked) { + /* + * Call 'drm_vblank_put' only in case that 'drm_vblank_get' was + * called. + */ + if (atomic_read(&vigs_dev->drm_dev->vblank[0].refcount) > 0) { + drm_vblank_put(vigs_dev->drm_dev, 0); + } + } + + spin_unlock_irqrestore(&vigs_dev->drm_dev->event_lock, flags); +} + +int vigs_enable_vblank(struct drm_device *drm_dev, int crtc) +{ + struct vigs_device *vigs_dev = drm_dev->dev_private; + u32 value; + + DRM_DEBUG_KMS("enter: crtc = %d\n", crtc); + + if (crtc != 0) { + DRM_ERROR("bad crtc = %d", crtc); + return -EINVAL; + } + + value = VIGS_REG_CON_VBLANK_ENABLE; + + writel(value, vigs_dev->io_map->handle + VIGS_REG_CON); + + return 0; +} + +void vigs_disable_vblank(struct drm_device *drm_dev, int crtc) +{ + struct vigs_device *vigs_dev = drm_dev->dev_private; + u32 value; + + DRM_DEBUG_KMS("enter: crtc = %d\n", crtc); + + if (crtc != 0) { + DRM_ERROR("bad crtc = %d", crtc); + } + + value = 0; + + writel(value, vigs_dev->io_map->handle + VIGS_REG_CON); +} + +irqreturn_t vigs_irq_handler(int irq, void *arg) +{ + struct drm_device *drm_dev = (struct drm_device*)arg; + struct vigs_device *vigs_dev = drm_dev->dev_private; + u32 int_value; + irqreturn_t ret = IRQ_NONE; + + int_value = readl(vigs_dev->io_map->handle + VIGS_REG_INT); + + if ((int_value & (VIGS_REG_INT_VBLANK_PENDING | VIGS_REG_INT_FENCE_ACK_PENDING)) != 0) { + /* + * Clear the interrupt first in order + * not to stall the hardware. + */ + + writel(int_value, vigs_dev->io_map->handle + VIGS_REG_INT); + + ret = IRQ_HANDLED; + } + + if ((int_value & VIGS_REG_INT_FENCE_ACK_PENDING) != 0) { + u32 lower, upper; + + while (1) { + spin_lock(&vigs_dev->irq_lock); + + lower = readl(vigs_dev->io_map->handle + VIGS_REG_FENCE_LOWER); + upper = readl(vigs_dev->io_map->handle + VIGS_REG_FENCE_UPPER); + + spin_unlock(&vigs_dev->irq_lock); + + if (lower) { + vigs_fenceman_ack(vigs_dev->fenceman, lower, upper); + } else { + break; + } + } + } + + if ((int_value & VIGS_REG_INT_VBLANK_PENDING) != 0) { + drm_handle_vblank(drm_dev, 0); + + vigs_finish_pageflips(vigs_dev); + } + + return ret; +} diff --git a/drivers/gpu/drm/vigs/vigs_irq.h b/drivers/gpu/drm/vigs/vigs_irq.h new file mode 100644 index 0000000..d650087 --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_irq.h @@ -0,0 +1,12 @@ +#ifndef _VIGS_IRQ_H_ +#define _VIGS_IRQ_H_ + +#include "drmP.h" + +int vigs_enable_vblank(struct drm_device *drm_dev, int crtc); + +void vigs_disable_vblank(struct drm_device *drm_dev, int crtc); + +irqreturn_t vigs_irq_handler(int irq, void *arg); + +#endif diff --git a/drivers/gpu/drm/vigs/vigs_mman.c b/drivers/gpu/drm/vigs/vigs_mman.c new file mode 100644 index 0000000..b1790a1 --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_mman.c @@ -0,0 +1,716 @@ +#include "vigs_mman.h" +#include "vigs_fence.h" +#include +#include + +/* + * This is TTM-based memory manager for VIGS, it supports 4 memory placements: + * CPU - This is for target-only memory, not shared with host, forced by TTM, + * not used. + * GPU - This is host-only memory, not shared with target. + * VRAM - This gets allocated on "VRAM" PCI BAR, shared with host, used + * for surface placement. + * RAM - This gets allocated on "RAM" PCI BAR, shared with host, used for + * execbuffer placement. + * + * Eviction is supported, so buffers can be moved between some placements. + * Allowed movements: + * VRAM -> GPU + * GPU -> VRAM + */ + +/* + * Offsets for mmap will start at DRM_FILE_OFFSET + */ +#define DRM_FILE_OFFSET 0x100000000ULL +#define DRM_FILE_PAGE_OFFSET (DRM_FILE_OFFSET >> PAGE_SHIFT) + +/* + * DRM_GLOBAL_TTM_MEM init/release thunks + * @{ + */ + +static int vigs_ttm_mem_global_init(struct drm_global_reference *ref) +{ + return ttm_mem_global_init(ref->object); +} + +static void vigs_ttm_mem_global_release(struct drm_global_reference *ref) +{ + ttm_mem_global_release(ref->object); +} + +/* + * @} + */ + +/* + * Here we initialize mman::bo_global_ref and mman::mem_global_ref. + * This is required in order to bring up TTM bo subsystem and TTM memory + * subsystem if they aren't already up. The first one who + * calls 'drm_global_item_ref' automatically initializes the specified + * subsystem and the last one who calls 'drm_global_item_unref' automatically + * brings down the specified subsystem. + * @{ + */ + +static int vigs_mman_global_init(struct vigs_mman *mman) +{ + struct drm_global_reference *global_ref = NULL; + int ret = 0; + + global_ref = &mman->mem_global_ref; + global_ref->global_type = DRM_GLOBAL_TTM_MEM; + global_ref->size = sizeof(struct ttm_mem_global); + global_ref->init = &vigs_ttm_mem_global_init; + global_ref->release = &vigs_ttm_mem_global_release; + + ret = drm_global_item_ref(global_ref); + + if (ret != 0) { + DRM_ERROR("failed setting up TTM memory subsystem: %d\n", ret); + return ret; + } + + mman->bo_global_ref.mem_glob = mman->mem_global_ref.object; + global_ref = &mman->bo_global_ref.ref; + global_ref->global_type = DRM_GLOBAL_TTM_BO; + global_ref->size = sizeof(struct ttm_bo_global); + global_ref->init = &ttm_bo_global_init; + global_ref->release = &ttm_bo_global_release; + + ret = drm_global_item_ref(global_ref); + + if (ret != 0) { + DRM_ERROR("failed setting up TTM bo subsystem: %d\n", ret); + drm_global_item_unref(&mman->mem_global_ref); + return ret; + } + + return 0; +} + +static void vigs_mman_global_cleanup(struct vigs_mman *mman) +{ + drm_global_item_unref(&mman->bo_global_ref.ref); + drm_global_item_unref(&mman->mem_global_ref); +} + +/* + * @} + */ + +/* + * TTM backend functions. + * @{ + */ + +static int vigs_ttm_backend_bind(struct ttm_tt *tt, + struct ttm_mem_reg *bo_mem) +{ + return 0; +} + +static int vigs_ttm_backend_unbind(struct ttm_tt *tt) +{ + return 0; +} + +static void vigs_ttm_backend_destroy(struct ttm_tt *tt) +{ + struct ttm_dma_tt *dma_tt = (void*)tt; + + ttm_dma_tt_fini(dma_tt); + kfree(dma_tt); +} + +static struct ttm_backend_func vigs_ttm_backend_func = { + .bind = &vigs_ttm_backend_bind, + .unbind = &vigs_ttm_backend_unbind, + .destroy = &vigs_ttm_backend_destroy, +}; + +static struct ttm_tt *vigs_ttm_tt_create(struct ttm_bo_device *bo_dev, + unsigned long size, + uint32_t page_flags, + struct page *dummy_read_page) +{ + struct ttm_dma_tt *dma_tt; + int ret; + + dma_tt = kzalloc(sizeof(struct ttm_dma_tt), GFP_KERNEL); + + if (dma_tt == NULL) { + DRM_ERROR("cannot allocate ttm_dma_tt: OOM\n"); + return NULL; + } + + dma_tt->ttm.func = &vigs_ttm_backend_func; + + ret = ttm_dma_tt_init(dma_tt, bo_dev, size, page_flags, + dummy_read_page); + + if (ret != 0) { + DRM_ERROR("ttm_dma_tt_init failed: %d\n", ret); + kfree(dma_tt); + return NULL; + } + + return &dma_tt->ttm; +} + +/* + * @} + */ + +static int vigs_ttm_invalidate_caches(struct ttm_bo_device *bo_dev, + uint32_t flags) +{ + return 0; +} + +static int vigs_ttm_init_mem_type(struct ttm_bo_device *bo_dev, + uint32_t type, + struct ttm_mem_type_manager *man) +{ + switch (type) { + case TTM_PL_SYSTEM: + man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; + man->available_caching = TTM_PL_MASK_CACHING; + man->default_caching = TTM_PL_FLAG_CACHED; + break; + case TTM_PL_TT: + man->func = &ttm_bo_manager_func; + man->gpu_offset = 0; + man->flags = TTM_MEMTYPE_FLAG_MAPPABLE | + TTM_MEMTYPE_FLAG_CMA; + man->available_caching = TTM_PL_MASK_CACHING; + man->default_caching = TTM_PL_FLAG_CACHED; + break; + case TTM_PL_VRAM: + case TTM_PL_PRIV0: + man->func = &ttm_bo_manager_func; + man->gpu_offset = 0; + man->flags = TTM_MEMTYPE_FLAG_FIXED | + TTM_MEMTYPE_FLAG_MAPPABLE; + man->available_caching = TTM_PL_MASK_CACHING; + man->default_caching = TTM_PL_FLAG_WC; + break; + default: + DRM_ERROR("unsupported memory type: %u\n", (unsigned)type); + return -EINVAL; + } + return 0; +} + +static const u32 evict_placements[1] = +{ + TTM_PL_FLAG_CACHED | TTM_PL_FLAG_TT | TTM_PL_FLAG_NO_EVICT +}; + +static const struct ttm_placement evict_placement = +{ + .fpfn = 0, + .lpfn = 0, + .num_placement = ARRAY_SIZE(evict_placements), + .placement = evict_placements, + .num_busy_placement = ARRAY_SIZE(evict_placements), + .busy_placement = evict_placements +}; + +static void vigs_ttm_evict_flags(struct ttm_buffer_object *bo, + struct ttm_placement *placement) +{ + BUG_ON(bo->mem.mem_type != TTM_PL_VRAM); + + *placement = evict_placement; +} + +static int vigs_ttm_move(struct ttm_buffer_object *bo, + bool evict, + bool interruptible, + bool no_wait_gpu, + struct ttm_mem_reg *new_mem) +{ + struct vigs_mman *mman = bo_dev_to_vigs_mman(bo->bdev); + struct ttm_mem_reg *old_mem = &bo->mem; + + if ((old_mem->mem_type == TTM_PL_VRAM) && + (new_mem->mem_type == TTM_PL_TT)) { + mman->ops->vram_to_gpu(mman->user_data, bo); + + ttm_bo_mem_put(bo, old_mem); + + *old_mem = *new_mem; + new_mem->mm_node = NULL; + + return 0; + } else if ((old_mem->mem_type == TTM_PL_TT) && + (new_mem->mem_type == TTM_PL_VRAM)) { + mman->ops->gpu_to_vram(mman->user_data, bo, + (new_mem->start << PAGE_SHIFT) + + bo->bdev->man[new_mem->mem_type].gpu_offset); + + ttm_bo_mem_put(bo, old_mem); + + *old_mem = *new_mem; + new_mem->mm_node = NULL; + + return 0; + } else { + return ttm_bo_move_memcpy(bo, evict, no_wait_gpu, new_mem); + } +} + +static int vigs_ttm_verify_access(struct ttm_buffer_object *bo, + struct file *filp) +{ + return 0; +} + +static bool vigs_ttm_sync_obj_signaled(void *sync_obj) +{ + return vigs_fence_signaled((struct vigs_fence*)sync_obj); +} + +static int vigs_ttm_sync_obj_wait(void *sync_obj, + bool lazy, + bool interruptible) +{ + return vigs_fence_wait((struct vigs_fence*)sync_obj, interruptible); +} + +static int vigs_ttm_sync_obj_flush(void *sync_obj) +{ + return 0; +} + +static void vigs_ttm_sync_obj_unref(void **sync_obj) +{ + struct vigs_fence* fence = *sync_obj; + vigs_fence_unref(fence); + *sync_obj = NULL; +} + +static void *vigs_ttm_sync_obj_ref(void *sync_obj) +{ + vigs_fence_ref((struct vigs_fence*)sync_obj); + return sync_obj; +} + +static int vigs_ttm_fault_reserve_notify(struct ttm_buffer_object *bo) +{ + u32 placements[1]; + struct ttm_placement placement; + int ret; + + if (bo->mem.mem_type != TTM_PL_TT) { + /* + * We're only interested in GPU memory page faults. + */ + + return 0; + } + + /* + * It's GPU memory page fault. Move this buffer into VRAM. + */ + + placements[0] = TTM_PL_FLAG_WC | TTM_PL_FLAG_VRAM; + + memset(&placement, 0, sizeof(placement)); + + placement.placement = placements; + placement.busy_placement = placements; + placement.num_placement = 1; + placement.num_busy_placement = 1; + + ret = ttm_bo_validate(bo, &placement, false, false); + + if (ret != 0) { + DRM_ERROR("movement failed for 0x%llX\n", + drm_vma_node_offset_addr(&bo->vma_node)); + return ret; + } + + return 0; +} + +static int vigs_ttm_io_mem_reserve(struct ttm_bo_device *bo_dev, + struct ttm_mem_reg *mem) +{ + struct ttm_mem_type_manager *man = &bo_dev->man[mem->mem_type]; + struct vigs_mman *mman = bo_dev_to_vigs_mman(bo_dev); + + mem->bus.addr = NULL; + mem->bus.offset = 0; + mem->bus.size = mem->num_pages << PAGE_SHIFT; + mem->bus.base = 0; + mem->bus.is_iomem = false; + + if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE)) { + return -EINVAL; + } + + switch (mem->mem_type) { + case TTM_PL_SYSTEM: + case TTM_PL_TT: + break; + case TTM_PL_VRAM: + mem->bus.is_iomem = true; + mem->bus.base = mman->vram_base; + mem->bus.offset = mem->start << PAGE_SHIFT; + break; + case TTM_PL_PRIV0: + mem->bus.is_iomem = true; + mem->bus.base = mman->ram_base; + mem->bus.offset = mem->start << PAGE_SHIFT; + break; + default: + return -EINVAL; + } + + return 0; +} + +static void vigs_ttm_io_mem_free(struct ttm_bo_device *bo_dev, + struct ttm_mem_reg *mem) +{ +} + +static struct ttm_bo_driver vigs_ttm_bo_driver = +{ + .ttm_tt_create = &vigs_ttm_tt_create, /* Needed for ttm_bo_type_kernel and TTM_PL_TT */ + .ttm_tt_populate = &ttm_pool_populate, /* Needed for TTM_PL_TT */ + .ttm_tt_unpopulate = &ttm_pool_unpopulate, /* Needed for TTM_PL_TT */ + .invalidate_caches = &vigs_ttm_invalidate_caches, + .init_mem_type = &vigs_ttm_init_mem_type, + .evict_flags = &vigs_ttm_evict_flags, + .move = &vigs_ttm_move, + .verify_access = &vigs_ttm_verify_access, + .sync_obj_signaled = vigs_ttm_sync_obj_signaled, + .sync_obj_wait = vigs_ttm_sync_obj_wait, + .sync_obj_flush = vigs_ttm_sync_obj_flush, + .sync_obj_unref = vigs_ttm_sync_obj_unref, + .sync_obj_ref = vigs_ttm_sync_obj_ref, + .fault_reserve_notify = &vigs_ttm_fault_reserve_notify, + .io_mem_reserve = &vigs_ttm_io_mem_reserve, + .io_mem_free = &vigs_ttm_io_mem_free, +}; + +/* + * VMA related. + * @{ + */ + +static u32 vigs_vma_cache_index = 0; +static struct vm_operations_struct vigs_ttm_vm_ops; +static const struct vm_operations_struct *ttm_vm_ops = NULL; + +/* + * Represents per-VMA data. + * + * Since TTM already uses struct vm_area_struct::vm_private_data + * we're forced to use some other way to add our own data + * to VMA. Currently we use struct vm_area_struct::vm_ops for this. + * Generally, TTM should be refactored to not use + * struct vm_area_struct directly, but provide helper functions + * instead so that user could store whatever he wants into + * struct vm_area_struct::vm_private_data. + */ +struct vigs_mman_vma +{ + struct vm_operations_struct vm_ops; + struct ttm_buffer_object *bo; + struct kref kref; + u8 data[1]; +}; + +static void vigs_mman_vma_release(struct kref *kref) +{ + struct vigs_mman_vma *vigs_vma = + container_of(kref, struct vigs_mman_vma, kref); + struct ttm_buffer_object *bo = vigs_vma->bo; + struct vigs_mman *mman = bo_dev_to_vigs_mman(bo->bdev); + + mman->ops->cleanup_vma(mman->user_data, &vigs_vma->data[0]); + + kmem_cache_free(mman->vma_cache, vigs_vma); +} + +/* + * @} + */ + +int vigs_mman_create(resource_size_t vram_base, + resource_size_t vram_size, + resource_size_t ram_base, + resource_size_t ram_size, + uint32_t vma_data_size, + struct vigs_mman_ops *ops, + void *user_data, + struct vigs_mman **mman) +{ + int ret = 0; + char vma_cache_name[100]; + unsigned long num_pages = 0; + + DRM_DEBUG_DRIVER("enter\n"); + + BUG_ON(vma_data_size <= 0); + + *mman = kzalloc(sizeof(**mman), GFP_KERNEL); + + if (!*mman) { + ret = -ENOMEM; + goto fail1; + } + + sprintf(vma_cache_name, "vigs_vma_cache%u", vigs_vma_cache_index++); + + (*mman)->vma_cache = kmem_cache_create(vma_cache_name, + sizeof(struct vigs_mman_vma) + + vma_data_size - 1, + 0, 0, NULL); + + if (!(*mman)->vma_cache) { + ret = -ENOMEM; + goto fail2; + } + + ret = vigs_mman_global_init(*mman); + + if (ret != 0) { + goto fail3; + } + + (*mman)->vram_base = vram_base; + (*mman)->ram_base = ram_base; + (*mman)->ops = ops; + (*mman)->user_data = user_data; + + ret = ttm_bo_device_init(&(*mman)->bo_dev, + (*mman)->bo_global_ref.ref.object, + &vigs_ttm_bo_driver, + DRM_FILE_PAGE_OFFSET, + 0); + if (ret != 0) { + DRM_ERROR("failed initializing bo driver: %d\n", ret); + goto fail4; + } + + /* + * Init GPU + * @{ + */ + + /* + * For GPU we're only limited by host resources, let the target create + * as many buffers as it likes. + */ + ret = ttm_bo_init_mm(&(*mman)->bo_dev, + TTM_PL_TT, + (0xFFFFFFFFUL / PAGE_SIZE)); + if (ret != 0) { + DRM_ERROR("failed initializing GPU mm\n"); + goto fail5; + } + + /* + * @} + */ + + /* + * Init VRAM + * @{ + */ + + num_pages = vram_size / PAGE_SIZE; + + ret = ttm_bo_init_mm(&(*mman)->bo_dev, + TTM_PL_VRAM, + num_pages); + if (ret != 0) { + DRM_ERROR("failed initializing VRAM mm\n"); + goto fail6; + } + + /* + * @} + */ + + /* + * Init RAM + * @{ + */ + + num_pages = ram_size / PAGE_SIZE; + + ret = ttm_bo_init_mm(&(*mman)->bo_dev, + TTM_PL_PRIV0, + num_pages); + if (ret != 0) { + DRM_ERROR("failed initializing RAM mm\n"); + goto fail7; + } + + /* + * @} + */ + + return 0; + +fail7: + ttm_bo_clean_mm(&(*mman)->bo_dev, TTM_PL_VRAM); +fail6: + ttm_bo_clean_mm(&(*mman)->bo_dev, TTM_PL_TT); +fail5: + ttm_bo_device_release(&(*mman)->bo_dev); +fail4: + vigs_mman_global_cleanup(*mman); +fail3: + kmem_cache_destroy((*mman)->vma_cache); +fail2: + kfree(*mman); +fail1: + *mman = NULL; + + return ret; +} + +void vigs_mman_destroy(struct vigs_mman *mman) +{ + DRM_DEBUG_DRIVER("enter\n"); + + ttm_bo_clean_mm(&mman->bo_dev, TTM_PL_PRIV0); + ttm_bo_clean_mm(&mman->bo_dev, TTM_PL_VRAM); + ttm_bo_clean_mm(&mman->bo_dev, TTM_PL_TT); + ttm_bo_device_release(&mman->bo_dev); + vigs_mman_global_cleanup(mman); + kmem_cache_destroy(mman->vma_cache); + + kfree(mman); +} + +static void vigs_ttm_open(struct vm_area_struct *vma) +{ + struct vigs_mman_vma *vigs_vma = (struct vigs_mman_vma*)vma->vm_ops; + + BUG_ON(vigs_vma->bo != (struct ttm_buffer_object*)vma->vm_private_data); + + ttm_vm_ops->open(vma); + kref_get(&vigs_vma->kref); +} + +static void vigs_ttm_close(struct vm_area_struct *vma) +{ + struct vigs_mman_vma *vigs_vma = (struct vigs_mman_vma*)vma->vm_ops; + + BUG_ON(vigs_vma->bo != (struct ttm_buffer_object*)vma->vm_private_data); + + vma->vm_ops = &vigs_ttm_vm_ops; + + kref_put(&vigs_vma->kref, &vigs_mman_vma_release); + ttm_vm_ops->close(vma); +} + +static int vigs_ttm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +{ + struct ttm_buffer_object *bo = vma->vm_private_data; + + if (bo == NULL) { + return VM_FAULT_NOPAGE; + } + + return ttm_vm_ops->fault(vma, vmf); +} + +int vigs_mman_mmap(struct vigs_mman *mman, + struct file *filp, + struct vm_area_struct *vma, + bool track_access) +{ + struct vigs_mman_vma *vigs_vma; + int ret; + struct ttm_buffer_object *bo; + + if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET)) { + return drm_mmap(filp, vma); + } + + vigs_vma = kmem_cache_alloc(mman->vma_cache, GFP_KERNEL); + + if (!vigs_vma) { + return -ENOMEM; + } + + ret = ttm_bo_mmap(filp, vma, &mman->bo_dev); + + if (unlikely(ret != 0)) { + kmem_cache_free(mman->vma_cache, vigs_vma); + return ret; + } + + if (unlikely(ttm_vm_ops == NULL)) { + ttm_vm_ops = vma->vm_ops; + vigs_ttm_vm_ops = *ttm_vm_ops; + vigs_ttm_vm_ops.fault = &vigs_ttm_fault; + } + + bo = vma->vm_private_data; + + vigs_vma->vm_ops = vigs_ttm_vm_ops; + vigs_vma->bo = bo; + vigs_vma->vm_ops.open = &vigs_ttm_open; + vigs_vma->vm_ops.close = &vigs_ttm_close; + kref_init(&vigs_vma->kref); + mman->ops->init_vma(mman->user_data, + &vigs_vma->data[0], + bo, + track_access); + + vma->vm_ops = &vigs_vma->vm_ops; + + return 0; +} + +int vigs_mman_access_vma(struct vigs_mman *mman, + unsigned long address, + vigs_mman_access_vma_func func, + void *user_data) +{ + struct mm_struct *mm = current->mm; + struct vm_area_struct *vma; + int ret; + struct ttm_buffer_object *bo; + struct vigs_mman_vma *vigs_vma; + + down_read(&mm->mmap_sem); + + vma = find_vma(mm, address); + + if (!vma || + !vma->vm_ops || + (vma->vm_ops->fault != &vigs_ttm_fault)) { + ret = -ENOENT; + goto out; + } + + bo = vma->vm_private_data; + + BUG_ON(!bo); + + if (bo->bdev != &mman->bo_dev) { + ret = -ENOENT; + goto out; + } + + vigs_vma = (struct vigs_mman_vma*)vma->vm_ops; + + ret = func(user_data, &vigs_vma->data[0]); + +out: + up_read(&mm->mmap_sem); + + return ret; +} diff --git a/drivers/gpu/drm/vigs/vigs_mman.h b/drivers/gpu/drm/vigs/vigs_mman.h new file mode 100644 index 0000000..286e99a --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_mman.h @@ -0,0 +1,92 @@ +#ifndef _VIGS_MMAN_H_ +#define _VIGS_MMAN_H_ + +#include "drmP.h" +#include +#include + +struct vigs_mman_ops +{ + /* + * 'bo' is reserved while calling these. + * @{ + */ + + void (*vram_to_gpu)(void *user_data, struct ttm_buffer_object *bo); + void (*gpu_to_vram)(void *user_data, struct ttm_buffer_object *bo, + unsigned long new_offset); + /* + * @} + */ + + /* + * Per-VMA data init/cleanup. VMA may be opened/closed many times + * as the result of split/copy, but the init/cleanup handlers are called + * only once, i.e. vigs_mman is handling the reference counts. + * + * current's 'mmap_sem' is locked while calling this. + * @{ + */ + + void (*init_vma)(void *user_data, + void *vma_data, + struct ttm_buffer_object *bo, + bool track_access); + + /* + * current's 'mmap_sem' is locked while calling this. + */ + void (*cleanup_vma)(void *user_data, void *vma_data); + + /* + * @} + */ +}; + +typedef int (*vigs_mman_access_vma_func)(void *user_data, void *vma_data); + +struct vigs_mman +{ + struct kmem_cache *vma_cache; + + struct drm_global_reference mem_global_ref; + struct ttm_bo_global_ref bo_global_ref; + struct ttm_bo_device bo_dev; + + resource_size_t vram_base; + resource_size_t ram_base; + + struct vigs_mman_ops *ops; + void *user_data; +}; + +static inline struct vigs_mman *bo_dev_to_vigs_mman(struct ttm_bo_device *bo_dev) +{ + return container_of(bo_dev, struct vigs_mman, bo_dev); +} + +int vigs_mman_create(resource_size_t vram_base, + resource_size_t vram_size, + resource_size_t ram_base, + resource_size_t ram_size, + uint32_t vma_data_size, + struct vigs_mman_ops *ops, + void *user_data, + struct vigs_mman **mman); + +void vigs_mman_destroy(struct vigs_mman *mman); + +int vigs_mman_mmap(struct vigs_mman *mman, + struct file *filp, + struct vm_area_struct *vma, + bool track_access); + +/* + * current's 'mmap_sem' is locked while calling 'func'. + */ +int vigs_mman_access_vma(struct vigs_mman *mman, + unsigned long address, + vigs_mman_access_vma_func func, + void *user_data); + +#endif diff --git a/drivers/gpu/drm/vigs/vigs_output.c b/drivers/gpu/drm/vigs/vigs_output.c new file mode 100644 index 0000000..84f7890 --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_output.c @@ -0,0 +1,297 @@ +#include "vigs_output.h" +#include "vigs_device.h" +#include "drm_crtc_helper.h" +#include + +#define DPI_DEF_VALUE 316 +#define DPI_MIN_VALUE 100 +#define DPI_MAX_VALUE 600 + +#ifndef MODULE +static int vigs_atoi(const char *str) +{ + int val = 0; + + for (;; ++str) { + switch (*str) { + case '0' ... '9': + val = (10 * val) + (*str - '0'); + break; + default: + return val; + } + } +} +#endif + +struct vigs_output +{ + /* + * 'connector' is the owner of the 'vigs_output', i.e. + * when 'connector' is destroyed whole structure is destroyed. + */ + struct drm_connector connector; + struct drm_encoder encoder; +}; + +static inline struct vigs_output *connector_to_vigs_output(struct drm_connector *connector) +{ + return container_of(connector, struct vigs_output, connector); +} + +static inline struct vigs_output *encoder_to_vigs_output(struct drm_encoder *encoder) +{ + return container_of(encoder, struct vigs_output, encoder); +} + +static void vigs_connector_save(struct drm_connector *connector) +{ + DRM_DEBUG_KMS("enter\n"); +} + +static void vigs_connector_restore(struct drm_connector *connector) +{ + DRM_DEBUG_KMS("enter\n"); +} + +static enum drm_connector_status vigs_connector_detect( + struct drm_connector *connector, + bool force) +{ + DRM_DEBUG_KMS("enter: force = %d\n", force); + + return connector_status_connected; +} + +static int vigs_connector_set_property(struct drm_connector *connector, + struct drm_property *property, + uint64_t value) +{ + DRM_DEBUG_KMS("enter: %s = %llu\n", property->name, value); + + return 0; +} + +static void vigs_connector_destroy(struct drm_connector *connector) +{ + struct vigs_output *vigs_output = connector_to_vigs_output(connector); + + DRM_DEBUG_KMS("enter\n"); + + drm_sysfs_connector_remove(connector); + drm_connector_cleanup(connector); + + kfree(vigs_output); +} + +static int vigs_connector_get_modes(struct drm_connector *connector) +{ + struct vigs_output *vigs_output = connector_to_vigs_output(connector); + struct drm_device *drm_dev = vigs_output->connector.dev; + char *option = NULL; + + DRM_DEBUG_KMS("enter\n"); + + if (fb_get_options(drm_get_connector_name(connector), &option) == 0) { + struct drm_cmdline_mode cmdline_mode; + + if (drm_mode_parse_command_line_for_connector(option, + connector, + &cmdline_mode)) { + struct drm_display_mode *preferred_mode = + drm_mode_create_from_cmdline_mode(drm_dev, + &cmdline_mode); + + /* qHD workaround (540x960) */ + if (cmdline_mode.xres == 540 && cmdline_mode.yres == 960) { + preferred_mode->hdisplay = cmdline_mode.xres; + preferred_mode->hsync_start = preferred_mode->hsync_start - 1; + preferred_mode->hsync_end = preferred_mode->hsync_end - 1; + } + + preferred_mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER; + drm_mode_set_crtcinfo(preferred_mode, CRTC_INTERLACE_HALVE_V); + drm_mode_probed_add(connector, preferred_mode); + return 1; + } + } + + return 0; +} + +static int vigs_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + DRM_DEBUG_KMS("enter\n"); + + return MODE_OK; +} + +struct drm_encoder *vigs_connector_best_encoder(struct drm_connector *connector) +{ + struct vigs_output *vigs_output = connector_to_vigs_output(connector); + + DRM_DEBUG_KMS("enter\n"); + + return &vigs_output->encoder; +} + +static void vigs_encoder_destroy(struct drm_encoder *encoder) +{ + DRM_DEBUG_KMS("enter\n"); + + drm_encoder_cleanup(encoder); +} + +static void vigs_encoder_dpms(struct drm_encoder *encoder, int mode) +{ + DRM_DEBUG_KMS("enter: mode = %d\n", mode); +} + +static bool vigs_encoder_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + DRM_DEBUG_KMS("enter\n"); + + return true; +} + +static void vigs_encoder_prepare(struct drm_encoder *encoder) +{ + DRM_DEBUG_KMS("enter\n"); +} + +static void vigs_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + DRM_DEBUG_KMS("enter\n"); +} + +static void vigs_encoder_commit(struct drm_encoder *encoder) +{ + DRM_DEBUG_KMS("enter\n"); +} + +static const struct drm_connector_funcs vigs_connector_funcs = +{ + .dpms = drm_helper_connector_dpms, + .save = vigs_connector_save, + .restore = vigs_connector_restore, + .detect = vigs_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .set_property = vigs_connector_set_property, + .destroy = vigs_connector_destroy, +}; + +static const struct drm_connector_helper_funcs vigs_connector_helper_funcs = +{ + .get_modes = vigs_connector_get_modes, + .mode_valid = vigs_connector_mode_valid, + .best_encoder = vigs_connector_best_encoder, +}; + +static const struct drm_encoder_funcs vigs_encoder_funcs = +{ + .destroy = vigs_encoder_destroy, +}; + +static const struct drm_encoder_helper_funcs vigs_encoder_helper_funcs = +{ + .dpms = vigs_encoder_dpms, + .mode_fixup = vigs_encoder_mode_fixup, + .prepare = vigs_encoder_prepare, + .mode_set = vigs_encoder_mode_set, + .commit = vigs_encoder_commit, +}; + +int vigs_output_init(struct vigs_device *vigs_dev) +{ + struct vigs_output *vigs_output; + int ret; + + DRM_DEBUG_KMS("enter\n"); + + vigs_output = kzalloc(sizeof(*vigs_output), GFP_KERNEL); + + if (!vigs_output) { + return -ENOMEM; + } + + ret = drm_connector_init(vigs_dev->drm_dev, + &vigs_output->connector, + &vigs_connector_funcs, + DRM_MODE_CONNECTOR_LVDS); + + if (ret != 0) { + kfree(vigs_output); + return ret; + } + + ret = drm_encoder_init(vigs_dev->drm_dev, + &vigs_output->encoder, + &vigs_encoder_funcs, + DRM_MODE_ENCODER_LVDS); + + if (ret != 0) { + /* + * KMS subsystem will delete 'vigs_output' + */ + + return ret; + } + + /* + * We only have a single CRTC. + */ + vigs_output->encoder.possible_crtcs = (1 << 0); + + ret = drm_mode_connector_attach_encoder(&vigs_output->connector, + &vigs_output->encoder); + + if (ret != 0) { + return ret; + } + + drm_encoder_helper_add(&vigs_output->encoder, &vigs_encoder_helper_funcs); + + drm_connector_helper_add(&vigs_output->connector, &vigs_connector_helper_funcs); + + ret = drm_sysfs_connector_add(&vigs_output->connector); + + if (ret != 0) { + return ret; + } + + return 0; +} + +int vigs_output_get_dpi(void) +{ + int dpi = DPI_DEF_VALUE; +#ifndef MODULE + char *str; + + str = strstr(saved_command_line, "dpi="); + + if (str != NULL) { + str += 4; + dpi = vigs_atoi(str); + if ((dpi < DPI_MIN_VALUE) || (dpi > DPI_MAX_VALUE)) { + dpi = DPI_DEF_VALUE; + } + } +#endif + return dpi; +} + +int vigs_output_get_phys_width(int dpi, u32 width) +{ + return ((width * 254 / dpi) + 5) / 10; +} + +int vigs_output_get_phys_height(int dpi, u32 height) +{ + return ((height * 254 / dpi) + 5) / 10; +} diff --git a/drivers/gpu/drm/vigs/vigs_output.h b/drivers/gpu/drm/vigs/vigs_output.h new file mode 100644 index 0000000..2af43f6 --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_output.h @@ -0,0 +1,16 @@ +#ifndef _VIGS_OUTPUT_H_ +#define _VIGS_OUTPUT_H_ + +#include "drmP.h" + +struct vigs_device; + +int vigs_output_init(struct vigs_device *vigs_dev); + +int vigs_output_get_dpi(void); + +int vigs_output_get_phys_width(int dpi, u32 width); + +int vigs_output_get_phys_height(int dpi, u32 height); + +#endif diff --git a/drivers/gpu/drm/vigs/vigs_plane.c b/drivers/gpu/drm/vigs/vigs_plane.c new file mode 100644 index 0000000..74d1a8c --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_plane.c @@ -0,0 +1,286 @@ +#include "vigs_plane.h" +#include "vigs_device.h" +#include "vigs_framebuffer.h" +#include "vigs_surface.h" +#include "vigs_comm.h" +#include + +static const uint32_t formats[] = +{ + DRM_FORMAT_XRGB8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_NV21, + fourcc_code('N', 'V', '4', '2'), + DRM_FORMAT_NV61, + DRM_FORMAT_YUV420 +}; + +static int vigs_plane_update(struct drm_plane *plane, + struct drm_crtc *crtc, + struct drm_framebuffer *fb, + int crtc_x, int crtc_y, + unsigned int crtc_w, + unsigned int crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h) +{ + struct vigs_plane *vigs_plane = plane_to_vigs_plane(plane); + struct vigs_device *vigs_dev = plane->dev->dev_private; + struct vigs_framebuffer *vigs_fb = fb_to_vigs_fb(fb); + int ret, i; + uint32_t src_x_whole = src_x >> 16; + uint32_t src_y_whole = src_y >> 16; + uint32_t src_w_whole = src_w >> 16; + uint32_t src_h_whole = src_h >> 16; + vigsp_surface_id surface_ids[4] = { 0, 0, 0, 0 }; + vigsp_plane_format format; + + DRM_DEBUG_KMS("enter: crtc_x = %d, crtc_y = %d, crtc_w = %u, crtc_h = %u, src_x = %u, src_y = %u, src_w = %u, src_h = %u\n", + crtc_x, crtc_y, crtc_w, crtc_h, src_x, src_y, src_w, src_h); + + if (vigs_fb->surfaces[0]->scanout) { + vigs_gem_reserve(&vigs_fb->surfaces[0]->gem); + + if (vigs_gem_in_vram(&vigs_fb->surfaces[0]->gem) && + vigs_surface_need_gpu_update(vigs_fb->surfaces[0])) { + vigs_comm_update_gpu(vigs_dev->comm, + vigs_fb->surfaces[0]->id, + vigs_fb->surfaces[0]->width, + vigs_fb->surfaces[0]->height, + vigs_gem_offset(&vigs_fb->surfaces[0]->gem)); + } + + vigs_gem_unreserve(&vigs_fb->surfaces[0]->gem); + } + + for (i = 0; i < 4; ++i) { + if (vigs_fb->surfaces[i]) { + surface_ids[i] = vigs_fb->surfaces[i]->id; + } + } + + switch (fb->pixel_format) { + case DRM_FORMAT_XRGB8888: + format = vigsp_plane_bgrx8888; + break; + case DRM_FORMAT_ARGB8888: + format = vigsp_plane_bgra8888; + break; + case DRM_FORMAT_NV21: + format = vigsp_plane_nv21; + break; + case fourcc_code('N', 'V', '4', '2'): + format = vigsp_plane_nv42; + break; + case DRM_FORMAT_NV61: + format = vigsp_plane_nv61; + break; + case DRM_FORMAT_YUV420: + format = vigsp_plane_yuv420; + break; + default: + BUG(); + format = vigsp_plane_bgrx8888; + break; + } + + ret = vigs_comm_set_plane(vigs_dev->comm, + vigs_plane->index, + fb->width, + fb->height, + format, + surface_ids, + src_x_whole, + src_y_whole, + src_w_whole, + src_h_whole, + crtc_x, + crtc_y, + crtc_w, + crtc_h, + vigs_plane->z_pos, + vigs_plane->hflip, + vigs_plane->vflip, + vigs_plane->rotation); + + if (ret == 0) { + vigs_plane->src_x = src_x; + vigs_plane->src_y = src_y; + vigs_plane->src_w = src_w; + vigs_plane->src_h = src_h; + + vigs_plane->crtc_x = crtc_x; + vigs_plane->crtc_y = crtc_y; + vigs_plane->crtc_w = crtc_w; + vigs_plane->crtc_h = crtc_h; + + vigs_plane->enabled = true; + } + + return ret; +} + +static int vigs_plane_disable(struct drm_plane *plane) +{ + struct vigs_plane *vigs_plane = plane_to_vigs_plane(plane); + struct vigs_device *vigs_dev = plane->dev->dev_private; + int ret; + vigsp_surface_id surface_ids[4] = { 0, 0, 0, 0 }; + + DRM_DEBUG_KMS("enter\n"); + + if (!vigs_plane->enabled) { + return 0; + } + + ret = vigs_comm_set_plane(vigs_dev->comm, + vigs_plane->index, + 0, + 0, + 0, + surface_ids, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0); + + if (ret == 0) { + vigs_plane->src_x = 0; + vigs_plane->src_y = 0; + vigs_plane->src_w = 0; + vigs_plane->src_h = 0; + + vigs_plane->crtc_x = 0; + vigs_plane->crtc_y = 0; + vigs_plane->crtc_w = 0; + vigs_plane->crtc_h = 0; + + vigs_plane->enabled = false; + } + + return ret; +} + +static void vigs_plane_destroy(struct drm_plane *plane) +{ + struct vigs_plane *vigs_plane = plane_to_vigs_plane(plane); + + DRM_DEBUG_KMS("enter\n"); + + vigs_plane_disable(plane); + drm_plane_cleanup(plane); + kfree(vigs_plane); +} + +static const struct drm_plane_funcs vigs_plane_funcs = +{ + .update_plane = vigs_plane_update, + .disable_plane = vigs_plane_disable, + .destroy = vigs_plane_destroy, +}; + +int vigs_plane_init(struct vigs_device *vigs_dev, u32 index) +{ + struct vigs_plane *vigs_plane; + int ret; + + DRM_DEBUG_KMS("enter\n"); + + vigs_plane = kzalloc(sizeof(*vigs_plane), GFP_KERNEL); + + if (!vigs_plane) { + return -ENOMEM; + } + + vigs_plane->index = index; + + ret = drm_plane_init(vigs_dev->drm_dev, + &vigs_plane->base, + (1 << 0), + &vigs_plane_funcs, + formats, + ARRAY_SIZE(formats), + false); + + if (ret != 0) { + return ret; + } + + return 0; +} + +int vigs_plane_set_zpos_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv) +{ + struct drm_vigs_plane_set_zpos *args = data; + struct drm_mode_object *obj; + struct drm_plane *plane; + struct vigs_plane *vigs_plane; + int ret; + + drm_modeset_lock_all(drm_dev); + + obj = drm_mode_object_find(drm_dev, + args->plane_id, + DRM_MODE_OBJECT_PLANE); + if (!obj) { + ret = -EINVAL; + goto out; + } + + plane = obj_to_plane(obj); + vigs_plane = plane_to_vigs_plane(plane); + + vigs_plane->z_pos = args->zpos; + + ret = 0; + +out: + drm_modeset_unlock_all(drm_dev); + + return ret; +} + +int vigs_plane_set_transform_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv) +{ + struct drm_vigs_plane_set_transform *args = data; + struct drm_mode_object *obj; + struct drm_plane *plane; + struct vigs_plane *vigs_plane; + int ret; + + drm_modeset_lock_all(drm_dev); + + obj = drm_mode_object_find(drm_dev, + args->plane_id, + DRM_MODE_OBJECT_PLANE); + if (!obj) { + ret = -EINVAL; + goto out; + } + + plane = obj_to_plane(obj); + vigs_plane = plane_to_vigs_plane(plane); + + vigs_plane->hflip = args->hflip; + vigs_plane->vflip = args->vflip; + vigs_plane->rotation = args->rotation; + + ret = 0; + +out: + drm_modeset_unlock_all(drm_dev); + + return ret; +} diff --git a/drivers/gpu/drm/vigs/vigs_plane.h b/drivers/gpu/drm/vigs/vigs_plane.h new file mode 100644 index 0000000..327f55c --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_plane.h @@ -0,0 +1,56 @@ +#ifndef _VIGS_PLANE_H_ +#define _VIGS_PLANE_H_ + +#include "drmP.h" + +struct vigs_device; + +struct vigs_plane +{ + struct drm_plane base; + + u32 index; + + unsigned int src_x; + unsigned int src_y; + unsigned int src_w; + unsigned int src_h; + + int crtc_x; + int crtc_y; + unsigned int crtc_w; + unsigned int crtc_h; + + int z_pos; + int hflip; + int vflip; + int rotation; + + bool enabled; +}; + +static inline struct vigs_plane *plane_to_vigs_plane(struct drm_plane *plane) +{ + return container_of(plane, struct vigs_plane, base); +} + +int vigs_plane_init(struct vigs_device *vigs_dev, u32 index); + +/* + * IOCTLs + * @{ + */ + +int vigs_plane_set_zpos_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv); + +int vigs_plane_set_transform_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv); + +/* + * @} + */ + +#endif diff --git a/drivers/gpu/drm/vigs/vigs_protocol.h b/drivers/gpu/drm/vigs/vigs_protocol.h new file mode 100644 index 0000000..1115c9a --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_protocol.h @@ -0,0 +1,380 @@ +#ifndef _VIGS_PROTOCOL_H_ +#define _VIGS_PROTOCOL_H_ + +/* + * VIGS protocol is a multiple request-no response protocol. + */ + +/* + * Bump this whenever protocol changes. + */ +#define VIGS_PROTOCOL_VERSION 21 + +#define VIGS_MAX_PLANES 3 + +typedef signed char vigsp_s8; +typedef signed short vigsp_s16; +typedef signed int vigsp_s32; +typedef signed long long vigsp_s64; +typedef unsigned char vigsp_u8; +typedef unsigned short vigsp_u16; +typedef unsigned int vigsp_u32; +typedef unsigned long long vigsp_u64; + +typedef vigsp_u32 vigsp_bool; +typedef vigsp_u32 vigsp_surface_id; +typedef vigsp_u32 vigsp_offset; +typedef vigsp_u32 vigsp_color; +typedef vigsp_u32 vigsp_fence_seq; + +typedef enum +{ + /* + * These command are guaranteed to sync on host, i.e. + * no fence is required. + * @{ + */ + vigsp_cmd_init = 0x0, + vigsp_cmd_reset = 0x1, + vigsp_cmd_exit = 0x2, + vigsp_cmd_set_root_surface = 0x3, + /* + * @} + */ + /* + * These commands are executed asynchronously. + * @{ + */ + vigsp_cmd_create_surface = 0x4, + vigsp_cmd_destroy_surface = 0x5, + vigsp_cmd_update_vram = 0x6, + vigsp_cmd_update_gpu = 0x7, + vigsp_cmd_copy = 0x8, + vigsp_cmd_solid_fill = 0x9, + vigsp_cmd_set_plane = 0xA, + vigsp_cmd_ga_copy = 0xB + /* + * @} + */ +} vigsp_cmd; + +typedef enum +{ + vigsp_surface_bgrx8888 = 0x0, + vigsp_surface_bgra8888 = 0x1, +} vigsp_surface_format; + +typedef enum +{ + vigsp_plane_bgrx8888 = 0x0, + vigsp_plane_bgra8888 = 0x1, + vigsp_plane_nv21 = 0x2, + vigsp_plane_nv42 = 0x3, + vigsp_plane_nv61 = 0x4, + vigsp_plane_yuv420 = 0x5 +} vigsp_plane_format; + +typedef enum +{ + vigsp_rotation0 = 0x0, + vigsp_rotation90 = 0x1, + vigsp_rotation180 = 0x2, + vigsp_rotation270 = 0x3 +} vigsp_rotation; + +#pragma pack(1) + +struct vigsp_point +{ + vigsp_u32 x; + vigsp_u32 y; +}; + +struct vigsp_size +{ + vigsp_u32 w; + vigsp_u32 h; +}; + +struct vigsp_rect +{ + struct vigsp_point pos; + struct vigsp_size size; +}; + +struct vigsp_copy +{ + struct vigsp_point from; + struct vigsp_point to; + struct vigsp_size size; +}; + +struct vigsp_cmd_batch_header +{ + /* + * Fence sequence requested by this batch. + * 0 for none. + */ + vigsp_fence_seq fence_seq; + + /* + * Batch size starting from batch header. + * Can be 0. + */ + vigsp_u32 size; +}; + +struct vigsp_cmd_request_header +{ + vigsp_cmd cmd; + + /* + * Request size starting from request header. + */ + vigsp_u32 size; +}; + +/* + * cmd_init + * + * First command to be sent, client passes its protocol version + * and receives server's in response. If 'client_version' doesn't match + * 'server_version' then initialization is considered failed. This + * is typically called on target's DRM driver load. + * + * @{ + */ + +struct vigsp_cmd_init_request +{ + vigsp_u32 client_version; + vigsp_u32 server_version; +}; + +/* + * @} + */ + +/* + * cmd_reset + * + * Destroys all surfaces but root surface, this typically happens + * or DRM's lastclose. + * + * @{ + * @} + */ + +/* + * cmd_exit + * + * Destroys all surfaces and transitions into uninitialized state, this + * typically happens when target's DRM driver gets unloaded. + * + * @{ + * @} + */ + +/* + * cmd_create_surface + * + * Called for each surface created. Client passes 'id' of the surface, + * all further operations must be carried out using this is. 'id' is + * unique across whole target system. + * + * @{ + */ + +struct vigsp_cmd_create_surface_request +{ + vigsp_u32 width; + vigsp_u32 height; + vigsp_u32 stride; + vigsp_surface_format format; + vigsp_surface_id id; +}; + +/* + * @} + */ + +/* + * cmd_destroy_surface + * + * Destroys the surface identified by 'id'. Surface 'id' may not be used + * after this call and its id can be assigned to some other surface right + * after this call. + * + * @{ + */ + +struct vigsp_cmd_destroy_surface_request +{ + vigsp_surface_id id; +}; + +/* + * @} + */ + +/* + * cmd_set_root_surface + * + * Sets surface identified by 'id' as new root surface. Root surface is the + * one that's displayed on screen. Root surface resides in VRAM + * all the time if 'scanout' is true. + * + * Pass 0 as id in order to reset the root surface. + * + * @{ + */ + +struct vigsp_cmd_set_root_surface_request +{ + vigsp_surface_id id; + vigsp_bool scanout; + vigsp_offset offset; +}; + +/* + * @} + */ + +/* + * cmd_update_vram + * + * Updates 'sfc_id' in vram. + * + * @{ + */ + +struct vigsp_cmd_update_vram_request +{ + vigsp_surface_id sfc_id; + vigsp_offset offset; +}; + +/* + * @} + */ + +/* + * cmd_update_gpu + * + * Updates 'sfc_id' in GPU. + * + * @{ + */ + +struct vigsp_cmd_update_gpu_request +{ + vigsp_surface_id sfc_id; + vigsp_offset offset; + vigsp_u32 num_entries; + struct vigsp_rect entries[0]; +}; + +/* + * @} + */ + +/* + * cmd_copy + * + * Copies parts of surface 'src_id' to + * surface 'dst_id'. + * + * @{ + */ + +struct vigsp_cmd_copy_request +{ + vigsp_surface_id src_id; + vigsp_surface_id dst_id; + vigsp_u32 num_entries; + struct vigsp_copy entries[0]; +}; + +/* + * @} + */ + +/* + * cmd_solid_fill + * + * Fills surface 'sfc_id' with color 'color' at 'entries'. + * + * @{ + */ + +struct vigsp_cmd_solid_fill_request +{ + vigsp_surface_id sfc_id; + vigsp_color color; + vigsp_u32 num_entries; + struct vigsp_rect entries[0]; +}; + +/* + * @} + */ + +/* + * cmd_set_plane + * + * Assigns surfaces 'surfaces' to plane identified by 'plane'. + * + * Pass 0 as surfaces[0] in order to disable the plane. + * + * @{ + */ + +struct vigsp_cmd_set_plane_request +{ + vigsp_u32 plane; + vigsp_u32 width; + vigsp_u32 height; + vigsp_plane_format format; + vigsp_surface_id surfaces[4]; + struct vigsp_rect src_rect; + vigsp_s32 dst_x; + vigsp_s32 dst_y; + struct vigsp_size dst_size; + vigsp_s32 z_pos; + vigsp_bool hflip; + vigsp_bool vflip; + vigsp_rotation rotation; +}; + +/* + * @} + */ + +/* + * cmd_ga_copy + * + * Copies part of surface 'src_id' to + * surface 'dst_id' given surface + * sizes. + * + * @{ + */ + +struct vigsp_cmd_ga_copy_request +{ + vigsp_surface_id src_id; + vigsp_bool src_scanout; + vigsp_offset src_offset; + vigsp_u32 src_stride; + vigsp_surface_id dst_id; + vigsp_u32 dst_stride; + struct vigsp_copy entry; +}; + +/* + * @} + */ + +#pragma pack() + +#endif diff --git a/drivers/gpu/drm/vigs/vigs_regs.h b/drivers/gpu/drm/vigs/vigs_regs.h new file mode 100644 index 0000000..003479c --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_regs.h @@ -0,0 +1,15 @@ +#ifndef _VIGS_REGS_H_ +#define _VIGS_REGS_H_ + +#define VIGS_REG_EXEC 0 +#define VIGS_REG_CON 8 +#define VIGS_REG_INT 16 +#define VIGS_REG_FENCE_LOWER 24 +#define VIGS_REG_FENCE_UPPER 32 + +#define VIGS_REG_CON_VBLANK_ENABLE 1 + +#define VIGS_REG_INT_VBLANK_PENDING 1 +#define VIGS_REG_INT_FENCE_ACK_PENDING 2 + +#endif diff --git a/drivers/gpu/drm/vigs/vigs_surface.c b/drivers/gpu/drm/vigs/vigs_surface.c new file mode 100644 index 0000000..b5ec2f4 --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_surface.c @@ -0,0 +1,472 @@ +#include "vigs_surface.h" +#include "vigs_device.h" +#include "vigs_comm.h" +#include "vigs_mman.h" +#include + +/* + * Functions below MUST be accessed between + * vigs_gem_reserve/vigs_gem_unreserve. + * @{ + */ + +static u32 vigs_surface_saf(struct vigs_surface *sfc) +{ + u32 saf = 0; + + if (sfc->num_readers > 0) { + saf |= DRM_VIGS_SAF_READ; + } + + if (sfc->num_writers > 0) { + saf |= DRM_VIGS_SAF_WRITE; + } + + return saf; +} + +static void vigs_surface_saf_changed(struct vigs_surface *sfc, + u32 old_saf) +{ + u32 new_saf = vigs_surface_saf(sfc); + + if (old_saf == new_saf) { + return; + } + + /* + * If we're in GPU and access is write-only then we can + * obviously skip first VRAM update, since there's nothing + * to read back yet. After first VRAM update, however, we must + * read back every time since the clients must see their + * changes. + */ + + sfc->skip_vram_update = !vigs_gem_in_vram(&sfc->gem) && + (new_saf == DRM_VIGS_SAF_WRITE) && + !(old_saf & DRM_VIGS_SAF_WRITE); +} + +static void vigs_vma_data_end_access(struct vigs_vma_data *vma_data, bool sync) +{ + struct vigs_surface *sfc = vma_data->sfc; + struct vigs_device *vigs_dev = sfc->gem.base.dev->dev_private; + u32 old_saf = vigs_surface_saf(sfc); + + if (vma_data->saf & DRM_VIGS_SAF_READ) { + --sfc->num_readers; + } + + if ((vma_data->saf & DRM_VIGS_SAF_WRITE) == 0) { + goto out; + } + + if (sync) { + /* + * We have a sync, drop all pending + * writers. + */ + sfc->num_writers -= sfc->num_pending_writers; + sfc->num_pending_writers = 0; + } + + if (!vigs_gem_in_vram(&sfc->gem)) { + --sfc->num_writers; + goto out; + } + + if (sync) { + --sfc->num_writers; + vigs_comm_update_gpu(vigs_dev->comm, + sfc->id, + sfc->width, + sfc->height, + vigs_gem_offset(&sfc->gem)); + sfc->is_gpu_dirty = false; + } else { + ++sfc->num_pending_writers; + } + +out: + vma_data->saf = 0; + + vigs_surface_saf_changed(sfc, old_saf); +} + +/* + * @} + */ + +void vigs_vma_data_init(struct vigs_vma_data *vma_data, + struct vigs_surface *sfc, + bool track_access) +{ + struct vigs_device *vigs_dev = sfc->gem.base.dev->dev_private; + u32 old_saf; + + vma_data->sfc = sfc; + vma_data->saf = 0; + vma_data->track_access = track_access; + + if (track_access) { + return; + } + + /* + * If we don't want to track access for this VMA + * then register as both reader and writer. + */ + + vigs_gem_reserve(&sfc->gem); + + old_saf = vigs_surface_saf(sfc); + + ++sfc->num_writers; + ++sfc->num_readers; + + if (vigs_gem_in_vram(&sfc->gem) && sfc->is_gpu_dirty) { + vigs_comm_update_vram(vigs_dev->comm, + sfc->id, + vigs_gem_offset(&sfc->gem)); + sfc->is_gpu_dirty = false; + } + + vma_data->saf = DRM_VIGS_SAF_READ | DRM_VIGS_SAF_WRITE; + + vigs_surface_saf_changed(sfc, old_saf); + + vigs_gem_unreserve(&sfc->gem); +} + +void vigs_vma_data_cleanup(struct vigs_vma_data *vma_data) +{ + vigs_gem_reserve(&vma_data->sfc->gem); + + /* + * On unmap we sync only when access tracking is enabled. + * Otherwise, we pretend we're going to sync + * some time later, but we never will. + */ + vigs_vma_data_end_access(vma_data, + vma_data->track_access); + + vigs_gem_unreserve(&vma_data->sfc->gem); +} + +static void vigs_surface_destroy(struct vigs_gem_object *gem) +{ + struct vigs_surface *sfc = vigs_gem_to_vigs_surface(gem); + struct vigs_device *vigs_dev = gem->base.dev->dev_private; + + if (sfc->id) { + vigs_comm_destroy_surface(vigs_dev->comm, sfc->id); + + vigs_device_remove_surface(vigs_dev, sfc->id); + + DRM_DEBUG_DRIVER("Surface destroyed (id = %u)\n", sfc->id); + } +} + +int vigs_surface_create(struct vigs_device *vigs_dev, + u32 width, + u32 height, + u32 stride, + vigsp_surface_format format, + bool scanout, + struct vigs_surface **sfc) +{ + int ret = 0; + + *sfc = kzalloc(sizeof(**sfc), GFP_KERNEL); + + if (!*sfc) { + ret = -ENOMEM; + goto fail1; + } + + (*sfc)->width = width; + (*sfc)->height = height; + (*sfc)->stride = stride; + (*sfc)->format = format; + (*sfc)->scanout = scanout; + + ret = vigs_gem_init(&(*sfc)->gem, + vigs_dev, + VIGS_GEM_TYPE_SURFACE, + stride * height, + false, + &vigs_surface_destroy); + + if (ret != 0) { + goto fail1; + } + + ret = vigs_device_add_surface_unlocked(vigs_dev, *sfc, &(*sfc)->id); + + if (ret != 0) { + goto fail2; + } + + ret = vigs_comm_create_surface(vigs_dev->comm, + width, + height, + stride, + format, + (*sfc)->id); + + if (ret != 0) { + goto fail3; + } + + return 0; + +fail3: + vigs_device_remove_surface_unlocked(vigs_dev, (*sfc)->id); +fail2: + (*sfc)->id = 0; + vigs_gem_cleanup(&(*sfc)->gem); +fail1: + *sfc = NULL; + + return ret; +} + +bool vigs_surface_need_vram_update(struct vigs_surface *sfc) +{ + u32 saf = vigs_surface_saf(sfc); + bool skip_vram_update = sfc->skip_vram_update; + + sfc->skip_vram_update = false; + + return (saf != 0) && !skip_vram_update; +} + +bool vigs_surface_need_gpu_update(struct vigs_surface *sfc) +{ + u32 old_saf = vigs_surface_saf(sfc); + + sfc->num_writers -= sfc->num_pending_writers; + sfc->num_pending_writers = 0; + + vigs_surface_saf_changed(sfc, old_saf); + + return old_saf & DRM_VIGS_SAF_WRITE; +} + +int vigs_surface_create_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv) +{ + struct vigs_device *vigs_dev = drm_dev->dev_private; + struct drm_vigs_create_surface *args = data; + struct vigs_surface *sfc = NULL; + uint32_t handle; + int ret; + + ret = vigs_surface_create(vigs_dev, + args->width, + args->height, + args->stride, + args->format, + args->scanout, + &sfc); + + if (ret != 0) { + return ret; + } + + ret = drm_gem_handle_create(file_priv, + &sfc->gem.base, + &handle); + + drm_gem_object_unreference_unlocked(&sfc->gem.base); + + if (ret == 0) { + args->handle = handle; + args->size = vigs_gem_size(&sfc->gem); + args->id = sfc->id; + } + + return ret; +} + +int vigs_surface_info_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv) +{ + struct drm_vigs_surface_info *args = data; + struct drm_gem_object *gem; + struct vigs_gem_object *vigs_gem; + struct vigs_surface *sfc; + + gem = drm_gem_object_lookup(drm_dev, file_priv, args->handle); + + if (gem == NULL) { + return -ENOENT; + } + + vigs_gem = gem_to_vigs_gem(gem); + + if (vigs_gem->type != VIGS_GEM_TYPE_SURFACE) { + drm_gem_object_unreference_unlocked(gem); + return -ENOENT; + } + + sfc = vigs_gem_to_vigs_surface(vigs_gem); + + args->width = sfc->width; + args->height = sfc->height; + args->stride = sfc->stride; + args->format = sfc->format; + args->scanout = sfc->scanout; + args->size = vigs_gem_size(vigs_gem); + args->id = sfc->id; + + drm_gem_object_unreference_unlocked(gem); + + return 0; +} + +int vigs_surface_set_gpu_dirty_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv) +{ + struct drm_vigs_surface_set_gpu_dirty *args = data; + struct drm_gem_object *gem; + struct vigs_gem_object *vigs_gem; + struct vigs_surface *sfc; + + gem = drm_gem_object_lookup(drm_dev, file_priv, args->handle); + + if (gem == NULL) { + return -ENOENT; + } + + vigs_gem = gem_to_vigs_gem(gem); + + if (vigs_gem->type != VIGS_GEM_TYPE_SURFACE) { + drm_gem_object_unreference_unlocked(gem); + return -ENOENT; + } + + sfc = vigs_gem_to_vigs_surface(vigs_gem); + + vigs_gem_reserve(&sfc->gem); + + if (vigs_gem_in_vram(&sfc->gem)) { + sfc->is_gpu_dirty = true; + } + + vigs_gem_unreserve(&sfc->gem); + + drm_gem_object_unreference_unlocked(gem); + + return 0; +} + +static int vigs_surface_start_access(void *user_data, void *vma_data_opaque) +{ + struct drm_vigs_surface_start_access *args = user_data; + struct vigs_vma_data *vma_data = vma_data_opaque; + struct vigs_surface *sfc = vma_data->sfc; + struct vigs_device *vigs_dev; + u32 old_saf; + + if (!sfc) { + return -ENOENT; + } + + if (!vma_data->track_access) { + return 0; + } + + vigs_dev = sfc->gem.base.dev->dev_private; + + if ((args->saf & ~DRM_VIGS_SAF_MASK) != 0) { + return -EINVAL; + } + + vigs_gem_reserve(&sfc->gem); + + old_saf = vigs_surface_saf(sfc); + + if (vma_data->saf & DRM_VIGS_SAF_READ) { + --sfc->num_readers; + } + + if (vma_data->saf & DRM_VIGS_SAF_WRITE) { + --sfc->num_writers; + } + + if (args->saf & DRM_VIGS_SAF_WRITE) { + ++sfc->num_writers; + } + + if (args->saf & DRM_VIGS_SAF_READ) { + ++sfc->num_readers; + + if (vigs_gem_in_vram(&sfc->gem) && sfc->is_gpu_dirty) { + vigs_comm_update_vram(vigs_dev->comm, + sfc->id, + vigs_gem_offset(&sfc->gem)); + sfc->is_gpu_dirty = false; + } + } + + vma_data->saf = args->saf; + + vigs_surface_saf_changed(sfc, old_saf); + + vigs_gem_unreserve(&sfc->gem); + + return 0; +} + +int vigs_surface_start_access_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv) +{ + struct vigs_device *vigs_dev = drm_dev->dev_private; + struct drm_vigs_surface_start_access *args = data; + + return vigs_mman_access_vma(vigs_dev->mman, + args->address, + &vigs_surface_start_access, + args); +} + +static int vigs_surface_end_access(void *user_data, void *vma_data_opaque) +{ + struct drm_vigs_surface_end_access *args = user_data; + struct vigs_vma_data *vma_data = vma_data_opaque; + struct vigs_surface *sfc = vma_data->sfc; + + if (!sfc) { + return -ENOENT; + } + + if (!vma_data->track_access) { + return 0; + } + + vigs_gem_reserve(&sfc->gem); + + vigs_vma_data_end_access(vma_data, args->sync); + + vigs_gem_unreserve(&sfc->gem); + + return 0; +} + +int vigs_surface_end_access_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv) +{ + struct vigs_device *vigs_dev = drm_dev->dev_private; + struct drm_vigs_surface_end_access *args = data; + + return vigs_mman_access_vma(vigs_dev->mman, + args->address, + &vigs_surface_end_access, + args); +} diff --git a/drivers/gpu/drm/vigs/vigs_surface.h b/drivers/gpu/drm/vigs/vigs_surface.h new file mode 100644 index 0000000..1d89e3b --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_surface.h @@ -0,0 +1,128 @@ +#ifndef _VIGS_SURFACE_H_ +#define _VIGS_SURFACE_H_ + +#include "drmP.h" +#include "vigs_protocol.h" +#include "vigs_gem.h" + +struct vigs_surface +{ + /* + * Must be first member! + */ + struct vigs_gem_object gem; + + u32 width; + u32 height; + u32 stride; + vigsp_surface_format format; + bool scanout; + vigsp_surface_id id; + + /* + * Members below MUST be accessed between + * vigs_gem_reserve/vigs_gem_unreserve. + * @{ + */ + + bool is_gpu_dirty; + + /* + * Number of mmap areas (vmas) that accessed this surface for + * read/write. + * @{ + */ + u32 num_readers; + u32 num_writers; + /* + * @} + */ + + /* + * Number of mmap area writers that ended access asynchronously, i.e. + * they still account for in 'num_writers', but as soon as first GPU + * update operation takes place they'll be gone. + */ + u32 num_pending_writers; + + /* + * Specifies that we should not update VRAM on next 'update_vram' + * request. Lasts for one request. + */ + bool skip_vram_update; + + /* + * @} + */ +}; + +struct vigs_vma_data +{ + struct vigs_surface *sfc; + u32 saf; + bool track_access; +}; + +void vigs_vma_data_init(struct vigs_vma_data *vma_data, + struct vigs_surface *sfc, + bool track_access); + +void vigs_vma_data_cleanup(struct vigs_vma_data *vma_data); + +static inline struct vigs_surface *vigs_gem_to_vigs_surface(struct vigs_gem_object *vigs_gem) +{ + return container_of(vigs_gem, struct vigs_surface, gem); +} + +int vigs_surface_create(struct vigs_device *vigs_dev, + u32 width, + u32 height, + u32 stride, + vigsp_surface_format format, + bool scanout, + struct vigs_surface **sfc); + +/* + * Functions below MUST be accessed between + * vigs_gem_reserve/vigs_gem_unreserve. + * @{ + */ + +bool vigs_surface_need_vram_update(struct vigs_surface *sfc); + +bool vigs_surface_need_gpu_update(struct vigs_surface *sfc); + +/* + * @} + */ + +/* + * IOCTLs + * @{ + */ + +int vigs_surface_create_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv); + +int vigs_surface_info_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv); + +int vigs_surface_set_gpu_dirty_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv); + +int vigs_surface_start_access_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv); + +int vigs_surface_end_access_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv); + +/* + * @} + */ + +#endif diff --git a/drivers/gpu/yagl/Kconfig b/drivers/gpu/yagl/Kconfig new file mode 100644 index 0000000..363eaf9 --- /dev/null +++ b/drivers/gpu/yagl/Kconfig @@ -0,0 +1,19 @@ +# +# YaGL configuration +# + +config YAGL + tristate "YaGL passthrough driver" + depends on (DRM || FB) + default n + help + This module enables YaGL passthrough from emulated system + to hypervisor (for example, QEMU). Must be used together with fake + (hypervisor-aware) OpenGL ES libraries. + +config YAGL_DEBUG + bool "YaGL calls debug messages" + depends on YAGL + default n + help + Enable YaGL debug messages. diff --git a/drivers/gpu/yagl/Makefile b/drivers/gpu/yagl/Makefile new file mode 100644 index 0000000..a4d58c1 --- /dev/null +++ b/drivers/gpu/yagl/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for the exynos video drivers. +# + +ccflags-y := -Idrivers/gpu/yagl -Werror + +yagl-y := main.o yagl_driver.o + +obj-$(CONFIG_YAGL) += yagl.o diff --git a/drivers/gpu/yagl/debug.h b/drivers/gpu/yagl/debug.h new file mode 100644 index 0000000..e96e3e1 --- /dev/null +++ b/drivers/gpu/yagl/debug.h @@ -0,0 +1,14 @@ +#ifndef _YAGL_DEBUG_H_ +#define _YAGL_DEBUG_H_ + +#include +#include +#include "yagl.h" + +#ifdef CONFIG_YAGL_DEBUG +# define dprintk(fmt, args...) printk(KERN_DEBUG YAGL_NAME "::%s: " fmt, __FUNCTION__, ## args) +#else +# define dprintk(fmt, args...) +#endif + +#endif diff --git a/drivers/gpu/yagl/main.c b/drivers/gpu/yagl/main.c new file mode 100644 index 0000000..dca9358 --- /dev/null +++ b/drivers/gpu/yagl/main.c @@ -0,0 +1,31 @@ +#include +#include +#include "print.h" +#include "yagl_driver.h" + +MODULE_AUTHOR("Stanislav Vorobiov"); +MODULE_LICENSE("Dual BSD/GPL"); + +int yagl_init(void) +{ + int ret = yagl_driver_register(); + + if (ret != 0) + { + return ret; + } + + print_info("module loaded\n"); + + return 0; +} + +void yagl_cleanup(void) +{ + yagl_driver_unregister(); + + print_info("module unloaded\n"); +} + +module_init(yagl_init); +module_exit(yagl_cleanup); diff --git a/drivers/gpu/yagl/print.h b/drivers/gpu/yagl/print.h new file mode 100644 index 0000000..bdca900 --- /dev/null +++ b/drivers/gpu/yagl/print.h @@ -0,0 +1,11 @@ +#ifndef _YAGL_PRINT_H_ +#define _YAGL_PRINT_H_ + +#include +#include "yagl.h" + +#define print_info(fmt, args...) printk(KERN_INFO YAGL_NAME ": " fmt, ## args) + +#define print_error(fmt, args...) printk(KERN_ERR YAGL_NAME ": " fmt, ## args) + +#endif diff --git a/drivers/gpu/yagl/yagl.h b/drivers/gpu/yagl/yagl.h new file mode 100644 index 0000000..15449010 --- /dev/null +++ b/drivers/gpu/yagl/yagl.h @@ -0,0 +1,9 @@ +#ifndef _YAGL_H_ +#define _YAGL_H_ + +/* + * This is then module name. + */ +#define YAGL_NAME "yagl" + +#endif diff --git a/drivers/gpu/yagl/yagl_driver.c b/drivers/gpu/yagl/yagl_driver.c new file mode 100644 index 0000000..3a8f4ad --- /dev/null +++ b/drivers/gpu/yagl/yagl_driver.c @@ -0,0 +1,840 @@ +#include "yagl_driver.h" +#include "yagl_ioctl.h" +#include "yagl.h" +#include "debug.h" +#include "print.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define YAGL_REG_BUFFPTR 0 +#define YAGL_REG_TRIGGER 4 +#define YAGL_REGS_SIZE 8 + +#define YAGL_MAX_USERS (PAGE_SIZE / YAGL_REGS_SIZE) + +#define YAGL_USER_PTR(regs, index) ((regs) + ((index) * YAGL_REGS_SIZE)) + +#define PCI_VENDOR_ID_YAGL 0x19B1 +#define PCI_DEVICE_ID_YAGL 0x1010 + +static struct pci_device_id yagl_pci_table[] = +{ + { + .vendor = PCI_VENDOR_ID_YAGL, + .device = PCI_DEVICE_ID_YAGL, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { 0 } +}; +MODULE_DEVICE_TABLE(pci, yagl_pci_table); + +struct yagl_device +{ + /* PCI device we're managing */ + struct pci_dev *pci_dev; + + /* Misc device for accessing YaGL memory from user space */ + struct miscdevice miscdev; + + /* Physical address of YaGL registers. */ + unsigned long regs_pa; + + /* Memory area which is used for target <-> host communications */ + void __iomem *regs; + + /* 1 when user is active, 0 when slot can be used */ + int user_map[YAGL_MAX_USERS]; + + /* Mutex used to serialize device operations */ + struct mutex mutex; +}; + +struct yagl_mlock +{ + struct list_head list; + + unsigned long address; + + struct page **pages; + u32 num_pages; +}; + +struct yagl_file +{ + /* Owning device */ + struct yagl_device *device; + + /* Index in 'user_map', filled on 'open' */ + int index; + + /* Pages of a buffer. */ + struct page **pages; + u32 num_pages; + + /* Render type and host OpenGL version for this client, filled on 'open'. */ + u32 render_type; + u32 gl_version; + + /* List of mlock'ed memory regions. */ + struct list_head mlock_list; +}; + +static __inline void yagl_marshal_put_uint32_t(u8** buff, u32 value) +{ + *(u32*)(*buff) = value; + *buff += 8; +} + +static __inline u32 yagl_marshal_get_uint32_t(u8** buff) +{ + u32 tmp = *(u32*)*buff; + *buff += 8; + return tmp; +} + +static void yagl_marshal_put_page_list(u8 **buff, + struct page **pages, + u32 count) +{ + u32 i; + + yagl_marshal_put_uint32_t(buff, count); + + for (i = 0; i < count; ++i) { + yagl_marshal_put_uint32_t(buff, (uint32_t)page_to_phys(pages[i])); + } +} + +static void yagl_user_activate_update(void __iomem *regs, + int index, + unsigned long buff_pa) +{ + writel(buff_pa, YAGL_USER_PTR(regs, index) + YAGL_REG_BUFFPTR); +} + +static void yagl_user_deactivate(void __iomem *regs, int index) +{ + writel(0, YAGL_USER_PTR(regs, index) + YAGL_REG_BUFFPTR); +} + +static int yagl_alloc_pages(struct page ***pages, + u32 num_present, + u32 num_alloc) +{ + struct page **tmp; + int ret = 0, i; + + tmp = kzalloc((num_present + num_alloc) * sizeof(*tmp), GFP_KERNEL); + + if (!tmp) { + dprintk("unable to allocate memory\n"); + ret = -ENOMEM; + goto fail1; + } + + for (i = 0; i < (int)num_alloc; ++i) { + tmp[num_present + i] = alloc_page(GFP_KERNEL | __GFP_HIGHMEM); + if (!tmp[num_present + i]) { + dprintk("unable to allocate page\n"); + ret = -ENOMEM; + goto fail2; + } + } + + if (num_present > 0) { + memcpy(tmp, *pages, num_present * sizeof(*tmp)); + kfree(*pages); + } + + *pages = tmp; + + return 0; + +fail2: + while (--i >= 0) { + __free_page(tmp[num_present + i]); + } + kfree(tmp); +fail1: + return ret; +} + +static void yagl_put_pages(struct page ***pages, u32 num_present, u32 num_put) +{ + u32 i; + + for (i = 1; i <= num_put; ++i) { + __free_page((*pages)[num_present - i]); + } + + if (num_present == num_put) { + kfree(*pages); + *pages = NULL; + } +} + +static int yagl_misc_open(struct inode *inode, struct file *file) +{ + int ret = 0; + struct yagl_device *device = container_of(file->private_data, + struct yagl_device, + miscdev); + struct yagl_file *yfile; + int i; + u8 *buff; + pid_t process_id; + pid_t thread_id; + + mutex_lock(&device->mutex); + + if (file->f_mode & FMODE_EXEC) { + ret = -EPERM; + goto fail1; + } + + yfile = kzalloc(sizeof(*yfile), GFP_KERNEL); + + if (!yfile) { + dprintk("unable to allocate memory\n"); + ret = -ENOMEM; + goto fail1; + } + + yfile->device = device; + yfile->index = -1; + + for (i = 0; i < YAGL_MAX_USERS; ++i) { + if (!device->user_map[i]) { + yfile->index = i; + device->user_map[i] = 1; + break; + } + } + + if (yfile->index == -1) { + print_error("no free slots\n"); + ret = -ENOMEM; + goto fail2; + } + + yfile->num_pages = 1; + ret = yagl_alloc_pages(&yfile->pages, 0, yfile->num_pages); + + if (ret != 0) { + goto fail3; + } + + buff = kmap(yfile->pages[0]); + + memset(buff, 0, PAGE_SIZE); + + process_id = task_tgid_vnr(current); + thread_id = task_pid_vnr(current); + + yagl_marshal_put_uint32_t(&buff, YAGL_VERSION); + yagl_marshal_put_uint32_t(&buff, process_id); + yagl_marshal_put_uint32_t(&buff, thread_id); + + yagl_user_activate_update(device->regs, + yfile->index, + page_to_phys(yfile->pages[0])); + + if (yagl_marshal_get_uint32_t(&buff) != 1) { + ret = -EIO; + print_error("unable to init YaGL: probably version mismatch\n"); + goto fail4; + } + + yfile->render_type = yagl_marshal_get_uint32_t(&buff); + yfile->gl_version = yagl_marshal_get_uint32_t(&buff); + + kunmap(yfile->pages[0]); + + INIT_LIST_HEAD(&yfile->mlock_list); + + file->private_data = yfile; + + mutex_unlock(&device->mutex); + + print_info("%d opened\n", yfile->index); + + return nonseekable_open(inode, file); + +fail4: + kunmap(yfile->pages[0]); + yagl_put_pages(&yfile->pages, yfile->num_pages, yfile->num_pages); +fail3: + device->user_map[yfile->index] = 0; +fail2: + kfree(yfile); +fail1: + mutex_unlock(&device->mutex); + + return ret; +} + +static int yagl_misc_release(struct inode *inode, struct file *file) +{ + struct yagl_file *yfile = file->private_data; + struct yagl_mlock *mlock, *tmp; + u32 i; + + mutex_lock(&yfile->device->mutex); + + yagl_user_deactivate(yfile->device->regs, yfile->index); + + list_for_each_entry_safe(mlock, tmp, &yfile->mlock_list, list) { + for (i = 0; i < mlock->num_pages; ++i) { + set_page_dirty_lock(mlock->pages[i]); + put_page(mlock->pages[i]); + } + kfree(mlock->pages); + list_del(&mlock->list); + kfree(mlock); + } + + yagl_put_pages(&yfile->pages, yfile->num_pages, yfile->num_pages); + + yfile->device->user_map[yfile->index] = 0; + + mutex_unlock(&yfile->device->mutex); + + print_info("%d closed\n", yfile->index); + + kfree(file->private_data); + file->private_data = NULL; + + return 0; +} + +static int yagl_misc_mmap_regs(struct yagl_file *yfile, + struct vm_area_struct *vma) +{ + int ret = 0; + u32 num_pages = (vma->vm_end - vma->vm_start) / PAGE_SIZE; + + if (num_pages != 1) { + dprintk("%d mmap must be called for 1 page only\n", + yfile->index); + ret = -EINVAL; + goto out; + } + + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + ret = remap_pfn_range(vma, + vma->vm_start, + (yfile->device->regs_pa >> PAGE_SHIFT), + num_pages, + vma->vm_page_prot); + + if (ret != 0) { + dprintk("%d unable to remap regs memory: %d\n", + yfile->index, + ret); + goto out; + } + + ret = 0; + +out: + return ret; +} + +static int yagl_misc_mmap_buffer(struct yagl_file *yfile, + struct vm_area_struct *vma) +{ + int ret = 0; + u32 i, num_pages = (vma->vm_end - vma->vm_start) / PAGE_SIZE; + u8 *buff; + u32 status; + unsigned long addr; + + if (num_pages == 0) { + dprintk("%d mmap must be called with one page or more\n", + yfile->index); + return -EINVAL; + } + + if (num_pages > ((PAGE_SIZE / 8) - 2)) { + dprintk("%d mmap must be called with not more than %ld pages\n", + yfile->index, + ((PAGE_SIZE / 8) - 2)); + return -EINVAL; + } + + if (num_pages != yfile->num_pages) { + if (num_pages > yfile->num_pages) { + ret = yagl_alloc_pages(&yfile->pages, + yfile->num_pages, + (num_pages - yfile->num_pages)); + + if (ret != 0) { + goto out; + } + + /* + * We have at least one new page, use it for page list. + */ + + buff = kmap(yfile->pages[num_pages - 1]); + + memset(buff, 0, PAGE_SIZE); + + yagl_marshal_put_page_list(&buff, yfile->pages, num_pages); + + yagl_user_activate_update(yfile->device->regs, + yfile->index, + page_to_phys(yfile->pages[num_pages - 1])); + + status = yagl_marshal_get_uint32_t(&buff); + + kunmap(yfile->pages[num_pages - 1]); + + if (status != 1) { + yagl_put_pages(&yfile->pages, + num_pages, + (num_pages - yfile->num_pages)); + ret = -EIO; + print_error("%d unable to increase YaGL buffer due to host error\n", + yfile->index); + goto out; + } + } else { + /* + * We're putting at least one page, use it for page list before + * putting. + */ + + buff = kmap(yfile->pages[yfile->num_pages - 1]); + + memset(buff, 0, PAGE_SIZE); + + yagl_marshal_put_page_list(&buff, yfile->pages, num_pages); + + yagl_user_activate_update(yfile->device->regs, + yfile->index, + page_to_phys(yfile->pages[yfile->num_pages - 1])); + + status = yagl_marshal_get_uint32_t(&buff); + + kunmap(yfile->pages[yfile->num_pages - 1]); + + if (status != 1) { + ret = -EIO; + print_error("%d unable to decrease YaGL buffer due to host error\n", + yfile->index); + goto out; + } + + yagl_put_pages(&yfile->pages, + yfile->num_pages, + (yfile->num_pages - num_pages)); + } + } + + yfile->num_pages = num_pages; + + vma->vm_flags |= VM_DONTDUMP | VM_DONTEXPAND; + + addr = vma->vm_start; + + for (i = 0; i < num_pages; ++i) { + ret = vm_insert_page(vma, addr, yfile->pages[i]); + if (ret != 0) { + dprintk("%d unable to map buffer: %d\n", + yfile->index, + ret); + goto out; + } + + addr += PAGE_SIZE; + } + +out: + return ret; +} + +static int yagl_misc_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct yagl_file *yfile = file->private_data; + int ret = 0; + + dprintk("user = %d, pgoff = %lu, size = %lu\n", + yfile->index, + vma->vm_pgoff, + (vma->vm_end - vma->vm_start)); + + mutex_lock(&yfile->device->mutex); + + if (vma->vm_pgoff == 0) { + /* + * First page is 'regs'. + */ + + ret = yagl_misc_mmap_regs(yfile, vma); + } else if (vma->vm_pgoff == 1) { + /* + * Everything else is buffer. + */ + + ret = yagl_misc_mmap_buffer(yfile, vma); + } else { + dprintk("%d mmap must be called with page offset 0 or 1\n", + yfile->index); + ret = -EINVAL; + } + + mutex_unlock(&yfile->device->mutex); + + return ret; +} + +static int yagl_misc_mlock(struct yagl_file *yfile, + const struct yagl_mlock_arg *arg) +{ + int ret, i; + unsigned long address = arg->address & PAGE_MASK; + struct yagl_mlock *mlock; + + dprintk("user = %d, address = %p, size = %u\n", + yfile->index, + (void*)arg->address, + arg->size); + + if (arg->size == 0) { + dprintk("%d unable to mlock 0 bytes\n", + yfile->index); + return -EFAULT; + } + + down_read(¤t->mm->mmap_sem); + mutex_lock(&yfile->device->mutex); + + list_for_each_entry(mlock, &yfile->mlock_list, list) { + if (mlock->address == address) { + dprintk("%d address %p already locked\n", + yfile->index, + (void*)address); + ret = -EEXIST; + goto out; + } + } + + mlock = kzalloc(sizeof(*mlock), GFP_KERNEL); + + if (!mlock) { + dprintk("%d unable to allocate memory\n", + yfile->index); + ret = -ENOMEM; + goto out; + } + + mlock->address = address; + mlock->num_pages = PAGE_ALIGN((arg->address & ~PAGE_MASK) + arg->size) >> PAGE_SHIFT; + mlock->pages = kzalloc(mlock->num_pages * sizeof(*mlock->pages), GFP_KERNEL); + + if (!mlock->pages) { + dprintk("%d unable to allocate memory\n", + yfile->index); + kfree(mlock); + ret = -ENOMEM; + goto out; + } + + ret = get_user_pages(current, current->mm, mlock->address, + mlock->num_pages, 1, 0, mlock->pages, NULL); + + if (ret < (int)mlock->num_pages) { + mutex_unlock(&yfile->device->mutex); + up_read(¤t->mm->mmap_sem); + + for (i = 0; i < ret; ++i) { + put_page(mlock->pages[i]); + } + kfree(mlock->pages); + kfree(mlock); + + ret = (ret >= 0) ? -EFAULT : ret; + + dprintk("%d unable to get user pages: %d\n", + yfile->index, + ret); + + return ret; + } + + INIT_LIST_HEAD(&mlock->list); + + list_add_tail(&mlock->list, &yfile->mlock_list); + + ret = 0; + +out: + mutex_unlock(&yfile->device->mutex); + up_read(¤t->mm->mmap_sem); + + return ret; +} + +static int yagl_misc_munlock(struct yagl_file *yfile, + unsigned long address) +{ + u32 i; + struct yagl_mlock *mlock; + + dprintk("user = %d, address = %p\n", + yfile->index, + (void*)address); + + address &= PAGE_MASK; + + mutex_lock(&yfile->device->mutex); + + list_for_each_entry(mlock, &yfile->mlock_list, list) { + if (mlock->address == address) { + for (i = 0; i < mlock->num_pages; ++i) { + set_page_dirty_lock(mlock->pages[i]); + put_page(mlock->pages[i]); + } + kfree(mlock->pages); + list_del(&mlock->list); + kfree(mlock); + + mutex_unlock(&yfile->device->mutex); + + return 0; + } + } + + mutex_unlock(&yfile->device->mutex); + + dprintk("%d address %p not locked\n", + yfile->index, + (void*)address); + + return -ENOENT; +} + +static long yagl_misc_ioctl(struct file* file, unsigned int cmd, unsigned long arg) +{ + struct yagl_file *yfile = file->private_data; + int ret = 0; + union + { + unsigned int uint; + unsigned long ulong; + struct yagl_user_info user_info; + struct yagl_mlock_arg mlock_arg; + } value; + + if (_IOC_TYPE(cmd) != YAGL_IOC_MAGIC) { + return -ENOTTY; + } + + if (_IOC_DIR(cmd) & _IOC_READ) { + ret = !access_ok(VERIFY_WRITE, (void __user*)arg, _IOC_SIZE(cmd)); + } + + if (_IOC_DIR(cmd) & _IOC_WRITE) { + ret = ret || !access_ok(VERIFY_READ, (void __user*)arg, _IOC_SIZE(cmd)); + } + + if (ret != 0) { + return -EFAULT; + } + + ret = 0; + + switch (cmd) { + case YAGL_IOC_GET_VERSION: + value.uint = YAGL_VERSION; + ret = put_user(value.uint, (unsigned int __user*)arg); + break; + case YAGL_IOC_GET_USER_INFO: + value.user_info.index = yfile->index; + value.user_info.render_type = yfile->render_type; + value.user_info.gl_version = yfile->gl_version; + if (copy_to_user((struct yagl_user_info __user*)arg, + &value.user_info, + sizeof(value.user_info)) != 0) { + ret = -EFAULT; + } + break; + case YAGL_IOC_MLOCK: + if (copy_from_user(&value.mlock_arg, + (struct yagl_mlock_arg __user*)arg, + sizeof(value.mlock_arg)) == 0) { + ret = yagl_misc_mlock(yfile, &value.mlock_arg); + } else { + ret = -EFAULT; + } + break; + case YAGL_IOC_MUNLOCK: + ret = get_user(value.ulong, (unsigned long __user*)arg); + if (ret == 0) { + ret = yagl_misc_munlock(yfile, value.ulong); + } + break; + default: + ret = -ENOTTY; + break; + } + + return ret; +} + +static struct file_operations yagl_misc_fops = +{ + .owner = THIS_MODULE, + .open = yagl_misc_open, + .mmap = yagl_misc_mmap, + .release = yagl_misc_release, + .unlocked_ioctl = yagl_misc_ioctl, +}; + +static int yagl_driver_probe(struct pci_dev *pci_dev, + const struct pci_device_id *pci_id) +{ + int ret = 0; + struct yagl_device *device = NULL; + u32 mem_size = 0; + + dprintk("probing PCI device \"%s\"\n", dev_name(&pci_dev->dev)); + + device = kzalloc(sizeof(*device), GFP_KERNEL); + + if (!device) { + ret = -ENOMEM; + goto fail; + } + + ret = pci_enable_device(pci_dev); + + if (ret != 0) { + dprintk("%s: unable to enable PCI device\n", dev_name(&pci_dev->dev)); + + goto fail; + } + + device->pci_dev = pci_dev; + + pci_set_master(pci_dev); + + if (!pci_resource_start(pci_dev, 0)) { + dprintk("%s: bad PCI resource\n", dev_name(&pci_dev->dev)); + ret = -ENXIO; + goto fail; + } + + mem_size = pci_resource_len(pci_dev, 0); + + if (mem_size != PAGE_SIZE) { + dprintk("%s: mem size must be PAGE_SIZE\n", dev_name(&pci_dev->dev)); + ret = -ENXIO; + goto fail; + } + + if (!request_mem_region(pci_resource_start(pci_dev, 0), + PAGE_SIZE, + dev_name(&pci_dev->dev))) { + dprintk("%s: mem size must be PAGE_SIZE\n", dev_name(&pci_dev->dev)); + ret = -EBUSY; + goto fail; + } + + device->regs_pa = pci_resource_start(pci_dev, 0); + + device->regs = ioremap(device->regs_pa, mem_size); + + if (!device->regs) { + ret = -ENXIO; + goto fail; + } + + mutex_init(&device->mutex); + + device->miscdev.minor = MISC_DYNAMIC_MINOR; + device->miscdev.name = YAGL_NAME; + device->miscdev.fops = &yagl_misc_fops; + + ret = misc_register(&device->miscdev); + + if (ret != 0) { + dprintk("%s: unable to register misc device\n", dev_name(&pci_dev->dev)); + + goto fail; + } + + pci_set_drvdata(pci_dev, device); + + print_info("%s: device added\n", dev_name(&pci_dev->dev)); + + return 0; + +fail: + if (device) { + if (device->regs) { + iounmap(device->regs); + } + if (device->regs_pa) { + release_mem_region(device->regs_pa, mem_size); + } + if (device->pci_dev) { + pci_disable_device(device->pci_dev); + } + kfree(device); + } + + return ret; +} + +static void yagl_driver_remove(struct pci_dev *pci_dev) +{ + struct yagl_device* device; + + dprintk("removing driver from \"%s\"\n", dev_name(&pci_dev->dev)); + + device = pci_get_drvdata(pci_dev); + + if (device != NULL) { + misc_deregister(&device->miscdev); + + pci_set_drvdata(pci_dev, NULL); + + iounmap(device->regs); + release_mem_region(device->regs_pa, PAGE_SIZE); + pci_disable_device(device->pci_dev); + kfree(device); + } +} + +static struct pci_driver yagl_driver = +{ + .name = YAGL_NAME, + .id_table = yagl_pci_table, + .probe = yagl_driver_probe, + .remove = yagl_driver_remove, +}; + +int yagl_driver_register(void) +{ + return pci_register_driver(&yagl_driver); +} + +void yagl_driver_unregister(void) +{ + pci_unregister_driver(&yagl_driver); +} diff --git a/drivers/gpu/yagl/yagl_driver.h b/drivers/gpu/yagl/yagl_driver.h new file mode 100644 index 0000000..51cb95f --- /dev/null +++ b/drivers/gpu/yagl/yagl_driver.h @@ -0,0 +1,10 @@ +#ifndef _YAGL_DRIVER_H_ +#define _YAGL_DRIVER_H_ + +#include + +int yagl_driver_register(void); + +void yagl_driver_unregister(void); + +#endif diff --git a/drivers/gpu/yagl/yagl_ioctl.h b/drivers/gpu/yagl/yagl_ioctl.h new file mode 100644 index 0000000..f4f4ab9 --- /dev/null +++ b/drivers/gpu/yagl/yagl_ioctl.h @@ -0,0 +1,53 @@ +#ifndef _YAGL_IOCTL_H_ +#define _YAGL_IOCTL_H_ + +#include + +/* + * Version number. + */ +#define YAGL_VERSION 24 + +/* + * Device control codes magic. + */ +#define YAGL_IOC_MAGIC 'Y' + +/* + * Get version number. + */ +#define YAGL_IOC_GET_VERSION _IOR(YAGL_IOC_MAGIC, 0, unsigned int) + +/* + * Get user info. + */ +struct yagl_user_info +{ + unsigned int index; + unsigned int render_type; + unsigned int gl_version; +}; + +#define YAGL_IOC_GET_USER_INFO _IOR(YAGL_IOC_MAGIC, 1, struct yagl_user_info) + +/* + * Locks/unlocks memory. Exists solely + * for offscreen backend's backing images. + * @{ + */ + +struct yagl_mlock_arg +{ + unsigned long address; + unsigned int size; +}; + +#define YAGL_IOC_MLOCK _IOW(YAGL_IOC_MAGIC, 2, struct yagl_mlock_arg) + +#define YAGL_IOC_MUNLOCK _IOW(YAGL_IOC_MAGIC, 3, unsigned long) + +/* + * @} + */ + +#endif diff --git a/drivers/maru/Kconfig b/drivers/maru/Kconfig new file mode 100644 index 0000000..623b349a --- /dev/null +++ b/drivers/maru/Kconfig @@ -0,0 +1,71 @@ +menuconfig MARU + tristate "MARU virtual device drivers for emulator" + default n + +config MARU_VIRTIO_TOUCHSCREEN + tristate "MARU Virtio Touchscreen Driver" + depends on MARU != n + +config MARU_CAMERA + tristate "MARU Camera Driver" + depends on MARU != n && VIDEO_DEV && VIDEO_V4L2 + select VIDEOBUF_VMALLOC + +config MARU_BACKLIGHT + tristate "MARU Backlight Driver" + depends on MARU && BACKLIGHT_CLASS_DEVICE + default y + help + Say Y to enable the backlight driver of MARU. + +config MARU_JACK + tristate "MARU Jack Driver" + depends on MARU != n + +config MARU_POWER_SUPPLY + tristate "MARU Power supply Driver" + depends on MARU != n && !POWER_SUPPLY + +config MARU_VIRTIO_HWKEY + tristate "MARU Virtio HW Key Driver" + depends on MARU != n + +config MARU_VIRTIO_TABLET + tristate "MARU Virtio Tablet Driver" + depends on MARU != n + +config MARU_VIRTIO_KEYBOARD + tristate "MARU Virtio Keyboard Driver" + depends on MARU != n + +config MARU_VIRTIO_EVDI + tristate "MARU VirtIO Emulator Virtual Device Interface Driver" + depends on MARU != n + +config MARU_VIRTIO_SENSOR + tristate "MARU VirtIO Virtual Sensor Device Driver" + depends on MARU != n + +config MARU_VIRTIO_NFC + tristate "MARU VirtIO Virtual NFC Device Driver" + depends on MARU != n + +config MARU_BRILLCODEC + tristate "MARU brillcodec driver" + depends on MARU != n + +config MARU_VIRTIO_VMODEM + tristate "MARU VirtIO Virtual Modem Device Driver" + depends on MARU != n + +config MARU_VIRTIO_ROTARY + tristate "MARU VirtIO Virtual Rotary Device Driver" + depends on MARU != n + +config MARU_EXTENSION_SOURCE + tristate "MARU Extension source" + depends on MARU != n + +config MARU_EXTENSION_SOURCE_PATH + string "MARU Extension source path" + depends on MARU != n && MARU_EXTENSION_SOURCE != n diff --git a/drivers/maru/Makefile b/drivers/maru/Makefile new file mode 100644 index 0000000..5b2d497 --- /dev/null +++ b/drivers/maru/Makefile @@ -0,0 +1,18 @@ +ccflags-y += -Werror +obj-$(CONFIG_MARU_VIRTIO_TOUCHSCREEN) += maru_virtio_touchscreen.o +ifndef CONFIG_MARU_EXTENSION_SOURCE +obj-$(CONFIG_MARU_CAMERA) += maru_camera.o +endif +obj-$(CONFIG_MARU_BACKLIGHT) += maru_bl.o +obj-$(CONFIG_MARU_VIRTIO_HWKEY) += maru_virtio_hwkey.o +obj-$(CONFIG_MARU_JACK) += maru_jack.o +obj-$(CONFIG_MARU_POWER_SUPPLY) += maru_power_supply.o +obj-$(CONFIG_MARU_VIRTIO_KEYBOARD) += maru_virtio_keyboard.o +obj-$(CONFIG_MARU_VIRTIO_NFC) += maru_virtio_nfc.o +obj-$(CONFIG_MARU_VIRTIO_EVDI) += maru_virtio_evdi.o +obj-$(CONFIG_MARU_VIRTIO_SENSOR) += sensors/ #maru_virtio_sensor.o +obj-$(CONFIG_MARU_BRILLCODEC) += maru_brillcodec.o +obj-$(CONFIG_MARU_VIRTIO_VMODEM) += maru_virtio_vmodem.o +obj-$(CONFIG_MARU_VIRTIO_ROTARY) += maru_virtio_rotary.o +obj-$(CONFIG_MARU_VIRTIO_TABLET) += maru_virtio_tablet.o +obj-$(CONFIG_MARU_EXTENSION_SOURCE) += $(CONFIG_MARU_EXTENSION_SOURCE_PATH)/ diff --git a/drivers/maru/maru_bl.c b/drivers/maru/maru_bl.c new file mode 100644 index 0000000..d317bfd --- /dev/null +++ b/drivers/maru/maru_bl.c @@ -0,0 +1,366 @@ +/* + * MARU Virtual Backlight Driver + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * Jinhyung Jo + * YeongKyoon Lee + * Dohyung Hong + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * Contributors: + * - S-Core Co., Ltd + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define MARUBL_DRIVER_NAME "maru_backlight" + +#define MIN_BRIGHTNESS 0 +#define MAX_BRIGHTNESS 100 + +static struct pci_device_id marubl_pci_table[] = { + { + .vendor = PCI_VENDOR_ID_TIZEN, + .device = PCI_DEVICE_ID_VIRTUAL_BRIGHTNESS, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, marubl_pci_table); + +/* MARU virtual brightness(backlight) device structure */ +struct marubl { + struct backlight_device *bl_dev; + struct lcd_device *lcd_dev; + unsigned int prev_brightness; + unsigned int brightness; + resource_size_t reg_start, reg_size; + /* memory mapped registers */ + unsigned char __iomem *marubl_mmreg; + int power_off; + int hbm_on; +}; + +/* ========================================================================== */ +static struct marubl *marubl_device; +/* ========================================================================== */ + +static int min_brightness = MIN_BRIGHTNESS; +static int max_brightness = MAX_BRIGHTNESS; + +static int marubl_get_intensity(struct backlight_device *bd) +{ + return marubl_device->brightness; +} + +static int marubl_send_intensity(struct backlight_device *bd) +{ + int intensity = bd->props.brightness; + unsigned int off = 0; + + if (bd->props.power != FB_BLANK_UNBLANK) { + intensity = 0; + off = 1; + } + if (bd->props.state & BL_CORE_FBBLANK) { + intensity = 0; + off = 1; + } + if (bd->props.state & BL_CORE_SUSPENDED) { + intensity = 0; + off = 1; + } + if (marubl_device->hbm_on && !off && intensity != MAX_BRIGHTNESS) { + marubl_device->hbm_on = 0; + printk(KERN_INFO "HBM is turned off because brightness reduced.\n"); + } + + writel(intensity, marubl_device->marubl_mmreg); + writel(off, marubl_device->marubl_mmreg + 0x04); + marubl_device->brightness = intensity; + marubl_device->power_off = off ? 1 : 0; + + return 0; +} + +static const struct backlight_ops marubl_ops = { + .options = BL_CORE_SUSPENDRESUME, + .get_brightness = marubl_get_intensity, + .update_status = marubl_send_intensity, +}; + +int maru_lcd_get_power(struct lcd_device *ld) +{ + int ret = 0; + + if (marubl_device->power_off) { + ret = FB_BLANK_POWERDOWN; + } else { + ret = FB_BLANK_UNBLANK; + } + return ret; +} + +static struct lcd_ops maru_lcd_ops = { + .get_power = maru_lcd_get_power, +}; + +static ssize_t hbm_show_status(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int rc; + + rc = sprintf(buf, "%s\n", marubl_device->hbm_on ? "on" : "off"); + printk(KERN_INFO "[%s] get: %d\n", __func__, marubl_device->hbm_on); + + return rc; +} + +static ssize_t hbm_store_status(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret = -1; + + if (strcmp(buf, "on") == 0) { + ret = 1; + } else if (strcmp(buf, "off") == 0) { + ret = 0; + } else { + return -EINVAL; + } + + /* If the same as the previous state, ignore it */ + if (ret == marubl_device->hbm_on) + return count; + + if (ret) { + /* Save previous level, set to MAX level */ + mutex_lock(&marubl_device->bl_dev->ops_lock); + marubl_device->prev_brightness = + marubl_device->bl_dev->props.brightness; + marubl_device->bl_dev->props.brightness = MAX_BRIGHTNESS; + marubl_send_intensity(marubl_device->bl_dev); + mutex_unlock(&marubl_device->bl_dev->ops_lock); + } else { + /* Restore previous level */ + mutex_lock(&marubl_device->bl_dev->ops_lock); + marubl_device->bl_dev->props.brightness = + marubl_device->prev_brightness; + marubl_send_intensity(marubl_device->bl_dev); + mutex_unlock(&marubl_device->bl_dev->ops_lock); + } + marubl_device->hbm_on = ret; + printk(KERN_INFO "[%s] hbm = %d\n", __func__, ret); + + return count; +} + +static struct device_attribute hbm_device_attr = + __ATTR(hbm, 0644, hbm_show_status, hbm_store_status); + +/* pci probe function +*/ +static int marubl_probe(struct pci_dev *pci_dev, + const struct pci_device_id *ent) +{ + int ret; + struct backlight_device *bd; + struct lcd_device *ld; + struct backlight_properties props; + + marubl_device = kmalloc(sizeof(struct marubl), GFP_KERNEL); + if (marubl_device == NULL) { + printk(KERN_ERR "marubl: kmalloc() is failed.\n"); + return -ENOMEM; + } + + memset(marubl_device, 0, sizeof(struct marubl)); + + ret = pci_enable_device(pci_dev); + if (ret < 0) { + printk(KERN_ERR "marubl: pci_enable_device is failed.\n"); + kfree(marubl_device); + marubl_device = NULL; + return ret; + } + + ret = -EIO; + + /* 1 : IORESOURCE_MEM */ + marubl_device->reg_start = pci_resource_start(pci_dev, 1); + marubl_device->reg_size = pci_resource_len(pci_dev, 1); + if (!request_mem_region(marubl_device->reg_start, + marubl_device->reg_size, + MARUBL_DRIVER_NAME)) { + pci_disable_device(pci_dev); + kfree(marubl_device); + marubl_device = NULL; + return ret; + } + + /* memory areas mapped kernel space */ + marubl_device->marubl_mmreg = ioremap(marubl_device->reg_start, + marubl_device->reg_size); + if (!marubl_device->marubl_mmreg) { + release_mem_region(marubl_device->reg_start, + marubl_device->reg_size); + pci_disable_device(pci_dev); + kfree(marubl_device); + marubl_device = NULL; + return ret; + } + + pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, 64); + pci_set_master(pci_dev); + + /* + * register High Brightness Mode + */ + ret = device_create_file(&pci_dev->dev, &hbm_device_attr); + if (ret < 0) { + iounmap(marubl_device->marubl_mmreg); + release_mem_region(marubl_device->reg_start, + marubl_device->reg_size); + pci_disable_device(pci_dev); + kfree(marubl_device); + marubl_device = NULL; + return ret; + } + + /* + * register backlight device + */ + memset(&props, 0, sizeof(struct backlight_properties)); + props.min_brightness = min_brightness; + props.max_brightness = max_brightness; + props.type = BACKLIGHT_PLATFORM; + bd = backlight_device_register("emulator", + &pci_dev->dev, + NULL, + &marubl_ops, + &props); + if (IS_ERR(bd)) { + ret = PTR_ERR(bd); + iounmap(marubl_device->marubl_mmreg); + release_mem_region(marubl_device->reg_start, + marubl_device->reg_size); + pci_disable_device(pci_dev); + kfree(marubl_device); + marubl_device = NULL; + return ret; + } + + ld = lcd_device_register("emulator", &pci_dev->dev, NULL, &maru_lcd_ops); + if (IS_ERR(ld)) { + ret = PTR_ERR(ld); + iounmap(marubl_device->marubl_mmreg); + release_mem_region(marubl_device->reg_start, + marubl_device->reg_size); + pci_disable_device(pci_dev); + kfree(marubl_device); + marubl_device = NULL; + return ret; + } + + bd->props.brightness = (unsigned int)readl(marubl_device->marubl_mmreg); + bd->props.power = FB_BLANK_UNBLANK; + backlight_update_status(bd); + + marubl_device->bl_dev = bd; + marubl_device->lcd_dev = ld; + + printk(KERN_INFO "marubl: MARU Virtual Backlight driver is loaded.\n"); + return 0; +} + +static void marubl_exit(struct pci_dev *pcidev) +{ + /* + * Unregister backlight device + */ + struct backlight_device *bd = marubl_device->bl_dev; + struct lcd_device *ld = marubl_device->lcd_dev; + + bd->props.power = 0; + bd->props.brightness = 0; + backlight_update_status(bd); + + lcd_device_unregister(ld); + backlight_device_unregister(bd); + device_remove_file(&pcidev->dev, &hbm_device_attr); + + /* + * Unregister pci device & delete device + */ + iounmap(marubl_device->marubl_mmreg); + release_mem_region(marubl_device->reg_start, marubl_device->reg_size); + pci_disable_device(pcidev); + kfree(marubl_device); + marubl_device = NULL; +} + +/* + * register pci driver + */ +static struct pci_driver marubl_pci_driver = { + .name = MARUBL_DRIVER_NAME, + .id_table = marubl_pci_table, + .probe = marubl_probe, + .remove = marubl_exit, +#ifdef CONFIG_PM + /* .suspend = marubl_suspend, */ + /* .resume = marubl_resume, */ +#endif +}; + +static int __init marubl_module_init(void) +{ + return pci_register_driver(&marubl_pci_driver); +} + +static void __exit marubl_module_exit(void) +{ + pci_unregister_driver(&marubl_pci_driver); +} + +/* + * if this is compiled into the kernel, we need to ensure that the + * class is registered before users of the class try to register lcd's + */ +module_init(marubl_module_init); +module_exit(marubl_module_exit); + +MODULE_LICENSE("GPL2"); +MODULE_AUTHOR("Jinhyung Jo "); +MODULE_DESCRIPTION("MARU Virtual Backlight Driver for x86"); diff --git a/drivers/maru/maru_brillcodec.c b/drivers/maru/maru_brillcodec.c new file mode 100644 index 0000000..31a11f2 --- /dev/null +++ b/drivers/maru/maru_brillcodec.c @@ -0,0 +1,1227 @@ +/* + * Virtual Codec Device Driver + * + * Copyright (c) 2013 - 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * Kitae Kim + * SeokYeon Hwang + * YeongKyoon Lee + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * Contributors: + * - S-Core Co., Ltd + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("Virtual Codec Device Driver"); +MODULE_AUTHOR("Kitae KIM memory_blocks[i]; + block->start_offset = offset; + for (cnt = 0; cnt < block->n_units; cnt++) { + block->units[cnt].mem_offset = offset; + list_add_tail(&block->units[cnt].entry, &block->available); + + offset += block->unit_size; + } + block->end_offset = offset; + } +} + +static void bh_func(struct work_struct *work) +{ + uint32_t value; + + DEBUG("%s\n", __func__); + do { + value = + readl(brillcodec_device->ioaddr + DEVICE_CMD_GET_CTX_FROM_QUEUE); + DEBUG("read a value from device %x.\n", value); + if (value) { + context_flags[value] = 1; + wake_up_interruptible(&wait_queue); + } else { + DEBUG("there is no available task\n"); + } + } while (value); +} + +static int secure_device_memory(uint32_t ctx_id, uint32_t buf_size, + int non_blocking, uint32_t* offset) +{ + int ret = 0; + struct device_mem *unit = NULL; + enum block_size index = SMALL; + struct memory_block* block = NULL; + + if (buf_size < CODEC_S_DEVICE_MEM_SIZE) { + index = SMALL; + } else if (buf_size < CODEC_M_DEVICE_MEM_SIZE) { + index = MEDIUM; + } else if (buf_size < CODEC_L_DEVICE_MEM_SIZE) { + index = LARGE; + } else { + ERROR("invalid buffer size: %x\n", buf_size); + return -1; + } + + block = &brillcodec_device->memory_blocks[index]; + + // decrease buffer_semaphore + DEBUG("before down buffer_sema: %d\n", block->semaphore.count); + + if (non_blocking) { + if (down_trylock(&block->semaphore)) { // if 1 + DEBUG("buffer is not available now\n"); + return -1; + } + } else { + if (down_trylock(&block->semaphore)) { // if 1 + if (down_interruptible(&block->last_buf_semaphore)) { // if -EINTR + DEBUG("down_interruptible interrupted\n"); + return -1; + } + block->last_buf_secured = 1; // protected under last_buf_semaphore + ret = 1; + DEBUG("lock last buffer semaphore.\n"); + } + } + + DEBUG("after down buffer_sema: %d\n", block->semaphore.count); + + mutex_lock(&block->access_mutex); + unit = list_first_entry(&block->available, struct device_mem, entry); + if (!unit) { + // available unit counts are protected under semaphore. + // so can not enter here... + ret = -1; + if (block->last_buf_secured) { + up(&block->last_buf_semaphore); + } else { + up(&block->semaphore); + } + ERROR("failed to get memory block.\n"); + } else { + unit->ctx_id = ctx_id; + list_move_tail(&unit->entry, &block->occupied); + *offset = unit->mem_offset; + DEBUG("get available memory region: 0x%x\n", ret); + } + mutex_unlock(&block->access_mutex); + + return ret; +} + +static int release_device_memory(uint32_t mem_offset) +{ + struct device_mem *unit = NULL; + enum block_size index = SMALL; + struct memory_block *block = NULL; + bool found = false; + int ret = 0; + + struct list_head *pos, *temp; + + if (mem_offset < brillcodec_device->memory_blocks[0].end_offset) { + index = SMALL; + } else if (mem_offset < brillcodec_device->memory_blocks[1].end_offset) { + index = MEDIUM; + } else if (mem_offset < brillcodec_device->memory_blocks[2].end_offset) { + index = LARGE; + } else { + // error + ERROR("invalid memory offsset. offset = 0x%x.\n", (uint32_t)mem_offset); + return -2; + } + + block = &brillcodec_device->memory_blocks[index]; + + mutex_lock(&block->access_mutex); + if (!list_empty(&block->occupied)) { + list_for_each_safe(pos, temp, &block->occupied) { + unit = list_entry(pos, struct device_mem, entry); + if (unit->mem_offset == (uint32_t)mem_offset) { + unit->ctx_id = 0; + list_move_tail(&unit->entry, &block->available); + + if (block->last_buf_secured) { + block->last_buf_secured = 0; + up(&block->last_buf_semaphore); + DEBUG("unlock last buffer semaphore.\n"); + } else { + up(&block->semaphore); + DEBUG("unlock semaphore: %d.\n", block->semaphore.count); + } + + found = true; + break; + } + } + if (!found) { + // can not enter here... + ERROR("cannot find this memory block. offset = 0x%x.\n", (uint32_t)mem_offset); + ret = -1; + } + } else { + // can not enter here... + ERROR("there is not any using memory block.\n"); + ret = -1; + } + mutex_unlock(&block->access_mutex); + + return ret; +} + +static void dispose_device_memory(uint32_t context_id) +{ + struct device_mem *unit = NULL; + int index = 0; + struct memory_block *block = NULL; + struct list_head *pos, *temp; + + for (index = SMALL; index <= LARGE; index++) { + block = &brillcodec_device->memory_blocks[index]; + + mutex_lock(&block->access_mutex); + if (!list_empty(&block->occupied)) { + list_for_each_safe(pos, temp, &block->occupied) { + unit = list_entry(pos, struct device_mem, entry); + if (unit->ctx_id == context_id) { + unit->ctx_id = 0; + list_move_tail(&unit->entry, &block->available); + INFO("dispose memory block: %x", unit->mem_offset); + } + } + } + mutex_unlock(&block->access_mutex); + } +} + +static inline bool is_memory_monopolizing_api(int api_index) { +#ifdef SUPPORT_MEMORY_MONOPOLIZING + if (brillcodec_device->memory_monopolizing & (1 << api_index)) { + DEBUG("API [%d] monopolize memory slot\n", api_index); + return true; + } +#endif + return false; +} + +static void cache_info(void) +{ + void __iomem *memaddr = NULL; + void *codec_info = NULL; + uint32_t codec_info_len = 0; + + memaddr = ioremap(brillcodec_device->mem_start, + brillcodec_device->mem_size); + if (!memaddr) { + ERROR("ioremap failed\n"); + return; + } + + codec_info_len = *(uint32_t *)memaddr; + + codec_info = + kzalloc(codec_info_len, GFP_KERNEL); + if (!codec_info) { + ERROR("falied to allocate codec_info memory!\n"); + return; + } + + memcpy(codec_info, (uint8_t *)memaddr + sizeof(uint32_t), codec_info_len); + iounmap(memaddr); + + brillcodec_device->codec_elem.buf = codec_info; + brillcodec_device->codec_elem.buf_size = codec_info_len; + brillcodec_device->codec_elem_cached = true; +} + +static long put_data_into_buffer(struct ioctl_data *data) { + long value = 0, ret = 0; + unsigned long flags; + + DEBUG("read data into buffer\n"); + + if (!is_memory_monopolizing_api(data->api_index)) { + value = secure_device_memory(data->ctx_index, data->buffer_size, 0, &data->mem_offset); + } + + if (value < 0) { + DEBUG("failed to get available memory\n"); + ret = -EINVAL; + } else { + DEBUG("send a request to pop data from device. %d\n", data->ctx_index); + + ENTER_CRITICAL_SECTION(flags); + writel((uint32_t)data->mem_offset, + brillcodec_device->ioaddr + DEVICE_CMD_DEVICE_MEM_OFFSET); + writel((uint32_t)data->ctx_index, + brillcodec_device->ioaddr + DEVICE_CMD_GET_DATA_FROM_QUEUE); + LEAVE_CRITICAL_SECTION(flags); + } + + /* 1 means that only an available buffer is left at the moment. + * gst-plugins-emulator will allocate heap buffer to store + output buffer of codec. + */ + if (value == 1) { + ret = 1; + } + + return ret; +} + +static long brillcodec_ioctl(struct file *file, + unsigned int request, + unsigned long arg) +{ + long value = 0, ret = 0; + + int cmd = _IOC_NR(request); + DEBUG("%s ioctl cmd: %d\n", DEVICE_NAME, cmd); + + switch (cmd) { + case IOCTL_CMD_GET_VERSION: + { + DEBUG("%s version: %d\n", DEVICE_NAME, brillcodec_device->major_version); + + if (copy_to_user((void *)arg, &brillcodec_device->major_version, sizeof(int))) { + ERROR("ioctl: failed to copy data to user\n"); + ret = -EIO; + } + break; + } + case IOCTL_CMD_GET_ELEMENTS_SIZE: + { + uint32_t len = 0; + unsigned long flags; + + DEBUG("request a device to get codec elements\n"); + + ENTER_CRITICAL_SECTION(flags); + if (!brillcodec_device->codec_elem_cached) { + value = readl(brillcodec_device->ioaddr + DEVICE_CMD_GET_ELEMENT); + if (value < 0) { + ERROR("ioctl: failed to get elements. %d\n", (int)value); + ret = -EINVAL; + } + cache_info(); + } + len = brillcodec_device->codec_elem.buf_size; + LEAVE_CRITICAL_SECTION(flags); + + if (copy_to_user((void *)arg, &len, sizeof(uint32_t))) { + ERROR("ioctl: failed to copy data to user\n"); + ret = -EIO; + } + break; + } + case IOCTL_CMD_GET_ELEMENTS: + { + void *codec_elem = NULL; + uint32_t elem_len = brillcodec_device->codec_elem.buf_size; + + DEBUG("request codec elements.\n"); + + codec_elem = brillcodec_device->codec_elem.buf; + if (!codec_elem) { + ERROR("ioctl: codec elements is empty\n"); + ret = -EIO; + } else if (copy_to_user((void *)arg, codec_elem, elem_len)) { + ERROR("ioctl: failed to copy data to user\n"); + ret = -EIO; + } + break; + } + case IOCTL_CMD_GET_CONTEXT_INDEX: + { + DEBUG("request a device to get an index of codec context \n"); + + value = readl(brillcodec_device->ioaddr + DEVICE_CMD_GET_CONTEXT_INDEX); + if (value < 1 || value > (CODEC_CONTEXT_SIZE - 1)) { + ERROR("ioctl: failed to get proper context. %d\n", (int)value); + ret = -EINVAL; + } else { + // task_id & context_id + DEBUG("add context. ctx_id: %d\n", (int)value); + + context_add((uintptr_t)file, value); + if (copy_to_user((void *)arg, &value, sizeof(uint32_t))) { + ERROR("ioctl: failed to copy data to user.\n"); + ret = -EIO; + } + } + break; + } + case IOCTL_CMD_SECURE_BUFFER: + { + uint32_t offset = 0; + struct ioctl_data opaque; + + DEBUG("read data into small buffer\n"); + if (copy_from_user(&opaque, (void *)arg, sizeof(struct ioctl_data))) { + ERROR("ioctl: failed to copy data from user\n"); + ret = -EIO; + break; + } + + value = secure_device_memory(opaque.ctx_index, opaque.buffer_size, 0, &offset); + if (value < 0) { + DEBUG("failed to get available memory\n"); + ret = -EINVAL; + } else { + opaque.mem_offset = offset; + if (copy_to_user((void *)arg, &opaque, sizeof(struct ioctl_data))) { + ERROR("ioctl: failed to copy data to user.\n"); + ret = -EIO; + } + } + break; + } + case IOCTL_CMD_TRY_SECURE_BUFFER: + { + uint32_t offset = 0; + struct ioctl_data opaque; + + DEBUG("read data into small buffer\n"); + if (copy_from_user(&opaque, (void *)arg, sizeof(struct ioctl_data))) { + ERROR("ioctl: failed to copy data from user\n"); + ret = -EIO; + break; + } + + value = secure_device_memory(opaque.ctx_index, opaque.buffer_size, 1, &offset); + if (value < 0) { + DEBUG("failed to get available memory\n"); + ret = -EINVAL; + } else { + opaque.mem_offset = offset; + if (copy_to_user((void *)arg, &opaque, sizeof(struct ioctl_data))) { + ERROR("ioctl: failed to copy data to user.\n"); + ret = -EIO; + } + } + break; + } + case IOCTL_CMD_RELEASE_BUFFER: + { + uint32_t mem_offset; + + if (copy_from_user(&mem_offset, (void *)arg, sizeof(uint32_t))) { + ERROR("ioctl: failed to copy data from user\n"); + ret = -EIO; + break; + } + ret = release_device_memory(mem_offset); + if (ret < 0) { + ERROR("failed to release device memory\n"); + } + break; + } + case IOCTL_CMD_INVOKE_API_AND_GET_DATA: + { + struct ioctl_data opaque = { 0, }; + + if (copy_from_user(&opaque, (void *)arg, sizeof(struct ioctl_data))) { + ERROR("failed to get codec parameter info from user\n"); + ret = -EIO; + break; + } + + ret = invoke_api_and_release_buffer(&opaque); + if (ret < 0) { + ERROR("failed to invoke API : [%d]\n", opaque.api_index); + } + + if (opaque.buffer_size != -1) { + ret = put_data_into_buffer(&opaque); + if (ret < 0) { + ret = -EIO; + break; + } + + if (copy_to_user((void *)arg, &opaque, sizeof(struct ioctl_data))) { + ERROR("ioctl: failed to copy data to user.\n"); + ret = -EIO; + } + } + break; + } + case IOCTL_CMD_GET_PROFILE_STATUS: + { + DEBUG("%s profile status: %d\n", DEVICE_NAME, brillcodec_device->enable_profile); + + if (copy_to_user((void *)arg, &brillcodec_device->enable_profile, sizeof(uint8_t))) { + ERROR("ioctl: failed to copy data to user\n"); + ret = -EIO; + } + break; + } + default: + DEBUG("no available command."); + ret = -EINVAL; + break; + } + + return ret; +} + +static int invoke_api_and_release_buffer(struct ioctl_data *data) +{ + int api_index, ctx_index; + unsigned long flags; + int ret = 0; + + DEBUG("enter %s\n", __func__); + + api_index = data->api_index; + ctx_index = data->ctx_index; + + switch (api_index) { + case INIT: + case DECODE_VIDEO: + case ENCODE_VIDEO: + case DECODE_AUDIO: + case ENCODE_AUDIO: + case DECODE_VIDEO_AND_PICTURE_COPY: + { + ENTER_CRITICAL_SECTION(flags); + writel((uint32_t)data->mem_offset, + brillcodec_device->ioaddr + DEVICE_CMD_DEVICE_MEM_OFFSET); + writel((int32_t)data->ctx_index, + brillcodec_device->ioaddr + DEVICE_CMD_CONTEXT_INDEX); + writel((int32_t)data->api_index, + brillcodec_device->ioaddr + DEVICE_CMD_API_INDEX); + LEAVE_CRITICAL_SECTION(flags); + + if (!is_memory_monopolizing_api(api_index)) { + ret = release_device_memory(data->mem_offset); + } + + if (ret < 0) { + ERROR("failed to release device memory\n"); + } + + break; + } + case PICTURE_COPY: + case DEINIT: + case FLUSH_BUFFERS: + { + ENTER_CRITICAL_SECTION(flags); + writel((int32_t)data->ctx_index, + brillcodec_device->ioaddr + DEVICE_CMD_CONTEXT_INDEX); + writel((int32_t)data->api_index, + brillcodec_device->ioaddr + DEVICE_CMD_API_INDEX); + LEAVE_CRITICAL_SECTION(flags); + + break; + } + default: + DEBUG("invalid API commands: %d", api_index); + return -1; + } + + wait_event_interruptible(wait_queue, context_flags[ctx_index] != 0); + context_flags[ctx_index] = 0; + + if (api_index == DEINIT) { + dispose_device_memory(data->ctx_index); + } + + DEBUG("leave %s\n", __func__); + + return ret; +} + +static int brillcodec_mmap(struct file *file, struct vm_area_struct *vm) +{ + unsigned long off; + unsigned long phys_addr; + unsigned long size; + int ret = -1; + + size = vm->vm_end - vm->vm_start; + if (size > brillcodec_device->mem_size) { + ERROR("over mapping size\n"); + return -EINVAL; + } + off = vm->vm_pgoff << PAGE_SHIFT; + phys_addr = (PAGE_ALIGN(brillcodec_device->mem_start) + off) >> PAGE_SHIFT; + + /* VM_IO | VM_DONTEXPAND | VM_DONTDUMP are set by remap_pfn_range() */ + ret = remap_pfn_range(vm, vm->vm_start, phys_addr, + size, vm->vm_page_prot); + if (ret < 0) { + ERROR("failed to remap page range\n"); + return -EAGAIN; + } + + return 0; +} + +static irqreturn_t irq_handler(int irq, void *dev_id) +{ + struct brillcodec_device *dev = (struct brillcodec_device *)dev_id; + unsigned long flags = 0; + int val = 0; + + val = readl(dev->ioaddr + DEVICE_CMD_GET_THREAD_STATE); + if (!(val & CODEC_IRQ_TASK)) { + return IRQ_NONE; + } + + DEBUG("handle an interrupt from codec device.\n"); + + spin_lock_irqsave(&dev->lock, flags); + + DEBUG("add bottom-half function to codec_workqueue\n"); + queue_work(bh_workqueue, &bh_work); + + spin_unlock_irqrestore(&dev->lock, flags); + + return IRQ_HANDLED; +} + +static void context_add(uintptr_t user_pid, uint32_t ctx_id) +{ + struct list_head *pos, *temp; + struct user_process_id *pid_elem = NULL; + struct context_id *cid_elem = NULL; + unsigned long flags; + + DEBUG("enter: %s\n", __func__); + + DEBUG("before inserting context. user_pid: %x, ctx_id: %d\n", + user_pid, ctx_id); + + ENTER_CRITICAL_SECTION(flags); + if (!list_empty(&brillcodec_device->user_pid_mgr)) { + list_for_each_safe(pos, temp, &brillcodec_device->user_pid_mgr) { + pid_elem = list_entry(pos, struct user_process_id, pid_node); + + DEBUG("add context. pid_elem: %p\n", pid_elem); + if (pid_elem && pid_elem->id == user_pid) { + + DEBUG("add context. user_pid: %x, ctx_id: %d\n", + user_pid, ctx_id); + + cid_elem = kzalloc(sizeof(struct context_id), GFP_KERNEL); + if (!cid_elem) { + ERROR("failed to allocate context_mgr memory\n"); + return; + } + + INIT_LIST_HEAD(&cid_elem->node); + + DEBUG("add context. user_pid: %x, pid_elem: %p, cid_elem: %p, node: %p\n", + user_pid, pid_elem, cid_elem, &cid_elem->node); + + cid_elem->id = ctx_id; + list_add_tail(&cid_elem->node, &pid_elem->ctx_id_mgr); + } + } + } else { + DEBUG("user_pid_mgr is empty\n"); + } + LEAVE_CRITICAL_SECTION(flags); + + DEBUG("leave: %s\n", __func__); +} + +static void brillcodec_context_remove(struct user_process_id *pid_elem) +{ + struct list_head *pos, *temp; + struct context_id *cid_elem = NULL; + + DEBUG("enter: %s\n", __func__); + + if (!list_empty(&pid_elem->ctx_id_mgr)) { + list_for_each_safe(pos, temp, &pid_elem->ctx_id_mgr) { + cid_elem = list_entry(pos, struct context_id, node); + if (cid_elem) { + if (cid_elem->id > 0 && cid_elem->id < CODEC_CONTEXT_SIZE) { + DEBUG("remove context. ctx_id: %d\n", cid_elem->id); + writel(cid_elem->id, + brillcodec_device->ioaddr + DEVICE_CMD_RELEASE_CONTEXT); + dispose_device_memory(cid_elem->id); + } + + DEBUG("delete node from ctx_id_mgr. %p\n", &cid_elem->node); + __list_del_entry(&cid_elem->node); + DEBUG("release cid_elem. %p\n", cid_elem); + kfree(cid_elem); + } else { + DEBUG("no context in the pid_elem\n"); + } + } + } else { + DEBUG("ctx_id_mgr is empty. user_pid: %x\n", pid_elem->id); + } + DEBUG("leave: %s\n", __func__); +} + +static void task_add(uintptr_t user_pid) +{ + struct user_process_id *pid_elem = NULL; + unsigned long flags; + + DEBUG("enter: %s\n", __func__); + + ENTER_CRITICAL_SECTION(flags); + pid_elem = kzalloc(sizeof(struct user_process_id), GFP_KERNEL); + if (!pid_elem) { + ERROR("failed to allocate user_process memory\n"); + return; + } + + INIT_LIST_HEAD(&pid_elem->pid_node); + INIT_LIST_HEAD(&pid_elem->ctx_id_mgr); + + DEBUG("add task. user_pid: %x, pid_elem: %p, pid_node: %p\n", + user_pid, pid_elem, &pid_elem->pid_node); + pid_elem->id = user_pid; + list_add_tail(&pid_elem->pid_node, &brillcodec_device->user_pid_mgr); + LEAVE_CRITICAL_SECTION(flags); + + DEBUG("leave: %s\n", __func__); +} + +static void task_remove(uintptr_t user_pid) +{ + struct list_head *pos, *temp; + struct user_process_id *pid_elem = NULL; + unsigned long flags; + + DEBUG("enter: %s\n", __func__); + + ENTER_CRITICAL_SECTION(flags); + if (!list_empty(&brillcodec_device->user_pid_mgr)) { + list_for_each_safe(pos, temp, &brillcodec_device->user_pid_mgr) { + pid_elem = list_entry(pos, struct user_process_id, pid_node); + if (pid_elem) { + if (pid_elem->id == user_pid) { + // remove task and codec contexts that is running in the task. + DEBUG("remove task. user_pid: %x, pid_elem: %p\n", + user_pid, pid_elem); + brillcodec_context_remove(pid_elem); + } + + DEBUG("move pid_node from user_pid_mgr. %p\n", &pid_elem->pid_node); + __list_del_entry(&pid_elem->pid_node); + DEBUG("release pid_elem. %p\n", pid_elem); + kfree(pid_elem); + } else { + DEBUG("no task in the user_pid_mgr\n"); + } + } + } else { + DEBUG("user_pid_mgr is empty\n"); + } + LEAVE_CRITICAL_SECTION(flags); + + DEBUG("leave: %s\n", __func__); +} + + +static int brillcodec_open(struct inode *inode, struct file *file) +{ + DEBUG("open! struct file: %p\n", file); + + /* register interrupt handler */ + if (request_irq(brillcodec_device->dev->irq, irq_handler, + IRQF_SHARED, DEVICE_NAME, brillcodec_device)) { + ERROR("failed to register irq handle\n"); + return -EBUSY; + } + + task_add((uintptr_t)file); + + try_module_get(THIS_MODULE); + + return 0; +} + +static int brillcodec_release(struct inode *inode, struct file *file) +{ + DEBUG("close! struct file: %p\n", file); + + /* free irq */ + if (brillcodec_device->dev->irq) { + DEBUG("free registered irq\n"); + free_irq(brillcodec_device->dev->irq, brillcodec_device); + } + + DEBUG("before removing task: %x\n", (uint32_t)file); + /* free resource */ + task_remove((uintptr_t)file); + + module_put(THIS_MODULE); + + return 0; +} + +/* define file opertion for CODEC */ +const struct file_operations brillcodec_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = brillcodec_ioctl, + .open = brillcodec_open, + .mmap = brillcodec_mmap, + .release = brillcodec_release, +}; + +static struct miscdevice codec_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = DEVICE_NAME, + .fops = &brillcodec_fops, + .mode = S_IRUGO | S_IWUGO, +}; + +static bool get_device_info(void) +{ + uint32_t info = readl(brillcodec_device->ioaddr + DEVICE_CMD_GET_DEVICE_INFO); + + brillcodec_device->major_version = (uint32_t)((info & 0x0000FF00) >> 8); + brillcodec_device->minor_version = (uint8_t)(info & 0x000000FF); + + if (brillcodec_device->major_version != DRIVER_VERSION) { + ERROR("Version mismatch. driver version [%d], device version [%d.%d].\n", + DRIVER_VERSION, brillcodec_device->major_version, + brillcodec_device->minor_version); + return false; + } + + // check memory monopolizing API + brillcodec_device->memory_monopolizing = (info & 0xFFFF0000) >> 16; + + // check profile status + info = readl(brillcodec_device->ioaddr + DEVICE_CMD_GET_PROFILE_STATUS); + brillcodec_device->enable_profile = (uint8_t)info; + + return true; +} + +static int brillcodec_probe(struct pci_dev *pci_dev, + const struct pci_device_id *pci_id) +{ + int ret = 0; + int index = 0; + + brillcodec_device = + kzalloc(sizeof(struct brillcodec_device), GFP_KERNEL); + if (!brillcodec_device) { + ERROR("Failed to allocate memory for codec.\n"); + return -ENOMEM; + } + + brillcodec_device->dev = pci_dev; + + INIT_LIST_HEAD(&brillcodec_device->user_pid_mgr); + + // initialize memory block structures + brillcodec_device->memory_blocks[0].unit_size = CODEC_S_DEVICE_MEM_SIZE; + brillcodec_device->memory_blocks[0].n_units = CODEC_S_DEVICE_MEM_COUNT; + brillcodec_device->memory_blocks[1].unit_size = CODEC_M_DEVICE_MEM_SIZE; + brillcodec_device->memory_blocks[1].n_units = CODEC_M_DEVICE_MEM_COUNT; + brillcodec_device->memory_blocks[2].unit_size = CODEC_L_DEVICE_MEM_SIZE; + brillcodec_device->memory_blocks[2].n_units = CODEC_L_DEVICE_MEM_COUNT; + + for (index = 0; index < 3; ++index) { + struct memory_block *block = &brillcodec_device->memory_blocks[index]; + block->units = + kzalloc(sizeof(struct device_mem) * block->n_units, GFP_KERNEL); + + block->last_buf_secured = 0; + + INIT_LIST_HEAD(&block->available); + INIT_LIST_HEAD(&block->occupied); + sema_init(&block->semaphore, block->n_units - 1); + sema_init(&block->last_buf_semaphore, 1); + mutex_init(&block->access_mutex); + } + + divide_device_memory(); + + spin_lock_init(&brillcodec_device->lock); + + if ((ret = pci_enable_device(pci_dev))) { + ERROR("pci_enable_device failed\n"); + return ret; + } + pci_set_master(pci_dev); + + brillcodec_device->mem_start = pci_resource_start(pci_dev, 0); + brillcodec_device->mem_size = pci_resource_len(pci_dev, 0); + if (!brillcodec_device->mem_start) { + ERROR("pci_resource_start failed\n"); + pci_disable_device(pci_dev); + return -ENODEV; + } + + if (!request_mem_region(brillcodec_device->mem_start, + brillcodec_device->mem_size, + DEVICE_NAME)) { + ERROR("request_mem_region failed\n"); + pci_disable_device(pci_dev); + return -EINVAL; + } + + brillcodec_device->io_start = pci_resource_start(pci_dev, 1); + brillcodec_device->io_size = pci_resource_len(pci_dev, 1); + if (!brillcodec_device->io_start) { + ERROR("pci_resource_start failed\n"); + release_mem_region(brillcodec_device->mem_start, brillcodec_device->mem_size); + pci_disable_device(pci_dev); + return -ENODEV; + } + + if (!request_mem_region(brillcodec_device->io_start, + brillcodec_device->io_size, + DEVICE_NAME)) { + ERROR("request_io_region failed\n"); + release_mem_region(brillcodec_device->mem_start, brillcodec_device->mem_size); + pci_disable_device(pci_dev); + return -EINVAL; + } + + brillcodec_device->ioaddr = + ioremap_nocache(brillcodec_device->io_start, brillcodec_device->io_size); + if (!brillcodec_device->ioaddr) { + ERROR("ioremap failed\n"); + release_mem_region(brillcodec_device->io_start, brillcodec_device->io_size); + release_mem_region(brillcodec_device->mem_start, brillcodec_device->mem_size); + pci_disable_device(pci_dev); + return -EINVAL; + } + + if (!get_device_info()) { + return -EINVAL; + } + + if ((ret = misc_register(&codec_dev))) { + ERROR("cannot register codec as misc\n"); + iounmap(brillcodec_device->ioaddr); + release_mem_region(brillcodec_device->io_start, brillcodec_device->io_size); + release_mem_region(brillcodec_device->mem_start, brillcodec_device->mem_size); + pci_disable_device(pci_dev); + return ret; + } + + printk(KERN_INFO "%s: driver is probed. driver version [%d], device version [%d.%d]\n", + DEVICE_NAME, DRIVER_VERSION, brillcodec_device->major_version, + brillcodec_device->minor_version); + + if (brillcodec_device->enable_profile) { + printk(KERN_INFO "%s: profile enabled\n", DEVICE_NAME); + } + + return 0; +} + +static void brillcodec_remove(struct pci_dev *pci_dev) +{ + if (brillcodec_device) { + if (brillcodec_device->ioaddr) { + iounmap(brillcodec_device->ioaddr); + brillcodec_device->ioaddr = NULL; + } + + if (brillcodec_device->io_start) { + release_mem_region(brillcodec_device->io_start, + brillcodec_device->io_size); + brillcodec_device->io_start = 0; + } + + if (brillcodec_device->mem_start) { + release_mem_region(brillcodec_device->mem_start, + brillcodec_device->mem_size); + brillcodec_device->mem_start = 0; + } + +/* + if (brillcodec_device->units) { +// FIXME +// kfree(brillcodec_device->elem); + brillcodec_device->units= NULL; + } +*/ + + if (brillcodec_device->codec_elem.buf) { + kfree(brillcodec_device->codec_elem.buf); + brillcodec_device->codec_elem.buf = NULL; + } + + kfree(brillcodec_device); + } + + misc_deregister(&codec_dev); + pci_disable_device(pci_dev); +} + +static struct pci_device_id brillcodec_pci_table[] = { + { + .vendor = PCI_VENDOR_ID_TIZEN_EMUL, + .device = PCI_DEVICE_ID_VIRTUAL_BRILL_CODEC, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + {}, +}; +MODULE_DEVICE_TABLE(pci, brillcodec_pci_table); + +static struct pci_driver driver = { + .name = DEVICE_NAME, + .id_table = brillcodec_pci_table, + .probe = brillcodec_probe, + .remove = brillcodec_remove, +}; + +static int __init brillcodec_init(void) +{ + printk(KERN_INFO "%s: driver is initialized.\n", DEVICE_NAME); + + bh_workqueue = create_workqueue ("maru_brillcodec"); + if (!bh_workqueue) { + ERROR("failed to allocate workqueue\n"); + return -ENOMEM; + } + + return pci_register_driver(&driver); +} + +static void __exit brillcodec_exit(void) +{ + printk(KERN_INFO "%s: driver is finalized.\n", DEVICE_NAME); + + if (bh_workqueue) { + destroy_workqueue (bh_workqueue); + bh_workqueue = NULL; + } + pci_unregister_driver(&driver); +} +module_init(brillcodec_init); +module_exit(brillcodec_exit); diff --git a/drivers/maru/maru_camera.c b/drivers/maru/maru_camera.c new file mode 100644 index 0000000..483c036 --- /dev/null +++ b/drivers/maru/maru_camera.c @@ -0,0 +1,1453 @@ +/* + * MARU Virtual Camera Driver + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * Jinhyung Jo + * Sangho Park + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * Contributors: + * - S-Core Co., Ltd + * + */ + +/* + * Some code based on vivi driver or videobuf_vmalloc. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MARUCAM_DEBUG_LEVEL 0 + +static unsigned debug; + +#define MARUCAM_MODULE_NAME "marucam" + +#define marucam_err(fmt, arg...) \ + printk(KERN_ERR "%s: error [%s:%d]: " fmt, MARUCAM_MODULE_NAME, \ + __func__, __LINE__, ##arg) + +#define marucam_warn(fmt, arg...) \ + printk(KERN_WARNING "%s: " fmt, MARUCAM_MODULE_NAME, ##arg) + +#define marucam_info(fmt, arg...) \ + printk(KERN_INFO "%s: " fmt, MARUCAM_MODULE_NAME, ##arg) + +#define marucam_dbg(level, fmt, arg...) \ + do { \ + if (debug >= (level)) { \ + printk(KERN_DEBUG "%s: [%s:%d]: " fmt, \ + MARUCAM_MODULE_NAME, \ + __func__, __LINE__, ##arg); \ + } \ + } while (0) + +#define MARUCAM_MAJOR_VERSION 1 +#define MARUCAM_MINOR_VERSION 0 +#define MARUCAM_RELEASE 2 +#define MARUCAM_VERSION \ + KERNEL_VERSION(MARUCAM_MAJOR_VERSION, \ + MARUCAM_MINOR_VERSION, MARUCAM_RELEASE) + +MODULE_DESCRIPTION("MARU Virtual Camera Driver"); +MODULE_AUTHOR("Jinhyung Jo "); +MODULE_LICENSE("GPL"); + +/* + * Basic structures + */ +#define MARUCAM_INIT 0x00 +#define MARUCAM_OPEN 0x04 +#define MARUCAM_CLOSE 0x08 +#define MARUCAM_ISR 0x0C +#define MARUCAM_STREAMON 0x10 +#define MARUCAM_STREAMOFF 0x14 +#define MARUCAM_S_PARM 0x18 +#define MARUCAM_G_PARM 0x1C +#define MARUCAM_ENUM_FMT 0x20 +#define MARUCAM_TRY_FMT 0x24 +#define MARUCAM_S_FMT 0x28 +#define MARUCAM_G_FMT 0x2C +#define MARUCAM_QUERYCTRL 0x30 +#define MARUCAM_S_CTRL 0x34 +#define MARUCAM_G_CTRL 0x38 +#define MARUCAM_ENUM_FSIZES 0x3C +#define MARUCAM_ENUM_FINTV 0x40 +#define MARUCAM_REQFRAME 0x44 +#define MARUCAM_EXIT 0x48 + +enum marucam_opstate { + S_IDLE = 0, + S_RUNNING = 1 +}; + +struct marucam_device { + struct v4l2_device v4l2_dev; + + unsigned char dev_index; + spinlock_t slock; + struct mutex mlock; + enum marucam_opstate opstate; + unsigned int in_use; + + struct video_device *vfd; + struct pci_dev *pdev; + + void __iomem *mmregs; + void __iomem *args; + resource_size_t mem_base; + resource_size_t mem_size; + resource_size_t iomem_size; + + enum v4l2_buf_type type; + unsigned int width; + unsigned int height; + unsigned int pixelformat; + struct videobuf_queue vb_vidq; + + struct list_head active; +}; + +/* + * Use only one instance. + */ +static struct marucam_device *marucam_instance[2]; + +/* + * The code below has been modified from 'videobuf_vmalloc.c'. + */ + +#define MAGIC_MARUCAM_MEM 0x18221223 + +#define MAGIC_CHECK(is, should) \ + do { \ + if (unlikely((is) != (should))) { \ + marucam_err("invalid magic number:" \ + " %x (expected %x)\n", is, should); \ + BUG(); \ + } \ + } while (0) + +struct videobuf_marucam_memory { + u32 magic; + u32 mapped; +}; + +static void videobuf_vm_open(struct vm_area_struct *vma) +{ + struct videobuf_mapping *map = vma->vm_private_data; + + map->count++; +} + +static void videobuf_vm_close(struct vm_area_struct *vma) +{ + int i; + struct videobuf_mapping *map = vma->vm_private_data; + struct videobuf_queue *q = map->q; + + map->count--; + if (0 == map->count) { + struct videobuf_marucam_memory *mem; + + mutex_lock(&q->vb_lock); + + if (q->streaming) + videobuf_queue_cancel(q); + + for (i = 0; i < VIDEO_MAX_FRAME; i++) { + if (NULL == q->bufs[i]) + continue; + + if (q->bufs[i]->map != map) + continue; + + mem = q->bufs[i]->priv; + if (mem) { + MAGIC_CHECK(mem->magic, MAGIC_MARUCAM_MEM); + mem->mapped = 0; + } + + q->bufs[i]->map = NULL; + q->bufs[i]->baddr = 0; + } + + kfree(map); + + mutex_unlock(&q->vb_lock); + } + + return; +} + +static const struct vm_operations_struct videobuf_vm_ops = { + .open = videobuf_vm_open, + .close = videobuf_vm_close, +}; + +static struct videobuf_buffer *__videobuf_alloc_vb(size_t size) +{ + struct videobuf_marucam_memory *mem; + struct videobuf_buffer *vb; + + vb = kzalloc(size + sizeof(*mem), GFP_KERNEL); + if (vb == NULL) { + marucam_err("memory allocation failed for a video buffer\n"); + return vb; + } + + mem = vb->priv = ((char *)vb) + size; + mem->magic = MAGIC_MARUCAM_MEM; + + return vb; +} + +static int __videobuf_iolock(struct videobuf_queue *q, + struct videobuf_buffer *vb, + struct v4l2_framebuffer *fbuf) +{ + struct videobuf_marucam_memory *mem = vb->priv; + + BUG_ON(!mem); + + MAGIC_CHECK(mem->magic, MAGIC_MARUCAM_MEM); + + switch (vb->memory) { + case V4L2_MEMORY_MMAP: + if (!mem->mapped) { + marucam_err("memory is not mapped\n"); + return -EINVAL; + } + break; + default: + marucam_err("V4L2_MEMORY_MMAP only supported\n"); + return -EINVAL; + } + + return 0; +} + +static int __videobuf_mmap_mapper(struct videobuf_queue *q, + struct videobuf_buffer *buf, struct vm_area_struct *vma) +{ + struct videobuf_marucam_memory *mem; + struct videobuf_mapping *map; + int retval, pages; + + map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL); + if (NULL == map) { + marucam_err("memory allocation failed for a video buffer mapping\n"); + return -ENOMEM; + } + + buf->map = map; + map->q = q; + + buf->baddr = vma->vm_start; + + mem = buf->priv; + BUG_ON(!mem); + mem->mapped = 1; + MAGIC_CHECK(mem->magic, MAGIC_MARUCAM_MEM); + + pages = PAGE_ALIGN(vma->vm_end - vma->vm_start); + + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + retval = remap_pfn_range(vma, vma->vm_start, + (((struct marucam_device *)q->priv_data)->mem_base + + vma->vm_pgoff) >> PAGE_SHIFT, + pages, vma->vm_page_prot); + if (retval < 0) { + marucam_err("remap failed: %d\n", retval); + mem->mapped = 0; + mem = NULL; + kfree(map); + return -ENOMEM; + } + + vma->vm_ops = &videobuf_vm_ops; + vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; + vma->vm_private_data = map; + + videobuf_vm_open(vma); + + return 0; +} + +static struct videobuf_qtype_ops qops = { + .magic = MAGIC_QTYPE_OPS, + .alloc_vb = __videobuf_alloc_vb, + .iolock = __videobuf_iolock, + .mmap_mapper = __videobuf_mmap_mapper, +}; + +void videobuf_queue_marucam_init(struct videobuf_queue *q, + struct videobuf_queue_ops *ops, + void *dev, + spinlock_t *irqlock, + enum v4l2_buf_type type, + enum v4l2_field field, + unsigned int msize, + void *priv, + struct mutex *ext_lock) +{ + videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize, + priv, &qops, ext_lock); +} + + +/* + * interrupt handling + */ + +static int get_image_size(struct marucam_device *dev) +{ + int size; + + switch (dev->pixelformat) { + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + size = dev->width * dev->height * 3; + break; + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + case V4L2_PIX_FMT_NV12: + size = (dev->width * dev->height * 3) / 2; + break; + case V4L2_PIX_FMT_YUYV: + default: + size = dev->width * dev->height * 2; + break; + } + + return size; +} + +static void marucam_fillbuf(struct marucam_device *dev, uint32_t isr) +{ + struct videobuf_queue *q1 = &dev->vb_vidq; + struct videobuf_buffer *buf = NULL; + unsigned long flags = 0; + + spin_lock_irqsave(q1->irqlock, flags); + if (dev->opstate != S_RUNNING) { + marucam_err("state is not S_RUNNING\n"); + spin_unlock_irqrestore(q1->irqlock, flags); + return; + } + if (list_empty(&dev->active)) { + marucam_err("active list is empty\n"); + spin_unlock_irqrestore(q1->irqlock, flags); + return; + } + + buf = list_entry(dev->active.next, struct videobuf_buffer, queue); + if (!waitqueue_active(&buf->done)) { + marucam_err("wait queue list is empty\n"); + spin_unlock_irqrestore(q1->irqlock, flags); + return; + } + + list_del(&buf->queue); + + if (isr & 0x08) { + marucam_err("invalid state\n"); + buf->state = 0xFF; /* invalid state */ + } else { + marucam_dbg(2, "video buffer is filled\n"); + buf->state = VIDEOBUF_DONE; + } + do_gettimeofday(&buf->ts); + buf->field_count++; + wake_up_interruptible(&buf->done); + spin_unlock_irqrestore(q1->irqlock, flags); +} + +static irqreturn_t marucam_irq_handler(int irq, void *dev_id) +{ + struct marucam_device *dev = dev_id; + uint32_t isr = 0; + + isr = ioread32(dev->mmregs + MARUCAM_ISR); + if (!isr) { + marucam_dbg(1, "mismatched irq\n"); + return IRQ_NONE; + } + + marucam_fillbuf(dev, isr); + return IRQ_HANDLED; +} + +/* + * IOCTL vidioc handling + */ +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct marucam_device *dev = priv; + + strcpy(cap->driver, MARUCAM_MODULE_NAME); + strcpy(cap->card, MARUCAM_MODULE_NAME); + strlcpy(cap->bus_info, dev->v4l2_dev.name, sizeof(cap->bus_info)); + cap->version = MARUCAM_VERSION; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + + return 0; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + unsigned int ret; + struct marucam_device *dev = priv; + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + marucam_err("buf type is not V4L2_BUF_TYPE_VIDEO_CAPTURE\n"); + return -EINVAL; + } + + mutex_lock(&dev->mlock); + memset_io(dev->args, 0x00, dev->iomem_size); + memcpy_toio(dev->args, (const void *)f, sizeof(struct v4l2_fmtdesc)); + iowrite32(0, dev->mmregs + MARUCAM_ENUM_FMT); + ret = ioread32(dev->mmregs + MARUCAM_ENUM_FMT); + if (ret) { + if (ret != EINVAL) { + marucam_err("enum_fmt failed: ret(%d), idx(%u)\n", + ret, f->index); + } + mutex_unlock(&dev->mlock); + return -ret; + } + memcpy_fromio((void *)f, dev->args, sizeof(struct v4l2_fmtdesc)); + mutex_unlock(&dev->mlock); + + return 0; +} + +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + unsigned int ret; + struct marucam_device *dev = priv; + struct v4l2_pix_format *pf = &f->fmt.pix; + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + marucam_err("buf type is not V4L2_BUF_TYPE_VIDEO_CAPTURE\n"); + return -EINVAL; + } + + mutex_lock(&dev->mlock); + memset_io(dev->args, 0x00, dev->iomem_size); + memcpy_toio(dev->args, (const void *)pf, sizeof(struct v4l2_format)); + iowrite32(0, dev->mmregs + MARUCAM_G_FMT); + ret = ioread32(dev->mmregs + MARUCAM_G_FMT); + if (ret) { + marucam_err("g_fmt failed: ret(%d)\n", ret); + mutex_unlock(&dev->mlock); + return -ret; + } + memcpy_fromio((void *)pf, dev->args, sizeof(struct v4l2_format)); + + dev->pixelformat = pf->pixelformat; + dev->width = pf->width; + dev->height = pf->height; + dev->vb_vidq.field = pf->field; + dev->type = f->type; + + mutex_unlock(&dev->mlock); + return 0; +} + +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + unsigned int ret; + struct marucam_device *dev = priv; + struct v4l2_pix_format *pf = &f->fmt.pix; + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + marucam_err("buf type is not V4L2_BUF_TYPE_VIDEO_CAPTURE\n"); + return -EINVAL; + } + + mutex_lock(&dev->mlock); + memset_io(dev->args, 0x00, dev->iomem_size); + memcpy_toio(dev->args, (const void *)pf, sizeof(struct v4l2_format)); + iowrite32(0, dev->mmregs + MARUCAM_TRY_FMT); + ret = ioread32(dev->mmregs + MARUCAM_TRY_FMT); + if (ret) { + marucam_err("try_fmt failed: ret(%d), wxh(%ux%u), pf(0x%x)\n", + ret, pf->width, pf->height, + pf->pixelformat); + mutex_unlock(&dev->mlock); + return -ret; + } + memcpy_fromio((void *)pf, dev->args, sizeof(struct v4l2_format)); + mutex_unlock(&dev->mlock); + return 0; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + unsigned int ret; + struct marucam_device *dev = priv; + struct videobuf_queue *q2 = &dev->vb_vidq; + struct v4l2_pix_format *pf = &f->fmt.pix; + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + marucam_err("buf type is not V4L2_BUF_TYPE_VIDEO_CAPTURE\n"); + return -EINVAL; + } + + mutex_lock(&dev->mlock); + if (dev->opstate != S_IDLE) { + marucam_err("state is not S_IDLE\n"); + mutex_unlock(&dev->mlock); + return -EBUSY; + } + mutex_lock(&q2->vb_lock); + if (videobuf_queue_is_busy(&dev->vb_vidq)) { + marucam_err("videobuf queue is busy\n"); + mutex_unlock(&q2->vb_lock); + mutex_unlock(&dev->mlock); + return -EBUSY; + } + mutex_unlock(&q2->vb_lock); + + memset_io(dev->args, 0x00, dev->iomem_size); + memcpy_toio(dev->args, (const void *)pf, sizeof(struct v4l2_format)); + + iowrite32(0, dev->mmregs + MARUCAM_S_FMT); + ret = ioread32(dev->mmregs + MARUCAM_S_FMT); + if (ret) { + marucam_err("s_fmt failed: ret(%d), wxh(%ux%u), pf(0x%x)\n", + ret, pf->width, pf->height, + pf->pixelformat); + mutex_unlock(&dev->mlock); + return -ret; + } + memcpy_fromio((void *)pf, dev->args, sizeof(struct v4l2_format)); + + dev->pixelformat = pf->pixelformat; + dev->width = pf->width; + dev->height = pf->height; + dev->vb_vidq.field = pf->field; + dev->type = f->type; + + mutex_unlock(&dev->mlock); + return 0; +} + +static int vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + int ret; + struct marucam_device *dev = priv; + + dev->type = p->type; + + ret = videobuf_reqbufs(&dev->vb_vidq, p); + if (ret) { + marucam_err("%s failed: ret(%d), count(%u), type(%u), memory(%u)\n", + __func__, ret, p->count, + p->type, p->memory); + } + + return ret; +} + +static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + int ret; + struct marucam_device *dev = priv; + + ret = videobuf_querybuf(&dev->vb_vidq, p); + if (ret) { + marucam_err("%s failed: ret(%d), idx(%u), type(%u), memory(%u)\n", + __func__, ret, p->index, + p->type, p->memory); + } + + return ret; +} + +static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + int ret; + struct marucam_device *dev = priv; + + ret = videobuf_qbuf(&dev->vb_vidq, p); + if (ret) { + marucam_err("%s failed: ret(%d), idx(%u), type(%u), memory(%u)\n", + __func__, ret, p->index, + p->type, p->memory); + } + + return ret; +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + int ret; + struct marucam_device *dev = priv; + + ret = videobuf_dqbuf(&dev->vb_vidq, p, file->f_flags & O_NONBLOCK); + if (ret) { + marucam_err("%s failed: ret(%d), idx(%u), type(%u), memory(%u)\n", + __func__, ret, p->index, + p->type, p->memory); + } + + return ret; +} + +static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + int ret = 0; + unsigned int dev_ret; + struct marucam_device *dev = priv; + + if (dev->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + marucam_err("buf type is not V4L2_BUF_TYPE_VIDEO_CAPTURE\n"); + return -EINVAL; + } + if (i != dev->type) { + marucam_err("mismatched buf type\n"); + return -EINVAL; + } + + mutex_lock(&dev->mlock); + if (dev->opstate != S_IDLE) { + marucam_err("state is not S_IDLE\n"); + mutex_unlock(&dev->mlock); + return -EBUSY; + } + + memset_io(dev->args, 0x00, dev->iomem_size); + iowrite32(0, dev->mmregs + MARUCAM_STREAMON); + dev_ret = ioread32(dev->mmregs + MARUCAM_STREAMON); + if (dev_ret) { + marucam_err("stream_on failed: ret(%d)\n", dev_ret); + mutex_unlock(&dev->mlock); + return -dev_ret; + } + + INIT_LIST_HEAD(&dev->active); + ret = videobuf_streamon(&dev->vb_vidq); + if (ret) { + marucam_err("videobuf_streamon() failed: ret(%d)\n", ret); + mutex_unlock(&dev->mlock); + return ret; + } + + dev->opstate = S_RUNNING; + mutex_unlock(&dev->mlock); + return ret; +} + +static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + int ret = 0; + unsigned int dev_ret; + struct marucam_device *dev = priv; + + if (dev->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + marucam_err("buf type is not V4L2_BUF_TYPE_VIDEO_CAPTURE\n"); + return -EINVAL; + } + if (i != dev->type) { + marucam_err("mismatched buf type\n"); + return -EINVAL; + } + + mutex_lock(&dev->mlock); + if (dev->opstate != S_RUNNING) { + marucam_err("The device state is not S_RUNNING. Do nothing\n"); + mutex_unlock(&dev->mlock); + return 0; + } + + memset_io(dev->args, 0x00, dev->iomem_size); + iowrite32(0, dev->mmregs + MARUCAM_STREAMOFF); + dev_ret = ioread32(dev->mmregs + MARUCAM_STREAMOFF); + if (dev_ret) { + marucam_err("stream_off failed: ret(%d)\n", dev_ret); + mutex_unlock(&dev->mlock); + return -dev_ret; + } + + dev->opstate = S_IDLE; + ret = videobuf_streamoff(&dev->vb_vidq); + if (ret) + marucam_err("videobuf_streamoff() failed: ret(%d)\n", ret); + + INIT_LIST_HEAD(&dev->active); + mutex_unlock(&dev->mlock); + return ret; +} + +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id i) +{ + return 0; +} + +static int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + if (inp->index != 0) + return -EINVAL; + + inp->type = V4L2_INPUT_TYPE_CAMERA; + sprintf(inp->name, "MARU Virtual Camera %u", inp->index); + + return 0; +} + +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; + + return 0; +} +static int vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + return 0; +} + +/* controls + * + */ +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + unsigned int ret; + struct marucam_device *dev = priv; + + mutex_lock(&dev->mlock); + switch (qc->id) { + /* we only support followed items. */ + case V4L2_CID_BRIGHTNESS: + case V4L2_CID_CONTRAST: + case V4L2_CID_SATURATION: + case V4L2_CID_SHARPNESS: + break; + default: + mutex_unlock(&dev->mlock); + return -EINVAL; + } + + memset_io(dev->args, 0x00, dev->iomem_size); + memcpy_toio(dev->args, (const void *)qc, + sizeof(struct v4l2_queryctrl)); + iowrite32(0, dev->mmregs + MARUCAM_QUERYCTRL); + ret = ioread32(dev->mmregs + MARUCAM_QUERYCTRL); + if (ret) { + marucam_err("query_ctrl failed: ret(%d), id(%u)\n", + ret, qc->id); + mutex_unlock(&dev->mlock); + return -ret; + } + memcpy_fromio((void *)qc, dev->args, + sizeof(struct v4l2_queryctrl)); + + mutex_unlock(&dev->mlock); + return 0; +} + +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + unsigned int ret; + struct marucam_device *dev = priv; + + mutex_lock(&dev->mlock); + memset_io(dev->args, 0x00, dev->iomem_size); + memcpy_toio(dev->args, (const void *)ctrl, + sizeof(struct v4l2_control)); + iowrite32(0, dev->mmregs + MARUCAM_G_CTRL); + ret = ioread32(dev->mmregs + MARUCAM_G_CTRL); + if (ret) { + marucam_err("g_ctrl failed: ret(%d), id(%u)\n", + ret, ctrl->id); + mutex_unlock(&dev->mlock); + return -ret; + } + memcpy_fromio((void *)ctrl, dev->args, + sizeof(struct v4l2_control)); + mutex_unlock(&dev->mlock); + return 0; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + unsigned int ret; + struct marucam_device *dev = priv; + + mutex_lock(&dev->mlock); + memset_io(dev->args, 0x00, dev->iomem_size); + memcpy_toio(dev->args, (const void *)ctrl, + sizeof(struct v4l2_control)); + iowrite32(0, dev->mmregs + MARUCAM_S_CTRL); + ret = ioread32(dev->mmregs + MARUCAM_S_CTRL); + if (ret) { + marucam_err("s_ctrl failed: ret(%d), id(%u), val(%d)\n", + ret, ctrl->id, ctrl->value); + mutex_unlock(&dev->mlock); + return -ret; + } + memcpy_fromio((void *)ctrl, dev->args, + sizeof(struct v4l2_control)); + mutex_unlock(&dev->mlock); + return 0; +} + +static int vidioc_s_parm(struct file *file, void *priv, + struct v4l2_streamparm *parm) +{ + unsigned int ret; + struct marucam_device *dev = priv; + struct v4l2_captureparm *cp = &parm->parm.capture; + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + marucam_err("buf type is not V4L2_BUF_TYPE_VIDEO_CAPTURE\n"); + return -EINVAL; + } + + mutex_lock(&dev->mlock); + memset_io(dev->args, 0x00, dev->iomem_size); + memcpy_toio(dev->args, (const void *)cp, + sizeof(struct v4l2_captureparm)); + iowrite32(0, dev->mmregs + MARUCAM_S_PARM); + ret = ioread32(dev->mmregs + MARUCAM_S_PARM); + if (ret) { + marucam_err("s_parm failed: ret(%d)\n", ret); + mutex_unlock(&dev->mlock); + return -ret; + } + memcpy_fromio((void *)cp, dev->args, + sizeof(struct v4l2_captureparm)); + mutex_unlock(&dev->mlock); + return 0; +} + +static int vidioc_g_parm(struct file *file, void *priv, + struct v4l2_streamparm *parm) +{ + unsigned int ret; + struct marucam_device *dev = priv; + struct v4l2_captureparm *cp = &parm->parm.capture; + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + marucam_err("buf type is not V4L2_BUF_TYPE_VIDEO_CAPTURE\n"); + return -EINVAL; + } + + mutex_lock(&dev->mlock); + memset_io(dev->args, 0x00, dev->iomem_size); + memcpy_toio(dev->args, (const void *)cp, + sizeof(struct v4l2_captureparm)); + iowrite32(0, dev->mmregs + MARUCAM_G_PARM); + ret = ioread32(dev->mmregs + MARUCAM_G_PARM); + if (ret) { + marucam_err("g_parm failed: ret(%d)\n", ret); + mutex_unlock(&dev->mlock); + return -ret; + } + memcpy_fromio((void *)cp, dev->args, + sizeof(struct v4l2_captureparm)); + mutex_unlock(&dev->mlock); + return 0; +} + +static int vidioc_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + unsigned int ret; + struct marucam_device *dev = priv; + + mutex_lock(&dev->mlock); + memset_io(dev->args, 0x00, dev->iomem_size); + memcpy_toio(dev->args, (const void *)fsize, + sizeof(struct v4l2_frmsizeenum)); + iowrite32(0, dev->mmregs + MARUCAM_ENUM_FSIZES); + ret = ioread32(dev->mmregs + MARUCAM_ENUM_FSIZES); + if (ret) { + if (ret != EINVAL) { + marucam_err("enum_framesizes failed: %d, index(%u), pix(%u)\n", + ret, fsize->index, fsize->pixel_format); + } + mutex_unlock(&dev->mlock); + return -ret; + } + memcpy_fromio((void *)fsize, dev->args, + sizeof(struct v4l2_frmsizeenum)); + mutex_unlock(&dev->mlock); + return 0; +} + +static int vidioc_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *fival) +{ + unsigned int ret; + struct marucam_device *dev = priv; + + mutex_lock(&dev->mlock); + memset_io(dev->args, 0x00, dev->iomem_size); + memcpy_toio(dev->args, (const void *)fival, + sizeof(struct v4l2_frmivalenum)); + iowrite32(0, dev->mmregs + MARUCAM_ENUM_FINTV); + ret = ioread32(dev->mmregs + MARUCAM_ENUM_FINTV); + if (ret) { + if (ret != EINVAL) { + marucam_err("%s failed: ret(%d), idx(%u), pf(%u), %ux%u\n", + __func__, ret, fival->index, + fival->pixel_format, fival->width, + fival->height); + } + mutex_unlock(&dev->mlock); + return -ret; + } + memcpy_fromio((void *)fival, dev->args, + sizeof(struct v4l2_frmivalenum)); + mutex_unlock(&dev->mlock); + return 0; +} + +/* ------------------------------------------------------------------ + Videobuf operations + ------------------------------------------------------------------*/ +static int buffer_setup(struct videobuf_queue *vq, + unsigned int *count, + unsigned int *size) +{ + struct marucam_device *dev = vq->priv_data; + + *size = get_image_size(dev); + + if (*count > 2) + *count = 2; + else if (*count == 0) + *count = 2; + + marucam_dbg(1, "count=%d, size=%d\n", *count, *size); + + return 0; +} + +static int buffer_prepare(struct videobuf_queue *vq, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + int rc; + struct marucam_device *dev = vq->priv_data; + + marucam_dbg(1, "field=%d\n", field); + + vb->size = get_image_size(dev); + + if (0 != vb->baddr && vb->bsize < vb->size) { + marucam_err("invalid buffer size\n"); + return -EINVAL; + } + + if (vb->state == VIDEOBUF_NEEDS_INIT) { + rc = videobuf_iolock(vq, vb, NULL); + if (rc < 0) { + marucam_err("videobuf_iolock() failed: ret(%d)\n", rc); + vb->state = VIDEOBUF_NEEDS_INIT; + return rc; + } + } + + vb->width = dev->width; + vb->height = dev->height; + vb->field = field; + vb->state = VIDEOBUF_PREPARED; + + return 0; +} + +static void buffer_queue(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct marucam_device *dev = vq->priv_data; + + marucam_dbg(1, "\n"); + + vb->state = VIDEOBUF_QUEUED; + list_add_tail(&vb->queue, &dev->active); +} + +static void buffer_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + marucam_dbg(1, "buffer freed\n"); + vb->state = VIDEOBUF_NEEDS_INIT; +} + +static struct videobuf_queue_ops marucam_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +/* ------------------------------------------------------------------ + File operations for the device + ------------------------------------------------------------------*/ + +static int marucam_open(struct file *file) +{ + int ret; + unsigned int dev_ret; + struct marucam_device *dev = video_drvdata(file); + + file->private_data = dev; + + mutex_lock(&dev->mlock); + if (dev->in_use) { + marucam_err("already opened\n"); + mutex_unlock(&dev->mlock); + return -EBUSY; + } + + dev->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dev->pixelformat = 0; + dev->width = 0; + dev->height = 0; + + ret = request_irq(dev->pdev->irq, marucam_irq_handler, + IRQF_SHARED, MARUCAM_MODULE_NAME, dev); + if (ret) { + marucam_err("request_irq() failed: ret(%d), irq(#%d)\n", + ret, dev->pdev->irq); + mutex_unlock(&dev->mlock); + return ret; + } + + videobuf_queue_marucam_init(&dev->vb_vidq, &marucam_video_qops, + &dev->pdev->dev, &dev->slock, dev->type, + V4L2_FIELD_NONE, sizeof(struct videobuf_buffer), + dev, NULL); + + memset_io(dev->args, 0x00, dev->iomem_size); + iowrite32(0, dev->mmregs + MARUCAM_OPEN); + dev_ret = ioread32(dev->mmregs + MARUCAM_OPEN); + if (dev_ret) { + marucam_err("device open failed: ret(%d)\n", dev_ret); + free_irq(dev->pdev->irq, dev); + mutex_unlock(&dev->mlock); + return -dev_ret; + } + + dev->in_use = 1; + mutex_unlock(&dev->mlock); + return 0; + +} + +static int marucam_close(struct file *file) +{ + unsigned int ret; + struct marucam_device *dev = file->private_data; + + mutex_lock(&dev->mlock); + if (dev->opstate == S_RUNNING) { + marucam_err("unexpectedly terminated\n"); + iowrite32(0, dev->mmregs + MARUCAM_STREAMOFF); + ret = ioread32(dev->mmregs + MARUCAM_STREAMOFF); + if (ret) { + marucam_err("stream_off failed: ret(%d)\n", ret); + mutex_unlock(&dev->mlock); + return -ret; + } + + dev->opstate = S_IDLE; + } + + videobuf_stop(&dev->vb_vidq); + videobuf_mmap_free(&dev->vb_vidq); + INIT_LIST_HEAD(&dev->active); + + free_irq(dev->pdev->irq, dev); + + memset_io(dev->args, 0x00, dev->iomem_size); + iowrite32(0, dev->mmregs + MARUCAM_CLOSE); + ret = ioread32(dev->mmregs + MARUCAM_CLOSE); + if (ret) { + marucam_err("close failed: ret(%d)\n", ret); + mutex_unlock(&dev->mlock); + return -ret; + } + + dev->in_use = 0; + mutex_unlock(&dev->mlock); + return 0; +} + +static unsigned int marucam_poll(struct file *file, + struct poll_table_struct *wait) +{ + unsigned int rval = 0; + struct marucam_device *poll_dev = file->private_data; + struct videobuf_queue *q3 = &poll_dev->vb_vidq; + struct videobuf_buffer *vbuf = NULL; + + if (q3->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return POLLERR; + + mutex_lock(&q3->vb_lock); + if (q3->streaming) { + if (!list_empty(&q3->stream)) { + vbuf = list_entry(q3->stream.next, + struct videobuf_buffer, stream); + } + } + if (!vbuf) { + marucam_err("video buffer list is empty\n"); + rval = POLLERR; + } + + if (rval == 0) { + poll_wait(file, &vbuf->done, wait); + if (vbuf->state == VIDEOBUF_DONE || + vbuf->state == VIDEOBUF_ERROR || + vbuf->state == 0xFF) { + rval = POLLIN | POLLRDNORM; + } else { + iowrite32(vbuf->i, + poll_dev->mmregs + MARUCAM_REQFRAME); + } + } + mutex_unlock(&q3->vb_lock); + return rval; +} + +static int marucam_mmap(struct file *file, struct vm_area_struct *vma) +{ + int return_val; + struct marucam_device *mmap_dev = file->private_data; + + marucam_dbg(1, "mmap called, vma=0x%08lx\n", (unsigned long)vma); + + return_val = videobuf_mmap_mapper(&mmap_dev->vb_vidq, vma); + + marucam_dbg(1, "vma start=0x%08lx, size=%ld, ret=%d\n", + (unsigned long)vma->vm_start, + (unsigned long)vma->vm_end-(unsigned long)vma->vm_start, + return_val); + + return return_val; +} + +static const struct v4l2_ioctl_ops marucam_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_s_std = vidioc_s_std, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_g_parm = vidioc_g_parm, + .vidioc_s_parm = vidioc_s_parm, + .vidioc_enum_framesizes = vidioc_enum_framesizes, + .vidioc_enum_frameintervals = vidioc_enum_frameintervals, +}; + +static const struct v4l2_file_operations marucam_fops = { + .owner = THIS_MODULE, + .open = marucam_open, + .release = marucam_close, + .poll = marucam_poll, + .mmap = marucam_mmap, + .ioctl = video_ioctl2, +}; + +static struct video_device marucam_video_dev = { + .name = MARUCAM_MODULE_NAME, + .fops = &marucam_fops, + .ioctl_ops = &marucam_ioctl_ops, + .minor = -1, + .release = video_device_release, +}; + +/* ----------------------------------------------------------------- + Initialization and module stuff + ------------------------------------------------------------------*/ + +static const struct pci_device_id marucam_pci_id_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_TIZEN, PCI_DEVICE_ID_VIRTUAL_CAMERA) }, + {} +}; + +MODULE_DEVICE_TABLE(pci, marucam_pci_id_tbl); + +/* The following function already exist in the latest linux stable kernel. + * https://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/ + * commit/?id=c43996f4001de629af4a4d6713782e883677e5b9 + * This should be removed if emulator-kernel is upgraded. + */ +static void __iomem *pci_ioremap_wc_bar(struct pci_dev *pdev, int bar) +{ + /* + * Make sure the BAR is actually a memory resource, not an IO resource + */ + if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) { + WARN_ON(1); + return NULL; + } + + return ioremap_wc(pci_resource_start(pdev, bar), + pci_resource_len(pdev, bar)); +} + +static int marucam_pci_initdev(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + int ret_val; + struct marucam_device *dev; + + debug = MARUCAM_DEBUG_LEVEL; + + if (!pci_resource_len(pdev, 0)) { + marucam_info("No available device\n"); + return -ENODEV; + } + + if (marucam_instance[0] && marucam_instance[1]) { + marucam_err("Two devices already exists\n"); + return -EBUSY; + } + + dev = kzalloc(sizeof(struct marucam_device), GFP_KERNEL); + if (!dev) { + marucam_err("Memory allocation failed for a marucam device\n"); + return -ENOMEM; + } + + ret_val = pci_enable_device(pdev); + if (ret_val) { + marucam_err("pci_enable_device failed\n"); + kfree(dev); + return ret_val; + } + + dev->mem_base = pci_resource_start(pdev, 0); + dev->mem_size = pci_resource_len(pdev, 0); + + if (pci_request_region(pdev, 0, MARUCAM_MODULE_NAME)) { + marucam_err("request region failed for 0 bar\n"); + pci_disable_device(pdev); + kfree(dev); + return -EBUSY; + } + + if (pci_request_region(pdev, 1, MARUCAM_MODULE_NAME)) { + marucam_err("request region failed for 1 bar\n"); + pci_release_region(pdev, 0); + pci_disable_device(pdev); + kfree(dev); + return -EBUSY; + } + + if (pci_request_region(pdev, 2, MARUCAM_MODULE_NAME)) { + marucam_err("request region failed for 2 bar\n"); + pci_release_region(pdev, 0); + pci_release_region(pdev, 1); + pci_disable_device(pdev); + kfree(dev); + return -EBUSY; + } + + dev->args = pci_ioremap_wc_bar(pdev, 1); + if (!dev->args) { + marucam_err("pci_ioremap_wc_bar failed for 1 bar\n"); + pci_release_region(pdev, 0); + pci_release_region(pdev, 1); + pci_release_region(pdev, 2); + pci_disable_device(pdev); + kfree(dev); + return -EIO; + } + dev->iomem_size = pci_resource_len(pdev, 1); + + dev->mmregs = pci_ioremap_wc_bar(pdev, 2); + if (!dev->mmregs) { + marucam_err("pci_ioremap_wc_bar failed for 2 bar\n"); + iounmap(dev->args); + pci_release_region(pdev, 0); + pci_release_region(pdev, 1); + pci_release_region(pdev, 2); + pci_disable_device(pdev); + kfree(dev); + return -EIO; + } + + dev->dev_index = ioread32(dev->mmregs + MARUCAM_INIT); + marucam_info("device index is %d", dev->dev_index); + pci_set_master(pdev); + pci_set_drvdata(pdev, dev); + + ret_val = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); + if (ret_val < 0) { + marucam_err("v4l2_device_register() failed: %d\n", ret_val); + iounmap(dev->args); + iounmap(dev->mmregs); + pci_release_region(pdev, 0); + pci_release_region(pdev, 1); + pci_release_region(pdev, 2); + pci_disable_device(pdev); + kfree(dev); + return ret_val; + } + + dev->vfd = video_device_alloc(); + if (dev->vfd == NULL) { + v4l2_device_unregister(&dev->v4l2_dev); + iounmap(dev->args); + iounmap(dev->mmregs); + pci_release_region(pdev, 0); + pci_release_region(pdev, 1); + pci_release_region(pdev, 2); + pci_disable_device(pdev); + kfree(dev); + return -ENOMEM; + } + + memcpy(dev->vfd, &marucam_video_dev, sizeof(marucam_video_dev)); + dev->vfd->dev_parent = &pdev->dev; + dev->vfd->v4l2_dev = &dev->v4l2_dev; + + ret_val = video_register_device(dev->vfd, + VFL_TYPE_GRABBER, + dev->dev_index); + if (ret_val < 0) { + marucam_err("video_register_device() failed: %d\n", ret_val); + video_device_release(dev->vfd); + v4l2_device_unregister(&dev->v4l2_dev); + iounmap(dev->args); + iounmap(dev->mmregs); + pci_release_region(pdev, 0); + pci_release_region(pdev, 1); + pci_release_region(pdev, 2); + pci_disable_device(pdev); + kfree(dev); + return ret_val; + } + video_set_drvdata(dev->vfd, dev); + + INIT_LIST_HEAD(&dev->active); + spin_lock_init(&dev->slock); + mutex_init(&dev->mlock); + dev->opstate = S_IDLE; + dev->pdev = pdev; + + + snprintf(dev->vfd->name, sizeof(dev->vfd->name), "%s (%i)", + marucam_video_dev.name, dev->vfd->num); + + marucam_instance[dev->dev_index] = dev; + marucam_info("Maru Camera(%u.%u.%u) device is registerd as /dev/video%d\n", + (MARUCAM_VERSION >> 16) & 0xFF, + (MARUCAM_VERSION >> 8) & 0xFF, + MARUCAM_VERSION & 0xFF, + dev->vfd->num); + + return 0; +} + +static void marucam_pci_removedev(struct pci_dev *pdev) +{ + unsigned char dev_index; + struct marucam_device *dev = pci_get_drvdata(pdev); + + if (dev == NULL) { + marucam_warn("pci_remove on unknown pdev %p\n", pdev); + return; + } + dev_index = dev->dev_index; + + video_unregister_device(dev->vfd); + v4l2_device_unregister(&dev->v4l2_dev); + iounmap(dev->args); + iounmap(dev->mmregs); + pci_release_region(dev->pdev, 0); + pci_release_region(dev->pdev, 1); + pci_release_region(dev->pdev, 2); + pci_disable_device(dev->pdev); + + memset(dev, 0x00, sizeof(struct marucam_device)); + kfree(dev); + dev = NULL; + marucam_instance[dev_index] = NULL; +} + +static struct pci_driver marucam_pci_driver = { + .name = MARUCAM_MODULE_NAME, + .id_table = marucam_pci_id_tbl, + .probe = marucam_pci_initdev, + .remove = marucam_pci_removedev, +}; + +static int __init marucam_init(void) +{ + return pci_register_driver(&marucam_pci_driver); +} + +static void __exit marucam_exit(void) +{ + pci_unregister_driver(&marucam_pci_driver); +} + +module_init(marucam_init); +module_exit(marucam_exit); diff --git a/drivers/maru/maru_jack.c b/drivers/maru/maru_jack.c new file mode 100644 index 0000000..abfecf0 --- /dev/null +++ b/drivers/maru/maru_jack.c @@ -0,0 +1,514 @@ +/* + * Virtual device node + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * JinHyung Choi + * SooYoung Ha + * Sungmin Ha + * YeongKyoon Lee +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define __MAX_BUF_SIZE 1024 +#define __MAX_BUF_JACK 512 + +struct msg_info { + char buf[__MAX_BUF_SIZE]; + + uint16_t type; + uint16_t req; +}; + +struct virtio_jack { + struct virtio_device* vdev; + struct virtqueue* vq; + + struct msg_info msginfo; + + struct scatterlist sg_vq[2]; + + int flags; + struct mutex lock; +}; + +enum jack_types { + jack_type_list = 0, + jack_type_charger, + jack_type_earjack, + jack_type_earkey, + jack_type_hdmi, + jack_type_usb, + jack_type_max +}; + +enum jack_capabilities { + jack_cap_charger = 0x01, + jack_cap_earjack = 0x02, + jack_cap_earkey = 0x04, + jack_cap_hdmi = 0x08, + jack_cap_usb = 0x10 +}; + +enum request_cmd { + request_get = 0, + request_set, + request_answer +}; + +struct jack_data { + int no; + char buffer[50]; +}; + +struct virtio_jack *v_jack; + +static char jack_data [__MAX_BUF_JACK]; +static int jack_capability = 0; + +static DECLARE_WAIT_QUEUE_HEAD(wq); + +static struct virtio_device_id id_table[] = { { VIRTIO_ID_JACK, + VIRTIO_DEV_ANY_ID }, { 0 }, }; + +#define DEVICE_NAME "jack" +#define JACK_DEBUG + +#ifdef JACK_DEBUG +#define DLOG(level, fmt, ...) \ + printk(level "maru_%s: " fmt, DEVICE_NAME, ##__VA_ARGS__) +#else +// do nothing +#define DLOG(level, fmt, ...) +#endif + +static int jack_atoi(const char *name) +{ + int val = 0; + + for (;; name++) { + switch (*name) { + case '0' ... '9': + val = 10*val+(*name-'0'); + break; + default: + return val; + } + } +} + +static void jack_vq_done(struct virtqueue *vq) { + unsigned int len; + struct msg_info* msg; + + msg = (struct msg_info*) virtqueue_get_buf(v_jack->vq, &len); + if (msg == NULL) { + DLOG(KERN_ERR, "failed to virtqueue_get_buf"); + return; + } + + if (msg->req != request_answer) { + DLOG(KERN_DEBUG, "receive queue- not an answer message: %d", msg->req); + return; + } + if (msg->buf == NULL) { + DLOG(KERN_ERR, "receive queue- message from host is NULL."); + return; + } + + DLOG(KERN_DEBUG, "msg buf: %s, req: %d, type: %d", msg->buf, msg->req, msg->type); + + mutex_lock(&v_jack->lock); + strcpy(jack_data, msg->buf); + v_jack->flags = 1; + mutex_unlock(&v_jack->lock); + + wake_up_interruptible(&wq); +} + +static void set_jack_data(int type, const char* buf) +{ + int err = 0; + + if (buf == NULL) { + DLOG(KERN_ERR, "set_jack buf is NULL."); + return; + } + + if (v_jack == NULL) { + DLOG(KERN_ERR, "Invalid jack handle"); + return; + } + + mutex_lock(&v_jack->lock); + memset(jack_data, 0, sizeof(jack_data)); + memset(&v_jack->msginfo, 0, sizeof(v_jack->msginfo)); + + strcpy(jack_data, buf); + + v_jack->msginfo.req = request_set; + v_jack->msginfo.type = type; + strcpy(v_jack->msginfo.buf, buf); + mutex_unlock(&v_jack->lock); + + DLOG(KERN_DEBUG, "set_jack_data type: %d, req: %d, buf: %s", + v_jack->msginfo.type, v_jack->msginfo.req, v_jack->msginfo.buf); + + err = virtqueue_add_outbuf(v_jack->vq, v_jack->sg_vq, 1, &v_jack->msginfo, GFP_ATOMIC); + if (err < 0) { + DLOG(KERN_ERR, "failed to add buffer to virtqueue (err = %d)", err); + return; + } + + virtqueue_kick(v_jack->vq); +} + +static int get_jack_data(int type, char* data) +{ + struct scatterlist *sgs[2]; + int err = 0; + + if (v_jack == NULL) { + DLOG(KERN_ERR, "Invalid jack handle"); + return -1; + } + + mutex_lock(&v_jack->lock); + memset(&v_jack->msginfo, 0, sizeof(v_jack->msginfo)); + + v_jack->msginfo.req = request_get; + v_jack->msginfo.type = type; + mutex_unlock(&v_jack->lock); + + DLOG(KERN_DEBUG, "get_jack_data type: %d, req: %d", + v_jack->msginfo.type, v_jack->msginfo.req); + + sgs[0] = &v_jack->sg_vq[0]; + sgs[1] = &v_jack->sg_vq[1]; + err = virtqueue_add_sgs(v_jack->vq, sgs, 1, 1, &v_jack->msginfo, GFP_ATOMIC); + if (err < 0) { + DLOG(KERN_ERR, "failed to add buffer to virtqueue (err = %d)", err); + return -1; + } + + virtqueue_kick(v_jack->vq); + + wait_event_interruptible(wq, v_jack->flags != 0); + + mutex_lock(&v_jack->lock); + v_jack->flags = 0; + memcpy(data, jack_data, strlen(jack_data)); + mutex_unlock(&v_jack->lock); + + return 0; +} + +static int get_data_for_show(int type, char* buf) +{ + int ret; + char jack_data[__MAX_BUF_JACK]; + memset(jack_data, 0, sizeof(jack_data)); + ret = get_jack_data(type, jack_data); + if (ret) + return 0; + return sprintf(buf, "%s", jack_data); + +} + +static ssize_t show_charger_online(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return get_data_for_show(jack_type_charger, buf); +} + +static ssize_t store_charger_online(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + set_jack_data(jack_type_charger, buf); + return strnlen(buf, count); +} + +static ssize_t show_earjack_online(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return get_data_for_show(jack_type_earjack, buf); +} + +static ssize_t store_earjack_online(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + set_jack_data(jack_type_earjack, buf); + return strnlen(buf, count); +} + +static ssize_t show_earkey_online(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return get_data_for_show(jack_type_earkey, buf); +} + +static ssize_t store_earkey_online(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + set_jack_data(jack_type_earkey, buf); + return strnlen(buf, count); +} + +static ssize_t show_hdmi_online(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return get_data_for_show(jack_type_hdmi, buf); +} + +static ssize_t store_hdmi_online(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + set_jack_data(jack_type_hdmi, buf); + return strnlen(buf, count); +} + +static ssize_t show_usb_online(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return get_data_for_show(jack_type_usb, buf); +} + +static ssize_t store_usb_online(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + set_jack_data(jack_type_usb, buf); + return strnlen(buf, count); +} + +static DEVICE_ATTR(charger_online, S_IRUGO | S_IWUSR, show_charger_online, store_charger_online); +static DEVICE_ATTR(earjack_online, S_IRUGO | S_IWUSR, show_earjack_online, store_earjack_online); +static DEVICE_ATTR(earkey_online, S_IRUGO | S_IWUSR, show_earkey_online, store_earkey_online); +static DEVICE_ATTR(hdmi_online, S_IRUGO | S_IWUSR, show_hdmi_online, store_hdmi_online); +static DEVICE_ATTR(usb_online, S_IRUGO | S_IWUSR, show_usb_online, store_usb_online); + +static int maru_jack_sysfs_create_file(struct device *dev) +{ + int result = 0; + + DLOG(KERN_INFO, "sysfs_create_file\n"); + + if (jack_capability & jack_cap_charger) { + result = device_create_file(dev, &dev_attr_charger_online); + if (result){ + DLOG(KERN_ERR, "failed to create charger_online file\n"); + return result; + } + } + + if (jack_capability & jack_cap_earjack) { + result = device_create_file(dev, &dev_attr_earjack_online); + if (result){ + DLOG(KERN_ERR, "failed to create earjack_online file\n"); + return result; + } + } + + if (jack_capability & jack_cap_earkey) { + result = device_create_file(dev, &dev_attr_earkey_online); + if (result){ + DLOG(KERN_ERR, "failed to create earkey_online file\n"); + return result; + } + } + + if (jack_capability & jack_cap_hdmi) { + result = device_create_file(dev, &dev_attr_hdmi_online); + if (result){ + DLOG(KERN_ERR, "failed to create hdmi_online file\n"); + return result; + } + } + + if (jack_capability & jack_cap_usb) { + result = device_create_file(dev, &dev_attr_usb_online); + if (result){ + DLOG(KERN_ERR, "failed to create usb_online file\n"); + return result; + } + } + + return 0; +} + + +static void maru_jack_sysfs_remove_file(struct device *dev) +{ + DLOG(KERN_INFO, "sysfs_remove_file\n"); + + device_remove_file(dev, &dev_attr_charger_online); + device_remove_file(dev, &dev_attr_earjack_online); + device_remove_file(dev, &dev_attr_earkey_online); + device_remove_file(dev, &dev_attr_hdmi_online); + device_remove_file(dev, &dev_attr_usb_online); +} + +static void maru_jack_sysfs_dev_release(struct device *dev) +{ + DLOG(KERN_INFO, "sysfs_dev_release\n"); +} + +static struct platform_device the_pdev = { + .name = DEVICE_NAME, + .id = -1, + .dev = { + .release = maru_jack_sysfs_dev_release, + } +}; + +static int jack_probe(struct virtio_device* dev){ + int err = 0, index = 0; + struct jack_data *data; + char jack_data[__MAX_BUF_JACK]; + + DLOG(KERN_INFO, "jack_probe\n"); + + v_jack = kmalloc(sizeof(struct virtio_jack), GFP_KERNEL); + + v_jack->vdev = dev; + dev->priv = v_jack; + v_jack->flags = 0; + + err = platform_device_register(&the_pdev); + if (err) { + DLOG(KERN_ERR, "platform_device_register failure\n"); + return err; + } + + data = kzalloc(sizeof(struct jack_data), GFP_KERNEL); + if (!data) { + DLOG(KERN_ERR, "kzalloc failure\n"); + platform_device_unregister(&the_pdev); + return -ENOMEM; + } + + dev_set_drvdata(&the_pdev.dev, (void*)data); + + v_jack->vq = virtio_find_single_vq(dev, jack_vq_done, "jack"); + if (IS_ERR(v_jack->vq)) { + DLOG(KERN_ERR, "virtio queue is not found.\n"); + kfree(data); + platform_device_unregister(&the_pdev); + return err; + } + + virtqueue_enable_cb(v_jack->vq); + + memset(&v_jack->msginfo, 0x00, sizeof(v_jack->msginfo)); + + sg_init_table(v_jack->sg_vq, 2); + for (; index < 2; index++) { + sg_set_buf(&v_jack->sg_vq[index], &v_jack->msginfo, sizeof(v_jack->msginfo)); + } + + mutex_init(&v_jack->lock); + + DLOG(KERN_INFO, "request jack capability"); + + memset(jack_data, 0, sizeof(jack_data)); + + err = get_jack_data(jack_type_list, jack_data); + if (err) { + DLOG(KERN_ERR, "Cannot get jack list.\n"); + kfree(data); + platform_device_unregister(&the_pdev); + } + + jack_capability = jack_atoi(jack_data); + DLOG(KERN_INFO, "jack capability is %02x", jack_capability); + + err = maru_jack_sysfs_create_file(&the_pdev.dev); + if (err) { + DLOG(KERN_ERR, "sysfs_create_file failure\n"); + kfree(data); + platform_device_unregister(&the_pdev); + return err; + } + + return 0; +} + +static void jack_remove(struct virtio_device* dev){ + void *data = dev_get_drvdata(&the_pdev.dev); + + DLOG(KERN_INFO, "sysfs_exit\n"); + + if (data) { + kfree(data); + } + maru_jack_sysfs_remove_file(&the_pdev.dev); + platform_device_unregister(&the_pdev); + + if (v_jack) { + kfree(v_jack); + v_jack = NULL; + } +} + +MODULE_DEVICE_TABLE(virtio, id_table); + +static struct virtio_driver virtio_jack_driver = { + .driver = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE , + }, + .id_table = id_table, + .probe = jack_probe, + .remove = jack_remove, +}; + +static int __init maru_jack_init(void) +{ + DLOG(KERN_INFO, "maru_%s: init\n", DEVICE_NAME); + return register_virtio_driver(&virtio_jack_driver); +} + +static void __exit maru_jack_exit(void) +{ + DLOG(KERN_INFO, "maru_%s: exit\n", DEVICE_NAME); + unregister_virtio_driver(&virtio_jack_driver); +} + +module_init(maru_jack_init); +module_exit(maru_jack_exit); + +MODULE_LICENSE("GPL2"); +MODULE_AUTHOR("Jinhyung Choi "); +MODULE_DESCRIPTION("Emulator Virtio Power Driver"); diff --git a/drivers/maru/maru_power_supply.c b/drivers/maru/maru_power_supply.c new file mode 100644 index 0000000..612b5b3 --- /dev/null +++ b/drivers/maru/maru_power_supply.c @@ -0,0 +1,410 @@ +/* + * Virtual device node + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * JinHyung Choi + * SooYoung Ha + * Sungmin Ha + * YeongKyoon Lee +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define DEVICE_NAME "power_supply" +#define FILE_PERMISSION (S_IRUGO | S_IWUSR) + +#define __MAX_BUF_POWER 512 + +//#define DEBUG_MARU_POWER_SUPPLY + +#ifdef DEBUG_MARU_POWER_SUPPLY +#define DLOG(level, fmt, ...) \ + printk(level "maru_%s: " fmt, DEVICE_NAME, ##__VA_ARGS__) +#else +// do nothing +#define DLOG(level, fmt, ...) +#endif + +#define __MAX_BUF_SIZE 1024 + +static struct virtio_device_id id_table[] = { { VIRTIO_ID_POWER, + VIRTIO_DEV_ANY_ID }, { 0 }, }; + +struct msg_info { + char buf[__MAX_BUF_SIZE]; + + uint16_t type; + uint16_t req; +}; + +struct virtio_power { + struct virtio_device* vdev; + struct virtqueue* vq; + + struct msg_info msginfo; + + struct scatterlist sg_vq[2]; + + int flags; + struct mutex lock; +}; + +enum power_types { + power_type_capacity = 0, + power_type_charge_full, + power_type_charge_now, + power_type_max +}; + +enum request_cmd { + request_get = 0, + request_set, + request_answer +}; + +struct virtio_power *v_power; + +static struct class* power_class; +static struct device* power_device; + +static char power_data [__MAX_BUF_POWER]; + +static DECLARE_WAIT_QUEUE_HEAD(wq); + +static void power_vq_done(struct virtqueue *vq) { + unsigned int len; + struct msg_info* msg; + + msg = (struct msg_info*) virtqueue_get_buf(v_power->vq, &len); + if (msg == NULL) { + DLOG(KERN_ERR, "failed to virtqueue_get_buf"); + return; + } + + if (msg->req != request_answer) { + DLOG(KERN_DEBUG, "receive queue- not an answer message: %d", msg->req); + return; + } + if (msg->buf == NULL) { + DLOG(KERN_ERR, "receive queue- message from host is NULL."); + return; + } + + DLOG(KERN_DEBUG, "msg buf: %s, req: %d, type: %d", msg->buf, msg->req, msg->type); + + mutex_lock(&v_power->lock); + memset(power_data, 0, __MAX_BUF_POWER); + strcpy(power_data, msg->buf); + v_power->flags = 1; + mutex_unlock(&v_power->lock); + + wake_up_interruptible(&wq); +} + +static void set_power_data(int type, const char* buf) +{ + int err = 0; + + if (buf == NULL) { + DLOG(KERN_ERR, "set_power buf is NULL."); + return; + } + + if (v_power == NULL) { + DLOG(KERN_ERR, "Invalid power handle"); + return; + } + + mutex_lock(&v_power->lock); + memset(power_data, 0, __MAX_BUF_POWER); + memset(&v_power->msginfo, 0, sizeof(v_power->msginfo)); + + strcpy(power_data, buf); + + v_power->msginfo.req = request_set; + v_power->msginfo.type = type; + strcpy(v_power->msginfo.buf, buf); + mutex_unlock(&v_power->lock); + + DLOG(KERN_DEBUG, "set_power_data type: %d, req: %d, buf: %s", + v_power->msginfo.type, v_power->msginfo.req, v_power->msginfo.buf); + + err = virtqueue_add_outbuf(v_power->vq, v_power->sg_vq, 1, &v_power->msginfo, GFP_ATOMIC); + if (err < 0) { + DLOG(KERN_ERR, "failed to add buffer to virtqueue (err = %d)", err); + return; + } + + virtqueue_kick(v_power->vq); +} + +static int get_power_data(int type, char* data) +{ + struct scatterlist *sgs[2]; + int err = 0; + + if (v_power == NULL || data == NULL) { + DLOG(KERN_ERR, "Invalid power handle or data is NULL"); + return -1; + } + + mutex_lock(&v_power->lock); + memset(&v_power->msginfo, 0, sizeof(v_power->msginfo)); + + v_power->msginfo.req = request_get; + v_power->msginfo.type = type; + mutex_unlock(&v_power->lock); + + DLOG(KERN_DEBUG, "get_power_data type: %d, req: %d", + v_power->msginfo.type, v_power->msginfo.req); + + sgs[0] = &v_power->sg_vq[0]; + sgs[1] = &v_power->sg_vq[1]; + err = virtqueue_add_sgs(v_power->vq, sgs, 1, 1, &v_power->msginfo, GFP_ATOMIC); + if (err < 0) { + DLOG(KERN_ERR, "failed to add buffer to virtqueue (err = %d)", err); + return -1; + } + + virtqueue_kick(v_power->vq); + + wait_event_interruptible(wq, v_power->flags != 0); + + mutex_lock(&v_power->lock); + v_power->flags = 0; + memcpy(data, power_data, strlen(power_data)); + mutex_unlock(&v_power->lock); + + return 0; +} + +static int get_data_for_show(int type, char* buf) +{ + int ret; + char power_data[__MAX_BUF_POWER]; + memset(power_data, 0, sizeof(power_data)); + ret = get_power_data(type, power_data); + if (ret) + return 0; + return sprintf(buf, "%s", power_data); + +} + +static ssize_t show_capacity(struct device *dev, struct device_attribute *attr, char *buf) +{ + return get_data_for_show(power_type_capacity, buf); +} + +static ssize_t store_capacity(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + set_power_data(power_type_capacity, buf); + return strnlen(buf, count); +} + +static ssize_t show_charge_full(struct device *dev, struct device_attribute *attr, char *buf) +{ + return get_data_for_show(power_type_charge_full, buf); +} + +static ssize_t store_charge_full(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + set_power_data(power_type_charge_full, buf); + return strnlen(buf, count); +} + +static ssize_t show_charge_now(struct device *dev, struct device_attribute *attr, char *buf) +{ + return get_data_for_show(power_type_charge_now, buf); +} + +static ssize_t store_charge_now(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + set_power_data(power_type_charge_now, buf); + return strnlen(buf, count); +} + +static struct device_attribute ps_device_attributes[] = { + __ATTR(capacity, FILE_PERMISSION, show_capacity, store_capacity), + __ATTR(charge_full, FILE_PERMISSION, show_charge_full, store_charge_full), + __ATTR(charge_now, FILE_PERMISSION, show_charge_now, store_charge_now), +}; + +static void class_cleanup (void) +{ + int i = 2; + + for (; i > 0; i--) { + + if (power_device == NULL) + continue; + + device_remove_file(power_device, &ps_device_attributes[i]); + + device_unregister(power_device); + + device_destroy(power_class, MKDEV(0,0)); + } + + class_destroy(power_class); + power_class = NULL; +} + +static int init_device(void) +{ + int err = 0, i = 0; + power_device = device_create(power_class, NULL, MKDEV(0,0), NULL, "battery"); + + for (i = 0; i < 3; i++) { + err = device_create_file(power_device, &ps_device_attributes[i]); + if (err) { + printk(KERN_ERR + "maru_%s: failed to create power_supply files\n", DEVICE_NAME); + goto device_err; + } + } + + return err; +device_err: + class_cleanup(); + return -1; +} + +static void cleanup(struct virtio_device* dev) { + dev->config->del_vqs(dev); + + if (v_power) { + kfree(v_power); + v_power = NULL; + } + + class_cleanup(); +} + +static int power_probe(struct virtio_device* dev) +{ + int err = 0; + int ret = 0; + int index = 0; + + DLOG(KERN_INFO, "Power probe starts"); + + v_power = kmalloc(sizeof(struct virtio_power), GFP_KERNEL); + + v_power->vdev = dev; + dev->priv = v_power; + + power_class = class_create(THIS_MODULE, DEVICE_NAME); + if (power_class == NULL) { + DLOG(KERN_ERR, "Power class creation is failed."); + return -1; + } + + ret = init_device(); + if (ret) { + cleanup(dev); + return ret; + } + + v_power->vq = virtio_find_single_vq(dev, power_vq_done, "power"); + if (IS_ERR(v_power->vq)) { + cleanup(dev); + DLOG(KERN_ERR, "failed to init virt queue"); + return ret; + } + + virtqueue_enable_cb(v_power->vq); + + memset(&v_power->msginfo, 0x00, sizeof(v_power->msginfo)); + + sg_init_table(v_power->sg_vq, 2); + for (; index < 2; index++) { + sg_set_buf(&v_power->sg_vq[index], &v_power->msginfo, sizeof(v_power->msginfo)); + } + + mutex_init(&v_power->lock); + + DLOG(KERN_INFO, "Power probe completes"); + + return err; +} + +static void power_remove(struct virtio_device* dev) +{ + struct virtio_power* v_power = dev->priv; + if (!v_power) + { + DLOG(KERN_ERR, "virtio_power is NULL"); + return; + } + + dev->config->reset(dev); + + cleanup(dev); + + DLOG(KERN_INFO, "Power driver is removed."); +} + +MODULE_DEVICE_TABLE(virtio, id_table); + +static struct virtio_driver virtio_power_driver = { + .driver = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE , + }, + .id_table = id_table, + .probe = power_probe, + .remove = power_remove, +}; + +static int __init maru_power_supply_init(void) +{ + DLOG(KERN_INFO, "maru_%s: init\n", DEVICE_NAME); + return register_virtio_driver(&virtio_power_driver); +} + +static void __exit maru_power_supply_exit(void) +{ + DLOG(KERN_INFO, "maru_%s: exit\n", DEVICE_NAME); + unregister_virtio_driver(&virtio_power_driver); +} + +module_init(maru_power_supply_init); +module_exit(maru_power_supply_exit); + +MODULE_LICENSE("GPL2"); +MODULE_AUTHOR("Jinhyung Choi "); +MODULE_DESCRIPTION("Emulator Virtio Power Driver"); diff --git a/drivers/maru/maru_virtio_evdi.c b/drivers/maru/maru_virtio_evdi.c new file mode 100644 index 0000000..8747750 --- /dev/null +++ b/drivers/maru/maru_virtio_evdi.c @@ -0,0 +1,666 @@ +/* + * Maru Virtio EmulatorVritualDeviceInterface Device Driver + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * DaiYoung Kim + * YeongKyoon Lee + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributors: + * - S-Core Co., Ltd + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "EVDI" + +#define LOGDEBUG(fmt, ...) \ + printk(KERN_DEBUG "%s: " fmt, DRIVER_NAME, ##__VA_ARGS__) + +#define LOGINFO(fmt, ...) \ + printk(KERN_INFO "%s: " fmt, DRIVER_NAME, ##__VA_ARGS__) + +#define LOGERR(fmt, ...) \ + printk(KERN_ERR "%s: " fmt, DRIVER_NAME, ##__VA_ARGS__) + +#define NUM_OF_EVDI 2 +#define DEVICE_NAME "evdi" + +/* device protocol */ +#define __MAX_BUF_SIZE 1024 + +enum ioctl_cmd { + IOCTL_CMD_BOOT_DONE, +}; + +enum +{ + route_qemu = 0, + route_control_server = 1, + route_monitor = 2 +}; + +typedef unsigned int CSCliSN; + +struct msg_info { + char buf[__MAX_BUF_SIZE]; + + uint32_t route; + uint32_t use; + uint16_t count; + uint16_t index; + + CSCliSN cclisn; +}; + +/* device protocol */ + +#define SIZEOF_MSG_INFO sizeof(struct msg_info) + +struct msg_buf { + struct msg_info msg; + struct list_head list; +}; + +#define SIZEOF_MSG_BUF sizeof(struct msg_buf) + +enum { + EVID_READ = 0, EVID_WRITE = 1 +}; + +struct virtevdi_info { + + wait_queue_head_t waitqueue; + spinlock_t inbuf_lock; + spinlock_t outvq_lock; + + struct cdev cdev; + char name[10]; + + int index; + bool guest_connected; + +} *pevdi_info[NUM_OF_EVDI]; + +struct virtio_evdi { + struct virtio_device* vdev; + struct virtqueue* rvq; + struct virtqueue* svq; + + struct msg_info read_msginfo; + struct msg_info send_msginfo; + + struct list_head read_list; + struct list_head write_list; + + struct scatterlist sg_read[2]; + struct scatterlist sg_send[2]; +}; + +struct virtio_evdi *vevdi; + +static struct virtio_device_id id_table[] = { { VIRTIO_ID_EVDI, + VIRTIO_DEV_ANY_ID }, { 0 }, }; + +static dev_t evdi_dev_number; +static struct class* evdi_class; + + +static void* __xmalloc(size_t size) +{ + void* p = kmalloc(size, GFP_KERNEL); + if (!p) + return NULL; + return p; +} + +int _make_buf_and_kick(void) +{ + int ret; + memset(&vevdi->read_msginfo, 0x00, sizeof(vevdi->read_msginfo)); + ret = virtqueue_add_inbuf(vevdi->rvq, vevdi->sg_read, + 1, &vevdi->read_msginfo, GFP_ATOMIC); + if (ret < 0) { + LOGERR("failed to add buffer to virtqueue.(%d)\n", ret); + return ret; + } + + virtqueue_kick(vevdi->rvq); + + return 0; +} + +static int add_inbuf(struct virtqueue *vq, struct msg_info *msg) +{ + struct scatterlist sg[1]; + int ret; + + sg_init_one(sg, msg, sizeof(struct msg_info)); + + ret = virtqueue_add_inbuf(vq, sg, 1, msg, GFP_ATOMIC); + virtqueue_kick(vq); + return ret; +} + +static bool has_readdata(struct virtevdi_info *evdi) +{ + bool ret; + unsigned long flags; + + spin_lock_irqsave(&evdi->inbuf_lock, flags); + + ret = true; + + if (list_empty(&vevdi->read_list)) + ret = false; + + spin_unlock_irqrestore(&evdi->inbuf_lock, flags); + + return ret; +} + +#define HEADER_SIZE 4 +#define ID_SIZE 10 +#define GUEST_CONNECTION_CATEGORY "guest" +static void send_guest_connected_msg(bool connected) +{ + int err; + struct msg_info* _msg; + char connect = (char)connected; + if (vevdi == NULL) { + LOGERR("invalid evdi handle\n"); + return; + } + + _msg = &vevdi->send_msginfo; + + memset(_msg, 0, sizeof(vevdi->send_msginfo)); + + memcpy(_msg->buf, GUEST_CONNECTION_CATEGORY, 7); + memcpy(_msg->buf + ID_SIZE + 3, &connect, 1); + _msg->route = route_control_server; + _msg->use = ID_SIZE + HEADER_SIZE; + _msg->count = 1; + _msg->index = 0; + _msg->cclisn = 0; + + err = virtqueue_add_outbuf(vevdi->svq, vevdi->sg_send, 1, + _msg, GFP_ATOMIC); + + LOGERR("send guest connection message to qemu with (%d)\n", connected); + + if (err < 0) { + LOGERR("failed to add buffer to virtqueue (err = %d)\n", err); + return; + } + + virtqueue_kick(vevdi->svq); +} + + +static int evdi_open(struct inode* inode, struct file* filp) +{ + int i, ret; + struct virtevdi_info* evdi_info; + struct cdev *cdev = inode->i_cdev; + + evdi_info = NULL; + LOGDEBUG("evdi_open\n"); + + for (i = 0; i < NUM_OF_EVDI; i++) + { + LOGDEBUG("evdi info index = %d, cdev dev = %d, inode dev = %d\n", + i, pevdi_info[i]->cdev.dev, cdev->dev); + + if (pevdi_info[i]->cdev.dev == cdev->dev) + { + evdi_info = pevdi_info[i]; + break; + } + } + + filp->private_data = evdi_info; + + evdi_info->guest_connected = true; + + ret = _make_buf_and_kick(); + if (ret < 0) + return ret; + + + LOGDEBUG("evdi_opened\n"); + return 0; +} + +static int evdi_close(struct inode* i, struct file* filp) { + struct virtevdi_info *evdi_info; + + evdi_info = filp->private_data; + evdi_info->guest_connected = false; + + send_guest_connected_msg(false); + + LOGDEBUG("evdi_closed\n"); + return 0; +} + + + +static ssize_t evdi_read(struct file *filp, char __user *ubuf, size_t len, + loff_t *f_pos) +{ + struct virtevdi_info *evdi; + + ssize_t ret; + struct msg_buf* next; + unsigned long flags; + + evdi = filp->private_data; + + if (!has_readdata(evdi)) + { + if (filp->f_flags & O_NONBLOCK) + { + LOGERR("list is empty, return EAGAIN\n"); + return -EAGAIN; + } + return -EFAULT; + } + + + next = list_first_entry(&vevdi->read_list, struct msg_buf, list); + if (next == NULL) { + LOGERR("invliad list entry\n"); + return -EFAULT; + } + + ret = copy_to_user(ubuf, &next->msg, len); + + list_del(&next->list); + kfree(next); + + spin_lock_irqsave(&pevdi_info[EVID_READ]->inbuf_lock, flags); + + + if (add_inbuf(vevdi->rvq, &vevdi->read_msginfo) < 0) + { + LOGERR("failed add_buf\n"); + } + + spin_unlock_irqrestore(&pevdi_info[EVID_READ]->inbuf_lock, flags); + + if (ret < 0) + return -EFAULT; + + + + *f_pos += len; + + return len; +} + +static ssize_t evdi_write(struct file *f, const char __user *ubuf, size_t len, + loff_t* f_pos) +{ + int err = 0; + ssize_t ret = 0; + + if (vevdi == NULL) { + LOGERR("invalid evdi handle\n"); + return 0; + } + + memset(&vevdi->send_msginfo, 0, sizeof(vevdi->send_msginfo)); + ret = copy_from_user(&vevdi->send_msginfo, ubuf, sizeof(vevdi->send_msginfo)); + + LOGDEBUG("copy_from_user ret = %zd, msg = %s", ret, vevdi->send_msginfo.buf); + + if (ret) { + ret = -EFAULT; + return ret; + } + + + err = virtqueue_add_outbuf(vevdi->svq, vevdi->sg_send, 1, + &vevdi->send_msginfo, GFP_ATOMIC); + + /* + err = virtqueue_add_buf(vevdi->svq, vevdi->sg_send, 1, 0, + &_msg, GFP_ATOMIC);*/ + + if (err < 0) { + LOGERR("failed to add buffer to virtqueue (err = %d)\n", err); + return 0; + } + + virtqueue_kick(vevdi->svq); + + //LOG("send to host\n"); + + return len; +} + +static unsigned int evdi_poll(struct file *filp, poll_table *wait) +{ + struct virtevdi_info *evdi; + unsigned int ret; + + evdi = filp->private_data; + poll_wait(filp, &evdi->waitqueue, wait); + + if (!evdi->guest_connected) { + /* evdi got unplugged */ + return POLLHUP; + } + + ret = 0; + + if (has_readdata(evdi)) + { + LOGDEBUG("POLLIN | POLLRDNORM\n"); + ret |= POLLIN | POLLRDNORM; + } + + return ret; +} + +static long evdi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case IOCTL_CMD_BOOT_DONE: + LOGINFO("BOOTING DONE.\n"); + break; + default: + LOGERR("not available command.\n"); + return -EIO; + } + return 0; +} + +static struct file_operations evdi_fops = { + .owner = THIS_MODULE, + .open = evdi_open, + .release = evdi_close, + .read = evdi_read, + .write = evdi_write, + .poll = evdi_poll, + .unlocked_ioctl = evdi_ioctl, +}; + + + +static void evdi_recv_done(struct virtqueue *rvq) { + + unsigned int len; + unsigned long flags; + struct msg_info* _msg; + struct msg_buf* msgbuf; + + + + /* TODO : check if guest has been connected. */ + + _msg = (struct msg_info*) virtqueue_get_buf(vevdi->rvq, &len); + if (_msg == NULL ) { + LOGERR("failed to virtqueue_get_buf\n"); + return; + } + + do { + //LOG("msg use = %d\n", _msg->use); + //LOG("msg data = %s\n", _msg->buf); + + /* insert into queue */ + msgbuf = (struct msg_buf*) __xmalloc(SIZEOF_MSG_BUF); + memset(msgbuf, 0x00, sizeof(*msgbuf)); + memcpy(&(msgbuf->msg), _msg, sizeof(*_msg)); + + //LOG("copied msg data = %s, %s\n", msgbuf->msg.buf, _msg->buf); + + spin_lock_irqsave(&pevdi_info[EVID_READ]->inbuf_lock, flags); + + list_add_tail(&msgbuf->list, &vevdi->read_list); + //LOG("== wake_up_interruptible = %d!\n", ++g_wake_up_interruptible_count); + + spin_unlock_irqrestore(&pevdi_info[EVID_READ]->inbuf_lock, flags); + + wake_up_interruptible(&pevdi_info[EVID_READ]->waitqueue); + + _msg = (struct msg_info*) virtqueue_get_buf(vevdi->rvq, &len); + if (_msg == NULL) { + break; + } + + } while (true); + + + /* + if (add_inbuf(vevdi->rvq, &vevdi->read_msginfo) < 0) + { + LOG("failed add_buf\n"); + } + */ +} + +static void evdi_send_done(struct virtqueue *svq) { + unsigned int len = 0; + + virtqueue_get_buf(svq, &len); +} + +/* + * + */ + +static int init_vqs(struct virtio_evdi *evdi) { + struct virtqueue *vqs[2]; + vq_callback_t *callbacks[] = { evdi_recv_done, evdi_send_done }; + const char *names[] = { "evdi_input", "evdi_output" }; + int err; + + err = evdi->vdev->config->find_vqs(evdi->vdev, 2, vqs, callbacks, names); + if (err < 0) + return err; + + evdi->rvq = vqs[0]; + evdi->svq = vqs[1]; + + return 0; +} + +int _init_device(void) +{ + int i, ret; + + if (alloc_chrdev_region(&evdi_dev_number, 0, NUM_OF_EVDI, DEVICE_NAME) < 0) { + LOGERR("fail to alloc_chrdev_region\n"); + return -1; + } + + evdi_class = class_create(THIS_MODULE, DEVICE_NAME); + + if (evdi_class == NULL ) { + unregister_chrdev_region(evdi_dev_number, NUM_OF_EVDI); + return -1; + } + + for (i = 0; i < NUM_OF_EVDI; i++) { + pevdi_info[i] = kmalloc(sizeof(struct virtevdi_info), GFP_KERNEL); + + if (!pevdi_info[i]) { + LOGERR("Bad malloc\n"); + return -ENOMEM; + } + + sprintf(pevdi_info[i]->name, "%s%d", DEVICE_NAME, i); + + pevdi_info[i]->index = i; + pevdi_info[i]->guest_connected = false; + + cdev_init(&pevdi_info[i]->cdev, &evdi_fops); + pevdi_info[i]->cdev.owner = THIS_MODULE; + ret = cdev_add(&pevdi_info[i]->cdev, (evdi_dev_number + i), 1); + + /* init wait queue */ + init_waitqueue_head(&pevdi_info[i]->waitqueue); + spin_lock_init(&pevdi_info[i]->inbuf_lock); + spin_lock_init(&pevdi_info[i]->outvq_lock); + + if (ret == -1) { + LOGERR("Bad cdev\n"); + return ret; + } + + device_create(evdi_class, NULL, (evdi_dev_number + i), NULL, "%s%d", + DEVICE_NAME, i); + } + + return 0; +} + + +static int evdi_probe(struct virtio_device* dev) { + int ret; + + vevdi = kmalloc(sizeof(struct virtio_evdi), GFP_KERNEL); + + INIT_LIST_HEAD(&vevdi->read_list); + + vevdi->vdev = dev; + dev->priv = vevdi; + + ret = _init_device(); + if (ret) + { + LOGERR("failed to _init_device\n"); + return ret; + } + ret = init_vqs(vevdi); + if (ret) { + dev->config->del_vqs(dev); + kfree(vevdi); + dev->priv = NULL; + + LOGERR("failed to init_vqs\n"); + return ret; + } + + /* enable callback */ + virtqueue_enable_cb(vevdi->rvq); + virtqueue_enable_cb(vevdi->svq); + + + memset(&vevdi->read_msginfo, 0x00, sizeof(vevdi->read_msginfo)); + sg_set_buf(vevdi->sg_read, &vevdi->read_msginfo, sizeof(struct msg_info)); + + memset(&vevdi->send_msginfo, 0x00, sizeof(vevdi->send_msginfo)); + sg_set_buf(vevdi->sg_send, &vevdi->send_msginfo, sizeof(struct msg_info)); + + + sg_init_one(vevdi->sg_read, &vevdi->read_msginfo, sizeof(vevdi->read_msginfo)); + sg_init_one(vevdi->sg_send, &vevdi->send_msginfo, sizeof(vevdi->send_msginfo)); + + + + LOGDEBUG("EVDI Probe completed"); + return 0; +} + +static void evdi_remove(struct virtio_device* dev) +{ + struct virtio_evdi* _evdi = dev->priv; + if (!_evdi) + { + LOGERR("evdi is NULL\n"); + return; + } + + dev->config->reset(dev); + dev->config->del_vqs(dev); + + kfree(_evdi); + + LOGDEBUG("driver is removed.\n"); +} + +MODULE_DEVICE_TABLE(virtio, id_table); + +static struct virtio_driver virtio_evdi_driver = { + .driver = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE , + }, + .id_table = id_table, + .probe = evdi_probe, + .remove = evdi_remove, +}; + +static int __init evdi_init(void) +{ + LOGDEBUG("EVDI driver initialized.\n"); + + return register_virtio_driver(&virtio_evdi_driver); +} + +static void __exit evdi_exit(void) +{ + int i; + + unregister_chrdev_region(evdi_dev_number, NUM_OF_EVDI); + + for (i = 0; i < NUM_OF_EVDI; i++) { + device_destroy(evdi_class, MKDEV(MAJOR(evdi_dev_number), i)); + cdev_del(&pevdi_info[i]->cdev); + kfree(pevdi_info[i]); + } + + /*device_destroy(evdi_class, evdi_dev_number);*/ + + class_destroy(evdi_class); + + unregister_virtio_driver(&virtio_evdi_driver); + + LOGDEBUG("EVDI driver is destroyed.\n"); +} + +module_init(evdi_init); +module_exit(evdi_exit); + +MODULE_LICENSE("GPL2"); +MODULE_AUTHOR("DaiYoung Kim "); +MODULE_DESCRIPTION("Emulator Virtio EmulatorVirtualDeviceInterface Driver"); + diff --git a/drivers/maru/maru_virtio_hwkey.c b/drivers/maru/maru_virtio_hwkey.c new file mode 100644 index 0000000..03f30eb --- /dev/null +++ b/drivers/maru/maru_virtio_hwkey.c @@ -0,0 +1,281 @@ +/* + * Maru Virtio Hwkey Device Driver + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * Sungmin Ha + * Sangjin Kim + * YeongKyoon Lee + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributors: + * - S-Core Co., Ltd + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL2"); +MODULE_AUTHOR("Sungmin Ha "); +MODULE_DESCRIPTION("Emulator Virtio Hwkey driver"); + +#define DEVICE_NAME "virtio-hwkey" +#define MAX_BUF_COUNT 64 +static int vqidx = 0; + +/* This structure must match the qemu definitions */ +typedef struct EmulHwkeyEvent { + uint8_t event_type; + uint32_t keycode; +} EmulHwkeyEvent; + +typedef struct virtio_hwkey +{ + struct virtio_device *vdev; + struct virtqueue *vq; + struct input_dev *idev; + + struct scatterlist sg[MAX_BUF_COUNT]; + struct EmulHwkeyEvent vbuf[MAX_BUF_COUNT]; + + struct mutex event_mutex; +} virtio_hwkey; + +virtio_hwkey *vh; + +static struct virtio_device_id id_table[] = { + { VIRTIO_ID_HWKEY, VIRTIO_DEV_ANY_ID }, + { 0 }, +}; + +/* keep it consistent with emulator-skin definition */ +enum { + KEY_PRESSED = 1, + KEY_RELEASED = 2, +}; + +static int err = 0; +static unsigned int index = 0; + +/** + * @brief : callback for virtqueue + */ +static void vq_hwkey_callback(struct virtqueue *vq) +{ + struct EmulHwkeyEvent hwkey_event; + unsigned int len = 0; + void *token = NULL; +#if 0 + printk(KERN_INFO "vq hwkey callback\n"); +#endif + while (1) { + memcpy(&hwkey_event, &vh->vbuf[vqidx], sizeof(hwkey_event)); + if (hwkey_event.event_type == 0) { + break; + } + printk(KERN_INFO "keycode: %d, event_type: %d, vqidx: %d\n", hwkey_event.keycode, hwkey_event.event_type, vqidx); + if (hwkey_event.event_type == KEY_PRESSED) { + input_event(vh->idev, EV_KEY, hwkey_event.keycode, true); + } + else if (hwkey_event.event_type == KEY_RELEASED) { + input_event(vh->idev, EV_KEY, hwkey_event.keycode, false); + } + else { + printk(KERN_ERR "Unknown event type\n"); + } + + input_sync(vh->idev); + memset(&vh->vbuf[vqidx], 0x00, sizeof(hwkey_event)); + token = virtqueue_get_buf(vh->vq, &len); + if (len > 0) { + err = virtqueue_add_inbuf(vh->vq, vh->sg, MAX_BUF_COUNT, token, GFP_ATOMIC); + } + + vqidx++; + if (vqidx == MAX_BUF_COUNT) { + vqidx = 0; + } + } + + virtqueue_kick(vh->vq); +} + +static int virtio_hwkey_open(struct inode *inode, struct file *file) +{ + printk(KERN_INFO "virtio hwkey device is opened\n"); + return 0; +} + +static int virtio_hwkey_release(struct inode *inode, struct file *file) +{ + printk(KERN_INFO "virtio hwkey device is closed\n"); + return 0; +} + +static int input_hwkey_open(struct input_dev *dev) +{ + printk(KERN_INFO "input hwkey device is opened\n"); + return 0; +} + +static void input_hwkey_close(struct input_dev *dev) +{ + printk(KERN_INFO "input hwkey device is closed\n"); +} + +struct file_operations virtio_hwkey_fops = { + .owner = THIS_MODULE, + .open = virtio_hwkey_open, + .release = virtio_hwkey_release, +}; + +static int virtio_hwkey_probe(struct virtio_device *vdev) +{ + int ret = 0; + vqidx = 0; + + printk(KERN_INFO "virtio hwkey driver is probed\n"); + + /* init virtio */ + vdev->priv = vh = kmalloc(sizeof(*vh), GFP_KERNEL); + if (!vh) { + return -ENOMEM; + } + memset(&vh->vbuf, 0x00, sizeof(vh->vbuf)); + + vh->vdev = vdev; + + vh->vq = virtio_find_single_vq(vh->vdev, vq_hwkey_callback, "virtio-hwkey-vq"); + if (IS_ERR(vh->vq)) { + ret = PTR_ERR(vh->vq); + + kfree(vh); + vdev->priv = NULL; + return ret; + } + + /* enable callback */ + virtqueue_enable_cb(vh->vq); + + sg_init_table(vh->sg, MAX_BUF_COUNT); + + /* prepare the buffers */ + for (index = 0; index < MAX_BUF_COUNT; index++) { + sg_set_buf(&vh->sg[index], &vh->vbuf[index], sizeof(EmulHwkeyEvent)); + + if (err < 0) { + printk(KERN_ERR "failed to add buffer\n"); + + kfree(vh); + vdev->priv = NULL; + return ret; + } + } + + err = virtqueue_add_inbuf(vh->vq, vh->sg, + MAX_BUF_COUNT, (void *)vh->vbuf, GFP_ATOMIC); + + /* register for input device */ + vh->idev = input_allocate_device(); + if (!vh->idev) { + printk(KERN_ERR "failed to allocate a input hwkey device\n"); + ret = -1; + + kfree(vh); + vdev->priv = NULL; + return ret; + } + + vh->idev->name = "Maru Virtio Hwkey"; + vh->idev->dev.parent = &(vdev->dev); + + input_set_drvdata(vh->idev, vh); + vh->idev->open = input_hwkey_open; + vh->idev->close = input_hwkey_close; + + vh->idev->evbit[0] = BIT_MASK(EV_KEY); + /* to support any keycode */ + memset(vh->idev->keybit, 0xffffffff, sizeof(unsigned long) * BITS_TO_LONGS(KEY_CNT)); + + ret = input_register_device(vh->idev); + if (ret) { + printk(KERN_ERR "input hwkey driver cannot registered\n"); + ret = -1; + + input_free_device(vh->idev); + kfree(vh); + vdev->priv = NULL; + return ret; + } + + virtqueue_kick(vh->vq); + index = 0; + + return 0; +} + +static void virtio_hwkey_remove(struct virtio_device *vdev) +{ + virtio_hwkey *vhk = NULL; + + printk(KERN_INFO "virtio hwkey driver is removed\n"); + + vhk = vdev->priv; + + vdev->config->reset(vdev); /* reset device */ + vdev->config->del_vqs(vdev); /* clean up the queues */ + + input_unregister_device(vhk->idev); + + kfree(vhk); +} + +MODULE_DEVICE_TABLE(virtio, id_table); + +static struct virtio_driver virtio_hwkey_driver = { + .driver.name = KBUILD_MODNAME, + .driver.owner = THIS_MODULE, + .id_table = id_table, + .probe = virtio_hwkey_probe, + .remove = virtio_hwkey_remove, +}; + +static int __init virtio_hwkey_init(void) +{ + printk(KERN_INFO "virtio hwkey device is initialized\n"); + return register_virtio_driver(&virtio_hwkey_driver); +} + +static void __exit virtio_hwkey_exit(void) +{ + printk(KERN_INFO "virtio hwkey device is destroyed\n"); + unregister_virtio_driver(&virtio_hwkey_driver); +} + +module_init(virtio_hwkey_init); +module_exit(virtio_hwkey_exit); + diff --git a/drivers/maru/maru_virtio_keyboard.c b/drivers/maru/maru_virtio_keyboard.c new file mode 100644 index 0000000..f318ef1 --- /dev/null +++ b/drivers/maru/maru_virtio_keyboard.c @@ -0,0 +1,291 @@ +/* + * Maru Virtio Keyboard Device Driver + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * Kitae Kim + * SeokYeon Hwang + * YeongKyoon Lee + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributors: + * - S-Core Co., Ltd + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL2"); +MODULE_AUTHOR("Kitae Kim "); +MODULE_DESCRIPTION("Emulator Virtio Keyboard Driver"); + +#define DRIVER_NAME "virtio-keyboard" +#define VKBD_LOG(log_level, fmt, ...) \ + printk(log_level "%s: " fmt, DRIVER_NAME, ##__VA_ARGS__) + +#define KBD_BUF_SIZE 100 +static int vqidx = 0; + +struct EmulKbdEvent +{ + uint16_t code; + uint16_t value; +}; + +struct virtio_keyboard +{ + struct virtio_device *vdev; + struct virtqueue *vq; + struct input_dev *idev; + + struct EmulKbdEvent kbdevt[KBD_BUF_SIZE]; + struct scatterlist sg[KBD_BUF_SIZE]; + + struct mutex event_mutex; +}; + +struct virtio_keyboard *vkbd; + +static struct virtio_device_id id_table[] = { + { VIRTIO_ID_KEYBOARD, VIRTIO_DEV_ANY_ID }, + { 0 }, +}; + +static void vq_keyboard_handle(struct virtqueue *vq) +{ + int err = 0, len = 0; + void *data; + struct EmulKbdEvent kbdevent; + + VKBD_LOG(KERN_DEBUG, "virtqueue callback.\n"); + data = virtqueue_get_buf(vq, &len); + if (!data) { + VKBD_LOG(KERN_ERR, "there is no available buffer.\n"); + return; + } + + VKBD_LOG(KERN_DEBUG, "vqidx: %d\n", vqidx); + while (1) { + memcpy(&kbdevent, &vkbd->kbdevt[vqidx], sizeof(kbdevent)); +#if 1 + if (kbdevent.code == 0) { + break; + } +#endif + /* how to get keycode and value. */ + input_event(vkbd->idev, EV_KEY, kbdevent.code, kbdevent.value); + input_sync(vkbd->idev); + printk(KERN_ERR "input_event code = %d, value = %d\n", kbdevent.code, kbdevent.value); + memset(&vkbd->kbdevt[vqidx], 0x00, sizeof(kbdevent)); + vqidx++; + if (vqidx == KBD_BUF_SIZE) { + vqidx = 0; + } + } + err = virtqueue_add_inbuf(vq, vkbd->sg, KBD_BUF_SIZE, (void *)vkbd->kbdevt, GFP_ATOMIC); + if (err < 0) { + VKBD_LOG(KERN_ERR, "failed to add buffer to virtqueue.\n"); + return; + } + + virtqueue_kick(vkbd->vq); +} + +static int input_keyboard_open(struct input_dev *dev) +{ + VKBD_LOG(KERN_DEBUG, "input_keyboard_open\n"); + return 0; +} + +static void input_keyboard_close(struct input_dev *dev) +{ + VKBD_LOG(KERN_DEBUG, "input_keyboard_close\n"); +} + +#if 0 +static int virtio_keyboard_open(struct inode *inode, struct file *file) +{ + VKBD_LOG(KERN_DEBUG, "opened.\n"); + return 0; +} + +static int virtio_keyboard_release(struct inode *inode, struct file *file) +{ + VKBD_LOG(KERN_DEBUG, "closed\n"); + return 0; +} + +struct file_operations virtio_keyboard_fops = { + .owner = THIS_MODULE, + .open = virtio_keyboard_open, + .release = virtio_keyboard_release, +}; +#endif + +static int virtio_keyboard_probe(struct virtio_device *vdev) +{ + int ret = 0; + int index = 0; + + VKBD_LOG(KERN_INFO, "driver is probed\n"); + vqidx = 0; + + vdev->priv = vkbd = kmalloc(sizeof(struct virtio_keyboard), GFP_KERNEL); + if (!vkbd) { + return -ENOMEM; + } + memset(&vkbd->kbdevt, 0x00, sizeof(vkbd->kbdevt)); + + vkbd->vdev = vdev; + mutex_init(&vkbd->event_mutex); + + vkbd->vq = virtio_find_single_vq(vkbd->vdev, vq_keyboard_handle, "virtio-keyboard-vq"); + if (IS_ERR(vkbd->vq)) { + ret = PTR_ERR(vkbd->vq); + kfree(vkbd); + vdev->priv = NULL; + return ret; + } + + for (; index < KBD_BUF_SIZE; index++) { + sg_set_buf(&vkbd->sg[index], + &vkbd->kbdevt[index], + sizeof(struct EmulKbdEvent)); + } + + ret = virtqueue_add_inbuf(vkbd->vq, vkbd->sg, KBD_BUF_SIZE, (void *)vkbd->kbdevt, GFP_ATOMIC); + if (ret < 0) { + VKBD_LOG(KERN_ERR, "failed to add buffer to virtqueue.\n"); + kfree(vkbd); + vdev->priv = NULL; + return ret; + } + + /* register for input device */ + vkbd->idev = input_allocate_device(); + if (!vkbd->idev) { + VKBD_LOG(KERN_ERR, "failed to allocate a input device.\n"); + kfree(vkbd); + vdev->priv = NULL; + return -ENOMEM; + } + + vkbd->idev->name = "Maru VirtIO Keyboard"; + vkbd->idev->dev.parent = &(vdev->dev); + + input_set_drvdata(vkbd->idev, vkbd); + vkbd->idev->open = input_keyboard_open; + vkbd->idev->close = input_keyboard_close; + + /* initialize a device as a keyboard device. + * refer to struct input_dev from input.h. */ + vkbd->idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) + | BIT_MASK(EV_MSC) | BIT_MASK(EV_LED); + vkbd->idev->ledbit[0] = BIT_MASK(LED_NUML) | BIT_MASK(LED_CAPSL) + | BIT_MASK(LED_SCROLLL) | BIT_MASK(LED_COMPOSE) + | BIT_MASK(LED_KANA); + set_bit(MSC_SCAN, vkbd->idev->mscbit); + + /* set keybit field as xinput keyboard. */ + vkbd->idev->keybit[0] = 0xfffffffe; + vkbd->idev->keybit[1] = 0xffffffff; + vkbd->idev->keybit[2] = 0xffefffff; + vkbd->idev->keybit[3] = 0xfebeffdf; + vkbd->idev->keybit[4] = 0xc14057ff; + vkbd->idev->keybit[5] = 0xff9f207a; + vkbd->idev->keybit[6] = 0x7; + vkbd->idev->keybit[7] = 0x10000; + + ret = input_register_device(vkbd->idev); + if (ret) { + VKBD_LOG(KERN_ERR, "failed to register a input device.\n"); + input_free_device(vkbd->idev); + kfree(vkbd); + vdev->priv = NULL; + return ret; + } + + for (; index < KBD_BUF_SIZE; index++) { + sg_set_buf(&vkbd->sg[index], + &vkbd->kbdevt[index], + sizeof(struct EmulKbdEvent)); + } + + virtqueue_kick(vkbd->vq); + + return 0; +} + +static void virtio_keyboard_remove(struct virtio_device *vdev) +{ + VKBD_LOG(KERN_INFO, "driver is removed.\n"); + if (!vkbd) { + VKBD_LOG(KERN_ERR, "vkbd is NULL.\n"); + return; + } + + vdev->config->reset(vdev); + vdev->config->del_vqs(vdev); + + input_unregister_device(vkbd->idev); + + kfree(vkbd); + vkbd = NULL; +} + +MODULE_DEVICE_TABLE(virtio, id_table); + +static struct virtio_driver virtio_keyboard_driver = { + .driver = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE, + }, + .id_table = id_table, + .probe = virtio_keyboard_probe, + .remove = virtio_keyboard_remove, +#if 0 +#ifdef CONFIG_PM + .freeze = virtio_codec_freeze, + .restore = virtio_codec_restore, +#endif +#endif +}; + +static int __init virtio_keyboard_init(void) +{ + VKBD_LOG(KERN_INFO, "driver is initialized.\n"); + return register_virtio_driver(&virtio_keyboard_driver); +} + +static void __exit virtio_keyboard_exit(void) +{ + VKBD_LOG(KERN_INFO, "driver is destroyed.\n"); + unregister_virtio_driver(&virtio_keyboard_driver); +} + +module_init(virtio_keyboard_init); +module_exit(virtio_keyboard_exit); diff --git a/drivers/maru/maru_virtio_nfc.c b/drivers/maru/maru_virtio_nfc.c new file mode 100644 index 0000000..274ba87 --- /dev/null +++ b/drivers/maru/maru_virtio_nfc.c @@ -0,0 +1,581 @@ +/* + * Maru Virtio NFC Device Driver + * + * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * Munkyu Im + * YeongKyoon Lee + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributors: + * - S-Core Co., Ltd + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "NFC" + +#define LOG(fmt, ...) \ + printk(KERN_ERR "%s: " fmt, DRIVER_NAME, ##__VA_ARGS__) + +#define NUM_OF_NFC 2 +#define DEVICE_NAME "nfc" + +/* device protocol */ +#define NFC_MAX_BUF_SIZE 4096 + +struct msg_info { + unsigned char client_id; + unsigned char client_type; + uint32_t use; + char buf[NFC_MAX_BUF_SIZE]; +}; + +static int g_read_count = 0; + +/* device protocol */ + +struct msg_buf { + struct msg_info msg; + struct list_head list; +}; + +#define SIZEOF_MSG_BUF sizeof(struct msg_buf) + +enum { + NFC_READ = 0, NFC_WRITE = 1 +}; + +struct virtnfc_info { + + wait_queue_head_t waitqueue; + spinlock_t inbuf_lock; + spinlock_t outvq_lock; + + struct cdev cdev; + char name[10]; + + int index; + bool guest_connected; + +} *pnfc_info[NUM_OF_NFC]; + +struct virtio_nfc { + struct virtio_device* vdev; + struct virtqueue* rvq; + struct virtqueue* svq; + + struct msg_info read_msginfo; + struct msg_info send_msginfo; + + struct list_head read_list; + struct list_head write_list; + + struct scatterlist sg_read[2]; + struct scatterlist sg_send[2]; +}; + +struct virtio_nfc *vnfc; + +static struct virtio_device_id id_table[] = { { VIRTIO_ID_NFC, + VIRTIO_DEV_ANY_ID }, { 0 }, }; + +static dev_t nfc_dev_number; +static struct class* nfc_class; + + +static void* __xmalloc(size_t size) +{ + void* p = kmalloc(size, GFP_KERNEL); + if (!p) + return NULL; + return p; +} + +int make_buf_and_kick(void) +{ + int ret; + memset(&vnfc->read_msginfo, 0x00, sizeof(vnfc->read_msginfo)); + ret = virtqueue_add_inbuf(vnfc->rvq, vnfc->sg_read, + 1, &vnfc->read_msginfo, GFP_ATOMIC); + if (ret < 0) { + LOG("failed to add buffer to virtqueue.(%d)\n", ret); + return ret; + } + + virtqueue_kick(vnfc->rvq); + + return 0; +} + +static int add_inbuf(struct virtqueue *vq, struct msg_info *msg) +{ + struct scatterlist sg[1]; + int ret; + + sg_init_one(sg, msg, NFC_MAX_BUF_SIZE); + + ret = virtqueue_add_inbuf(vq, sg, 1, msg, GFP_ATOMIC); + virtqueue_kick(vq); + return ret; +} + +static bool has_readdata(struct virtnfc_info *nfc) +{ + bool ret; + unsigned long flags; + + spin_lock_irqsave(&nfc->inbuf_lock, flags); + + ret = true; + + if (list_empty(&vnfc->read_list)) + ret = false; + + spin_unlock_irqrestore(&nfc->inbuf_lock, flags); + + return ret; +} + + +static int nfc_open(struct inode* inode, struct file* filp) +{ + int i, ret; + struct virtnfc_info* nfc_info; + struct cdev *cdev = inode->i_cdev; + + nfc_info = NULL; + LOG("nfc_open\n"); + + for (i = 0; i < NUM_OF_NFC; i++) { + LOG("nfc info index = %d, cdev dev = %d, inode dev = %d\n", + i, pnfc_info[i]->cdev.dev, cdev->dev); + + if (pnfc_info[i]->cdev.dev == cdev->dev) { + nfc_info = pnfc_info[i]; + break; + } + } + + filp->private_data = nfc_info; + + nfc_info->guest_connected = true; + + + ret = make_buf_and_kick(); + if (ret < 0) + return ret; + + LOG("nfc_opened\n"); + return 0; +} + +static int nfc_close(struct inode* i, struct file* filp) { + struct virtnfc_info *nfc_info; + + nfc_info = filp->private_data; + nfc_info->guest_connected = false; + + LOG("nfc_closed\n"); + return 0; +} + + + +static ssize_t nfc_read(struct file *filp, char __user *ubuf, size_t len, + loff_t *f_pos) +{ + struct virtnfc_info *nfc; + + ssize_t ret; + struct msg_buf* next; + unsigned long flags; + + LOG("nfc_read\n"); + nfc = filp->private_data; + if (!has_readdata(nfc)) { + if (filp->f_flags & O_NONBLOCK) { + LOG("list is empty, return EAGAIN\n"); + return -EAGAIN; + } + return -EFAULT; + } + + next = list_first_entry(&vnfc->read_list, struct msg_buf, list); + if (next == NULL) { + LOG("invliad list entry\n"); + return -EFAULT; + } + + ret = copy_to_user(ubuf, &next->msg, len); + + list_del(&next->list); + kfree(next); + + spin_lock_irqsave(&pnfc_info[NFC_READ]->inbuf_lock, flags); + + + if (add_inbuf(vnfc->rvq, &vnfc->read_msginfo) < 0){ + LOG("failed add_buf\n"); + } + + spin_unlock_irqrestore(&pnfc_info[NFC_READ]->inbuf_lock, flags); + + + LOG("nfc_read count = %d!\n", ++g_read_count); + + if (ret < 0) + return -EFAULT; + + *f_pos += len; + + return len; +} + +static ssize_t nfc_write(struct file *f, const char __user *ubuf, size_t len, + loff_t* f_pos) +{ + int err = 0; + ssize_t ret = 0; + + LOG("start of nfc_write len= %zu, msglen = %zu\n", len, sizeof(vnfc->send_msginfo)); + + if (vnfc == NULL) { + LOG("invalid nfc handle\n"); + return 0; + } + + memset(&vnfc->send_msginfo, 0, sizeof(vnfc->send_msginfo)); + ret = copy_from_user(&vnfc->send_msginfo, ubuf, sizeof(vnfc->send_msginfo)); + + LOG("copy_from_user ret = %zd id = %02x, type = %02x, msg = %s use = %d\n", + ret, vnfc->send_msginfo.client_id, vnfc->send_msginfo.client_type, + vnfc->send_msginfo.buf, vnfc->send_msginfo.use); + + if (ret) { + ret = -EFAULT; + return ret; + } + + sg_init_one(vnfc->sg_send, &vnfc->send_msginfo, sizeof(vnfc->send_msginfo)); + + err = virtqueue_add_outbuf(vnfc->svq, vnfc->sg_send, 1, + &vnfc->send_msginfo, GFP_ATOMIC); + + /* + err = virtqueue_add_buf(vnfc->svq, vnfc->sg_send, 1, 0, + &_msg, GFP_ATOMIC);*/ + + if (err < 0) { + LOG("failed to add buffer to virtqueue (err = %d)\n", err); + return 0; + } + + virtqueue_kick(vnfc->svq); + + LOG("send to host\n"); + + return len; +} + +static unsigned int nfc_poll(struct file *filp, poll_table *wait) +{ + struct virtnfc_info *nfc; + unsigned int ret; + + nfc = filp->private_data; + poll_wait(filp, &nfc->waitqueue, wait); + + if (!nfc->guest_connected) { + /* nfc got unplugged */ + return POLLHUP; + } + + ret = 0; + + if (has_readdata(nfc)) { + LOG("POLLIN | POLLRDNORM\n"); + ret |= POLLIN | POLLRDNORM; + } + + return ret; +} + +static struct file_operations nfc_fops = { + .owner = THIS_MODULE, + .open = nfc_open, + .release = nfc_close, + .read = nfc_read, + .write = nfc_write, + .poll = nfc_poll, +}; + + + +static void nfc_recv_done(struct virtqueue *rvq) { + + unsigned int len; + unsigned long flags; + unsigned char *msg; + struct msg_buf* msgbuf; + LOG("nfc_recv_done\n"); + /* TODO : check if guest has been connected. */ + + msg = (unsigned char*) virtqueue_get_buf(vnfc->rvq, &len); + if (msg == NULL ) { + LOG("failed to virtqueue_get_buf\n"); + return; + } + + INIT_LIST_HEAD(&vnfc->read_list); + do { + + /* insert into queue */ + msgbuf = (struct msg_buf*) __xmalloc(SIZEOF_MSG_BUF); + memset(msgbuf, 0x00, sizeof(*msgbuf)); + memcpy(&(msgbuf->msg), msg, len); + + //LOG("copied msg data = %s, %s\n", msgbuf->msg.buf, msg->buf); + + spin_lock_irqsave(&pnfc_info[NFC_READ]->inbuf_lock, flags); + + list_add_tail(&msgbuf->list, &vnfc->read_list); + //LOG("== wake_up_interruptible = %d!\n", ++g_wake_up_interruptible_count); + + spin_unlock_irqrestore(&pnfc_info[NFC_READ]->inbuf_lock, flags); + + wake_up_interruptible(&pnfc_info[NFC_READ]->waitqueue); + + msg = (unsigned char*) virtqueue_get_buf(vnfc->rvq, &len); + if (msg == NULL) { + break; + } + + } while (true); + /* + if (add_inbuf(vnfc->rvq, &vnfc->readmsginfo) < 0) + { + LOG("failed add_buf\n"); + } + */ +} + +static void nfc_send_done(struct virtqueue *svq) { + unsigned int len = 0; + + virtqueue_get_buf(svq, &len); +} + +/* + * + */ + +static int init_vqs(struct virtio_nfc *nfc) { + struct virtqueue *vqs[2]; + vq_callback_t *callbacks[] = { nfc_recv_done, nfc_send_done }; + const char *names[] = { "nfc_input", "nfc_output" }; + int err; + + err = nfc->vdev->config->find_vqs(nfc->vdev, 2, vqs, callbacks, names); + if (err < 0) + return err; + + nfc->rvq = vqs[0]; + nfc->svq = vqs[1]; + + return 0; +} + +int init_device(void) +{ + int i, ret; + + if (alloc_chrdev_region(&nfc_dev_number, 0, NUM_OF_NFC, DEVICE_NAME) < 0) { + LOG("fail to alloc_chrdev_region\n"); + return -1; + } + + nfc_class = class_create(THIS_MODULE, DEVICE_NAME); + + if (nfc_class == NULL ) { + unregister_chrdev_region(nfc_dev_number, NUM_OF_NFC); + return -1; + } + + for (i = 0; i < NUM_OF_NFC; i++) { + pnfc_info[i] = kmalloc(sizeof(struct virtnfc_info), GFP_KERNEL); + + if (!pnfc_info[i]) { + LOG("Bad malloc\n"); + return -ENOMEM; + } + + sprintf(pnfc_info[i]->name, "%s%d", DEVICE_NAME, i); + + pnfc_info[i]->index = i; + pnfc_info[i]->guest_connected = false; + + cdev_init(&pnfc_info[i]->cdev, &nfc_fops); + pnfc_info[i]->cdev.owner = THIS_MODULE; + ret = cdev_add(&pnfc_info[i]->cdev, (nfc_dev_number + i), 1); + + /* init wait queue */ + init_waitqueue_head(&pnfc_info[i]->waitqueue); + spin_lock_init(&pnfc_info[i]->inbuf_lock); + spin_lock_init(&pnfc_info[i]->outvq_lock); + + if (ret == -1) { + LOG("Bad cdev\n"); + return ret; + } + + device_create(nfc_class, NULL, (nfc_dev_number + i), NULL, "%s%d", + DEVICE_NAME, i); + } + + return 0; +} + + +static int nfc_probe(struct virtio_device* dev) { + int ret; + LOG("nfc_probe\n"); + vnfc = kmalloc(sizeof(struct virtio_nfc), GFP_KERNEL); + + INIT_LIST_HEAD(&vnfc->read_list); + + vnfc->vdev = dev; + dev->priv = vnfc; + + ret = init_device(); + if (ret) { + LOG("failed to init_device\n"); + return ret; + } + ret = init_vqs(vnfc); + if (ret) { + dev->config->del_vqs(dev); + kfree(vnfc); + dev->priv = NULL; + + LOG("failed to init_vqs\n"); + return ret; + } + + /* enable callback */ + virtqueue_enable_cb(vnfc->rvq); + virtqueue_enable_cb(vnfc->svq); + + + memset(&vnfc->read_msginfo, 0x00, sizeof(vnfc->read_msginfo)); + sg_set_buf(vnfc->sg_read, &vnfc->read_msginfo, NFC_MAX_BUF_SIZE); + + memset(&vnfc->send_msginfo, 0x00, sizeof(vnfc->send_msginfo)); + sg_set_buf(vnfc->sg_send, &vnfc->send_msginfo, NFC_MAX_BUF_SIZE); + + + sg_init_one(vnfc->sg_read, &vnfc->read_msginfo, sizeof(vnfc->read_msginfo)); + sg_init_one(vnfc->sg_send, &vnfc->send_msginfo, sizeof(vnfc->send_msginfo)); + + + + LOG("NFC Probe completed"); + return 0; +} + +static void nfc_remove(struct virtio_device* dev) +{ + struct virtio_nfc* _nfc = dev->priv; + if (!_nfc) { + LOG("nfc is NULL\n"); + return; + } + + dev->config->reset(dev); + dev->config->del_vqs(dev); + + kfree(_nfc); + + LOG("driver is removed.\n"); +} + +MODULE_DEVICE_TABLE(virtio, id_table); + +static struct virtio_driver virtio_nfc_driver = { + .driver = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE , + }, + .id_table = id_table, + .probe = nfc_probe, + .remove = nfc_remove, +}; + +static int __init nfc_init(void) +{ + LOG("NFC driver initialized.\n"); + + return register_virtio_driver(&virtio_nfc_driver); +} + +static void __exit nfc_exit(void) +{ + int i; + + unregister_chrdev_region(nfc_dev_number, NUM_OF_NFC); + + for (i = 0; i < NUM_OF_NFC; i++) { + device_destroy(nfc_class, MKDEV(MAJOR(nfc_dev_number), i)); + cdev_del(&pnfc_info[i]->cdev); + kfree(pnfc_info[i]); + } + + /*device_destroy(nfc_class, nfc_dev_number);*/ + + class_destroy(nfc_class); + + unregister_virtio_driver(&virtio_nfc_driver); + + LOG("NFC driver is destroyed.\n"); +} + +module_init(nfc_init); +module_exit(nfc_exit); + +MODULE_LICENSE("GPL2"); +MODULE_AUTHOR("Munkyu Im "); +MODULE_DESCRIPTION("Emulator Virtio NFC Driver"); + diff --git a/drivers/maru/maru_virtio_rotary.c b/drivers/maru/maru_virtio_rotary.c new file mode 100644 index 0000000..73056d0 --- /dev/null +++ b/drivers/maru/maru_virtio_rotary.c @@ -0,0 +1,312 @@ +/* + * Maru Virtio Rotary Device Driver + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * Jinhyung Jo + * Sangho Park + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * Contributors: + * - S-Core Co., Ltd + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL2"); +MODULE_AUTHOR("Jinhyung Jo "); +MODULE_DESCRIPTION("Emulator Virtio Rotary Driver"); + +#define DRIVER_NAME "tizen_detent" +#define VR_LOG(log_level, fmt, ...) \ + printk(log_level "%s: " fmt, DRIVER_NAME, ##__VA_ARGS__) + +#define ROTARY_EVENT_MAX 32 +struct rotary_event { + int32_t delta; + int32_t type; +}; + +#define ROTARY_EVENT_BUF_SIZE \ + (ROTARY_EVENT_MAX * sizeof(struct rotary_event)) + +struct virtio_rotary { + struct virtio_device *vdev; + struct virtqueue *vq; + struct input_dev *idev; + struct mutex mutex; + + struct rotary_event event[ROTARY_EVENT_MAX]; +}; + +struct virtio_rotary *vrtr; + +static int last_pos; /* 0 ~ 360 */ +static int last_detent; + +static struct virtio_device_id id_table[] = { + { VIRTIO_ID_ROTARY, VIRTIO_DEV_ANY_ID }, + { 0 }, +}; + +#define DETENT_UNIT (15) +#define REMAINDER(n, div) ({ \ +typeof(n) _n = (n) % (div); \ + if (_n < 0) { \ + _n += (div); \ + } \ + _n; \ +}) + +static int add_inbuf(struct virtqueue *vq, struct rotary_event *event) +{ + struct scatterlist sg[1]; + int ret; + + memset(event, 0x00, ROTARY_EVENT_BUF_SIZE); + sg_init_one(sg, event, ROTARY_EVENT_BUF_SIZE); + + ret = virtqueue_add_inbuf(vq, sg, 1, event, GFP_ATOMIC); + virtqueue_kick(vq); + + return ret; +} + +static int get_rotary_pos(int value) +{ + return REMAINDER(value, 360); +} + +static void vq_rotary_handler(struct virtqueue *vq) +{ + int err = 0; + struct rotary_event *data; + unsigned int len = 0; + size_t j, num_event; + + data = (struct rotary_event *)virtqueue_get_buf(vq, &len); + if (!data) { + VR_LOG(KERN_ERR, "there is no available buffer\n"); + return; + } + + num_event = (size_t)len / sizeof(struct rotary_event); + VR_LOG(KERN_DEBUG, "len(%u), num_event(%zu)\n", len, num_event); + + for (j = 0; j < num_event; j++) { + int i = 0; + int pos = 0; + int value = 0; + struct rotary_event event; + + memcpy(&event, &data[j], + sizeof(struct rotary_event)); + + event.delta %= 360; + if (event.delta == 0) + continue; + + pos = get_rotary_pos(last_pos + event.delta); + + VR_LOG(KERN_DEBUG, + "rotary event: idx(%zu), event.delta(%d), pos(%d)\n", + j, event.delta, pos); + + for (i = 1; i <= abs(event.delta); i++) { + value = (event.delta > 0) ? last_pos + i : last_pos - i; + if ((value % DETENT_UNIT) == 0) { + input_report_rel(vrtr->idev, REL_WHEEL, 1); + input_sync(vrtr->idev); + if (get_rotary_pos(value) != last_detent) { + last_detent = get_rotary_pos(value); + if (event.delta > 0) { /* CW */ + input_report_rel(vrtr->idev, + REL_WHEEL, 2); + } else { /* CCW */ + input_report_rel(vrtr->idev, + REL_WHEEL, -2); + } + } else { + input_report_rel(vrtr->idev, + REL_WHEEL, -1); + } + input_sync(vrtr->idev); + + VR_LOG(KERN_INFO, + "rotary event: delta(%d), detent(%d)\n", + event.delta, last_detent); + } + } + last_pos = pos; + } + + err = add_inbuf(vrtr->vq, vrtr->event); + if (err < 0) { + VR_LOG(KERN_ERR, "failed to add buffer to virtqueue\n"); + return; + } + virtqueue_kick(vrtr->vq); +} + +static int input_rotary_open(struct input_dev *dev) +{ + VR_LOG(KERN_DEBUG, "input_rotary_open\n"); + return 0; +} + +static void input_rotary_close(struct input_dev *dev) +{ + VR_LOG(KERN_DEBUG, "input_rotary_close\n"); +} + +static int virtio_rotary_probe(struct virtio_device *vdev) +{ + int ret = 0; + + if (vrtr) { + VR_LOG(KERN_ERR, "driver is already exist\n"); + return -EINVAL; + } + + vdev->priv = vrtr = kzalloc(sizeof(struct virtio_rotary), GFP_KERNEL); + if (!vrtr) + return -ENOMEM; + + vrtr->vdev = vdev; + mutex_init(&vrtr->mutex); + + vrtr->vq = virtio_find_single_vq(vrtr->vdev, + vq_rotary_handler, + "maru-rotary-vq"); + if (IS_ERR(vrtr->vq)) { + ret = PTR_ERR(vrtr->vq); + kfree(vrtr); + vdev->priv = NULL; + return ret; + } + + /* register for input device */ + vrtr->idev = input_allocate_device(); + if (!vrtr->idev) { + VR_LOG(KERN_ERR, "failed to allocate a input device\n"); + kfree(vrtr); + vdev->priv = NULL; + return -ENOMEM; + } + + vrtr->idev->name = DRIVER_NAME; + vrtr->idev->dev.parent = &vdev->dev; + vrtr->idev->id.vendor = 0x0001; + vrtr->idev->id.product = 0x0001; + vrtr->idev->id.version = 0x0100; + + input_set_drvdata(vrtr->idev, vrtr); + vrtr->idev->open = input_rotary_open; + vrtr->idev->close = input_rotary_close; + + input_set_capability(vrtr->idev, EV_REL, REL_X); + input_set_capability(vrtr->idev, EV_REL, REL_Y); + input_set_capability(vrtr->idev, EV_REL, REL_WHEEL); + input_set_capability(vrtr->idev, EV_KEY, BTN_LEFT); + + ret = input_register_device(vrtr->idev); + if (ret) { + VR_LOG(KERN_ERR, "failed to register a input device\n"); + input_free_device(vrtr->idev); + kfree(vrtr); + vdev->priv = NULL; + return ret; + } + + ret = add_inbuf(vrtr->vq, vrtr->event); + if (ret < 0) { + VR_LOG(KERN_ERR, "failed to add buffer to virtqueue\n"); + input_unregister_device(vrtr->idev); + input_free_device(vrtr->idev); + kfree(vrtr); + vdev->priv = NULL; + return ret; + } + + VR_LOG(KERN_INFO, "driver probe done\n"); + + return 0; +} + +static void virtio_rotary_remove(struct virtio_device *vdev) +{ + if (!vrtr) { + VR_LOG(KERN_ERR, "rotary instance is NULL\n"); + return; + } + + vdev->config->reset(vdev); + vdev->config->del_vqs(vdev); + + input_unregister_device(vrtr->idev); + input_free_device(vrtr->idev); + + kfree(vrtr); + vrtr = NULL; + vdev->priv = NULL; + VR_LOG(KERN_INFO, "driver is removed\n"); +} + +MODULE_DEVICE_TABLE(virtio, id_table); + +static struct virtio_driver virtio_rotary_driver = { + .driver = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE, + }, + .id_table = id_table, + .probe = virtio_rotary_probe, + .remove = virtio_rotary_remove, +#if 0 +#ifdef CONFIG_PM + .freeze = virtio_rotary_freeze, + .restore = virtio_rotary_restore, +#endif +#endif +}; + +static int __init virtio_rotary_init(void) +{ + VR_LOG(KERN_INFO, "driver is initialized\n"); + return register_virtio_driver(&virtio_rotary_driver); +} + +static void __exit virtio_rotary_exit(void) +{ + VR_LOG(KERN_INFO, "driver is destroyed\n"); + unregister_virtio_driver(&virtio_rotary_driver); +} + +module_init(virtio_rotary_init); +module_exit(virtio_rotary_exit); diff --git a/drivers/maru/maru_virtio_tablet.c b/drivers/maru/maru_virtio_tablet.c new file mode 100644 index 0000000..225db24 --- /dev/null +++ b/drivers/maru/maru_virtio_tablet.c @@ -0,0 +1,277 @@ +/* + * Maru Virtio Tablet Device Driver + * + * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * Jinhyung Jo + * SeokYeon Hwang + * Sungmin Ha + * Sangho Park + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributors: + * - S-Core Co., Ltd + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL2"); +MODULE_AUTHOR("Sungmin Ha "); +MODULE_DESCRIPTION("Emulator Virtio Tablet driver"); + +#define DEVICE_NAME "virtio-tablet" +#define MAX_BUF_COUNT 25 + +/* This structure must match the qemu definitions */ +typedef struct EmulTabletEvent { + uint8_t event_type; + uint32_t x; + uint32_t y; + uint32_t btn; + uint32_t btn_status; +} EmulTabletEvent; + +#define MAX_EVENT_BUF_SIZE (MAX_BUF_COUNT * sizeof(EmulTabletEvent)) + +typedef struct virtio_tablet +{ + struct virtio_device *vdev; + struct virtqueue *vq; + struct input_dev *idev; + + struct scatterlist sg[1]; + struct EmulTabletEvent vbuf[MAX_BUF_COUNT]; + + struct mutex event_mutex; +} virtio_tablet; + +virtio_tablet *vtb; + +static struct virtio_device_id id_table[] = { + { VIRTIO_ID_TABLET, VIRTIO_DEV_ANY_ID }, + { 0 }, +}; + +/* keep it consistent with emulator-skin definition */ +enum { + INPUT_MOVE = 1, + INPUT_BTN = 2, +}; + +typedef enum InputButton +{ + INPUT_BUTTON_LEFT = 0, + INPUT_BUTTON_MIDDLE = 1, + INPUT_BUTTON_RIGHT = 2, + INPUT_BUTTON_WHEEL_UP = 3, + INPUT_BUTTON_WHEEL_DOWN = 4, + INPUT_BUTTON_MAX = 5, +} InputButton; + +static int err = 0; +static unsigned int index = 0; + +/** +* @brief : callback for virtqueue +*/ +static void vq_tablet_callback(struct virtqueue *vq) +{ + struct EmulTabletEvent *token = NULL; + unsigned int len = 0; + size_t i, num_event; + + token = (struct EmulTabletEvent *)virtqueue_get_buf(vq, &len); + if (!token) { + printk(KERN_ERR "there is no available buffer\n"); + return; + } + + num_event = (size_t)len / sizeof(struct EmulTabletEvent); + + for (i = 0; i < num_event; i++) { + struct EmulTabletEvent event; + + memcpy(&event, &token[i], sizeof(event)); + if (event.event_type == 0) + continue; + + if (event.event_type == INPUT_BTN) { + /* TODO: Implementation for + * the remaining events are required. */ + if (event.btn == INPUT_BUTTON_LEFT) { + /* 0x90001 is scan code. + * (logitech left click) */ + input_event(vtb->idev, EV_MSC, MSC_SCAN, + 0x90001); + input_event(vtb->idev, EV_KEY, BTN_LEFT, + event.btn_status); + input_sync(vtb->idev); + } + } else if (event.event_type == INPUT_MOVE) { + input_event(vtb->idev, EV_ABS, ABS_X, + event.x); + input_event(vtb->idev, EV_ABS, ABS_Y, + event.y); + input_sync(vtb->idev); + } else { + printk(KERN_ERR "Unknown event type\n"); + break; + } + } + memset(vtb->vbuf, 0x00, MAX_EVENT_BUF_SIZE); + + err = virtqueue_add_inbuf(vtb->vq, vtb->sg, 1, + (void *)vtb->vbuf, GFP_ATOMIC); + if (err < 0) { + printk(KERN_ERR "failed to add buffer to virtqueue\n"); + return; + } + + virtqueue_kick(vtb->vq); +} + +static int virtio_tablet_probe(struct virtio_device *vdev) +{ + int ret = 0; + + printk(KERN_INFO "virtio tablet driver is probed\n"); + + /* init virtio */ + vdev->priv = vtb = kmalloc(sizeof(*vtb), GFP_KERNEL); + if (!vtb) { + return -ENOMEM; + } + + memset(&vtb->vbuf, 0x00, sizeof(vtb->vbuf)); + vtb->vdev = vdev; + vtb->vq = virtio_find_single_vq(vtb->vdev, + vq_tablet_callback, "virtio-tablet-vq"); + if (IS_ERR(vtb->vq)) { + ret = PTR_ERR(vtb->vq); + kfree(vtb); + vdev->priv = NULL; + return ret; + } + + /* enable callback */ + virtqueue_enable_cb(vtb->vq); + + /* prepare the buffers */ + sg_init_one(vtb->sg, vtb->vbuf, MAX_EVENT_BUF_SIZE); + + err = virtqueue_add_inbuf(vtb->vq, vtb->sg, 1, + (void *)vtb->vbuf, GFP_ATOMIC); + + /* register for input device */ + vtb->idev = input_allocate_device(); + if (!vtb->idev) { + printk(KERN_ERR "failed to allocate a input tablet device\n"); + ret = -1; + kfree(vtb); + vdev->priv = NULL; + return ret; + } + + vtb->idev->name = "Maru VirtIO Tablet"; + vtb->idev->dev.parent = &(vdev->dev); + + input_set_drvdata(vtb->idev, vtb); + + vtb->idev->evbit[0] = BIT_MASK(EV_KEY) + | BIT_MASK(EV_REL) + | BIT_MASK(EV_ABS) + | BIT_MASK(EV_MSC); + + /* 32767 is max size of usbdevice tablet. */ + input_abs_set_max(vtb->idev, ABS_X, 32767); + input_abs_set_max(vtb->idev, ABS_Y, 32767); + + set_bit(BTN_LEFT, vtb->idev->keybit); + set_bit(BTN_RIGHT, vtb->idev->keybit); + set_bit(BTN_MIDDLE, vtb->idev->keybit); + set_bit(REL_WHEEL, vtb->idev->relbit); + set_bit(ABS_X, vtb->idev->absbit); + set_bit(ABS_Y, vtb->idev->absbit); + set_bit(MSC_SCAN, vtb->idev->mscbit); + + ret = input_register_device(vtb->idev); + if (ret) { + printk(KERN_ERR "input tablet driver cannot registered\n"); + ret = -1; + input_free_device(vtb->idev); + kfree(vtb); + vdev->priv = NULL; + return ret; + } + + virtqueue_kick(vtb->vq); + index = 0; + + return 0; +} + +static void virtio_tablet_remove(struct virtio_device *vdev) +{ + virtio_tablet *vtk = NULL; + + printk(KERN_INFO "virtio tablet driver is removed\n"); + + vtk = vdev->priv; + + vdev->config->reset(vdev); /* reset device */ + vdev->config->del_vqs(vdev); /* clean up the queues */ + + input_unregister_device(vtk->idev); + + kfree(vtk); +} + +MODULE_DEVICE_TABLE(virtio, id_table); + +static struct virtio_driver virtio_tablet_driver = { + .driver.name = KBUILD_MODNAME, + .driver.owner = THIS_MODULE, + .id_table = id_table, + .probe = virtio_tablet_probe, + .remove = virtio_tablet_remove, +}; + +static int __init virtio_tablet_init(void) +{ + printk(KERN_INFO "virtio tablet device is initialized\n"); + return register_virtio_driver(&virtio_tablet_driver); +} + +static void __exit virtio_tablet_exit(void) +{ + printk(KERN_INFO "virtio tablet device is destroyed\n"); + unregister_virtio_driver(&virtio_tablet_driver); +} + +module_init(virtio_tablet_init); +module_exit(virtio_tablet_exit); diff --git a/drivers/maru/maru_virtio_touchscreen.c b/drivers/maru/maru_virtio_touchscreen.c new file mode 100644 index 0000000..2619b49 --- /dev/null +++ b/drivers/maru/maru_virtio_touchscreen.c @@ -0,0 +1,482 @@ +/* + * Maru Virtio Touchscreen Device Driver + * + * Copyright (c) 2012 - 2013 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * GiWoong Kim + * SeokYeon Hwang + * YeongKyoon Lee + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributors: + * - S-Core Co., Ltd + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL2"); +MODULE_AUTHOR("GiWoong Kim "); +MODULE_DESCRIPTION("Emulator Virtio Touchscreen driver"); + + +#define DEVICE_NAME "virtio-touchscreen" + +/* This structure must match the qemu definitions */ +typedef struct EmulTouchEvent { + uint16_t x, y, z; + uint8_t state; +} EmulTouchEvent; +EmulTouchEvent *event; + +typedef struct virtio_touchscreen +{ + struct virtio_device *vdev; + struct virtqueue *vq; + struct input_dev *idev; + + /* The thread servicing the touchscreen */ + struct task_struct *thread; +} virtio_touchscreen; +virtio_touchscreen *vt; + + +#define MAX_TRKID 10 +#define TOUCHSCREEN_RESOLUTION_X 5040 +#define TOUCHSCREEN_RESOLUTION_Y 3780 +#define ABS_PRESSURE_MAX 255 + +#define MAX_BUF_COUNT MAX_TRKID +struct scatterlist sg[MAX_BUF_COUNT]; +EmulTouchEvent vbuf[MAX_BUF_COUNT]; + +static struct virtio_device_id id_table[] = { + { VIRTIO_ID_TOUCHSCREEN, VIRTIO_DEV_ANY_ID }, + { 0 }, +}; + + +#if 0 +/** + * @brief : event polling + */ +static int run_touchscreen(void *_vtouchscreen) +{ + virtio_touchscreen *vt = NULL; + int err = 0; + unsigned int len = 0; /* not used */ + unsigned int index = 0; + unsigned int recv_index = 0; + unsigned int id = 0; /* finger id */ + + struct input_dev *input_dev = NULL; + EmulTouchEvent *event = NULL; + + vt = (virtio_touchscreen *)_vtouchscreen; + input_dev = vt->idev; + + sg_init_table(sg, MAX_BUF_COUNT); + + for (index = 0; index < MAX_BUF_COUNT; index++) { + sg_set_buf(&sg[index], &vbuf[index], sizeof(EmulTouchEvent)); + + err = virtqueue_add_inbuf(vt->vq, sg, index + 1, (void *)index + 1, GFP_ATOMIC); + if (err < 0) { + printk(KERN_ERR "failed to add buf\n"); + } + } + virtqueue_kick(vt->vq); + + index = 0; + + while (!kthread_should_stop()) + { + while ((recv_index = (unsigned int)virtqueue_get_buf(vt->vq, &len)) == 0) { + cpu_relax(); + } + + do { + event = &vbuf[recv_index - 1]; +#if 0 + printk(KERN_INFO "touch x=%d, y=%d, z=%d, state=%d, recv_index=%d\n", + event->x, event->y, event->z, event->state, recv_index); +#endif + + id = event->z; + + /* Multi-touch Protocol is B */ + if (event->state != 0) + { /* pressed */ + input_mt_slot(input_dev, id); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, true); + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, 10); + input_report_abs(input_dev, ABS_MT_POSITION_X, event->x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, event->y); + } + else + { /* released */ + input_mt_slot(input_dev, id); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false); + } + + input_sync(input_dev); + + /* expose buffer to other end */ + err = virtqueue_add_inbuf(vt->vq, sg, recv_index, (void *)recv_index, GFP_ATOMIC); + if (err < 0) { + printk(KERN_ERR "failed to add buf\n"); + } + + recv_index = (unsigned int)virtqueue_get_buf(vt->vq, &len); + if (recv_index == 0) { + break; + } + } while(true); + + virtqueue_kick(vt->vq); + } + + printk(KERN_INFO "virtio touchscreen thread is stopped\n"); + + return 0; +} +#endif + + +int err; +unsigned int len; /* not used */ +size_t buf_index; +size_t recv_index; +unsigned int finger_id; /* finger id */ + +/** + * @brief : callback for virtqueue + */ +static void vq_touchscreen_callback(struct virtqueue *vq) +{ +#if 0 + printk(KERN_INFO "vq touchscreen callback\n"); +#endif + + recv_index = (size_t)virtqueue_get_buf(vt->vq, &len); + if (recv_index == 0) { + printk(KERN_ERR "failed to get buffer\n"); + return; + } + + do { + event = &vbuf[recv_index - 1]; + +#if 0 + printk(KERN_INFO "touch x=%d, y=%d, z=%d, state=%d, recv_index=%d\n", + event->x, event->y, event->z, event->state, recv_index); +#endif + + finger_id = event->z; + + if (finger_id < MAX_TRKID) { + /* Multi-touch Protocol is B */ + + if (event->state != 0) + { /* pressed */ + input_mt_slot(vt->idev, finger_id); + input_mt_report_slot_state(vt->idev, MT_TOOL_FINGER, true); + input_report_abs(vt->idev, ABS_MT_TOUCH_MAJOR, 10); + input_report_abs(vt->idev, ABS_MT_POSITION_X, event->x); + input_report_abs(vt->idev, ABS_MT_POSITION_Y, event->y); + } + else + { /* released */ + input_mt_slot(vt->idev, finger_id); + input_mt_report_slot_state(vt->idev, MT_TOOL_FINGER, false); + } + + input_sync(vt->idev); + } else { + printk(KERN_ERR "%d is an invalid finger id!\n", finger_id); + } + + /* expose buffer to other end */ + err = virtqueue_add_inbuf(vt->vq, sg, + (unsigned int)recv_index, (void *)recv_index, GFP_ATOMIC); + + if (err < 0) { + printk(KERN_ERR "failed to add buffer!\n"); + } + + recv_index = (size_t)virtqueue_get_buf(vt->vq, &len); + if (recv_index == 0) { + break; + } + } while(true); + + virtqueue_kick(vt->vq); +} + +static int virtio_touchscreen_open(struct inode *inode, struct file *file) +{ + printk(KERN_INFO "virtio touchscreen device is opened\n"); + return 0; +} + +static int virtio_touchscreen_release(struct inode *inode, struct file *file) +{ + printk(KERN_INFO "virtio touchscreen device is closed\n"); + return 0; +} + +static int input_touchscreen_open(struct input_dev *dev) +{ + printk(KERN_INFO "input touchscreen device is opened\n"); + return 0; +} + +static void input_touchscreen_close(struct input_dev *dev) +{ + printk(KERN_INFO "input touchscreen device is closed\n"); +} + +struct file_operations virtio_touchscreen_fops = { + .owner = THIS_MODULE, + .open = virtio_touchscreen_open, + .release = virtio_touchscreen_release, +}; + +extern char *saved_command_line; +#define VM_RESOLUTION_KEY "vm_resolution=" + +static int virtio_touchscreen_probe(struct virtio_device *vdev) +{ + unsigned long width = 0; + unsigned long height = 0; + char *cmdline = NULL; + char *value = NULL; + char *tmp = NULL; + int err = 0; + int ret = 0; + + printk(KERN_INFO "virtio touchscreen driver is probed\n"); + + /* init virtio */ + vdev->priv = vt = kmalloc(sizeof(*vt), GFP_KERNEL); + if (!vt) { + return -ENOMEM; + } + + vt->vdev = vdev; + + vt->vq = virtio_find_single_vq(vt->vdev, + vq_touchscreen_callback, "virtio-touchscreen-vq"); + if (IS_ERR(vt->vq)) { + ret = PTR_ERR(vt->vq); + + kfree(vt); + vdev->priv = NULL; + return ret; + } + + /* enable callback */ + virtqueue_enable_cb(vt->vq); + + sg_init_table(sg, MAX_BUF_COUNT); + + /* prepare the buffers */ + for (buf_index = 0; buf_index < MAX_BUF_COUNT; buf_index++) { + sg_set_buf(&sg[buf_index], &vbuf[buf_index], sizeof(EmulTouchEvent)); + + err = virtqueue_add_inbuf(vt->vq, sg, + buf_index + 1, (void *)buf_index + 1, GFP_ATOMIC); + + if (err < 0) { + printk(KERN_ERR "failed to add buffer\n"); + + kfree(vt); + vdev->priv = NULL; + return ret; + } + } + + cmdline = kzalloc(strlen(saved_command_line) + 1, GFP_KERNEL); + if (cmdline) { + /* get VM resolution */ + strcpy(cmdline, saved_command_line); + tmp = strstr(cmdline, VM_RESOLUTION_KEY); + + if (tmp != NULL) { + tmp += strlen(VM_RESOLUTION_KEY); + + value = strsep(&tmp, "x"); + err = kstrtoul(value, 10, &width); + if (err) { + printk(KERN_WARNING "vm width option is not defined\n"); + width = 0; + } + + value = strsep(&tmp, " "); + err = kstrtoul(value, 10, &height); + if (err) { + printk(KERN_WARNING "vm height option is not defined\n"); + height = 0; + } + } + + kfree(cmdline); + } + + if (width != 0 && height != 0) { + printk(KERN_INFO "emul resolution : %lux%lu\n", width, height); + } + + /* register for input device */ + vt->idev = input_allocate_device(); + if (!vt->idev) { + printk(KERN_ERR "failed to allocate a input touchscreen device\n"); + ret = -1; + + kfree(vt); + vdev->priv = NULL; + return ret; + } + + vt->idev->name = "Maru Virtio Touchscreen"; + vt->idev->dev.parent = &(vdev->dev); + + input_set_drvdata(vt->idev, vt); + vt->idev->open = input_touchscreen_open; + vt->idev->close = input_touchscreen_close; + + vt->idev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + vt->idev->absbit[BIT_WORD(ABS_MISC)] |= BIT_MASK(ABS_MISC); + vt->idev->keybit[BIT_WORD(BTN_TOUCH)] |= BIT_MASK(BTN_TOUCH); + + input_mt_init_slots(vt->idev, MAX_TRKID, 0); + + input_set_abs_params(vt->idev, ABS_X, 0, + width, 0, 0); + input_set_abs_params(vt->idev, ABS_Y, 0, + height, 0, 0); + input_set_abs_params(vt->idev, ABS_MT_TRACKING_ID, 0, + MAX_TRKID, 0, 0); + input_set_abs_params(vt->idev, ABS_MT_TOUCH_MAJOR, 0, + ABS_PRESSURE_MAX, 0, 0); + input_set_abs_params(vt->idev, ABS_MT_POSITION_X, 0, + width, 0, 0); + input_set_abs_params(vt->idev, ABS_MT_POSITION_Y, 0, + height, 0, 0); + + ret = input_register_device(vt->idev); + if (ret) { + printk(KERN_ERR "input touchscreen driver cannot registered\n"); + ret = -1; + + input_mt_destroy_slots(vt->idev); + input_free_device(vt->idev); + kfree(vt); + vdev->priv = NULL; + return ret; + } + +#if 0 /* using a thread */ + + /* Responses from the hypervisor occur through the get_buf function */ + vt->thread = kthread_run(run_touchscreen, vt, "vtouchscreen"); + if (IS_ERR(vt->thread)) { + printk(KERN_ERR "unable to start the virtio touchscreen thread\n"); + ret = PTR_ERR(vt->thread); + + input_mt_destroy_slots(vt->idev); + input_free_device(vt->idev); + kfree(vt); + vdev->priv = NULL; + return ret; + } +#else /* using a callback */ + + virtqueue_kick(vt->vq); + + buf_index = 0; + +#endif + + return 0; +} + +static void virtio_touchscreen_remove(struct virtio_device *vdev) +{ + virtio_touchscreen *vts = NULL; + + printk(KERN_INFO "virtio touchscreen driver is removed\n"); + + vts = vdev->priv; + + kthread_stop(vts->thread); + + vdev->config->reset(vdev); /* reset device */ + vdev->config->del_vqs(vdev); /* clean up the queues */ + + input_unregister_device(vts->idev); + input_mt_destroy_slots(vts->idev); + + kfree(vts); +} + +MODULE_DEVICE_TABLE(virtio, id_table); + +static struct virtio_driver virtio_touchscreen_driver = { + .driver.name = KBUILD_MODNAME, + .driver.owner = THIS_MODULE, + .id_table = id_table, + .probe = virtio_touchscreen_probe, + .remove = virtio_touchscreen_remove, +#if 0 + .feature_table = features, + .feature_table_size = ARRAY_SIZE(features), + .config_changed = +#ifdef CONFIG_PM + .freeze = + .restore = +#endif +#endif +}; + +static int __init virtio_touchscreen_init(void) +{ + printk(KERN_INFO "virtio touchscreen device is initialized\n"); + return register_virtio_driver(&virtio_touchscreen_driver); +} + +static void __exit virtio_touchscreen_exit(void) +{ + printk(KERN_INFO "virtio touchscreen device is destroyed\n"); + unregister_virtio_driver(&virtio_touchscreen_driver); +} + +module_init(virtio_touchscreen_init); +module_exit(virtio_touchscreen_exit); + diff --git a/drivers/maru/maru_virtio_vmodem.c b/drivers/maru/maru_virtio_vmodem.c new file mode 100644 index 0000000..1acea48 --- /dev/null +++ b/drivers/maru/maru_virtio_vmodem.c @@ -0,0 +1,554 @@ +/* + * Maru Virtio Virtual Modem Device Driver + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * Sooyoung Ha + * SeokYeon Hwang + * Sangho Park + * YeongKyoon Lee + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributors: + * - S-Core Co., Ltd + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "VMODEM" + +#define LOG(fmt, ...) \ + printk(KERN_ERR "%s: " fmt, DRIVER_NAME, ##__VA_ARGS__) + +#define NUM_OF_VMODEM 2 +#define DEVICE_NAME "vmodem" + +MODULE_LICENSE("GPL2"); +MODULE_AUTHOR("Sooyoung Ha "); +MODULE_DESCRIPTION("Emulator Virtio Virtual Modem Device Driver"); + +#define __MAX_BUF_SIZE 1024 + +enum +{ + route_qemu = 0, + route_control_server = 1, + route_monitor = 2 +}; + +typedef unsigned int CSCliSN; + +struct msg_info { + char buf[__MAX_BUF_SIZE]; + + uint32_t route; + uint32_t use; + uint16_t count; + uint16_t index; + + CSCliSN cclisn; +}; + +#define SIZEOF_MSG_INFO sizeof(struct msg_info) + +struct msg_buf { + struct msg_info msg; + struct list_head list; +}; + +#define SIZEOF_MSG_BUF sizeof(struct msg_buf) + +enum { + EVID_READ = 0, EVID_WRITE = 1 +}; + +struct virtvmodem_info { + + wait_queue_head_t waitqueue; + spinlock_t inbuf_lock; + spinlock_t outvq_lock; + + struct cdev cdev; + char name[10]; + + int index; + bool guest_connected; + +} *pvmodem_info[NUM_OF_VMODEM]; + +struct virtio_vmodem { + struct virtio_device* vdev; + struct virtqueue* rvq; + struct virtqueue* svq; + + struct msg_info read_msginfo; + struct msg_info send_msginfo; + + struct list_head read_list; + struct list_head write_list; + + struct scatterlist sg_read[2]; + struct scatterlist sg_send[2]; +}; +struct virtio_vmodem *vvmodem; + +static struct virtio_device_id id_table[] = { { VIRTIO_ID_VMODEM, + VIRTIO_DEV_ANY_ID }, { 0 }, }; + +static dev_t vmodem_dev_number; +static struct class* vmodem_class; + + +static void* __xmalloc(size_t size) +{ + void* p = kmalloc(size, GFP_KERNEL); + if (!p) { + return NULL; + } + return p; +} + +int make_vmodem_buf_and_kick(void) +{ + int ret; + memset(&vvmodem->read_msginfo, 0x00, sizeof(vvmodem->read_msginfo)); + ret = virtqueue_add_inbuf(vvmodem->rvq, vvmodem->sg_read, 1, &vvmodem->read_msginfo, + GFP_ATOMIC ); + if (ret < 0) { + LOG("failed to add buffer to virtqueue.(%d)\n", ret); + return ret; + } + + virtqueue_kick(vvmodem->rvq); + + return 0; +} + +static int add_inbuf(struct virtqueue *vq, struct msg_info *msg) +{ + struct scatterlist sg[1]; + int ret; + + sg_init_one(sg, msg, sizeof(struct msg_info)); + + ret = virtqueue_add_inbuf(vq, sg, 1, msg, GFP_ATOMIC); + virtqueue_kick(vq); + return ret; +} + +static bool has_readdata(struct virtvmodem_info *vvinfo) +{ + bool ret; + unsigned long flags; + + spin_lock_irqsave(&vvinfo->inbuf_lock, flags); + + ret = true; + + if (list_empty(&vvmodem->read_list)) { + ret = false; + } + + spin_unlock_irqrestore(&vvinfo->inbuf_lock, flags); + + return ret; +} + + +static int vmodem_open(struct inode* inode, struct file* filp) +{ + int i, ret; + struct virtvmodem_info* vmodem_info; + struct cdev *cdev = inode->i_cdev; + + vmodem_info = NULL; + LOG("vmodem_open\n"); + + for (i = 0; i < NUM_OF_VMODEM; i++) { + LOG("vmodem info index = %d, cdev dev = %d, inode dev = %d\n", + i, pvmodem_info[i]->cdev.dev, cdev->dev); + + if (pvmodem_info[i]->cdev.dev == cdev->dev) { + vmodem_info = pvmodem_info[i]; + break; + } + } + + filp->private_data = vmodem_info; + + vmodem_info->guest_connected = true; + + + ret = make_vmodem_buf_and_kick(); + if (ret < 0) { + return ret; + } + + LOG("vmodem is opened\n"); + return 0; +} + +static int vmodem_close(struct inode* i, struct file* filp) { + struct virtvmodem_info *vvinfo; + + vvinfo = filp->private_data; + vvinfo->guest_connected = false; + + LOG("vmodem is closed\n"); + return 0; +} + +static ssize_t vmodem_read(struct file *filp, char __user *ubuf, size_t len, + loff_t *f_pos) +{ + struct virtvmodem_info *vvinfo; + + ssize_t ret; + struct msg_buf* next; + unsigned long flags; + + vvinfo = filp->private_data; + + if (!has_readdata(vvinfo)) { + if (filp->f_flags & O_NONBLOCK) { + LOG("list is empty, return EAGAIN\n"); + return -EAGAIN; + } + return -EFAULT; + } + + next = list_first_entry(&vvmodem->read_list, struct msg_buf, list); + if (next == NULL) { + LOG("invliad list entry\n"); + return -EFAULT; + } + + ret = copy_to_user(ubuf, &next->msg, len); + + list_del(&next->list); + kfree(next); + + spin_lock_irqsave(&pvmodem_info[EVID_READ]->inbuf_lock, flags); + + + if (add_inbuf(vvmodem->rvq, &vvmodem->read_msginfo) < 0) { + LOG("failed add_buf\n"); + } + + spin_unlock_irqrestore(&pvmodem_info[EVID_READ]->inbuf_lock, flags); + + if (ret < 0) { + return -EFAULT; + } + + *f_pos += len; + + return len; +} + +static ssize_t vmodem_write(struct file *f, const char __user *ubuf, size_t len, + loff_t* f_pos) +{ + int err = 0; + ssize_t ret = 0; + + if (vvmodem == NULL) { + LOG("invalid vmodem handle\n"); + return 0; + } + + memset(&vvmodem->send_msginfo, 0, sizeof(vvmodem->send_msginfo)); + ret = copy_from_user(&vvmodem->send_msginfo, ubuf, sizeof(vvmodem->send_msginfo)); + + if (ret) { + LOG("vmodem's copy_from_user is failed\n"); + ret = -EFAULT; + return ret; + } + + + err = virtqueue_add_outbuf(vvmodem->svq, vvmodem->sg_send, 1, + &vvmodem->send_msginfo, GFP_ATOMIC); + + if (err < 0) { + LOG("failed to add buffer to virtqueue (err = %d)\n", err); + return 0; + } + + virtqueue_kick(vvmodem->svq); + LOG("vmodem kick the data to ecs\n"); + + return len; +} + +static unsigned int vmodem_poll(struct file *filp, poll_table *wait) +{ + struct virtvmodem_info *vvinfo; + unsigned int ret; + + vvinfo = filp->private_data; + poll_wait(filp, &vvinfo->waitqueue, wait); + + if (!vvinfo->guest_connected) { + return POLLHUP; + } + + ret = 0; + + if (has_readdata(vvinfo)) { + LOG("POLLIN | POLLRDNORM\n"); + ret |= POLLIN | POLLRDNORM; + } + + return ret; +} + +static struct file_operations vmodem_fops = { + .owner = THIS_MODULE, + .open = vmodem_open, + .release = vmodem_close, + .read = vmodem_read, + .write = vmodem_write, + .poll = vmodem_poll, +}; + + + +static void vmodem_recv_done(struct virtqueue *rvq) { + + unsigned int len; + unsigned long flags; + struct msg_info* _msg; + struct msg_buf* msgbuf; + + _msg = (struct msg_info*) virtqueue_get_buf(vvmodem->rvq, &len); + if (_msg == NULL ) { + LOG("failed to virtqueue_get_buf\n"); + return; + } + + do { + msgbuf = (struct msg_buf*) __xmalloc(SIZEOF_MSG_BUF); + memset(msgbuf, 0x00, sizeof(*msgbuf)); + memcpy(&(msgbuf->msg), _msg, sizeof(*_msg)); + + spin_lock_irqsave(&pvmodem_info[EVID_READ]->inbuf_lock, flags); + + list_add_tail(&msgbuf->list, &vvmodem->read_list); + + spin_unlock_irqrestore(&pvmodem_info[EVID_READ]->inbuf_lock, flags); + + wake_up_interruptible(&pvmodem_info[EVID_READ]->waitqueue); + + _msg = (struct msg_info*) virtqueue_get_buf(vvmodem->rvq, &len); + if (_msg == NULL) { + break; + } + + } while (true); +} + +static void vmodem_send_done(struct virtqueue *svq) { + unsigned int len = 0; + + LOG("vmodem send done\n"); + virtqueue_get_buf(svq, &len); +} + +static int init_vqs(struct virtio_vmodem *v_vmodem) { + struct virtqueue *vqs[2]; + vq_callback_t *callbacks[] = { vmodem_recv_done, vmodem_send_done }; + const char *names[] = { "vmodem_input", "vmodem_output" }; + int err; + + err = v_vmodem->vdev->config->find_vqs(v_vmodem->vdev, 2, vqs, callbacks, names); + if (err < 0) { + LOG("find_vqs of vmodem device is failed\n"); + return err; + } + + v_vmodem->rvq = vqs[0]; + v_vmodem->svq = vqs[1]; + + return 0; +} + +int init_vmodem_device(void) +{ + int i, ret; + + if (alloc_chrdev_region(&vmodem_dev_number, 0, NUM_OF_VMODEM, DEVICE_NAME) < 0) { + LOG("fail to alloc_chrdev_region\n"); + return -1; + } + + vmodem_class = class_create(THIS_MODULE, DEVICE_NAME); + + if (vmodem_class == NULL ) { + unregister_chrdev_region(vmodem_dev_number, NUM_OF_VMODEM); + return -1; + } + + for (i = 0; i < NUM_OF_VMODEM; i++) { + pvmodem_info[i] = kmalloc(sizeof(struct virtvmodem_info), GFP_KERNEL); + if (!pvmodem_info[i]) { + LOG("pvmodem_info malloc is failed\n"); + return -ENOMEM; + } + + sprintf(pvmodem_info[i]->name, "%s%d", DEVICE_NAME, i); + + pvmodem_info[i]->index = i; + pvmodem_info[i]->guest_connected = false; + + cdev_init(&pvmodem_info[i]->cdev, &vmodem_fops); + pvmodem_info[i]->cdev.owner = THIS_MODULE; + ret = cdev_add(&pvmodem_info[i]->cdev, (vmodem_dev_number + i), 1); + + init_waitqueue_head(&pvmodem_info[i]->waitqueue); + spin_lock_init(&pvmodem_info[i]->inbuf_lock); + spin_lock_init(&pvmodem_info[i]->outvq_lock); + + if (ret == -1) { + LOG("cdev_add(%d) is failed\n", i); + return ret; + } + + device_create(vmodem_class, NULL, (vmodem_dev_number + i), NULL, "%s%d", + DEVICE_NAME, i); + } + + return 0; +} + + +static int vmodem_probe(struct virtio_device* dev) { + int ret; + + vvmodem = kmalloc(sizeof(struct virtio_vmodem), GFP_KERNEL); + + INIT_LIST_HEAD(&vvmodem->read_list); + + vvmodem->vdev = dev; + dev->priv = vvmodem; + + ret = init_vmodem_device(); + if (ret) { + LOG("failed to init_vmodem_device\n"); + return ret; + } + ret = init_vqs(vvmodem); + if (ret) { + dev->config->del_vqs(dev); + kfree(vvmodem); + dev->priv = NULL; + + LOG("failed to init_vqs\n"); + return ret; + } + + virtqueue_enable_cb(vvmodem->rvq); + virtqueue_enable_cb(vvmodem->svq); + + memset(&vvmodem->read_msginfo, 0x00, sizeof(vvmodem->read_msginfo)); + sg_set_buf(vvmodem->sg_read, &vvmodem->read_msginfo, sizeof(struct msg_info)); + + memset(&vvmodem->send_msginfo, 0x00, sizeof(vvmodem->send_msginfo)); + sg_set_buf(vvmodem->sg_send, &vvmodem->send_msginfo, sizeof(struct msg_info)); + + sg_init_one(vvmodem->sg_read, &vvmodem->read_msginfo, sizeof(vvmodem->read_msginfo)); + sg_init_one(vvmodem->sg_send, &vvmodem->send_msginfo, sizeof(vvmodem->send_msginfo)); + + LOG("vmodem is probed"); + return 0; +} + +static void vmodem_remove(struct virtio_device* dev) +{ + struct virtio_vmodem* _vmodem = dev->priv; + if (!_vmodem) { + LOG("vmodem is NULL\n"); + return; + } + + dev->config->reset(dev); + dev->config->del_vqs(dev); + + kfree(_vmodem); + + LOG("driver is removed.\n"); +} + +MODULE_DEVICE_TABLE(virtio, id_table); + +static struct virtio_driver virtio_vmodem_driver = { + .driver = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE , + }, + .id_table = id_table, + .probe = vmodem_probe, + .remove = vmodem_remove, +}; + +static int __init vmodem_init(void) +{ + LOG("VMODEM driver initialized.\n"); + + return register_virtio_driver(&virtio_vmodem_driver); +} + +static void __exit vmodem_exit(void) +{ + int i; + + unregister_chrdev_region(vmodem_dev_number, NUM_OF_VMODEM); + + for (i = 0; i < NUM_OF_VMODEM; i++) { + device_destroy(vmodem_class, MKDEV(MAJOR(vmodem_dev_number), i)); + cdev_del(&pvmodem_info[i]->cdev); + kfree(pvmodem_info[i]); + } + + class_destroy(vmodem_class); + + unregister_virtio_driver(&virtio_vmodem_driver); + + LOG("VMODEM driver is destroyed.\n"); +} + +module_init(vmodem_init); +module_exit(vmodem_exit); + diff --git a/drivers/maru/sensors/Makefile b/drivers/maru/sensors/Makefile new file mode 100644 index 0000000..5a99146 --- /dev/null +++ b/drivers/maru/sensors/Makefile @@ -0,0 +1,11 @@ +obj-$(CONFIG_MARU_VIRTIO_SENSOR) += maru_virtio_sensor.o \ + maru_accel.o \ + maru_geo.o \ + maru_gyro.o \ + maru_light.o \ + maru_proxi.o \ + maru_rotation_vector.o \ + maru_haptic.o \ + maru_pressure.o \ + maru_uv.o \ + maru_hrm.o diff --git a/drivers/maru/sensors/maru_accel.c b/drivers/maru/sensors/maru_accel.c new file mode 100644 index 0000000..2faec4b --- /dev/null +++ b/drivers/maru/sensors/maru_accel.c @@ -0,0 +1,443 @@ +/* + * Maru Virtio Accelerometer Sensor Device Driver + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * Jinhyung Choi + * Sangho Park + * YeongKyoon Lee + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributors: + * - S-Core Co., Ltd + * + */ + +#include + +#include "maru_virtio_sensor.h" + +struct maru_accel_data { + struct input_dev *input_data; + struct delayed_work work; + + struct virtio_sensor* vs; + + atomic_t enable; + atomic_t poll_delay; +}; + +static struct device *accel_sensor_device; + +#ifdef SUPPORT_LEGACY_SENSOR +static struct device *l_accel_sensor_device; +#endif + +#define GRAVITY_CHANGE_UNIT 15322 +static short sensor_convert_data(int number) +{ + int temp; + temp = number / 64; + temp = temp * (SHRT_MAX / 2); + temp = temp / GRAVITY_CHANGE_UNIT; + return (short)temp; +} + +static void maru_accel_input_work_func(struct work_struct *work) { + + int poll_time = 200000000; + int enable = 0; + int ret = 0; + int accel_x, accel_y, accel_z; + short raw_x, raw_y, raw_z; + char sensor_data[__MAX_BUF_SENSOR]; + struct maru_accel_data *data = container_of((struct delayed_work *)work, + struct maru_accel_data, work); + + LOG(1, "maru_accel_input_work_func starts"); + + enable = atomic_read(&data->enable); + poll_time = atomic_read(&data->poll_delay); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + + if (enable) { + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_accel, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (!ret) { + sscanf(sensor_data, "%d,%d,%d", &accel_x, &accel_y, &accel_z); + LOG(1, "accel_set act %d, %d, %d", accel_x, accel_y, accel_z); + raw_x = sensor_convert_data(accel_x); + raw_y = sensor_convert_data(accel_y); + raw_z = sensor_convert_data(accel_z); + LOG(1, "accel_set raw %d, %d, %d", raw_x, raw_y, raw_z); + + if (raw_x == 0) { + raw_x = 1; + } + + if (raw_y == 0) { + raw_y = 1; + } + + if (raw_z == 0) { + raw_z = 1; + } + + input_report_rel(data->input_data, REL_X, raw_x); + input_report_rel(data->input_data, REL_Y, raw_y); + input_report_rel(data->input_data, REL_Z, raw_z); + input_sync(data->input_data); + } + } + + enable = atomic_read(&data->enable); + + LOG(1, "enable: %d, poll_time: %d", enable, poll_time); + if (enable) { + if (poll_time > 0) { + schedule_delayed_work(&data->work, nsecs_to_jiffies(poll_time)); + } else { + schedule_delayed_work(&data->work, 0); + } + } + + LOG(1, "maru_accel_input_work_func ends"); + +} + +static ssize_t maru_name_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", MARU_ACCEL_DEVICE_NAME); +} + +static ssize_t maru_vendor_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", MARU_SENSOR_DEVICE_VENDOR); +} + +static ssize_t maru_enable_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + char sensor_data[__MAX_BUF_SENSOR]; + int ret; + struct input_dev *input_data = to_input_dev(dev); + struct maru_accel_data *data = input_get_drvdata(input_data); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_accel_enable, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) + return sprintf(buf, "%d", -1); + + return sprintf(buf, "%s", sensor_data); +} + +static ssize_t maru_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct maru_accel_data *data = input_get_drvdata(input_data); + int value = simple_strtoul(buf, NULL, 10); + + if (value != 0 && value != 1) + return count; + + mutex_lock(&data->vs->vqlock); + set_sensor_data(sensor_type_accel_enable, buf); + mutex_unlock(&data->vs->vqlock); + + if (value) { + if (atomic_read(&data->enable) != 1) { + atomic_set(&data->enable, 1); + schedule_delayed_work(&data->work, 0); + + } + } else { + if (atomic_read(&data->enable) != 0) { + atomic_set(&data->enable, 0); + cancel_delayed_work(&data->work); + } + } + + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static ssize_t maru_poll_delay_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + char sensor_data[__MAX_BUF_SENSOR]; + int ret; + struct input_dev *input_data = to_input_dev(dev); + struct maru_accel_data *data = input_get_drvdata(input_data); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_accel_delay, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) + return sprintf(buf, "%d", -1); + + return sprintf(buf, "%s", sensor_data); +} + +static ssize_t maru_poll_delay_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct maru_accel_data *data = input_get_drvdata(input_data); + int value = simple_strtoul(buf, NULL, 10); + + if (value < __MIN_DELAY_SENSOR) + return count; + + mutex_lock(&data->vs->vqlock); + set_sensor_data(sensor_type_accel_delay, buf); + mutex_unlock(&data->vs->vqlock); + atomic_set(&data->poll_delay, value); + + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static struct device_attribute dev_attr_sensor_name = + __ATTR(name, S_IRUGO, maru_name_show, NULL); + +static struct device_attribute dev_attr_sensor_vendor = + __ATTR(vendor, S_IRUGO, maru_vendor_show, NULL); + +static struct device_attribute *accel_sensor_attrs [] = { + &dev_attr_sensor_name, + &dev_attr_sensor_vendor, + NULL, +}; + +#ifdef SUPPORT_LEGACY_SENSOR +#define ACCEL_NAME_STR "accel_sim" + +static ssize_t accel_name_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", ACCEL_NAME_STR); +} + +static ssize_t xyz_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + char sensor_data[__MAX_BUF_SENSOR]; + int ret; + struct input_dev *input_data = to_input_dev(dev); + struct maru_accel_data *data = input_get_drvdata(input_data); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_accel, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) + return sprintf(buf, "%d", -1); + + return sprintf(buf, "%s", sensor_data); +} + +static ssize_t xyz_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct maru_accel_data *data = input_get_drvdata(input_data); + mutex_lock(&data->vs->vqlock); + set_sensor_data(sensor_type_accel, buf); + mutex_unlock(&data->vs->vqlock); + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static struct device_attribute dev_attr_l_sensor_name = + __ATTR(name, S_IRUGO, accel_name_show, NULL); + +static DEVICE_ATTR(xyz, 0644, xyz_show, xyz_store); + +static struct device_attribute *l_accel_sensor_attrs [] = { + &dev_attr_l_sensor_name, + &dev_attr_xyz, + NULL, +}; +#endif + +static struct device_attribute attr_accel [] = +{ + MARU_ATTR_RW(enable), + MARU_ATTR_RW(poll_delay), +}; + +static struct attribute *maru_accel_attribute[] = { + &attr_accel[0].attr, + &attr_accel[1].attr, + NULL +}; + +static struct attribute_group maru_accel_attribute_group = { + .attrs = maru_accel_attribute +}; + +static void accel_clear(struct maru_accel_data *data) { + if (data == NULL) + return; + + if (data->input_data) { + sysfs_remove_group(&data->input_data->dev.kobj, + &maru_accel_attribute_group); + input_free_device(data->input_data); + } + + kfree(data); + data = NULL; +} + +static int set_initial_value(struct maru_accel_data *data) +{ + int delay = 0; + int ret = 0; + char sensor_data [__MAX_BUF_SENSOR]; + + memset(sensor_data, 0, sizeof(sensor_data)); + + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_accel_delay, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) { + ERR("failed to get initial delay time"); + return ret; + } + + delay = sensor_atoi(sensor_data); + if (delay < 0) { + ERR("weird value is set initial delay"); + return ret; + } + + atomic_set(&data->poll_delay, delay); + + memset(sensor_data, 0, sizeof(sensor_data)); + sensor_data[0] = '0'; + + mutex_lock(&data->vs->vqlock); + set_sensor_data(sensor_type_accel_enable, sensor_data); + mutex_unlock(&data->vs->vqlock); + atomic_set(&data->enable, 0); + + return ret; +} + +static int create_input_device(struct maru_accel_data *data) +{ + int ret = 0; + struct input_dev *input_data = NULL; + + input_data = input_allocate_device(); + if (input_data == NULL) { + ERR("failed initialing input handler"); + accel_clear(data); + return -ENOMEM; + } + + input_data->name = SENSOR_ACCEL_INPUT_NAME; + input_data->id.bustype = BUS_I2C; + + set_bit(EV_REL, input_data->evbit); + set_bit(EV_SYN, input_data->evbit); + input_set_capability(input_data, EV_REL, REL_X); + input_set_capability(input_data, EV_REL, REL_Y); + input_set_capability(input_data, EV_REL, REL_Z); + + data->input_data = input_data; + + ret = input_register_device(input_data); + if (ret) { + ERR("failed to register input data"); + accel_clear(data); + return ret; + } + + input_set_drvdata(input_data, data); + + ret = sysfs_create_group(&input_data->dev.kobj, + &maru_accel_attribute_group); + if (ret) { + accel_clear(data); + ERR("failed initialing devices"); + return ret; + } + + return ret; +} + +int maru_accel_init(struct virtio_sensor *vs) { + int ret = 0; + struct maru_accel_data *data = NULL; + + INFO("maru_accel device init starts."); + + data = kmalloc(sizeof(struct maru_accel_data), GFP_KERNEL); + if (data == NULL) { + ERR("failed to create accel data."); + return -ENOMEM; + } + + vs->accel_handle = data; + data->vs = vs; + + INIT_DELAYED_WORK(&data->work, maru_accel_input_work_func); + + // create name & vendor + ret = register_sensor_device(accel_sensor_device, vs, + accel_sensor_attrs, DRIVER_ACCEL_NAME); + if (ret) { + ERR("failed to register accel device"); + accel_clear(data); + return -1; + } + +#ifdef SUPPORT_LEGACY_SENSOR + ret = l_register_sensor_device(l_accel_sensor_device, vs, + l_accel_sensor_attrs, DRIVER_ACCEL_NAME); + if (ret) { + ERR("failed to register legacy accel device"); + accel_clear(data); + return -1; + } +#endif + + // create input + ret = create_input_device(data); + if (ret) { + ERR("failed to create input device"); + return ret; + } + + // set initial delay & enable + ret = set_initial_value(data); + if (ret) { + ERR("failed to set initial value"); + return ret; + } + + INFO("maru_accel device init ends."); + + return ret; +} + +int maru_accel_exit(struct virtio_sensor *vs) { + struct maru_accel_data *data = NULL; + + data = (struct maru_accel_data *)vs->accel_handle; + accel_clear(data); + INFO("maru_accel device exit ends."); + return 0; +} diff --git a/drivers/maru/sensors/maru_geo.c b/drivers/maru/sensors/maru_geo.c new file mode 100644 index 0000000..5f230b8 --- /dev/null +++ b/drivers/maru/sensors/maru_geo.c @@ -0,0 +1,468 @@ +/* + * Maru Virtio Geomagnetic Sensor Device Driver + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * Jinhyung Choi + * Sangho Park + * YeongKyoon Lee + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributors: + * - S-Core Co., Ltd + * + */ + +#include +#include + +#include "maru_virtio_sensor.h" + +struct maru_geo_data { + struct input_dev *input_data; + struct delayed_work work; + + struct virtio_sensor* vs; + + atomic_t enable; + atomic_t poll_delay; +}; + +static struct device *geo_sensor_device; + +#ifdef SUPPORT_LEGACY_SENSOR +static struct device *l_geo_sensor_device; +#endif + +static short sensor_convert_data(int number) +{ + return (short) ((number * 5) / 3); // divided by 0.6 +} + +static void maru_geo_input_work_func(struct work_struct *work) { + + int poll_time = 200000000; + int enable = 0; + int ret = 0; + int geo_x, geo_y, geo_z, hdst = 4; + short raw_x, raw_y, raw_z; + char sensor_data[__MAX_BUF_SENSOR]; + struct maru_geo_data *data = container_of((struct delayed_work *)work, + struct maru_geo_data, work); + + LOG(1, "maru_geo_input_work_func starts"); + + enable = atomic_read(&data->enable); + poll_time = atomic_read(&data->poll_delay); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + + if (enable) { + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_mag, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (!ret) { + sscanf(sensor_data, "%d %d %d", &geo_x, &geo_y, &geo_z); + LOG(1, "geo_set act %d, %d, %d", geo_x, geo_y, geo_z); + raw_x = sensor_convert_data(geo_x); + raw_y = sensor_convert_data(geo_y); + raw_z = sensor_convert_data(geo_z); + LOG(1, "geo_set raw %d, %d, %d, %d", raw_x, raw_y, raw_z, hdst); + + if (raw_x == 0) { + raw_x = 1; + } + + if (raw_y == 0) { + raw_y = 1; + } + + if (raw_z == 0) { + raw_z = 1; + } + + input_report_rel(data->input_data, REL_RX, raw_x); + input_report_rel(data->input_data, REL_RY, raw_y); + input_report_rel(data->input_data, REL_RZ, raw_z); + input_report_rel(data->input_data, REL_HWHEEL, hdst); + input_sync(data->input_data); + } + } + + enable = atomic_read(&data->enable); + + LOG(1, "enable: %d, poll_time: %d", enable, poll_time); + if (enable) { + if (poll_time > 0) { + schedule_delayed_work(&data->work, nsecs_to_jiffies(poll_time)); + } else { + schedule_delayed_work(&data->work, 0); + } + } + + LOG(1, "maru_geo_input_work_func ends"); + +} + +static ssize_t maru_name_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", MARU_GEO_DEVICE_NAME); +} + +static ssize_t maru_vendor_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", MARU_SENSOR_DEVICE_VENDOR); +} + +static ssize_t maru_enable_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + char sensor_data[__MAX_BUF_SENSOR]; + int ret; + struct input_dev *input_data = to_input_dev(dev); + struct maru_geo_data *data = input_get_drvdata(input_data); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_geo_enable, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) + return sprintf(buf, "%d", -1); + + return sprintf(buf, "%s", sensor_data); +} + +static ssize_t maru_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct maru_geo_data *data = input_get_drvdata(input_data); + int value = simple_strtoul(buf, NULL, 10); + + if (value != 0 && value != 1) + return count; + + mutex_lock(&data->vs->vqlock); + set_sensor_data(sensor_type_geo_enable, buf); + mutex_unlock(&data->vs->vqlock); + + if (value) { + if (atomic_read(&data->enable) != 1) { + atomic_set(&data->enable, 1); + schedule_delayed_work(&data->work, 0); + } + } else { + if (atomic_read(&data->enable) != 0) { + atomic_set(&data->enable, 0); + cancel_delayed_work(&data->work); + } + } + + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static ssize_t maru_poll_delay_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + char sensor_data[__MAX_BUF_SENSOR]; + int ret; + struct input_dev *input_data = to_input_dev(dev); + struct maru_geo_data *data = input_get_drvdata(input_data); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_geo_delay, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) + return sprintf(buf, "%d", -1); + + return sprintf(buf, "%s", sensor_data); +} + +static ssize_t maru_poll_delay_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct maru_geo_data *data = input_get_drvdata(input_data); + int value = simple_strtoul(buf, NULL, 10); + + if (value < __MIN_DELAY_SENSOR) + return count; + + mutex_lock(&data->vs->vqlock); + set_sensor_data(sensor_type_geo_delay, buf); + mutex_unlock(&data->vs->vqlock); + atomic_set(&data->poll_delay, value); + + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static struct device_attribute dev_attr_sensor_name = + __ATTR(name, S_IRUGO, maru_name_show, NULL); + +static struct device_attribute dev_attr_sensor_vendor = + __ATTR(vendor, S_IRUGO, maru_vendor_show, NULL); + +static struct device_attribute *geo_sensor_attrs [] = { + &dev_attr_sensor_name, + &dev_attr_sensor_vendor, + NULL, +}; + +#ifdef SUPPORT_LEGACY_SENSOR +#define GEO_NAME_STR "geo_sim" + +static ssize_t geo_name_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", GEO_NAME_STR); +} + +static ssize_t raw_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + char sensor_data[__MAX_BUF_SENSOR]; + int ret; + struct input_dev *input_data = to_input_dev(dev); + struct maru_geo_data *data = input_get_drvdata(input_data); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_tilt, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) + return sprintf(buf, "%d", -1); + + return sprintf(buf, "%s", sensor_data); +} + +static ssize_t raw_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct maru_geo_data *data = input_get_drvdata(input_data); + mutex_lock(&data->vs->vqlock); + set_sensor_data(sensor_type_tilt, buf); + mutex_unlock(&data->vs->vqlock); + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static ssize_t tesla_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + char sensor_data[__MAX_BUF_SENSOR]; + int ret; + struct input_dev *input_data = to_input_dev(dev); + struct maru_geo_data *data = input_get_drvdata(input_data); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_mag, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) + return sprintf(buf, "%d", -1); + + return sprintf(buf, "%s", sensor_data); +} + +static ssize_t tesla_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct maru_geo_data *data = input_get_drvdata(input_data); + mutex_lock(&data->vs->vqlock); + set_sensor_data(sensor_type_mag, buf); + mutex_unlock(&data->vs->vqlock); + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static struct device_attribute dev_attr_l_sensor_name = + __ATTR(name, S_IRUGO, geo_name_show, NULL); + +static DEVICE_ATTR(raw, 0644, raw_show, raw_store); +static DEVICE_ATTR(tesla, 0644, tesla_show, tesla_store); + +static struct device_attribute *l_geo_sensor_attrs [] = { + &dev_attr_l_sensor_name, + &dev_attr_raw, + &dev_attr_tesla, + NULL, +}; +#endif + +static struct device_attribute attr_geo [] = +{ + MARU_ATTR_RW(enable), + MARU_ATTR_RW(poll_delay), +}; + +static struct attribute *maru_geo_attribute[] = { + &attr_geo[0].attr, + &attr_geo[1].attr, + NULL +}; + +static struct attribute_group maru_geo_attribute_group = { + .attrs = maru_geo_attribute +}; + +static void geo_clear(struct maru_geo_data *data) { + if (data == NULL) + return; + + if (data->input_data) { + sysfs_remove_group(&data->input_data->dev.kobj, + &maru_geo_attribute_group); + input_free_device(data->input_data); + } + + kfree(data); + data = NULL; +} + +static int set_initial_value(struct maru_geo_data *data) +{ + int delay = 0; + int ret = 0; + char sensor_data [__MAX_BUF_SENSOR]; + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_geo_delay, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) { + ERR("failed to get initial delay time"); + return ret; + } + + delay = sensor_atoi(sensor_data); + if (delay < 0) { + ERR("weird value is set initial delay"); + return ret; + } + + atomic_set(&data->poll_delay, delay); + + memset(sensor_data, 0, sizeof(sensor_data)); + sensor_data[0] = '0'; + mutex_lock(&data->vs->vqlock); + set_sensor_data(sensor_type_geo_enable, sensor_data); + mutex_unlock(&data->vs->vqlock); + atomic_set(&data->enable, 0); + + return ret; +} + +static int create_input_device(struct maru_geo_data *data) +{ + int ret = 0; + struct input_dev *input_data = NULL; + + input_data = input_allocate_device(); + if (input_data == NULL) { + ERR("failed initialing input handler"); + geo_clear(data); + return -ENOMEM; + } + + input_data->name = SENSOR_GEO_INPUT_NAME; + input_data->id.bustype = BUS_I2C; + + set_bit(EV_REL, input_data->evbit); + set_bit(EV_SYN, input_data->evbit); + input_set_capability(input_data, EV_REL, REL_RX); + input_set_capability(input_data, EV_REL, REL_RY); + input_set_capability(input_data, EV_REL, REL_RZ); + input_set_capability(input_data, EV_REL, REL_HWHEEL); + + data->input_data = input_data; + + ret = input_register_device(input_data); + if (ret) { + ERR("failed to register input data"); + geo_clear(data); + return ret; + } + + input_set_drvdata(input_data, data); + + ret = sysfs_create_group(&input_data->dev.kobj, + &maru_geo_attribute_group); + if (ret) { + geo_clear(data); + ERR("failed initialing devices"); + return ret; + } + + return ret; +} + +int maru_geo_init(struct virtio_sensor *vs) { + int ret = 0; + struct maru_geo_data *data = NULL; + + INFO("maru_geo device init starts."); + + data = kmalloc(sizeof(struct maru_geo_data), GFP_KERNEL); + if (data == NULL) { + ERR("failed to create geo data."); + return -ENOMEM; + } + + vs->geo_handle = data; + data->vs = vs; + + INIT_DELAYED_WORK(&data->work, maru_geo_input_work_func); + + // create name & vendor + ret = register_sensor_device(geo_sensor_device, vs, + geo_sensor_attrs, DRIVER_GEO_NAME); + if (ret) { + ERR("failed to register geo device"); + geo_clear(data); + return -1; + } + +#ifdef SUPPORT_LEGACY_SENSOR + ret = l_register_sensor_device(l_geo_sensor_device, vs, + l_geo_sensor_attrs, DRIVER_GEO_NAME); + if (ret) { + ERR("failed to register legacy geo device"); + geo_clear(data); + return -1; + } +#endif + + // create input + ret = create_input_device(data); + if (ret) { + ERR("failed to create input device"); + return ret; + } + + // set initial delay & enable + ret = set_initial_value(data); + if (ret) { + ERR("failed to set initial value"); + return ret; + } + + INFO("maru_geo device init ends."); + + return ret; +} + +int maru_geo_exit(struct virtio_sensor *vs) { + struct maru_geo_data *data = NULL; + + data = (struct maru_geo_data *)vs->geo_handle; + geo_clear(data); + INFO("maru_geo device exit ends."); + return 0; +} diff --git a/drivers/maru/sensors/maru_gyro.c b/drivers/maru/sensors/maru_gyro.c new file mode 100644 index 0000000..083d85f --- /dev/null +++ b/drivers/maru/sensors/maru_gyro.c @@ -0,0 +1,493 @@ +/* + * Maru Virtio Gyroscope Sensor Device Driver + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * Jinhyung Choi + * Sangho Park + * YeongKyoon Lee + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributors: + * - S-Core Co., Ltd + * + */ + +#include +#include + +#include "maru_virtio_sensor.h" + +struct maru_gyro_data { + struct input_dev *input_data; + struct delayed_work work; + + struct virtio_sensor* vs; + + atomic_t enable; + atomic_t poll_delay; +}; + +static struct device *gyro_sensor_device; + +#ifdef SUPPORT_LEGACY_SENSOR +static struct device *l_gyro_sensor_device; +#endif + +static int gyro_adjust_x = 1; +static int gyro_adjust_y = 1; +static int gyro_adjust_z = 1; + +static void maru_gyro_input_work_func(struct work_struct *work) { + + int poll_time = 200000000; + int enable = 0; + int ret = 0; + int gyro_x, gyro_y, gyro_z; + char sensor_data[__MAX_BUF_SENSOR]; + struct maru_gyro_data *data = container_of((struct delayed_work *)work, + struct maru_gyro_data, work); + + LOG(1, "maru_gyro_input_work_func starts"); + + enable = atomic_read(&data->enable); + poll_time = atomic_read(&data->poll_delay); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + + if (enable) { + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_gyro, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (!ret) { + sscanf(sensor_data, "%d,%d,%d", &gyro_x, &gyro_y, &gyro_z); + LOG(1, "gyro_set act %d, %d, %d", gyro_x, gyro_y, gyro_z); + + if (gyro_x == 0) { + gyro_x = gyro_adjust_x; + gyro_adjust_x *= -1; + } + + if (gyro_y == 0) { + gyro_y = gyro_adjust_y; + gyro_adjust_y *= -1; + } + + if (gyro_z == 0) { + gyro_z = gyro_adjust_z; + gyro_adjust_z *= -1; + } + + input_report_rel(data->input_data, REL_RX, gyro_x); + input_report_rel(data->input_data, REL_RY, gyro_y); + input_report_rel(data->input_data, REL_RZ, gyro_z); + input_sync(data->input_data); + } + } + + enable = atomic_read(&data->enable); + + LOG(1, "enable: %d, poll_time: %d", enable, poll_time); + if (enable) { + if (poll_time > 0) { + schedule_delayed_work(&data->work, nsecs_to_jiffies(poll_time)); + } else { + schedule_delayed_work(&data->work, 0); + } + } + + LOG(1, "maru_gyro_input_work_func ends"); +} + +static ssize_t maru_name_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", MARU_GYRO_DEVICE_NAME); +} + +static ssize_t maru_vendor_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", MARU_SENSOR_DEVICE_VENDOR); +} + +static ssize_t maru_enable_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + char sensor_data[__MAX_BUF_SENSOR]; + int ret; + struct input_dev *input_data = to_input_dev(dev); + struct maru_gyro_data *data = input_get_drvdata(input_data); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_gyro_enable, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) + return sprintf(buf, "%d", -1); + + return sprintf(buf, "%s", sensor_data); +} + +static ssize_t maru_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct maru_gyro_data *data = input_get_drvdata(input_data); + int value = simple_strtoul(buf, NULL, 10); + + if (value != 0 && value != 1) + return count; + + mutex_lock(&data->vs->vqlock); + set_sensor_data(sensor_type_gyro_enable, buf); + mutex_unlock(&data->vs->vqlock); + + if (value) { + if (atomic_read(&data->enable) != 1) { + atomic_set(&data->enable, 1); + schedule_delayed_work(&data->work, 0); + + } + } else { + if (atomic_read(&data->enable) != 0) { + atomic_set(&data->enable, 0); + cancel_delayed_work(&data->work); + } + } + + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static ssize_t maru_poll_delay_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + char sensor_data[__MAX_BUF_SENSOR]; + int ret; + struct input_dev *input_data = to_input_dev(dev); + struct maru_gyro_data *data = input_get_drvdata(input_data); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_gyro_delay, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) + return sprintf(buf, "%d", -1); + + return sprintf(buf, "%s", sensor_data); +} + +static ssize_t maru_poll_delay_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct maru_gyro_data *data = input_get_drvdata(input_data); + int value = simple_strtoul(buf, NULL, 10); + + if (value < __MIN_DELAY_SENSOR) + return count; + + mutex_lock(&data->vs->vqlock); + set_sensor_data(sensor_type_gyro_delay, buf); + mutex_unlock(&data->vs->vqlock); + atomic_set(&data->poll_delay, value); + + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static struct device_attribute dev_attr_sensor_name = + __ATTR(name, S_IRUGO, maru_name_show, NULL); + +static struct device_attribute dev_attr_sensor_vendor = + __ATTR(vendor, S_IRUGO, maru_vendor_show, NULL); + +static struct device_attribute *gyro_sensor_attrs [] = { + &dev_attr_sensor_name, + &dev_attr_sensor_vendor, + NULL, +}; + +#ifdef SUPPORT_LEGACY_SENSOR +#define GYRO_NAME_STR "gyro_sim" + +static ssize_t gyro_name_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", GYRO_NAME_STR); +} + +static ssize_t gyro_x_raw_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + char sensor_data[__MAX_BUF_SENSOR]; + int ret; + struct input_dev *input_data = to_input_dev(dev); + struct maru_gyro_data *data = input_get_drvdata(input_data); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_gyro_x, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) + return sprintf(buf, "%d", -1); + + return sprintf(buf, "%s", sensor_data); +} + +static ssize_t gyro_x_raw_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct maru_gyro_data *data = input_get_drvdata(input_data); + mutex_lock(&data->vs->vqlock); + set_sensor_data(sensor_type_gyro_x, buf); + mutex_unlock(&data->vs->vqlock); + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static ssize_t gyro_y_raw_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + char sensor_data[__MAX_BUF_SENSOR]; + int ret; + struct input_dev *input_data = to_input_dev(dev); + struct maru_gyro_data *data = input_get_drvdata(input_data); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_gyro_y, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) + return sprintf(buf, "%d", -1); + + return sprintf(buf, "%s", sensor_data); +} + +static ssize_t gyro_y_raw_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct maru_gyro_data *data = input_get_drvdata(input_data); + mutex_lock(&data->vs->vqlock); + set_sensor_data(sensor_type_gyro_y, buf); + mutex_unlock(&data->vs->vqlock); + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static ssize_t gyro_z_raw_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + char sensor_data[__MAX_BUF_SENSOR]; + int ret; + struct input_dev *input_data = to_input_dev(dev); + struct maru_gyro_data *data = input_get_drvdata(input_data); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_gyro_z, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) + return sprintf(buf, "%d", -1); + + return sprintf(buf, "%s", sensor_data); +} + +static ssize_t gyro_z_raw_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct maru_gyro_data *data = input_get_drvdata(input_data); + mutex_lock(&data->vs->vqlock); + set_sensor_data(sensor_type_gyro_z, buf); + mutex_unlock(&data->vs->vqlock); + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static struct device_attribute dev_attr_l_sensor_name = + __ATTR(name, S_IRUGO, gyro_name_show, NULL); + +static DEVICE_ATTR(gyro_x_raw, 0644, gyro_x_raw_show, gyro_x_raw_store); +static DEVICE_ATTR(gyro_y_raw, 0644, gyro_y_raw_show, gyro_y_raw_store); +static DEVICE_ATTR(gyro_z_raw, 0644, gyro_z_raw_show, gyro_z_raw_store); + +static struct device_attribute *l_gyro_sensor_attrs [] = { + &dev_attr_l_sensor_name, + &dev_attr_gyro_x_raw, + &dev_attr_gyro_y_raw, + &dev_attr_gyro_z_raw, + NULL, +}; +#endif + +static struct device_attribute attr_gyro [] = +{ + MARU_ATTR_RW(enable), + MARU_ATTR_RW(poll_delay), +}; + +static struct attribute *maru_gyro_attribute[] = { + &attr_gyro[0].attr, + &attr_gyro[1].attr, + NULL +}; + +static struct attribute_group maru_gyro_attribute_group = { + .attrs = maru_gyro_attribute +}; + +static void gyro_clear(struct maru_gyro_data *data) { + if (data == NULL) + return; + + if (data->input_data) { + sysfs_remove_group(&data->input_data->dev.kobj, + &maru_gyro_attribute_group); + input_free_device(data->input_data); + } + + kfree(data); + data = NULL; +} + +static int set_initial_value(struct maru_gyro_data *data) +{ + int delay = 0; + int ret = 0; + char sensor_data [__MAX_BUF_SENSOR]; + + memset(sensor_data, 0, sizeof(sensor_data)); + + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_gyro_delay, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) { + ERR("failed to get initial delay time"); + return ret; + } + + delay = sensor_atoi(sensor_data); + if (delay < 0) { + ERR("weird value is set initial delay"); + return ret; + } + + atomic_set(&data->poll_delay, delay); + + memset(sensor_data, 0, sizeof(sensor_data)); + sensor_data[0] = '0'; + + mutex_lock(&data->vs->vqlock); + set_sensor_data(sensor_type_gyro_enable, sensor_data); + mutex_unlock(&data->vs->vqlock); + atomic_set(&data->enable, 0); + + return ret; +} + +static int create_input_device(struct maru_gyro_data *data) +{ + int ret = 0; + struct input_dev *input_data = NULL; + + input_data = input_allocate_device(); + if (input_data == NULL) { + ERR("failed initialing input handler"); + gyro_clear(data); + return -ENOMEM; + } + + input_data->name = SENSOR_GYRO_INPUT_NAME; + input_data->id.bustype = BUS_I2C; + + set_bit(EV_REL, input_data->evbit); + set_bit(EV_SYN, input_data->evbit); + input_set_capability(input_data, EV_REL, REL_RX); + input_set_capability(input_data, EV_REL, REL_RY); + input_set_capability(input_data, EV_REL, REL_RZ); + + data->input_data = input_data; + + ret = input_register_device(input_data); + if (ret) { + ERR("failed to register input data"); + gyro_clear(data); + return ret; + } + + input_set_drvdata(input_data, data); + + ret = sysfs_create_group(&input_data->dev.kobj, + &maru_gyro_attribute_group); + if (ret) { + gyro_clear(data); + ERR("failed initialing devices"); + return ret; + } + + return ret; +} + +int maru_gyro_init(struct virtio_sensor *vs) { + int ret = 0; + struct maru_gyro_data *data = NULL; + + INFO("maru_gyro device init starts."); + + data = kmalloc(sizeof(struct maru_gyro_data), GFP_KERNEL); + if (data == NULL) { + ERR("failed to create gyro data."); + return -ENOMEM; + } + + vs->gyro_handle = data; + data->vs = vs; + + INIT_DELAYED_WORK(&data->work, maru_gyro_input_work_func); + + // create name & vendor + ret = register_sensor_device(gyro_sensor_device, vs, + gyro_sensor_attrs, DRIVER_GYRO_NAME); + if (ret) { + ERR("failed to register gyro device"); + gyro_clear(data); + return -1; + } + +#ifdef SUPPORT_LEGACY_SENSOR + ret = l_register_sensor_device(l_gyro_sensor_device, vs, + l_gyro_sensor_attrs, DRIVER_GYRO_NAME); + if (ret) { + ERR("failed to register legacy gyro device"); + gyro_clear(data); + return -1; + } +#endif + + // create input + ret = create_input_device(data); + if (ret) { + ERR("failed to create input device"); + return ret; + } + + // set initial delay & enable + ret = set_initial_value(data); + if (ret) { + ERR("failed to set initial value"); + return ret; + } + + INFO("maru_gyro device init ends."); + + return ret; +} + +int maru_gyro_exit(struct virtio_sensor *vs) { + struct maru_gyro_data *data = NULL; + + data = (struct maru_gyro_data *)vs->gyro_handle; + gyro_clear(data); + INFO("maru_gyro device exit ends."); + return 0; +} diff --git a/drivers/maru/sensors/maru_haptic.c b/drivers/maru/sensors/maru_haptic.c new file mode 100644 index 0000000..0d8c7fa --- /dev/null +++ b/drivers/maru/sensors/maru_haptic.c @@ -0,0 +1,161 @@ +/* + * Maru Virtio Haptic Sensor Device Driver + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * Jinhyung Choi + * Sangho Park + * YeongKyoon Lee + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributors: + * - S-Core Co., Ltd + * + */ + +#include +#include + +#include "maru_virtio_sensor.h" + +struct maru_haptic_data { + struct input_dev *input_data; + + struct virtio_sensor* vs; +}; + +static void haptic_clear(struct maru_haptic_data *data) { + if (data == NULL) + return; + + if (data->input_data) { + input_ff_destroy(data->input_data); + input_free_device(data->input_data); + } + + kfree(data); + data = NULL; +} + +static int maru_upload_effect(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old) +{ + INFO("called maru_upload_effect. No work to do."); + return 0; +} + +static int maru_erase_effect(struct input_dev *dev, int effect_id) +{ + INFO("called maru_erase_effect. No work to do."); + return 0; +} + +static void maru_set_gain(struct input_dev *dev, u16 gain) +{ +} + +static void maru_set_autocenter(struct input_dev *dev, u16 magnitude) +{ +} + +static int maru_playback(struct input_dev *dev, int effect_id, int value) +{ + INFO("called maru_playback. No work to do."); + return 0; +} + +static int create_input_device(struct maru_haptic_data *data) +{ + int ret = 0; + struct ff_device *ff; + struct input_dev *input_data = NULL; + + input_data = input_allocate_device(); + if (input_data == NULL) { + ERR("failed initialing input handler"); + haptic_clear(data); + return -ENOMEM; + } + + input_data->name = SENSOR_HAPTIC_INPUT_NAME; + input_data->id.bustype = BUS_I2C; + + set_bit(EV_FF, input_data->evbit); + input_set_capability(input_data, EV_FF, FF_PERIODIC); + + data->input_data = input_data; + + input_set_drvdata(input_data, data); + + ret = input_ff_create(input_data, 16); + if (ret) + return ret; + + set_bit(FF_SQUARE, input_data->ffbit); + ff = input_data->ff; + ff->upload = maru_upload_effect; + ff->erase = maru_erase_effect; + ff->set_gain = maru_set_gain; + ff->set_autocenter = maru_set_autocenter; + ff->playback = maru_playback; + + ret = input_register_device(input_data); + if (ret) { + ERR("failed to register input data"); + haptic_clear(data); + return ret; + } + + + return ret; +} + +int maru_haptic_init(struct virtio_sensor *vs) { + int ret = 0; + struct maru_haptic_data *data = NULL; + + INFO("maru_haptic device init starts."); + + data = kmalloc(sizeof(struct maru_haptic_data), GFP_KERNEL); + if (data == NULL) { + ERR("failed to create haptic data."); + return -ENOMEM; + } + + data->vs = vs; + vs->haptic_handle = data; + + // create input + ret = create_input_device(data); + if (ret) { + ERR("failed to create input device"); + return ret; + } + + INFO("maru_haptic device init ends."); + + return ret; +} + +int maru_haptic_exit(struct virtio_sensor *vs) { + struct maru_haptic_data *data = NULL; + + data = (struct maru_haptic_data *)vs->haptic_handle; + haptic_clear(data); + INFO("maru_haptic device exit ends."); + + return 0; +} diff --git a/drivers/maru/sensors/maru_hrm.c b/drivers/maru/sensors/maru_hrm.c new file mode 100644 index 0000000..2a79ac4 --- /dev/null +++ b/drivers/maru/sensors/maru_hrm.c @@ -0,0 +1,364 @@ +/* + * Maru Virtio HRM(HeartBeat Rate Monitor) Sensor Device Driver + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * Jinhyung Choi + * Sangho Park + * YeongKyoon Lee + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributors: + * - S-Core Co., Ltd + * + */ + +#include +#include + +#include "maru_virtio_sensor.h" + +struct maru_hrm_data { + struct input_dev *input_data; + struct delayed_work work; + + struct virtio_sensor* vs; + + atomic_t enable; + atomic_t poll_delay; +}; + +static struct device *hrm_sensor_device; + +static void maru_hrm_input_work_func(struct work_struct *work) { + + int poll_time = 200000000; + int enable = 0; + int ret = 0; + int hrm = 0, rri = 0; + char sensor_data[__MAX_BUF_SENSOR]; + struct maru_hrm_data *data = container_of((struct delayed_work *)work, + struct maru_hrm_data, work); + + LOG(1, "maru_hrm_input_work_func starts"); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + poll_time = atomic_read(&data->poll_delay); + + enable = atomic_read(&data->enable); + + if (enable) { + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_hrm, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (!ret) { + sscanf(sensor_data, "%d, %d", &hrm, &rri); + LOG(1, "hrm_set %d %d", hrm, rri); + + input_report_rel(data->input_data, REL_X, hrm + 1); + input_report_rel(data->input_data, REL_Y, rri + 1); + input_report_rel(data->input_data, REL_Z, 1); + input_sync(data->input_data); + } + } + + enable = atomic_read(&data->enable); + + LOG(1, "enable: %d, poll_time: %d", enable, poll_time); + if (enable) { + if (poll_time > 0) { + schedule_delayed_work(&data->work, nsecs_to_jiffies(poll_time)); + } else { + schedule_delayed_work(&data->work, 0); + } + } + + LOG(1, "maru_hrm_input_work_func ends"); + +} + +static ssize_t maru_name_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", MARU_HRM_DEVICE_NAME); +} + +static ssize_t maru_vendor_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", MARU_SENSOR_DEVICE_VENDOR); +} + +static ssize_t maru_enable_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + char sensor_data[__MAX_BUF_SENSOR]; + int ret; + struct input_dev *input_data = to_input_dev(dev); + struct maru_hrm_data *data = input_get_drvdata(input_data); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_hrm_enable, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) + return sprintf(buf, "%d", -1); + + return sprintf(buf, "%s", sensor_data); +} + +static ssize_t maru_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct maru_hrm_data *data = input_get_drvdata(input_data); + int value = simple_strtoul(buf, NULL, 10); + + if (value != 0 && value != 1) + return count; + + mutex_lock(&data->vs->vqlock); + set_sensor_data(sensor_type_hrm_enable, buf); + mutex_unlock(&data->vs->vqlock); + + if (value) { + if (atomic_read(&data->enable) != 1) { + atomic_set(&data->enable, 1); + schedule_delayed_work(&data->work, 0); + + } + } else { + if (atomic_read(&data->enable) != 0) { + atomic_set(&data->enable, 0); + cancel_delayed_work(&data->work); + } + } + + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static ssize_t maru_poll_delay_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + char sensor_data[__MAX_BUF_SENSOR]; + int ret; + struct input_dev *input_data = to_input_dev(dev); + struct maru_hrm_data *data = input_get_drvdata(input_data); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_hrm_delay, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) + return sprintf(buf, "%d", -1); + + return sprintf(buf, "%s", sensor_data); +} + +static ssize_t maru_poll_delay_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct maru_hrm_data *data = input_get_drvdata(input_data); + int value = simple_strtoul(buf, NULL, 10); + + if (value < __MIN_DELAY_SENSOR) + return count; + + mutex_lock(&data->vs->vqlock); + set_sensor_data(sensor_type_hrm_delay, buf); + mutex_unlock(&data->vs->vqlock); + atomic_set(&data->poll_delay, value); + + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static struct device_attribute dev_attr_sensor_name = + __ATTR(name, S_IRUGO, maru_name_show, NULL); + +static struct device_attribute dev_attr_sensor_vendor = + __ATTR(vendor, S_IRUGO, maru_vendor_show, NULL); + +static struct device_attribute *hrm_sensor_attrs [] = { + &dev_attr_sensor_name, + &dev_attr_sensor_vendor, + NULL, +}; + +static struct device_attribute attr_hrm [] = +{ + MARU_ATTR_RW(enable), + MARU_ATTR_RW(poll_delay), +}; + +static struct attribute *maru_hrm_attribute[] = { + &attr_hrm[0].attr, + &attr_hrm[1].attr, + NULL +}; + +static struct attribute_group maru_hrm_attribute_group = { + .attrs = maru_hrm_attribute +}; + +static void hrm_clear(struct maru_hrm_data *data) { + if (data == NULL) + return; + + if (data->input_data) { + sysfs_remove_group(&data->input_data->dev.kobj, + &maru_hrm_attribute_group); + input_free_device(data->input_data); + } + + kfree(data); + data = NULL; +} + +static int set_initial_value(struct maru_hrm_data *data) +{ + int delay = 0; + int ret = 0; + int enable = 0; + char sensor_data [__MAX_BUF_SENSOR]; + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_hrm_delay, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) { + ERR("failed to get initial delay time"); + return ret; + } + + delay = sensor_atoi(sensor_data); + + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_hrm_enable, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) { + ERR("failed to get initial enable"); + return ret; + } + + enable = sensor_atoi(sensor_data); + + if (delay < 0) { + ERR("weird value is set initial delay"); + return ret; + } + + atomic_set(&data->poll_delay, delay); + + if (enable) { + atomic_set(&data->enable, 1); + schedule_delayed_work(&data->work, 0); + } + + return ret; +} + +static int create_input_device(struct maru_hrm_data *data) +{ + int ret = 0; + struct input_dev *input_data = NULL; + + input_data = input_allocate_device(); + if (input_data == NULL) { + ERR("failed initialing input handler"); + hrm_clear(data); + return -ENOMEM; + } + + input_data->name = SENSOR_HRM_INPUT_NAME; + input_data->id.bustype = BUS_I2C; + + set_bit(EV_REL, input_data->evbit); + input_set_capability(input_data, EV_REL, REL_X); + input_set_capability(input_data, EV_REL, REL_Y); + input_set_capability(input_data, EV_REL, REL_Z); + + data->input_data = input_data; + + ret = input_register_device(input_data); + if (ret) { + ERR("failed to register input data"); + hrm_clear(data); + return ret; + } + + input_set_drvdata(input_data, data); + + ret = sysfs_create_group(&input_data->dev.kobj, + &maru_hrm_attribute_group); + if (ret) { + hrm_clear(data); + ERR("failed initialing devices"); + return ret; + } + + return ret; +} + +int maru_hrm_init(struct virtio_sensor *vs) { + int ret = 0; + struct maru_hrm_data *data = NULL; + + INFO("maru_hrm device init starts."); + + data = kmalloc(sizeof(struct maru_hrm_data), GFP_KERNEL); + if (data == NULL) { + ERR("failed to create hrm data."); + return -ENOMEM; + } + + vs->hrm_handle = data; + data->vs = vs; + + INIT_DELAYED_WORK(&data->work, maru_hrm_input_work_func); + + // create name & vendor + ret = register_sensor_device(hrm_sensor_device, vs, + hrm_sensor_attrs, DRIVER_HRM_NAME); + if (ret) { + ERR("failed to register hrm device"); + hrm_clear(data); + return -1; + } + + // create input + ret = create_input_device(data); + if (ret) { + ERR("failed to create input device"); + return ret; + } + + // set initial delay & enable + ret = set_initial_value(data); + if (ret) { + ERR("failed to set initial value"); + return ret; + } + + INFO("maru_hrm device init ends."); + + return ret; +} + +int maru_hrm_exit(struct virtio_sensor *vs) { + struct maru_hrm_data *data = NULL; + + data = (struct maru_hrm_data *)vs->hrm_handle; + hrm_clear(data); + INFO("maru_hrm device exit ends."); + return 0; +} diff --git a/drivers/maru/sensors/maru_light.c b/drivers/maru/sensors/maru_light.c new file mode 100644 index 0000000..4abd7b5 --- /dev/null +++ b/drivers/maru/sensors/maru_light.c @@ -0,0 +1,458 @@ +/* + * Maru Virtio Light Sensor Device Driver + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * Jinhyung Choi + * Sangho Park + * YeongKyoon Lee + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributors: + * - S-Core Co., Ltd + * + */ + +#include +#include + +#include "maru_virtio_sensor.h" + +struct maru_light_data { + struct input_dev *input_data; + struct delayed_work work; + + struct virtio_sensor* vs; + + atomic_t enable; + atomic_t poll_delay; +}; + +static struct device *light_sensor_device; + +#ifdef SUPPORT_LEGACY_SENSOR +static struct device *l_light_sensor_device; +#endif + +static void maru_light_input_work_func(struct work_struct *work) { + + int poll_time = 200000000; + int enable = 0; + int ret = 0; + int light = 0; + char sensor_data[__MAX_BUF_SENSOR]; + struct maru_light_data *data = container_of((struct delayed_work *)work, + struct maru_light_data, work); + + LOG(1, "maru_light_input_work_func starts"); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + poll_time = atomic_read(&data->poll_delay); + + enable = atomic_read(&data->enable); + + if (enable) { + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_light, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (!ret) { + sscanf(sensor_data, "%d", &light); + LOG(1, "light_set %d", light); + + input_report_rel(data->input_data, REL_RX, (light + 1)); // LUX + input_report_rel(data->input_data, REL_HWHEEL, 0); // red + input_report_rel(data->input_data, REL_DIAL, 0); // green + input_report_rel(data->input_data, REL_WHEEL, 0); // blue + input_report_rel(data->input_data, REL_MISC, 0); // white + input_sync(data->input_data); + } + } + + enable = atomic_read(&data->enable); + + LOG(1, "enable: %d, poll_time: %d", enable, poll_time); + if (enable) { + if (poll_time > 0) { + schedule_delayed_work(&data->work, nsecs_to_jiffies(poll_time)); + } else { + schedule_delayed_work(&data->work, 0); + } + } + + LOG(1, "maru_light_input_work_func ends"); + +} + +static ssize_t maru_name_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", MARU_LIGHT_DEVICE_NAME); +} + +static ssize_t maru_vendor_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", MARU_SENSOR_DEVICE_VENDOR); +} + +static ssize_t maru_enable_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + char sensor_data[__MAX_BUF_SENSOR]; + int ret; + struct input_dev *input_data = to_input_dev(dev); + struct maru_light_data *data = input_get_drvdata(input_data); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_light_enable, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) + return sprintf(buf, "%d", -1); + + return sprintf(buf, "%s", sensor_data); +} + +static ssize_t maru_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct maru_light_data *data = input_get_drvdata(input_data); + int value = simple_strtoul(buf, NULL, 10); + + if (value != 0 && value != 1) + return count; + + mutex_lock(&data->vs->vqlock); + set_sensor_data(sensor_type_light_enable, buf); + mutex_unlock(&data->vs->vqlock); + + if (value) { + if (atomic_read(&data->enable) != 1) { + atomic_set(&data->enable, 1); + schedule_delayed_work(&data->work, 0); + + } + } else { + if (atomic_read(&data->enable) != 0) { + atomic_set(&data->enable, 0); + cancel_delayed_work(&data->work); + } + } + + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static ssize_t maru_poll_delay_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + char sensor_data[__MAX_BUF_SENSOR]; + int ret; + struct input_dev *input_data = to_input_dev(dev); + struct maru_light_data *data = input_get_drvdata(input_data); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_light_delay, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) + return sprintf(buf, "%d", -1); + + return sprintf(buf, "%s", sensor_data); +} + +static ssize_t maru_poll_delay_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct maru_light_data *data = input_get_drvdata(input_data); + int value = simple_strtoul(buf, NULL, 10); + + if (value < __MIN_DELAY_SENSOR) + return count; + + mutex_lock(&data->vs->vqlock); + set_sensor_data(sensor_type_light_delay, buf); + mutex_unlock(&data->vs->vqlock); + atomic_set(&data->poll_delay, value); + + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static struct device_attribute dev_attr_sensor_name = + __ATTR(name, S_IRUGO, maru_name_show, NULL); + +static struct device_attribute dev_attr_sensor_vendor = + __ATTR(vendor, S_IRUGO, maru_vendor_show, NULL); + +static struct device_attribute *light_sensor_attrs [] = { + &dev_attr_sensor_name, + &dev_attr_sensor_vendor, + NULL, +}; + +#ifdef SUPPORT_LEGACY_SENSOR +#define LIGHT_NAME_STR "light_sim" + +static ssize_t light_name_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", LIGHT_NAME_STR); +} + +static ssize_t adc_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + char sensor_data[__MAX_BUF_SENSOR]; + int ret; + struct input_dev *input_data = to_input_dev(dev); + struct maru_light_data *data = input_get_drvdata(input_data); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_light_adc, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) + return sprintf(buf, "%d", -1); + + return sprintf(buf, "%s", sensor_data); +} + +static ssize_t adc_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct maru_light_data *data = input_get_drvdata(input_data); + mutex_lock(&data->vs->vqlock); + set_sensor_data(sensor_type_light_adc, buf); + mutex_unlock(&data->vs->vqlock); + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static ssize_t level_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + char sensor_data[__MAX_BUF_SENSOR]; + int ret; + struct input_dev *input_data = to_input_dev(dev); + struct maru_light_data *data = input_get_drvdata(input_data); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_light_level, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) + return sprintf(buf, "%d", -1); + + return sprintf(buf, "%s", sensor_data); +} + +static ssize_t level_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct maru_light_data *data = input_get_drvdata(input_data); + mutex_lock(&data->vs->vqlock); + set_sensor_data(sensor_type_light_level, buf); + mutex_unlock(&data->vs->vqlock); + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static struct device_attribute dev_attr_l_sensor_name = + __ATTR(name, S_IRUGO, light_name_show, NULL); + +static DEVICE_ATTR(adc, 0644, adc_show, adc_store); +static DEVICE_ATTR(level, 0644, level_show, level_store); + +static struct device_attribute *l_light_sensor_attrs [] = { + &dev_attr_l_sensor_name, + &dev_attr_adc, + &dev_attr_level, + NULL, +}; +#endif + +static struct device_attribute attr_light [] = +{ + MARU_ATTR_RW(enable), + MARU_ATTR_RW(poll_delay), +}; + +static struct attribute *maru_light_attribute[] = { + &attr_light[0].attr, + &attr_light[1].attr, + NULL +}; + +static struct attribute_group maru_light_attribute_group = { + .attrs = maru_light_attribute +}; + +static void light_clear(struct maru_light_data *data) { + if (data == NULL) + return; + + if (data->input_data) { + sysfs_remove_group(&data->input_data->dev.kobj, + &maru_light_attribute_group); + input_free_device(data->input_data); + } + + kfree(data); + data = NULL; +} + +static int set_initial_value(struct maru_light_data *data) +{ + int delay = 0; + int ret = 0; + int enable = 0; + char sensor_data [__MAX_BUF_SENSOR]; + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_light_delay, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) { + ERR("failed to get initial delay time"); + return ret; + } + + delay = sensor_atoi(sensor_data); + + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_light_enable, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) { + ERR("failed to get initial enable"); + return ret; + } + + enable = sensor_atoi(sensor_data); + + if (delay < 0) { + ERR("weird value is set initial delay"); + return ret; + } + + atomic_set(&data->poll_delay, delay); + + if (enable) { + atomic_set(&data->enable, 1); + schedule_delayed_work(&data->work, 0); + } + + return ret; +} + +static int create_input_device(struct maru_light_data *data) +{ + int ret = 0; + struct input_dev *input_data = NULL; + + input_data = input_allocate_device(); + if (input_data == NULL) { + ERR("failed initialing input handler"); + light_clear(data); + return -ENOMEM; + } + + input_data->name = SENSOR_LIGHT_INPUT_NAME; + input_data->id.bustype = BUS_I2C; + + set_bit(EV_REL, input_data->evbit); + input_set_capability(input_data, EV_REL, REL_RX); + input_set_capability(input_data, EV_REL, REL_HWHEEL); + input_set_capability(input_data, EV_REL, REL_DIAL); + input_set_capability(input_data, EV_REL, REL_WHEEL); + input_set_capability(input_data, EV_REL, REL_MISC); + + data->input_data = input_data; + + ret = input_register_device(input_data); + if (ret) { + ERR("failed to register input data"); + light_clear(data); + return ret; + } + + input_set_drvdata(input_data, data); + + ret = sysfs_create_group(&input_data->dev.kobj, + &maru_light_attribute_group); + if (ret) { + light_clear(data); + ERR("failed initialing devices"); + return ret; + } + + return ret; +} + +int maru_light_init(struct virtio_sensor *vs) { + int ret = 0; + struct maru_light_data *data = NULL; + + INFO("maru_light device init starts."); + + data = kmalloc(sizeof(struct maru_light_data), GFP_KERNEL); + if (data == NULL) { + ERR("failed to create light data."); + return -ENOMEM; + } + + vs->light_handle = data; + data->vs = vs; + + INIT_DELAYED_WORK(&data->work, maru_light_input_work_func); + + // create name & vendor + ret = register_sensor_device(light_sensor_device, vs, + light_sensor_attrs, DRIVER_LIGHT_NAME); + if (ret) { + ERR("failed to register light device"); + light_clear(data); + return -1; + } + +#ifdef SUPPORT_LEGACY_SENSOR + ret = l_register_sensor_device(l_light_sensor_device, vs, + l_light_sensor_attrs, DRIVER_LIGHT_NAME); + if (ret) { + ERR("failed to register legacy light device"); + light_clear(data); + return -1; + } +#endif + + // create input + ret = create_input_device(data); + if (ret) { + ERR("failed to create input device"); + return ret; + } + + // set initial delay & enable + ret = set_initial_value(data); + if (ret) { + ERR("failed to set initial value"); + return ret; + } + + INFO("maru_light device init ends."); + + return ret; +} + +int maru_light_exit(struct virtio_sensor *vs) { + struct maru_light_data *data = NULL; + + data = (struct maru_light_data *)vs->light_handle; + light_clear(data); + INFO("maru_light device exit ends."); + return 0; +} diff --git a/drivers/maru/sensors/maru_pressure.c b/drivers/maru/sensors/maru_pressure.c new file mode 100644 index 0000000..18a450e --- /dev/null +++ b/drivers/maru/sensors/maru_pressure.c @@ -0,0 +1,389 @@ +/* + * Maru Virtio Pressure Sensor Device Driver + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * Jinhyung Choi + * Sangho Park + * YeongKyoon Lee + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributors: + * - S-Core Co., Ltd + * + */ + +#include +#include + +#include "maru_virtio_sensor.h" + +struct maru_pressure_data { + struct input_dev *input_data; + struct delayed_work work; + + struct virtio_sensor* vs; + + atomic_t enable; + atomic_t poll_delay; +}; + +static struct device *pressure_sensor_device; + +#define PRESSURE_ADJUST 193 +static int pressure_convert_data(int number) +{ + return number * 10000 / PRESSURE_ADJUST; +} + +#define TEMP_ADJUST 2 +static short temp_convert_data(int number) +{ + int temp; + temp = number * TEMP_ADJUST; + return (short)temp; +} + +static void maru_pressure_input_work_func(struct work_struct *work) { + + int poll_time = 200000000; + int enable = 0; + int ret = 0; + int pressure = 0; + int temperature = 0; + int raw_pressure; + short raw_temp; + char sensor_data[__MAX_BUF_SENSOR]; + struct maru_pressure_data *data = container_of((struct delayed_work *)work, + struct maru_pressure_data, work); + + LOG(1, "maru_pressure_input_work_func starts"); + + memset(sensor_data, 0, sizeof(sensor_data)); + poll_time = atomic_read(&data->poll_delay); + + enable = atomic_read(&data->enable); + + if (enable) { + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_pressure, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (!ret) { + sscanf(sensor_data, "%d, %d", &pressure, &temperature); + LOG(1, "pressure_set %d, %d", pressure, temperature); + + raw_pressure = pressure_convert_data(pressure); + if (temperature == 0) { + temperature = 1; + } + raw_temp = temp_convert_data(temperature); + + LOG(1, "pressure raw pressure %d, temp %d.\n", raw_pressure, raw_temp); + + input_report_rel(data->input_data, REL_HWHEEL, raw_pressure); + input_report_rel(data->input_data, REL_DIAL, 101325); + input_report_rel(data->input_data, REL_WHEEL, raw_temp); + input_sync(data->input_data); + } + } + + enable = atomic_read(&data->enable); + + LOG(1, "enable: %d, poll_time: %d", enable, poll_time); + if (enable) { + if (poll_time > 0) { + schedule_delayed_work(&data->work, nsecs_to_jiffies(poll_time)); + } else { + schedule_delayed_work(&data->work, 0); + } + } + + LOG(1, "maru_pressure_input_work_func ends"); + +} + +static ssize_t maru_name_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", MARU_PRESSURE_DEVICE_NAME); +} + +static ssize_t maru_vendor_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", MARU_SENSOR_DEVICE_VENDOR); +} + +static ssize_t maru_enable_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + char sensor_data[__MAX_BUF_SENSOR]; + int ret; + struct input_dev *input_data = to_input_dev(dev); + struct maru_pressure_data *data = input_get_drvdata(input_data); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_pressure_enable, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) + return sprintf(buf, "%d", -1); + + return sprintf(buf, "%s", sensor_data); +} + +static ssize_t maru_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct maru_pressure_data *data = input_get_drvdata(input_data); + int value = simple_strtoul(buf, NULL, 10); + + if (value != 0 && value != 1) + return count; + + mutex_lock(&data->vs->vqlock); + set_sensor_data(sensor_type_pressure_enable, buf); + mutex_unlock(&data->vs->vqlock); + + if (value) { + if (atomic_read(&data->enable) != 1) { + atomic_set(&data->enable, 1); + schedule_delayed_work(&data->work, 0); + + } + } else { + if (atomic_read(&data->enable) != 0) { + atomic_set(&data->enable, 0); + cancel_delayed_work(&data->work); + } + } + + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static ssize_t maru_poll_delay_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + char sensor_data[__MAX_BUF_SENSOR]; + int ret; + struct input_dev *input_data = to_input_dev(dev); + struct maru_pressure_data *data = input_get_drvdata(input_data); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_pressure_delay, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) + return sprintf(buf, "%d", -1); + + return sprintf(buf, "%s", sensor_data); +} + +static ssize_t maru_poll_delay_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct maru_pressure_data *data = input_get_drvdata(input_data); + int value = simple_strtoul(buf, NULL, 10); + + if (value < __MIN_DELAY_SENSOR) + return count; + + mutex_lock(&data->vs->vqlock); + set_sensor_data(sensor_type_pressure_delay, buf); + mutex_unlock(&data->vs->vqlock); + atomic_set(&data->poll_delay, value); + + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static struct device_attribute dev_attr_sensor_name = + __ATTR(name, S_IRUGO, maru_name_show, NULL); + +static struct device_attribute dev_attr_sensor_vendor = + __ATTR(vendor, S_IRUGO, maru_vendor_show, NULL); + +static struct device_attribute *pressure_sensor_attrs [] = { + &dev_attr_sensor_name, + &dev_attr_sensor_vendor, + NULL, +}; + +static struct device_attribute attr_pressure [] = +{ + MARU_ATTR_RW(enable), + MARU_ATTR_RW(poll_delay), +}; + +static struct attribute *maru_pressure_attribute[] = { + &attr_pressure[0].attr, + &attr_pressure[1].attr, + NULL +}; + +static struct attribute_group maru_pressure_attribute_group = { + .attrs = maru_pressure_attribute +}; + +static void pressure_clear(struct maru_pressure_data *data) { + if (data == NULL) + return; + + if (data->input_data) { + sysfs_remove_group(&data->input_data->dev.kobj, + &maru_pressure_attribute_group); + input_free_device(data->input_data); + } + + kfree(data); + data = NULL; +} + +static int set_initial_value(struct maru_pressure_data *data) +{ + int delay = 0; + int ret = 0; + int enable = 0; + char sensor_data [__MAX_BUF_SENSOR]; + + memset(sensor_data, 0, sizeof(sensor_data)); + + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_pressure_delay, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) { + ERR("failed to get initial delay time"); + return ret; + } + + delay = sensor_atoi(sensor_data); + + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_pressure_enable, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) { + ERR("failed to get initial enable"); + return ret; + } + + enable = sensor_atoi(sensor_data); + + if (delay < 0) { + ERR("weird value is set initial delay"); + return ret; + } + + atomic_set(&data->poll_delay, delay); + + if (enable) { + atomic_set(&data->enable, 1); + schedule_delayed_work(&data->work, 0); + } + + return ret; +} + +static int create_input_device(struct maru_pressure_data *data) +{ + int ret = 0; + struct input_dev *input_data = NULL; + + input_data = input_allocate_device(); + if (input_data == NULL) { + ERR("failed initialing input handler"); + pressure_clear(data); + return -ENOMEM; + } + + input_data->name = SENSOR_PRESSURE_INPUT_NAME; + input_data->id.bustype = BUS_I2C; + + set_bit(EV_REL, input_data->evbit); + input_set_capability(input_data, EV_REL, REL_HWHEEL); + input_set_capability(input_data, EV_REL, REL_DIAL); + input_set_capability(input_data, EV_REL, REL_WHEEL); + + data->input_data = input_data; + + ret = input_register_device(input_data); + if (ret) { + ERR("failed to register input data"); + pressure_clear(data); + return ret; + } + + input_set_drvdata(input_data, data); + + ret = sysfs_create_group(&input_data->dev.kobj, + &maru_pressure_attribute_group); + if (ret) { + pressure_clear(data); + ERR("failed initialing devices"); + return ret; + } + + return ret; +} + +int maru_pressure_init(struct virtio_sensor *vs) { + int ret = 0; + struct maru_pressure_data *data = NULL; + + INFO("maru_pressure device init starts."); + + data = kmalloc(sizeof(struct maru_pressure_data), GFP_KERNEL); + if (data == NULL) { + ERR("failed to create pressure data."); + return -ENOMEM; + } + + vs->pressure_handle = data; + data->vs = vs; + + INIT_DELAYED_WORK(&data->work, maru_pressure_input_work_func); + + // create name & vendor + ret = register_sensor_device(pressure_sensor_device, vs, + pressure_sensor_attrs, DRIVER_PRESSURE_NAME); + if (ret) { + ERR("failed to register pressure device"); + pressure_clear(data); + return -1; + } + + // create input + ret = create_input_device(data); + if (ret) { + ERR("failed to create input device"); + return ret; + } + + // set initial delay & enable + ret = set_initial_value(data); + if (ret) { + ERR("failed to set initial value"); + return ret; + } + + INFO("maru_pressure device init ends."); + + return ret; +} + +int maru_pressure_exit(struct virtio_sensor *vs) { + struct maru_pressure_data *data = NULL; + + data = (struct maru_pressure_data *)vs->pressure_handle; + pressure_clear(data); + INFO("maru_pressure device exit ends."); + return 0; +} diff --git a/drivers/maru/sensors/maru_proxi.c b/drivers/maru/sensors/maru_proxi.c new file mode 100644 index 0000000..1553cae --- /dev/null +++ b/drivers/maru/sensors/maru_proxi.c @@ -0,0 +1,453 @@ +/* + * Maru Virtio Proximity Sensor Device Driver + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * Jinhyung Choi + * Sangho Park + * YeongKyoon Lee + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributors: + * - S-Core Co., Ltd + * + */ + +#include +#include + +#include "maru_virtio_sensor.h" + +struct maru_proxi_data { + struct input_dev *input_data; + struct delayed_work work; + + struct virtio_sensor* vs; + + atomic_t enable; + atomic_t poll_delay; +}; + +static struct device *proxi_sensor_device; + +#ifdef SUPPORT_LEGACY_SENSOR +static struct device *l_proxi_sensor_device; +#endif + +static void maru_proxi_input_work_func(struct work_struct *work) { + + int poll_time = 200000000; + int enable = 0; + int ret = 0; + int proxi = 0; + char sensor_data[__MAX_BUF_SENSOR]; + struct maru_proxi_data *data = container_of((struct delayed_work *)work, + struct maru_proxi_data, work); + + LOG(1, "maru_proxi_input_work_func starts"); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + poll_time = atomic_read(&data->poll_delay); + + enable = atomic_read(&data->enable); + + if (enable) { + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_proxi, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (!ret) { + sscanf(sensor_data, "%d", &proxi); + if (proxi) + proxi = 1; + + LOG(1, "proxi_set %d", proxi); + + input_report_abs(data->input_data, ABS_DISTANCE, proxi); + input_sync(data->input_data); + } + } + + enable = atomic_read(&data->enable); + + LOG(1, "enable: %d, poll_time: %d", enable, poll_time); + if (enable) { + if (poll_time > 0) { + schedule_delayed_work(&data->work, nsecs_to_jiffies(poll_time)); + } else { + schedule_delayed_work(&data->work, 0); + } + } + + LOG(1, "maru_proxi_input_work_func ends"); + +} + +static ssize_t maru_name_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", MARU_PROXI_DEVICE_NAME); +} + +static ssize_t maru_vendor_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", MARU_SENSOR_DEVICE_VENDOR); +} + +static ssize_t maru_enable_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + char sensor_data[__MAX_BUF_SENSOR]; + int ret; + struct input_dev *input_data = to_input_dev(dev); + struct maru_proxi_data *data = input_get_drvdata(input_data); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_proxi_enable, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) + return sprintf(buf, "%d", -1); + + return sprintf(buf, "%s", sensor_data); +} + +static ssize_t maru_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct maru_proxi_data *data = input_get_drvdata(input_data); + int value = simple_strtoul(buf, NULL, 10); + + if (value != 0 && value != 1) + return count; + + mutex_lock(&data->vs->vqlock); + set_sensor_data(sensor_type_proxi_enable, buf); + mutex_unlock(&data->vs->vqlock); + + if (value) { + if (atomic_read(&data->enable) != 1) { + atomic_set(&data->enable, 1); + schedule_delayed_work(&data->work, 0); + + } + } else { + if (atomic_read(&data->enable) != 0) { + atomic_set(&data->enable, 0); + cancel_delayed_work(&data->work); + } + } + + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static ssize_t maru_poll_delay_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + char sensor_data[__MAX_BUF_SENSOR]; + int ret; + struct input_dev *input_data = to_input_dev(dev); + struct maru_proxi_data *data = input_get_drvdata(input_data); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_proxi_delay, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) + return sprintf(buf, "%d", -1); + + return sprintf(buf, "%s", sensor_data); +} + +static ssize_t maru_poll_delay_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct maru_proxi_data *data = input_get_drvdata(input_data); + int value = simple_strtoul(buf, NULL, 10); + + if (value < __MIN_DELAY_SENSOR) + return count; + + mutex_lock(&data->vs->vqlock); + set_sensor_data(sensor_type_proxi_delay, buf); + mutex_unlock(&data->vs->vqlock); + atomic_set(&data->poll_delay, value); + + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static struct device_attribute dev_attr_sensor_name = + __ATTR(name, S_IRUGO, maru_name_show, NULL); + +static struct device_attribute dev_attr_sensor_vendor = + __ATTR(vendor, S_IRUGO, maru_vendor_show, NULL); + +static struct device_attribute *proxi_sensor_attrs [] = { + &dev_attr_sensor_name, + &dev_attr_sensor_vendor, + NULL, +}; + +#ifdef SUPPORT_LEGACY_SENSOR +#define PROXI_NAME_STR "proxi_sim" + +static ssize_t proxi_name_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", PROXI_NAME_STR); +} + +static ssize_t enable_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + char sensor_data[__MAX_BUF_SENSOR]; + int ret; + struct input_dev *input_data = to_input_dev(dev); + struct maru_proxi_data *data = input_get_drvdata(input_data); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_proxi_enable, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) + return sprintf(buf, "%d", -1); + + return sprintf(buf, "%s", sensor_data); +} + +static ssize_t enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct maru_proxi_data *data = input_get_drvdata(input_data); + mutex_lock(&data->vs->vqlock); + set_sensor_data(sensor_type_proxi_enable, buf); + mutex_unlock(&data->vs->vqlock); + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static ssize_t vo_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + char sensor_data[__MAX_BUF_SENSOR]; + int ret; + struct input_dev *input_data = to_input_dev(dev); + struct maru_proxi_data *data = input_get_drvdata(input_data); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_proxi, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) + return sprintf(buf, "%d", -1); + + return sprintf(buf, "%s", sensor_data); +} + +static ssize_t vo_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct maru_proxi_data *data = input_get_drvdata(input_data); + mutex_lock(&data->vs->vqlock); + set_sensor_data(sensor_type_proxi, buf); + mutex_unlock(&data->vs->vqlock); + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static struct device_attribute dev_attr_l_sensor_name = + __ATTR(name, S_IRUGO, proxi_name_show, NULL); + +static DEVICE_ATTR(enable, 0644, enable_show, enable_store); +static DEVICE_ATTR(vo, 0644, vo_show, vo_store); + +static struct device_attribute *l_proxi_sensor_attrs [] = { + &dev_attr_l_sensor_name, + &dev_attr_enable, + &dev_attr_vo, + NULL, +}; +#endif + +static struct device_attribute attr_proxi [] = +{ + MARU_ATTR_RW(enable), + MARU_ATTR_RW(poll_delay), +}; + +static struct attribute *maru_proxi_attribute[] = { + &attr_proxi[0].attr, + &attr_proxi[1].attr, + NULL +}; + +static struct attribute_group maru_proxi_attribute_group = { + .attrs = maru_proxi_attribute +}; + +static void proxi_clear(struct maru_proxi_data *data) { + if (data == NULL) + return; + + if (data->input_data) { + sysfs_remove_group(&data->input_data->dev.kobj, + &maru_proxi_attribute_group); + input_free_device(data->input_data); + } + + kfree(data); + data = NULL; +} + +static int set_initial_value(struct maru_proxi_data *data) +{ + int delay = 0; + int ret = 0; + int enable = 0; + char sensor_data [__MAX_BUF_SENSOR]; + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_proxi_delay, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) { + ERR("failed to get initial delay time"); + return ret; + } + + delay = sensor_atoi(sensor_data); + + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_proxi_enable, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) { + ERR("failed to get initial enable"); + return ret; + } + + enable = sensor_atoi(sensor_data); + + if (delay < 0) { + ERR("weird value is set initial delay"); + return ret; + } + + atomic_set(&data->poll_delay, delay); + + if (enable) { + atomic_set(&data->enable, 1); + schedule_delayed_work(&data->work, 0); + } + + return ret; +} + +static int create_input_device(struct maru_proxi_data *data) +{ + int ret = 0; + struct input_dev *input_data = NULL; + + input_data = input_allocate_device(); + if (input_data == NULL) { + ERR("failed initialing input handler"); + proxi_clear(data); + return -ENOMEM; + } + + input_data->name = SENSOR_PROXI_INPUT_NAME; + + input_set_drvdata(input_data, data); + + set_bit(EV_ABS, input_data->evbit); + input_set_capability(input_data, EV_ABS, ABS_DISTANCE); + input_set_abs_params(input_data, ABS_DISTANCE, 0, 1, 0, 0); + + data->input_data = input_data; + + ret = input_register_device(input_data); + if (ret) { + ERR("failed to register input data"); + proxi_clear(data); + return ret; + } + + ret = sysfs_create_group(&input_data->dev.kobj, + &maru_proxi_attribute_group); + if (ret) { + proxi_clear(data); + ERR("failed initialing devices"); + return ret; + } + + return ret; +} + +int maru_proxi_init(struct virtio_sensor *vs) { + int ret = 0; + struct maru_proxi_data *data = NULL; + + INFO("maru_proxi device init starts."); + + data = kmalloc(sizeof(struct maru_proxi_data), GFP_KERNEL); + if (data == NULL) { + ERR("failed to create proxi data."); + return -ENOMEM; + } + + vs->proxi_handle = data; + data->vs = vs; + + INIT_DELAYED_WORK(&data->work, maru_proxi_input_work_func); + + // create name & vendor + ret = register_sensor_device(proxi_sensor_device, vs, + proxi_sensor_attrs, DRIVER_PROXI_NAME); + if (ret) { + ERR("failed to register proxi device"); + proxi_clear(data); + return -1; + } + +#ifdef SUPPORT_LEGACY_SENSOR + ret = l_register_sensor_device(l_proxi_sensor_device, vs, + l_proxi_sensor_attrs, DRIVER_PROXI_NAME); + if (ret) { + ERR("failed to register legacy proxi device"); + proxi_clear(data); + return -1; + } +#endif + + // create input + ret = create_input_device(data); + if (ret) { + ERR("failed to create input device"); + return ret; + } + + // set initial delay & enable + ret = set_initial_value(data); + if (ret) { + ERR("failed to set initial value"); + return ret; + } + + INFO("maru_proxi device init ends."); + + return ret; +} + +int maru_proxi_exit(struct virtio_sensor *vs) { + struct maru_proxi_data *data = NULL; + + data = (struct maru_proxi_data *)vs->proxi_handle; + proxi_clear(data); + INFO("maru_proxi device exit ends."); + return 0; +} diff --git a/drivers/maru/sensors/maru_rotation_vector.c b/drivers/maru/sensors/maru_rotation_vector.c new file mode 100644 index 0000000..ef03aa0 --- /dev/null +++ b/drivers/maru/sensors/maru_rotation_vector.c @@ -0,0 +1,359 @@ +/* + * Maru Virtio Rotation Vector Sensor Device Driver + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * Jinhyung Choi + * Sangho Park + * YeongKyoon Lee + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributors: + * - S-Core Co., Ltd + * + */ + +#include +#include + +#include "maru_virtio_sensor.h" + +struct maru_rotation_vector_data { + struct input_dev *input_data; + struct delayed_work work; + + struct virtio_sensor* vs; + + atomic_t enable; + atomic_t poll_delay; +}; + +static struct device *rotation_vector_sensor_device; + +static void maru_rotation_vector_input_work_func(struct work_struct *work) { + + int poll_time = 200000000; + int enable = 0; + int ret = 0; + int quad_a, quad_b, quad_c, quad_d, accuracy; + char sensor_data[__MAX_BUF_SENSOR]; + struct maru_rotation_vector_data *data = container_of((struct delayed_work *)work, + struct maru_rotation_vector_data, work); + + LOG(1, "maru_rotation_vector_input_work_func starts"); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + poll_time = atomic_read(&data->poll_delay); + + enable = atomic_read(&data->enable); + + if (enable) { + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_rotation_vector, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (!ret) { + sscanf(sensor_data, "%d,%d,%d,%d,%d", &quad_a, &quad_b, &quad_c, &quad_d, &accuracy); + LOG(1, "rotation_vector_set %d,%d,%d,%d,%d", quad_a, quad_b, quad_c, quad_d, accuracy); + + input_report_rel(data->input_data, REL_X, quad_a); + input_report_rel(data->input_data, REL_Y, quad_b); + input_report_rel(data->input_data, REL_Z, quad_c); + input_report_rel(data->input_data, REL_RX, quad_d); + input_report_rel(data->input_data, REL_RY, accuracy); + input_sync(data->input_data); + } + } + + enable = atomic_read(&data->enable); + + LOG(1, "enable: %d, poll_time: %d", enable, poll_time); + if (enable) { + if (poll_time > 0) { + schedule_delayed_work(&data->work, nsecs_to_jiffies(poll_time)); + } else { + schedule_delayed_work(&data->work, 0); + } + } + + LOG(1, "maru_rotation_vector_input_work_func ends"); + +} + +static ssize_t maru_name_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", MARU_ROTATION_DEVICE_NAME); +} + +static ssize_t maru_vendor_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", MARU_SENSOR_DEVICE_VENDOR); +} + +static ssize_t maru_enable_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + char sensor_data[__MAX_BUF_SENSOR]; + int ret; + struct input_dev *input_data = to_input_dev(dev); + struct maru_rotation_vector_data *data = input_get_drvdata(input_data); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_rotation_vector_enable, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) + return sprintf(buf, "%d", -1); + + return sprintf(buf, "%s", sensor_data); +} + +static ssize_t maru_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct maru_rotation_vector_data *data = input_get_drvdata(input_data); + int value = simple_strtoul(buf, NULL, 10); + + if (value != 0 && value != 1) + return count; + + mutex_lock(&data->vs->vqlock); + set_sensor_data(sensor_type_rotation_vector_enable, buf); + mutex_unlock(&data->vs->vqlock); + + if (value) { + if (atomic_read(&data->enable) != 1) { + atomic_set(&data->enable, 1); + schedule_delayed_work(&data->work, 0); + + } + } else { + if (atomic_read(&data->enable) != 0) { + atomic_set(&data->enable, 0); + cancel_delayed_work(&data->work); + } + } + + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static ssize_t maru_poll_delay_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + char sensor_data[__MAX_BUF_SENSOR]; + int ret; + struct input_dev *input_data = to_input_dev(dev); + struct maru_rotation_vector_data *data = input_get_drvdata(input_data); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_rotation_vector_delay, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) + return sprintf(buf, "%d", -1); + + return sprintf(buf, "%s", sensor_data); +} + +static ssize_t maru_poll_delay_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct maru_rotation_vector_data *data = input_get_drvdata(input_data); + int value = simple_strtoul(buf, NULL, 10); + + if (value < __MIN_DELAY_SENSOR) + return count; + + mutex_lock(&data->vs->vqlock); + set_sensor_data(sensor_type_rotation_vector_delay, buf); + mutex_unlock(&data->vs->vqlock); + atomic_set(&data->poll_delay, value); + + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static struct device_attribute dev_attr_sensor_name = + __ATTR(name, S_IRUGO, maru_name_show, NULL); + +static struct device_attribute dev_attr_sensor_vendor = + __ATTR(vendor, S_IRUGO, maru_vendor_show, NULL); + +static struct device_attribute *rotation_vector_sensor_attrs [] = { + &dev_attr_sensor_name, + &dev_attr_sensor_vendor, + NULL, +}; + +static struct device_attribute attr_rotation_vector [] = +{ + MARU_ATTR_RW(enable), + MARU_ATTR_RW(poll_delay), +}; + +static struct attribute *maru_rotation_vector_attribute[] = { + &attr_rotation_vector[0].attr, + &attr_rotation_vector[1].attr, + NULL +}; + +static struct attribute_group maru_rotation_vector_attribute_group = { + .attrs = maru_rotation_vector_attribute +}; + +static void rotation_vector_clear(struct maru_rotation_vector_data *data) { + if (data == NULL) + return; + + if (data->input_data) { + sysfs_remove_group(&data->input_data->dev.kobj, + &maru_rotation_vector_attribute_group); + input_free_device(data->input_data); + } + + kfree(data); + data = NULL; +} + +static int set_initial_value(struct maru_rotation_vector_data *data) +{ + int delay = 0; + int ret = 0; + char sensor_data [__MAX_BUF_SENSOR]; + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_rotation_vector_delay, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) { + ERR("failed to get initial delay time"); + return ret; + } + + delay = sensor_atoi(sensor_data); + if (delay < 0) { + ERR("weird value is set initial delay"); + return ret; + } + + atomic_set(&data->poll_delay, delay); + + memset(sensor_data, 0, sizeof(sensor_data)); + sensor_data[0] = '0'; + mutex_lock(&data->vs->vqlock); + set_sensor_data(sensor_type_rotation_vector_enable, sensor_data); + mutex_unlock(&data->vs->vqlock); + atomic_set(&data->enable, 0); + + return ret; +} + +static int create_input_device(struct maru_rotation_vector_data *data) +{ + int ret = 0; + struct input_dev *input_data = NULL; + + input_data = input_allocate_device(); + if (input_data == NULL) { + ERR("failed initialing input handler"); + rotation_vector_clear(data); + return -ENOMEM; + } + + input_data->name = SENSOR_ROTATION_INPUT_NAME; + input_data->id.bustype = BUS_I2C; + + set_bit(EV_REL, input_data->evbit); + set_bit(EV_SYN, input_data->evbit); + input_set_capability(input_data, EV_REL, REL_X); + input_set_capability(input_data, EV_REL, REL_Y); + input_set_capability(input_data, EV_REL, REL_Z); + input_set_capability(input_data, EV_REL, REL_RX); + input_set_capability(input_data, EV_REL, REL_RY); + + data->input_data = input_data; + + ret = input_register_device(input_data); + if (ret) { + ERR("failed to register input data"); + rotation_vector_clear(data); + return ret; + } + + input_set_drvdata(input_data, data); + + ret = sysfs_create_group(&input_data->dev.kobj, + &maru_rotation_vector_attribute_group); + if (ret) { + rotation_vector_clear(data); + ERR("failed initialing devices"); + return ret; + } + + return ret; +} + +int maru_rotation_vector_init(struct virtio_sensor *vs) { + int ret = 0; + struct maru_rotation_vector_data *data = NULL; + + INFO("maru_rotation_vector device init starts."); + + data = kmalloc(sizeof(struct maru_rotation_vector_data), GFP_KERNEL); + if (data == NULL) { + ERR("failed to create rotation_vector data."); + return -ENOMEM; + } + + vs->rotation_vector_handle = data; + data->vs = vs; + + INIT_DELAYED_WORK(&data->work, maru_rotation_vector_input_work_func); + + // create name & vendor + ret = register_sensor_device(rotation_vector_sensor_device, vs, + rotation_vector_sensor_attrs, DRIVER_ROTATION_NAME); + if (ret) { + ERR("failed to register rotation_vector device"); + rotation_vector_clear(data); + return -1; + } + + // create input + ret = create_input_device(data); + if (ret) { + ERR("failed to create input device"); + return ret; + } + + // set initial delay & enable + ret = set_initial_value(data); + if (ret) { + ERR("failed to set initial value"); + return ret; + } + + INFO("maru_rotation_vector device init ends."); + + return ret; +} + +int maru_rotation_vector_exit(struct virtio_sensor *vs) { + struct maru_rotation_vector_data *data = NULL; + + data = (struct maru_rotation_vector_data *)vs->rotation_vector_handle; + rotation_vector_clear(data); + INFO("maru_rotation_vector device exit ends."); + return 0; +} diff --git a/drivers/maru/sensors/maru_uv.c b/drivers/maru/sensors/maru_uv.c new file mode 100644 index 0000000..bf45384 --- /dev/null +++ b/drivers/maru/sensors/maru_uv.c @@ -0,0 +1,360 @@ +/* + * Maru Virtio UltraViolet Sensor Device Driver + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * Jinhyung Choi + * Sangho Park + * YeongKyoon Lee + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributors: + * - S-Core Co., Ltd + * + */ + +#include +#include + +#include "maru_virtio_sensor.h" + +struct maru_uv_data { + struct input_dev *input_data; + struct delayed_work work; + + struct virtio_sensor* vs; + + atomic_t enable; + atomic_t poll_delay; +}; + +static struct device *uv_sensor_device; + +static void maru_uv_input_work_func(struct work_struct *work) { + + int poll_time = 200000000; + int enable = 0; + int ret = 0; + int uv = 0; + char sensor_data[__MAX_BUF_SENSOR]; + struct maru_uv_data *data = container_of((struct delayed_work *)work, + struct maru_uv_data, work); + + LOG(1, "maru_uv_input_work_func starts"); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + poll_time = atomic_read(&data->poll_delay); + + enable = atomic_read(&data->enable); + + if (enable) { + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_uv, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (!ret) { + sscanf(sensor_data, "%d", &uv); + LOG(1, "uv_set %d", uv); + + input_report_rel(data->input_data, REL_MISC, (uv + 1)); + input_sync(data->input_data); + } + } + + enable = atomic_read(&data->enable); + + LOG(1, "enable: %d, poll_time: %d", enable, poll_time); + if (enable) { + if (poll_time > 0) { + schedule_delayed_work(&data->work, nsecs_to_jiffies(poll_time)); + } else { + schedule_delayed_work(&data->work, 0); + } + } + + LOG(1, "maru_uv_input_work_func ends"); + +} + +static ssize_t maru_name_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", MARU_UV_DEVICE_NAME); +} + +static ssize_t maru_vendor_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", MARU_SENSOR_DEVICE_VENDOR); +} + +static ssize_t maru_enable_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + char sensor_data[__MAX_BUF_SENSOR]; + int ret; + struct input_dev *input_data = to_input_dev(dev); + struct maru_uv_data *data = input_get_drvdata(input_data); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_uv_enable, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) + return sprintf(buf, "%d", -1); + + return sprintf(buf, "%s", sensor_data); +} + +static ssize_t maru_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct maru_uv_data *data = input_get_drvdata(input_data); + int value = simple_strtoul(buf, NULL, 10); + + if (value != 0 && value != 1) + return count; + + mutex_lock(&data->vs->vqlock); + set_sensor_data(sensor_type_uv_enable, buf); + mutex_unlock(&data->vs->vqlock); + + if (value) { + if (atomic_read(&data->enable) != 1) { + atomic_set(&data->enable, 1); + schedule_delayed_work(&data->work, 0); + + } + } else { + if (atomic_read(&data->enable) != 0) { + atomic_set(&data->enable, 0); + cancel_delayed_work(&data->work); + } + } + + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static ssize_t maru_poll_delay_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + char sensor_data[__MAX_BUF_SENSOR]; + int ret; + struct input_dev *input_data = to_input_dev(dev); + struct maru_uv_data *data = input_get_drvdata(input_data); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_uv_delay, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) + return sprintf(buf, "%d", -1); + + return sprintf(buf, "%s", sensor_data); +} + +static ssize_t maru_poll_delay_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct maru_uv_data *data = input_get_drvdata(input_data); + int value = simple_strtoul(buf, NULL, 10); + + if (value < __MIN_DELAY_SENSOR) + return count; + + mutex_lock(&data->vs->vqlock); + set_sensor_data(sensor_type_uv_delay, buf); + mutex_unlock(&data->vs->vqlock); + atomic_set(&data->poll_delay, value); + + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static struct device_attribute dev_attr_sensor_name = + __ATTR(name, S_IRUGO, maru_name_show, NULL); + +static struct device_attribute dev_attr_sensor_vendor = + __ATTR(vendor, S_IRUGO, maru_vendor_show, NULL); + +static struct device_attribute *uv_sensor_attrs [] = { + &dev_attr_sensor_name, + &dev_attr_sensor_vendor, + NULL, +}; + +static struct device_attribute attr_uv [] = +{ + MARU_ATTR_RW(enable), + MARU_ATTR_RW(poll_delay), +}; + +static struct attribute *maru_uv_attribute[] = { + &attr_uv[0].attr, + &attr_uv[1].attr, + NULL +}; + +static struct attribute_group maru_uv_attribute_group = { + .attrs = maru_uv_attribute +}; + +static void uv_clear(struct maru_uv_data *data) { + if (data == NULL) + return; + + if (data->input_data) { + sysfs_remove_group(&data->input_data->dev.kobj, + &maru_uv_attribute_group); + input_free_device(data->input_data); + } + + kfree(data); + data = NULL; +} + +static int set_initial_value(struct maru_uv_data *data) +{ + int delay = 0; + int ret = 0; + int enable = 0; + char sensor_data [__MAX_BUF_SENSOR]; + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_uv_delay, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) { + ERR("failed to get initial delay time"); + return ret; + } + + delay = sensor_atoi(sensor_data); + + mutex_lock(&data->vs->vqlock); + ret = get_sensor_data(sensor_type_uv_enable, sensor_data); + mutex_unlock(&data->vs->vqlock); + if (ret) { + ERR("failed to get initial enable"); + return ret; + } + + enable = sensor_atoi(sensor_data); + + if (delay < 0) { + ERR("weird value is set initial delay"); + return ret; + } + + atomic_set(&data->poll_delay, delay); + + if (enable) { + atomic_set(&data->enable, 1); + schedule_delayed_work(&data->work, 0); + } + + return ret; +} + +static int create_input_device(struct maru_uv_data *data) +{ + int ret = 0; + struct input_dev *input_data = NULL; + + input_data = input_allocate_device(); + if (input_data == NULL) { + ERR("failed initialing input handler"); + uv_clear(data); + return -ENOMEM; + } + + input_data->name = SENSOR_UV_INPUT_NAME; + input_data->id.bustype = BUS_I2C; + + set_bit(EV_REL, input_data->evbit); + input_set_capability(input_data, EV_REL, REL_MISC); + + data->input_data = input_data; + + ret = input_register_device(input_data); + if (ret) { + ERR("failed to register input data"); + uv_clear(data); + return ret; + } + + input_set_drvdata(input_data, data); + + ret = sysfs_create_group(&input_data->dev.kobj, + &maru_uv_attribute_group); + if (ret) { + uv_clear(data); + ERR("failed initialing devices"); + return ret; + } + + return ret; +} + +int maru_uv_init(struct virtio_sensor *vs) { + int ret = 0; + struct maru_uv_data *data = NULL; + + INFO("maru_uv device init starts."); + + data = kmalloc(sizeof(struct maru_uv_data), GFP_KERNEL); + if (data == NULL) { + ERR("failed to create uv data."); + return -ENOMEM; + } + + vs->uv_handle = data; + data->vs = vs; + + INIT_DELAYED_WORK(&data->work, maru_uv_input_work_func); + + // create name & vendor + ret = register_sensor_device(uv_sensor_device, vs, + uv_sensor_attrs, DRIVER_UV_NAME); + if (ret) { + ERR("failed to register uv device"); + uv_clear(data); + return -1; + } + + // create input + ret = create_input_device(data); + if (ret) { + ERR("failed to create input device"); + return ret; + } + + // set initial delay & enable + ret = set_initial_value(data); + if (ret) { + ERR("failed to set initial value"); + return ret; + } + + INFO("maru_uv device init ends."); + + return ret; +} + +int maru_uv_exit(struct virtio_sensor *vs) { + struct maru_uv_data *data = NULL; + + data = (struct maru_uv_data *)vs->uv_handle; + uv_clear(data); + INFO("maru_uv device exit ends."); + return 0; +} diff --git a/drivers/maru/sensors/maru_virtio_sensor.c b/drivers/maru/sensors/maru_virtio_sensor.c new file mode 100644 index 0000000..6bc38aa --- /dev/null +++ b/drivers/maru/sensors/maru_virtio_sensor.c @@ -0,0 +1,553 @@ +/* + * Maru Virtio Sensor Device Driver + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * Jinhyung Choi + * Daiyoung Kim + * YeongKyoon Lee + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributors: + * - S-Core Co., Ltd + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "maru_virtio_sensor.h" + +int sensor_driver_debug = 0; +module_param(sensor_driver_debug, int, 0644); +MODULE_PARM_DESC(sensor_driver_debug, "Turn on/off maru sensor debugging (default:off)."); + +static struct virtio_device_id id_table[] = { { VIRTIO_ID_SENSOR, + VIRTIO_DEV_ANY_ID }, { 0 }, }; + +static char sensor_data[__MAX_BUF_SENSOR]; + +struct virtio_sensor *vs; + +static DECLARE_WAIT_QUEUE_HEAD(wq); + +int sensor_atoi(const char *value) +{ + int val = 0; + + for (;; value++) { + switch (*value) { + case '0' ... '9': + val = 10*val+(*value-'0'); + break; + default: + return val; + } + } +} + +int register_sensor_device(struct device *dev, struct virtio_sensor *vs, + struct device_attribute *attributes[], const char* name) +{ + int i = 0, err = 0; + + if (!vs->sensor_class) { + ERR("sensor class is not created before make device"); + return -1; + } + + INFO("device creation: %s.", name); + + dev = device_create(vs->sensor_class, NULL, MKDEV(0,0), NULL, "%s", name); + if (dev < 0) { + ERR("register_device_create failed!"); + return -1; + } + + if (attributes == NULL) { + ERR("attributes is NULL."); + return -1; + } + + for (i = 0; attributes[i] != NULL; i++) { + if ((err = device_create_file(dev, attributes[i])) < 0) { + ERR("failed to create device file with attribute[%d - %d]", i, err); + return -1; + } + } + + INFO("register_sensor_device ends: %s.", name); + + return 0; +} + +#ifdef SUPPORT_LEGACY_SENSOR + +int l_register_sensor_device(struct device *dev, struct virtio_sensor *vs, + struct device_attribute *attributes[], const char* name) +{ + int i = 0, err = 0; + + if (!vs->l_sensor_class) { + ERR("l sensor class is not created before make device"); + return -1; + } + + dev = device_create(vs->l_sensor_class, NULL, MKDEV(0,0), NULL, "%s", name); + if (dev < 0) { + ERR("legacy register_device_create failed!"); + return -1; + } + + if (attributes == NULL) { + ERR("l sensor attributes is NULL."); + return -1; + } + + for (i = 0; attributes[i] != NULL; i++) { + if ((err = device_create_file(dev, attributes[i])) < 0) { + ERR("failed to create legacy device file with attribute[%d - %d]", i, err); + return -1; + } + } + + return 0; +} + +#endif + +static void sensor_vq_done(struct virtqueue *rvq) { + unsigned int len; + struct msg_info* msg; + + msg = (struct msg_info*) virtqueue_get_buf(vs->vq, &len); + if (msg == NULL) { + ERR("failed to virtqueue_get_buf"); + return; + } + + if (msg->req != request_answer) { + LOG(1, "set_sensor_data callback."); + mutex_lock(&vs->lock); + vs->flags = 1; + mutex_unlock(&vs->lock); + + wake_up_interruptible(&wq); + return; + } + + if (msg->buf == NULL) { + ERR("receive queue- message from host is NULL."); + return; + } + + mutex_lock(&vs->lock); + LOG(1, "msg buf: %s, req: %d, type: %d, vs->flags: %d", msg->buf, msg->req, msg->type, vs->flags); + + memset(sensor_data, 0, sizeof(sensor_data)); + strcpy(sensor_data, msg->buf); + vs->flags = 1; + mutex_unlock(&vs->lock); + + wake_up_interruptible(&wq); +} + +void set_sensor_data(int type, const char* buf) +{ + int err = 0; + + if (buf == NULL) { + ERR("set_sensor buf is NULL."); + return; + } + + if (vs == NULL) { + ERR("Invalid sensor handle"); + return; + } + + mutex_lock(&vs->lock); + memset(&vs->msginfo, 0, sizeof(vs->msginfo)); + + vs->msginfo.req = request_set; + vs->msginfo.type = type; + strcpy(vs->msginfo.buf, buf); + + LOG(1, "set_sensor_data type: %d, req: %d, buf: %s", + vs->msginfo.type, vs->msginfo.req, vs->msginfo.buf); + + mutex_unlock(&vs->lock); + + err = virtqueue_add_outbuf(vs->vq, vs->sg_svq, 1, &vs->msginfo, GFP_ATOMIC); + if (err < 0) { + ERR("failed to add_outbuf buffer to virtqueue (err = %d)", err); + return; + } + + virtqueue_kick(vs->vq); + + wait_event_interruptible(wq, vs->flags != 0); + + mutex_lock(&vs->lock); + vs->flags = 0; + mutex_unlock(&vs->lock); +} + +int get_sensor_data(int type, char* data) +{ + struct scatterlist *sgs[2]; + int err = 0; + + if (vs == NULL || data == NULL) { + ERR("Invalid sensor handle or data is NULL."); + return -1; + } + + mutex_lock(&vs->lock); + memset(&vs->msginfo, 0, sizeof(vs->msginfo)); + + vs->msginfo.req = request_get; + vs->msginfo.type = type; + + LOG(1, "get_sensor_data start type: %d, req: %d", + vs->msginfo.type, vs->msginfo.req); + + sgs[0] = &vs->sg_vq[0]; + sgs[1] = &vs->sg_vq[1]; + mutex_unlock(&vs->lock); + + err = virtqueue_add_sgs(vs->vq, sgs, 1, 1, &vs->msginfo, GFP_ATOMIC); + if (err < 0) { + ERR("failed to add_sgs buffer to virtqueue (err = %d)", err); + return err; + } + + virtqueue_kick(vs->vq); + + wait_event_interruptible(wq, vs->flags != 0); + + mutex_lock(&vs->lock); + vs->flags = 0; + memcpy(data, sensor_data, strlen(sensor_data)); + mutex_unlock(&vs->lock); + + LOG(1, "get_sensor_data end type: %d, data: %p", type, data); + return 0; +} + +static void device_init(struct virtio_sensor *vs) +{ + int ret = 0; + + if (vs->sensor_capability & sensor_cap_accel) { + ret = maru_accel_init(vs); + if (ret) { + vs->sensor_fail_init |= sensor_cap_accel; + ERR("failed to init accel with error %d", ret); + } + } + + if (vs->sensor_capability & sensor_cap_geo) { + ret = maru_geo_init(vs); + if (ret) { + vs->sensor_fail_init |= sensor_cap_geo; + ERR("failed to init geo with error %d", ret); + } + } + + if (vs->sensor_capability & sensor_cap_gyro) { + ret = maru_gyro_init(vs); + if (ret) { + vs->sensor_fail_init |= sensor_cap_gyro; + ERR("failed to init gyro with error %d", ret); + } + } + + if (vs->sensor_capability & sensor_cap_light) { + ret = maru_light_init(vs); + if (ret) { + vs->sensor_fail_init |= sensor_cap_light; + ERR("failed to init light with error %d", ret); + } + } + + if (vs->sensor_capability & sensor_cap_proxi) { + ret = maru_proxi_init(vs); + if (ret) { + vs->sensor_fail_init |= sensor_cap_proxi; + ERR("failed to init proxi with error %d", ret); + } + } + + if (vs->sensor_capability & sensor_cap_rotation_vector) { + ret = maru_rotation_vector_init(vs); + if (ret) { + vs->sensor_fail_init |= sensor_cap_rotation_vector; + ERR("failed to init rotation vector with error %d", ret); + } + } + + if (vs->sensor_capability & sensor_cap_haptic) { + ret = maru_haptic_init(vs); + if (ret) { + vs->sensor_fail_init |= sensor_cap_haptic; + ERR("failed to init haptic with error %d", ret); + } + } + + if (vs->sensor_capability & sensor_cap_pressure) { + ret = maru_pressure_init(vs); + if (ret) { + vs->sensor_fail_init |= sensor_cap_pressure; + ERR("failed to init pressure with error %d", ret); + } + } + + if (vs->sensor_capability & sensor_cap_uv) { + ret = maru_uv_init(vs); + if (ret) { + vs->sensor_fail_init |= sensor_cap_uv; + ERR("failed to init uv with error %d", ret); + } + } + + if (vs->sensor_capability & sensor_cap_hrm) { + ret = maru_hrm_init(vs); + if (ret) { + vs->sensor_fail_init |= sensor_cap_hrm; + ERR("failed to init hrm with error %d", ret); + } + } +} + +static void device_exit(struct virtio_sensor *vs) +{ + if (vs->sensor_capability & sensor_cap_accel && + !(vs->sensor_fail_init & sensor_cap_accel)) { + maru_accel_exit(vs); + } + + if (vs->sensor_capability & sensor_cap_geo && + !(vs->sensor_fail_init & sensor_cap_geo)) { + maru_geo_exit(vs); + } + + if (vs->sensor_capability & sensor_cap_gyro && + !(vs->sensor_fail_init & sensor_cap_gyro)) { + maru_gyro_exit(vs); + } + + if (vs->sensor_capability & sensor_cap_light && + !(vs->sensor_fail_init & sensor_cap_light)) { + maru_light_exit(vs); + } + + if (vs->sensor_capability & sensor_cap_proxi && + !(vs->sensor_fail_init & sensor_cap_proxi)) { + maru_proxi_exit(vs); + } + + if (vs->sensor_capability & sensor_cap_rotation_vector && + !(vs->sensor_fail_init & sensor_cap_rotation_vector)) { + maru_rotation_vector_exit(vs); + } + + if (vs->sensor_capability & sensor_cap_haptic && + !(vs->sensor_fail_init & sensor_cap_haptic)) { + maru_haptic_exit(vs); + } + + if (vs->sensor_capability & sensor_cap_pressure && + !(vs->sensor_fail_init & sensor_cap_pressure)) { + maru_pressure_exit(vs); + } + + if (vs->sensor_capability & sensor_cap_uv && + !(vs->sensor_fail_init & sensor_cap_uv)) { + maru_uv_exit(vs); + } + + if (vs->sensor_capability & sensor_cap_hrm && + !(vs->sensor_fail_init & sensor_cap_hrm)) { + maru_hrm_exit(vs); + } +} + +static void cleanup(struct virtio_device* dev) { + dev->config->del_vqs(dev); + + if (vs == NULL) + return; + + if (vs->sensor_class) { + device_destroy(vs->sensor_class, MKDEV(0,0)); + class_destroy(vs->sensor_class); + } + +#ifdef SUPPORT_LEGACY_SENSOR + if (vs->l_sensor_class) { + device_destroy(vs->l_sensor_class, MKDEV(0,0)); + class_destroy(vs->l_sensor_class); + } +#endif + + kfree(vs); + vs = NULL; +} + +static int sensor_probe(struct virtio_device* dev) +{ + int ret = 0; + char sensor_data[__MAX_BUF_SENSOR]; + + INFO("Sensor probe starts"); + + vs = kmalloc(sizeof(struct virtio_sensor), GFP_KERNEL); + if (!vs) { + ERR("failed to allocate sensor structure."); + return -ENOMEM; + } + + vs->vdev = dev; + dev->priv = vs; + + vs->sensor_class = class_create(THIS_MODULE, SENSOR_CLASS_NAME); + if (IS_ERR(vs->sensor_class)) { + ERR("sensor class creation is failed."); + return PTR_ERR(vs->sensor_class); + } + +#ifdef SUPPORT_LEGACY_SENSOR + vs->l_sensor_class = class_create(THIS_MODULE, L_SENSOR_CLASS_NAME); + if (IS_ERR(vs->sensor_class)) { + ERR("sensor class creation is failed."); + return PTR_ERR(vs->sensor_class); + } +#endif + + vs->vq = virtio_find_single_vq(dev, sensor_vq_done, "sensor"); + if (IS_ERR(vs->vq)) { + cleanup(dev); + ERR("failed to init virt queue"); + return ret; + } + + virtqueue_enable_cb(vs->vq); + + sg_init_one(&vs->sg_vq[0], &vs->msginfo, sizeof(vs->msginfo)); + sg_init_one(&vs->sg_vq[1], &vs->msginfo, sizeof(vs->msginfo)); + + sg_init_one(vs->sg_svq, &vs->msginfo, sizeof(vs->msginfo)); + + mutex_init(&vs->lock); + mutex_init(&vs->vqlock); + + memset(sensor_data, 0, sizeof(sensor_data)); + mutex_lock(&vs->vqlock); + ret = get_sensor_data(sensor_type_list, sensor_data); + mutex_unlock(&vs->vqlock); + if (ret) { + ERR("sensor capability data is null."); + cleanup(dev); + return ret; + } + + INFO("sensor raw capability is %s", sensor_data); + vs->sensor_capability = sensor_atoi(sensor_data); + INFO("sensor capability is %02x", vs->sensor_capability); + + if (vs->sensor_capability == 0) { + ERR("No sensor devices "); + cleanup(dev); + return ret; + } + + device_init(vs); + + if (vs->sensor_capability == vs->sensor_fail_init) { + ERR("failed initialing all devices"); + cleanup(dev); + return ret; + } + + INFO("Sensor probe completes"); + + return ret; +} + +static void sensor_remove(struct virtio_device* dev) +{ + struct virtio_sensor* vs = dev->priv; + if (!vs) + { + ERR("vs is NULL"); + return; + } + + dev->config->reset(dev); + + device_exit(vs); + + cleanup(dev); + + INFO("Sensor driver is removed."); +} + +MODULE_DEVICE_TABLE(virtio, id_table); + +static struct virtio_driver virtio_sensor_driver = { + .driver = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE , + }, + .id_table = id_table, + .probe = sensor_probe, + .remove = sensor_remove, +}; + + +static int __init sensor_init(void) +{ + INFO("Sensor driver initialized."); + + return register_virtio_driver(&virtio_sensor_driver); +} + +static void __exit sensor_exit(void) +{ + unregister_virtio_driver(&virtio_sensor_driver); + + INFO("Sensor driver is destroyed."); +} + +module_init(sensor_init); +module_exit(sensor_exit); + +MODULE_LICENSE("GPL2"); +MODULE_AUTHOR("Jinhyung Choi "); +MODULE_DESCRIPTION("Emulator Virtio Sensor Driver"); + diff --git a/drivers/maru/sensors/maru_virtio_sensor.h b/drivers/maru/sensors/maru_virtio_sensor.h new file mode 100644 index 0000000..580b6e0 --- /dev/null +++ b/drivers/maru/sensors/maru_virtio_sensor.h @@ -0,0 +1,294 @@ +/* + * Maru Virtio Sensor Device Driver + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * Jinhyung Choi + * Sangho Park + * YeongKyoon Lee + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributors: + * - S-Core Co., Ltd + * + */ + +#ifndef _MARU_VIRTIO_SENSOR_H +#define _MARU_VIRTIO_SENSOR_H + +#include +#include +#include +#include + +#define SUPPORT_LEGACY_SENSOR 1 + +enum request_cmd { + request_get = 0, + request_set, + request_answer +}; + +enum sensor_types { + sensor_type_list = 0, + sensor_type_accel, + sensor_type_accel_enable, + sensor_type_accel_delay, + sensor_type_geo, + sensor_type_geo_enable, // 5 + sensor_type_geo_delay, + sensor_type_gyro, + sensor_type_gyro_enable, + sensor_type_gyro_delay, + sensor_type_gyro_x, // 10 + sensor_type_gyro_y, + sensor_type_gyro_z, + sensor_type_light, + sensor_type_light_enable, + sensor_type_light_delay, // 15 + sensor_type_light_adc, + sensor_type_light_level, + sensor_type_proxi, + sensor_type_proxi_enable, + sensor_type_proxi_delay, // 20 + sensor_type_rotation_vector, + sensor_type_rotation_vector_enable, + sensor_type_rotation_vector_delay, + sensor_type_mag, + sensor_type_tilt, // 25 + sensor_type_pressure, + sensor_type_pressure_enable, + sensor_type_pressure_delay, + sensor_type_uv, + sensor_type_uv_enable, + sensor_type_uv_delay, + sensor_type_hrm, + sensor_type_hrm_heart, + sensor_type_hrm_rri, + sensor_type_hrm_enable, + sensor_type_hrm_delay, + sensor_type_max +}; + +enum sensor_capabilities { + sensor_cap_accel = 0x0001, + sensor_cap_geo = 0x0002, + sensor_cap_gyro = 0x0004, + sensor_cap_light = 0x0008, + sensor_cap_proxi = 0x0010, + sensor_cap_rotation_vector = 0x0020, + sensor_cap_haptic = 0x0040, + sensor_cap_pressure = 0x0080, + sensor_cap_uv = 0x0100, + sensor_cap_hrm = 0x0200 +}; + +#define __MAX_BUF_SIZE 1024 +#define __MAX_BUF_SENSOR 32 + +#define __MIN_DELAY_SENSOR 1000000 +#define __MAX_DELAY_SENSOR INT_MAX + +struct msg_info { + char buf[__MAX_BUF_SIZE]; + + uint16_t type; + uint16_t req; +}; + +#ifdef SUPPORT_LEGACY_SENSOR +# define L_SENSOR_CLASS_NAME "sensor" +#endif + +struct virtio_sensor { + struct virtio_device* vdev; + struct virtqueue* vq; + + struct msg_info msginfo; + struct scatterlist sg_vq[2]; + struct scatterlist sg_svq[1]; + + int flags; + struct mutex lock; + struct mutex vqlock; + + struct class* sensor_class; + +#ifdef SUPPORT_LEGACY_SENSOR + struct class* l_sensor_class; +#endif + + int sensor_capability; + int sensor_fail_init; + + void* accel_handle; + void* geo_handle; + void* gyro_handle; + void* light_handle; + void* proxi_handle; + void* rotation_vector_handle; + void* haptic_handle; + void* pressure_handle; + void* uv_handle; + void* hrm_handle; +}; + +#define MARU_DEVICE_ATTR(_name) \ + struct device_attribute dev_attr_##_name = MARU_ATTR_RONLY(_name) + +#define MARU_ATTR_RONLY(_name) { \ + .attr = { .name = __stringify(_name), .mode = 0444 }, \ + .show = maru_##_name##_show, \ +} + +#define MARU_ATTR_RW(_name) { \ + .attr = {.name = __stringify(_name), .mode = 0644 }, \ + .show = maru_##_name##_show, \ + .store = maru_##_name##_store, \ +} + +int sensor_atoi(const char *value); + +int register_sensor_device(struct device *dev, struct virtio_sensor *vs, + struct device_attribute *attributes[], const char* name); + +#ifdef SUPPORT_LEGACY_SENSOR +int l_register_sensor_device(struct device *dev, struct virtio_sensor *vs, + struct device_attribute *attributes[], const char* name); +#endif + +void set_sensor_data(int type, const char* buf); +int get_sensor_data(int type, char* data); + +#define SENSOR_CLASS_NAME "sensors" +#define MARU_SENSOR_DEVICE_VENDOR "Tizen_SDK" + +#define DRIVER_ACCEL_NAME "accel" +#define SENSOR_ACCEL_INPUT_NAME "accelerometer_sensor" +#define MARU_ACCEL_DEVICE_NAME "maru_sensor_accel_1" + +#define DRIVER_GEO_NAME "geo" +#define SENSOR_GEO_INPUT_NAME "geomagnetic_sensor" +#define MARU_GEO_DEVICE_NAME "maru_sensor_geo_1" + +#define DRIVER_GYRO_NAME "gyro" +#define SENSOR_GYRO_INPUT_NAME "gyro_sensor" +#define MARU_GYRO_DEVICE_NAME "maru_sensor_gyro_1" + +#define DRIVER_LIGHT_NAME "light" +#define SENSOR_LIGHT_INPUT_NAME "light_sensor" +#define MARU_LIGHT_DEVICE_NAME "maru_sensor_light_1" + +#define DRIVER_PROXI_NAME "proxi" +#define SENSOR_PROXI_INPUT_NAME "proximity_sensor" +#define MARU_PROXI_DEVICE_NAME "maru_sensor_proxi_1" + +#define DRIVER_ROTATION_NAME "rotation" +#define SENSOR_ROTATION_INPUT_NAME "rot_sensor" +#define MARU_ROTATION_DEVICE_NAME "maru_sensor_rotation_vector_1" + +#define SENSOR_HAPTIC_INPUT_NAME "haptic_sensor" + +#define DRIVER_PRESSURE_NAME "pressure" +#define SENSOR_PRESSURE_INPUT_NAME "pressure_sensor" +#define MARU_PRESSURE_DEVICE_NAME "maru_sensor_pressure_1" + +#define DRIVER_UV_NAME "ultraviolet" +#define SENSOR_UV_INPUT_NAME "uv_sensor" +#define MARU_UV_DEVICE_NAME "maru_sensor_uv_1" + +#define DRIVER_HRM_NAME "hrm" +#define SENSOR_HRM_INPUT_NAME "hrm_lib_sensor" +#define MARU_HRM_DEVICE_NAME "maru_sensor_hrm_1" + +// It locates /sys/module/maru_virtio_sensor/parameters/sensor_driver_debug +extern int sensor_driver_debug; + +#define ERR(fmt, ...) \ + printk(KERN_ERR "%s: " fmt "\n", SENSOR_CLASS_NAME, ##__VA_ARGS__) + +#define INFO(fmt, ...) \ + printk(KERN_INFO "%s: " fmt "\n", SENSOR_CLASS_NAME, ##__VA_ARGS__) + +#define LOG(log_level, fmt, ...) \ + do { \ + if (sensor_driver_debug == (log_level)) { \ + printk(KERN_INFO "%s: " fmt "\n", SENSOR_CLASS_NAME, ##__VA_ARGS__); \ + } \ + } while (0) + +/* + * Accelerometer device + */ +int maru_accel_init(struct virtio_sensor *vs); +int maru_accel_exit(struct virtio_sensor *vs); + +/* + * Geomagnetic device + */ +int maru_geo_init(struct virtio_sensor *vs); +int maru_geo_exit(struct virtio_sensor *vs); + +/* + * Gyroscope device + */ +int maru_gyro_init(struct virtio_sensor *vs); +int maru_gyro_exit(struct virtio_sensor *vs); + +/* + * Light device + */ +int maru_light_init(struct virtio_sensor *vs); +int maru_light_exit(struct virtio_sensor *vs); + +/* + * Proximity device + */ +int maru_proxi_init(struct virtio_sensor *vs); +int maru_proxi_exit(struct virtio_sensor *vs); + +/* + * Rotation Vector device + */ +int maru_rotation_vector_init(struct virtio_sensor *vs); +int maru_rotation_vector_exit(struct virtio_sensor *vs); + +/* + * Haptic device + */ +int maru_haptic_init(struct virtio_sensor *vs); +int maru_haptic_exit(struct virtio_sensor *vs); + +/* + * Pressure device + */ +int maru_pressure_init(struct virtio_sensor *vs); +int maru_pressure_exit(struct virtio_sensor *vs); + +/* + * UV(UltraViolet) device + */ +int maru_uv_init(struct virtio_sensor *vs); +int maru_uv_exit(struct virtio_sensor *vs); + +/* + * HRM(Heart Beat Rate) device + */ +int maru_hrm_init(struct virtio_sensor *vs); +int maru_hrm_exit(struct virtio_sensor *vs); + +#endif diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index e0606c0..8ef4f16 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -24,6 +24,10 @@ source "drivers/gpu/ipu-v3/Kconfig" source "drivers/gpu/drm/Kconfig" +# for maru board +source "drivers/gpu/yagl/Kconfig" +# + menu "Frame buffer Devices" source "drivers/video/fbdev/Kconfig" endmenu diff --git a/include/drm/vigs_drm.h b/include/drm/vigs_drm.h new file mode 100644 index 0000000..9ae8245 --- /dev/null +++ b/include/drm/vigs_drm.h @@ -0,0 +1,217 @@ +/* + * vigs_drm.h + */ + +#ifndef _VIGS_DRM_H_ +#define _VIGS_DRM_H_ + +/* + * Bump this whenever driver interface changes. + */ +#define DRM_VIGS_DRIVER_VERSION 14 + +/* + * Surface access flags. + */ +#define DRM_VIGS_SAF_READ 1 +#define DRM_VIGS_SAF_WRITE 2 +#define DRM_VIGS_SAF_MASK 3 + +/* + * Number of DP framebuffers. + */ +#define DRM_VIGS_NUM_DP_FB_BUF 4 + +/* + * DP memory types. + */ +#define DRM_VIGS_DP_FB_Y 2 +#define DRM_VIGS_DP_FB_C 3 + +struct drm_vigs_get_protocol_version +{ + uint32_t version; +}; + +struct drm_vigs_create_surface +{ + uint32_t width; + uint32_t height; + uint32_t stride; + uint32_t format; + int scanout; + uint32_t handle; + uint32_t size; + uint32_t id; +}; + +struct drm_vigs_create_execbuffer +{ + uint32_t size; + uint32_t handle; +}; + +struct drm_vigs_gem_map +{ + uint32_t handle; + int track_access; + unsigned long address; +}; + +struct drm_vigs_gem_wait +{ + uint32_t handle; +}; + +struct drm_vigs_surface_info +{ + uint32_t handle; + uint32_t width; + uint32_t height; + uint32_t stride; + uint32_t format; + int scanout; + uint32_t size; + uint32_t id; +}; + +struct drm_vigs_exec +{ + uint32_t handle; +}; + +struct drm_vigs_surface_set_gpu_dirty +{ + uint32_t handle; +}; + +struct drm_vigs_surface_start_access +{ + unsigned long address; + uint32_t saf; +}; + +struct drm_vigs_surface_end_access +{ + unsigned long address; + int sync; +}; + +struct drm_vigs_create_fence +{ + int send; + uint32_t handle; + uint32_t seq; +}; + +struct drm_vigs_fence_wait +{ + uint32_t handle; +}; + +struct drm_vigs_fence_signaled +{ + uint32_t handle; + int signaled; +}; + +struct drm_vigs_fence_unref +{ + uint32_t handle; +}; + +struct drm_vigs_plane_set_zpos +{ + uint32_t plane_id; + int zpos; +}; + +struct drm_vigs_plane_set_transform +{ + uint32_t plane_id; + int hflip; + int vflip; + int rotation; +}; + +struct drm_vigs_dp_create_surface +{ + uint32_t dp_plane; + uint32_t dp_fb_buf; + uint32_t dp_mem_flag; + uint32_t width; + uint32_t height; + uint32_t stride; + uint32_t format; + uint32_t handle; + uint32_t size; + uint32_t id; +}; + +struct drm_vigs_dp_open_surface +{ + uint32_t dp_plane; + uint32_t dp_fb_buf; + uint32_t dp_mem_flag; + uint32_t handle; +}; + +#define DRM_VIGS_GET_PROTOCOL_VERSION 0x00 +#define DRM_VIGS_CREATE_SURFACE 0x01 +#define DRM_VIGS_CREATE_EXECBUFFER 0x02 +#define DRM_VIGS_GEM_MAP 0x03 +#define DRM_VIGS_GEM_WAIT 0x04 +#define DRM_VIGS_SURFACE_INFO 0x05 +#define DRM_VIGS_EXEC 0x06 +#define DRM_VIGS_SURFACE_SET_GPU_DIRTY 0x07 +#define DRM_VIGS_SURFACE_START_ACCESS 0x08 +#define DRM_VIGS_SURFACE_END_ACCESS 0x09 +#define DRM_VIGS_CREATE_FENCE 0x0A +#define DRM_VIGS_FENCE_WAIT 0x0B +#define DRM_VIGS_FENCE_SIGNALED 0x0C +#define DRM_VIGS_FENCE_UNREF 0x0D +#define DRM_VIGS_PLANE_SET_ZPOS 0x0E +#define DRM_VIGS_PLANE_SET_TRANSFORM 0x0F + +#define DRM_VIGS_DP_CREATE_SURFACE 0x20 +#define DRM_VIGS_DP_OPEN_SURFACE 0x21 + +#define DRM_IOCTL_VIGS_GET_PROTOCOL_VERSION DRM_IOR(DRM_COMMAND_BASE + \ + DRM_VIGS_GET_PROTOCOL_VERSION, struct drm_vigs_get_protocol_version) +#define DRM_IOCTL_VIGS_CREATE_SURFACE DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_VIGS_CREATE_SURFACE, struct drm_vigs_create_surface) +#define DRM_IOCTL_VIGS_CREATE_EXECBUFFER DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_VIGS_CREATE_EXECBUFFER, struct drm_vigs_create_execbuffer) +#define DRM_IOCTL_VIGS_GEM_MAP DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_VIGS_GEM_MAP, struct drm_vigs_gem_map) +#define DRM_IOCTL_VIGS_GEM_WAIT DRM_IOW(DRM_COMMAND_BASE + \ + DRM_VIGS_GEM_WAIT, struct drm_vigs_gem_wait) +#define DRM_IOCTL_VIGS_SURFACE_INFO DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_VIGS_SURFACE_INFO, struct drm_vigs_surface_info) +#define DRM_IOCTL_VIGS_EXEC DRM_IOW(DRM_COMMAND_BASE + \ + DRM_VIGS_EXEC, struct drm_vigs_exec) +#define DRM_IOCTL_VIGS_SURFACE_SET_GPU_DIRTY DRM_IOW(DRM_COMMAND_BASE + \ + DRM_VIGS_SURFACE_SET_GPU_DIRTY, struct drm_vigs_surface_set_gpu_dirty) +#define DRM_IOCTL_VIGS_SURFACE_START_ACCESS DRM_IOW(DRM_COMMAND_BASE + \ + DRM_VIGS_SURFACE_START_ACCESS, struct drm_vigs_surface_start_access) +#define DRM_IOCTL_VIGS_SURFACE_END_ACCESS DRM_IOW(DRM_COMMAND_BASE + \ + DRM_VIGS_SURFACE_END_ACCESS, struct drm_vigs_surface_end_access) +#define DRM_IOCTL_VIGS_CREATE_FENCE DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_VIGS_CREATE_FENCE, struct drm_vigs_create_fence) +#define DRM_IOCTL_VIGS_FENCE_WAIT DRM_IOW(DRM_COMMAND_BASE + \ + DRM_VIGS_FENCE_WAIT, struct drm_vigs_fence_wait) +#define DRM_IOCTL_VIGS_FENCE_SIGNALED DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_VIGS_FENCE_SIGNALED, struct drm_vigs_fence_signaled) +#define DRM_IOCTL_VIGS_FENCE_UNREF DRM_IOW(DRM_COMMAND_BASE + \ + DRM_VIGS_FENCE_UNREF, struct drm_vigs_fence_unref) +#define DRM_IOCTL_VIGS_PLANE_SET_ZPOS DRM_IOW(DRM_COMMAND_BASE + \ + DRM_VIGS_PLANE_SET_ZPOS, struct drm_vigs_plane_set_zpos) +#define DRM_IOCTL_VIGS_PLANE_SET_TRANSFORM DRM_IOW(DRM_COMMAND_BASE + \ + DRM_VIGS_PLANE_SET_TRANSFORM, struct drm_vigs_plane_set_transform) + +#define DRM_IOCTL_VIGS_DP_CREATE_SURFACE DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_VIGS_DP_CREATE_SURFACE, struct drm_vigs_dp_create_surface) +#define DRM_IOCTL_VIGS_DP_OPEN_SURFACE DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_VIGS_DP_OPEN_SURFACE, struct drm_vigs_dp_open_surface) + +#endif diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index d9ba49c..4d0dfc6 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -3004,4 +3004,11 @@ #define PCI_VENDOR_ID_OCZ 0x1b85 +#ifdef CONFIG_MARU +/* maru devices */ +#define PCI_VENDOR_ID_TIZEN 0xC9B5 +#define PCI_DEVICE_ID_VIRTUAL_BRIGHTNESS 0x1014 +#define PCI_DEVICE_ID_VIRTUAL_CAMERA 0x1018 +#endif + #endif /* _LINUX_PCI_IDS_H */ diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h index 77925f5..a4460fe 100644 --- a/include/uapi/linux/virtio_ids.h +++ b/include/uapi/linux/virtio_ids.h @@ -42,4 +42,21 @@ #define VIRTIO_ID_GPU 16 /* virtio GPU */ #define VIRTIO_ID_INPUT 18 /* virtio input */ +#ifdef CONFIG_MARU +/* maru devices */ +#define VIRTIO_ID_TOUCHSCREEN 31 /* virtio touchscreen */ +#define VIRTIO_ID_KEYBOARD 32 /* virtio keyboard */ +#define VIRTIO_ID_ESM 33 /* virtio Emulator Status Monitor */ +#define VIRTIO_ID_HWKEY 34 /* virtio hwkey */ +#define VIRTIO_ID_EVDI 35 /* virtio emulator virtual device interface */ +#define VIRTIO_ID_GL 36 /* virtio glmem */ +#define VIRTIO_ID_SENSOR 37 /* virtio sensor */ +#define VIRTIO_ID_NFC 38 /* virtio nfc */ +#define VIRTIO_ID_JACK 39 /* virtio jack */ +#define VIRTIO_ID_POWER 40 /* virtio power supply */ +#define VIRTIO_ID_VMODEM 41 /* virtio VMODEM */ +#define VIRTIO_ID_ROTARY 42 /* virtio rotary */ +#define VIRTIO_ID_TABLET 43 /* virtio tablet */ +#endif + #endif /* _LINUX_VIRTIO_IDS_H */ diff --git a/package/build.linux b/package/build.linux new file mode 100755 index 0000000..7ded132 --- /dev/null +++ b/package/build.linux @@ -0,0 +1,35 @@ +#!/bin/sh -xe +# clean +clean() +{ + if ["${TARGET_OS}" != "ubuntu-32" ] && ["${TARGET_OS}" != "ubuntu-64" ] + then + exit 1 + fi + + rm -rf .git +} + +# build +build() +{ + # for i386 (32bit) kernel + ./build-i386.sh + mv arch/x86/boot/bzImage ./bzImage.i386 + + make distclean + + # for x86-64 (64bit) kernel + ./build-x86_64.sh + mv arch/x86/boot/bzImage ./bzImage.x86_64 +} + +# install +install() +{ + KERNEL_DIR=$SRCDIR/package/3.0-emulator-kernel-x86.package.${TARGET_OS}/data/platforms/tizen-3.0/common/emulator/data/kernel + mkdir -p $KERNEL_DIR + + cp ./bzImage.i386 $KERNEL_DIR/bzImage.i386 + cp ./bzImage.x86_64 $KERNEL_DIR/bzImage.x86_64 +} diff --git a/package/changelog b/package/changelog new file mode 100644 index 0000000..2f1af6f --- /dev/null +++ b/package/changelog @@ -0,0 +1,208 @@ +* 3.14.16 +- maru-inputs: fix unexpected termination using maru-input devices +== Jinhyung Jo 2016-03-08 +* 3.14.15 +- smack: apply patches for socket fd passing +== Jinhyung Jo 2016-03-02 +* 3.14.14 +- PAT: apply PAT features for W/A +- sensor: modify the proximity value appropriately +== Sooyoung Ha 2016-01-27 +* 3.14.13 +- VIGS: set correct DPI value +== Jinhyung Jo 2015-11-23 +* 3.14.12 +- YaGL: Bump version with the device and platform +== Jinhyung Jo 2015-11-06 +* 3.14.11 +- tablet: added input buffer to virtqueue +- VIGS: Temporary W/A for the extension +- tuner: separate dvb_frontend build for tv extension +- tablet: removed unused codes +- tablet: added maru tablet driver +== SeokYeon Hwang 2015-09-10 +* 3.14.10 +- Merge branch 'tizen_2.4_develop' into tizen_3.0_develop +== Sooyoung Ha 2015-08-19 +* 3.14.9 +- not build security partially when extension supported +== Jinhyung Choi 2015-07-21 +* 3.14.8 +- not build ac97_codec & ac97_pcm when maru_extension config is on +== Jinhyung Choi 2015-07-21 +* 3.14.7 +- Ready to build for 64bit & extensions +- Allow smack patches +- maru-camera: Changed the method for passing the argument to/from device +== Jinhyung Jo 2015-07-15 +* 3.14.6 +- Apply MII +== Munkyu Im 2015-05-27 +* 3.14.5 +- x86, mm/ASLR: Fix stack randomization on 64-bit systems +- net: sctp: fix slab corruption from use after free on INIT collisions +- vfs: read file_handle only once in handle_to_path +== Sungmin Ha 2015-04-15 +* 3.14.4 +- initramfs: mount devtmpfs before switching root +== Sooyoung Ha 2015-03-25 +* 3.14.3 +- package: version up +== Sungmin Ha 2015-03-20 +* 3.14.2 +- sensor: a global sensor mutex for message transfer +== Jinhyung Choi 2015-03-12 +* 3.14.1 +- package: version up +== Sungmin Ha 2015-03-11 +* 2.0.8 +- Merge branch 'tizen_next_linux_3.14' into tizen_next +== SeokYeon Hwang 2014-12-22 +* 2.0.7 +- config: turn CAN on +== Alice Liu 2014-11-19 +* 2.0.6 +- v4l2-core: Modified error code in v4l_enumstd() +- config: turn SMP on +== Alice Liu 2014-11-10 +* 2.0.5 +- packaging: build only for emulator supported target +- VIGS: Remove rotation definitions +- packaging: workaround missing v3.12.18 tag from upstream git +- packaging: makes repo / tarball name matching and other config fixes +- perf tools: define _DEFAULT_SOURCE for glibc_2.20 +- sensor: added pressure, ultraviolet, and hrm sensor +- Revert "uname: Add Emulator specific name" +- remove IVI specific config file +- config: enable CONFIG_FHANDLE +== Alice Liu 2014-11-07 +* 2.0.4 +- packaging: Initial packaging on 3.12.18 for Tizen +- package: Prevent marking "+" at kernel version +== Alice Liu 2014-11-07 +* 2.0.3 +- VIGS: Implement plane flip/rotate +- VIGS: Support YUV420 planar format +- VIGS: fix DPMS deadlock +- VIGS: Support DP memory and planar pixel formats +- brillcodec: add new command for reducing I/O +== Jinhyung Jo 2014-08-28 +* 2.0.2 +- Add emulator specific uname +== Munkyu Im 2014-07-11 +* 2.0.1 +- sensors: re-structure with input_event +- maru_haptic: force feedback driver is added +- jack & power driver: MAX buf size to 512 +- evdi: added guest emuld connection message +== Jinhyung Choi 2014-06-20 +* 2.0.0 +- Major package version up +== SeokYeon Hwang 2014-06-10 +* 1.4.36 +- Apply Linux Kernel 3.12 +== GiWoong Kim 2014-05-13 +* 1.4.35 +- set touchscreen resolution +== GiWoong Kim 2014-04-14 +* 1.4.34 +- enabling selective sensors and jacks +== Jinhyung Choi 2014-03-20 +* 1.4.33 +- Implemented the HBM(High Brightness Mode) for the maru brightness. +== Jinhyung Jo 2014-03-07 +* 1.4.32 +- Data is moved into qemu for jacks, battery, and sensors +== Jinhyung Choi 2014-03-06 +* 1.4.31 +- Implemented multicore rendering and fences +== GiWoong Kim 2014-01-17 +* 1.4.30 +- enable configs to support spice +== Sungmin Ha 2013-12-18 +* 1.4.29 +- support multi session on nfc +== Munkyu Im 2013-11-12 +* 1.4.28 +- Support emulator suspend +== SeokYeon Hwang 2013-09-23 +* 1.4.27 +- package version up +== Munkyu Im 2013-09-23 +* 1.4.26 +- package version up +== Munkyu Im 2013-09-02 +* 1.4.25 +- Support virtio evdi, sensor, nfc +== Jinhyung Choi 2013-07-30 +* 1.4.24 +- modified structure of virtio-hwkey +== Sungmin Ha 2013-07-25 +* 1.4.23 +- fixed one of Smack bugs which is about IP packet access check. +== Kitae Kim 2013-06-28 +* 1.4.22 +- applied smack patches. +== Kitae Kim 2013-05-29 +* 1.4.21 +- added initializing vqidx when the host keyboard is turned on. +== Sungmin Ha 2013-05-21 +* 1.4.19 +- modified process of using virtio keyboard queue +== Sungmin Ha 2013-05-14 +* 1.4.18 +- increased virtio keyboard queue +== Sungmin Ha 2013-04-06 +* 1.4.17 +- added virtio hwkey driver and improved hwkey mapping +== Sungmin Ha 2013-03-20 +* 1.4.15 +- source clean-up for overlay and backlight module. +== Kitae Kim 2013-03-08 +* 1.4.14 +- Enable CONFIG_BLK_DEV_CRYPTOLOOP option. +== Kitae Kim 2013-01-24 +* 1.4.13 +- modified block device name for sdcard +== Sungmin Ha 2013-01-21 +* 1.4.12 +- Fixed a bug does not close when the device has been shut down with no streaming data. +== Jinhyung Jo 2013-01-15 +* 1.4.11 +- Fixed a bug when audio some codecs are decoded simultaneously. +- Source cleanup and codec driver can get and set offset of device memory for audio type. +== Kitae Kim 2012-12-20 +* 1.4.5 +- Modified touchscreen range +== GiWoong Kim 2012-11-27 +* 1.4.3 +- fixed a bug when virtio-keyboard is removed. +- input_free_device function cannot be used after calling input_unregister_device function. +== Kita Kim 2012-11-13 +* 1.4.2 +- enable virtio-pci, virtio-touchscreen, net-9p, 9pfs drivers for arm kernel. +- Those drivers are necessary for host fileshare feature and virtio-touchscreen-pci device on arm emulator. +== Kita Kim 2012-11-09 +* 1.4.1 +- Do not ignore a touch event on emulator. +- The current touch event may be same as the previous coordinate data. +== GiWoong.kim 2012-11-05 +* 1.3.24 +- The range of brightness level has been changed.(1 ~ 24 => 0 ~ 100) +== Jinhyung.jo 2012-10-31 +* 1.3.23 +- Modify dibs build script for arm package. +- Since arm toolchain has been installed on build environment, kernel source can support arm package. +== Kitae Kim 2012-10-31 +* 1.3.22 +- Merge tizen_arm branch into develop branch. +- Merge arm kernel source for emulator into x86 kernel source. +== Kitae Kim 2012-10-30 +* 1.3.20 +- Change kernel keycode of KEY_MENU. +- Since X key mapping has been changed, kernel keycode has to be changed. +== GiWoong Kim 2012-10-23 +* 1.3.19 +- Enable devtmpfs config. +- Since udev module has been updated kernel need to enable devtmfs config. +== Kitae Kim 2012-10-23 diff --git a/package/pkginfo.manifest b/package/pkginfo.manifest new file mode 100644 index 0000000..82e0d35 --- /dev/null +++ b/package/pkginfo.manifest @@ -0,0 +1,13 @@ +Source: emulator-kernel +Version: 4.4.0 +Maintainer: SeokYeon Hwang + +Package: 3.0-emulator-kernel-x86 +OS: ubuntu-32, ubuntu-64, windows-32, windows-64, macos-64 +Build-host-os: ubuntu-64 +Description: Tizen x86 Emulator Kernel + +#Package: 3.0-emulator-kernel-arm +#OS: ubuntu-32, ubuntu-64, windows-32, windows-64, macos-64 +#Build-host-os: ubuntu-64 +#Description: Tizen ARM Emulator Kernel diff --git a/ramfs/busybox.i386 b/ramfs/busybox.i386 new file mode 100755 index 0000000000000000000000000000000000000000..f4305fe222d6b9727a5c15267977cfe5e86c2164 GIT binary patch literal 1881656 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