* [PATCH v4] sched/fair: Add advisory flag for borrowing a timeslice
From: Khalid Aziz @ 2014-12-03 19:40 UTC (permalink / raw)
To: tglx, corbet, mingo, hpa, peterz, riel, akpm, rientjes, ak,
mgorman, raistlin, kirill.shutemov, atomlin, avagin, gorcunov,
serge.hallyn, athorlton, oleg, vdavydov, daeseok.youn, keescook,
yangds.fnst, sbauer, vishnu.ps, axboe, paulmck
Cc: Khalid Aziz, linux-kernel, linux-doc, linux-api
sched/fair: Add advisory flag for borrowing a timeslice
This patch adds a way for a task to request to borrow one timeslice
from future if it is about to be preempted, so it could delay
preemption and complete any critical task it is in the middle of.
This feature improves performance for apps that use userspace locking
across large number of threads, for example large databases and Java,
and similar solutions have been used for many years on other OSs.
This feature helps in situation where a task acquires a lock before
performing a critical operation on shared data and happens to have
acquired the lock just before its timeslice is up which means it gets
preempted before it completes its task. This lock being held causes
all other tasks that also acquire the same lock to perform their
critical operation on shared data, to start queueing up and causing
large number of context switches. This queueing problem can be avoided
if the task that acquires lock first could request scheduler to let it
borrow one timeslice once it enters its critical section and hence
allow it to complete its critical section without causing queueing
problem. If critical section completes before the task is due for
preemption, the task can desassert its request which causes scheduler
to proceed with normal preemption. A task sends the scheduler this
request by setting a flag in a memory location it has shared with the
kernel. Kernel uses bytes in the same memory location to let the task
know when its request for amnesty from preemption has been granted.
These rules apply to the use of this feature:
- Request to borrow timeslice is not guranteed to be honored.
- If the task is allowed to borrow, kernel will inform the task
of this. When this happens, task must yield the processor as soon
as it completes its critical section.
- If the task fails to yield processor after being allowed to
borrow, it is penalized by not honoring its next request for
extra timeslice.
- Task is charged additional time for the borrowed timeslice as
accumulated run time. This pushes it further down in consideration
for the next task to run.
This feature was tested with a TPC-C workload. TPC-C workload shows
a 3% improvement in tpcc throughput when using this feature, which
is a significant improvement.
A new sysctl tunable kernel.preempt_delay_available enables this
feature at run time. The kernel boots up with this feature disabled
by default.
Documentation file included in this patch contains details on how to
use this feature, and conditions associated with its use. This patch
also adds a new field in scheduler statistics which keeps track of
how many times a task was granted amnesty from preemption.
Signed-off-by: Khalid Aziz <khalid.aziz@oracle.com>
---
With this new version of this patch, the kernel will not enable
preemption delay by default. This feature must be turned on by using
sysctl tunable kernel.preempt_delay_available. With this change, there
are now two ways to eliminate the impact of this feature on systems
that do not intend to use it and are sensitive to scheduling delays
that may be caused by the use of this feature. This feature can be
configured out for custom built kernels. For pre-compiled kernels where
this feature may have been configured in, it will stay off until enabled
through sysctl tunable.
Changelog:
v4:
- Added a shared data structure to define the memory location
used for requesting preemption delay.
- Fixed a hole in the code that allowed preemption delay to
continue to happen if preemption delay feature was disabled
with sysctl after a task had already started using this.
- Removed the restriction on setting location of shared data
structure only when one is not set currently.
- Moved almost all conditionally compiled code into header files
- Cleaned up config dependency for CONFIG_SCHED_PREEMPT_DELAY
- Changed permission on preempt_delay_available sysctl to allow
users to read it.
- Updated documentation file to match the code changes
v3:
- Use prctl() syscall to give kernel the location for shared flag
instead of using a proc file.
- Disabled this feature by default on a newly booted kernel and
added a sysctl tunable to enable/disable it at runtime.
v2:
- Replaced mmap operation with a more memory efficient futex
like communication between userspace and kernel
- Added a flag to let userspace know if it was granted amnesty
- Added a penalty for tasks failing to yield CPU when they
are granted amnesty from pre-emption
v1:
- Initial RFC patch with mmap for communication between userspace
and kernel
Documentation/scheduler/sched-preempt-delay.txt | 112 ++++++++++++++++++++
arch/x86/Kconfig | 11 ++
include/linux/sched.h | 38 +++++++
include/linux/sched/sysctl.h | 4 +
include/uapi/linux/prctl.h | 3 +
include/uapi/linux/sched.h | 9 ++
kernel/fork.c | 2 +
kernel/sched/core.c | 1 +
kernel/sched/debug.c | 1 +
kernel/sched/fair.c | 129 +++++++++++++++++++++++-
kernel/sys.c | 6 ++
kernel/sysctl.c | 9 ++
12 files changed, 322 insertions(+), 3 deletions(-)
create mode 100644 Documentation/scheduler/sched-preempt-delay.txt
diff --git a/Documentation/scheduler/sched-preempt-delay.txt b/Documentation/scheduler/sched-preempt-delay.txt
new file mode 100644
index 0000000..4c9e111
--- /dev/null
+++ b/Documentation/scheduler/sched-preempt-delay.txt
@@ -0,0 +1,112 @@
+=================================
+What is preemption delay feature?
+=================================
+
+There are times when a userspace task is executing a critical section
+which gates a number of other tasks that want access to the same
+critical section. If the task holding the lock that guards this critical
+section happens to grab the lock just before its timeslice is up and is
+preempted by the scheduler, scheduler ends up scheduling other
+tasks which immediately try to grab the lock to enter the critical
+section. This only results in lots of context switches as tasks wake up
+and go to sleep immediately. If on the other hand, the original task
+were allowed to run for an extra timeslice, it could have completed
+executing its critical section allowing other tasks to make progress
+when they get scheduled. Preemption delay feature allows a task to
+request scheduler to let it borrow one extra timeslice, if possible.
+
+
+==================================
+Using the preemption delay feature
+==================================
+
+This feature is compiled in the kernel by setting
+CONFIG_SCHED_PREEMPT_DELAY in kernel configuration. By default, the
+kernel boots up with this feature disabled. Enable it using sysctl
+tunable kernel.preempt_delay_available. Once this feature is
+enabled, the userspace process communicates with the kernel using a
+4-byte memory location in its address space. This location must be
+aligned to 4-byte boundary. It first gives the kernel address for this
+memory location by making a prctl() system call with PR_SET_PREEMPT_DELAY
+option. This memory location is interpreted as the following data
+structure (defined in linux/sched.h):
+
+struct sched_delay_req {
+ unsigned char nopreempt; /* flag to request preemption delay */
+ unsigned char yield; /* flag from kernel indicating */
+ /* preemption delay was granted */
+ unsigned char rsvd[2]; /* reserved */
+};
+
+Task requests a preemption delay by writing a non-zero value to the
+first byte - nopreempt. Scheduler checks this value before preempting
+the task. Scheduler can choose to grant one and only an additional
+time slice to the task for each delay request but this delay is not
+guaranteed. If scheduler does grant an additional timeslice, it will
+set the flag in second byte. Upon completion of the section of code
+where the task wants preemption delay, task should check the second byte.
+If the flag in second byte is set, it should clear this flag and call
+sched_yield() so as to not hog the processor. If a thread was granted
+additional timeslice and it fails to call sched_yield(), scheduler
+will penalize it by denying its next request for additional timeslice.
+Following sample code illustrates how to use this feature:
+
+#include <linux/sched.h>
+
+int main()
+{
+ unsigned char buf[256];
+ struct sched_delay_req delay;
+
+ bzero(&delay, sizeof(delay));
+
+ /* Tell kernel where the flag lives */
+ prctl(PR_SET_PREEMPT_DELAY, &delay);
+
+ while (/* some condition is true */) {
+ /* do some work and get ready to enter critical section */
+ delay.nopreempt = 1;
+ /*
+ * Obtain lock for critical section
+ */
+ /*
+ * critical section
+ */
+ /*
+ * Release lock for critical section
+ */
+ delay.nopreempt = 0;
+ /* Give the CPU up if required */
+ if (delay.yield) {
+ delay.yield = 0;
+ sched_yield();
+ }
+ /* do some more work */
+ }
+ /*
+ * Tell kernel we are done asking for preemption delay
+ */
+ prctl(PR_SET_PREEMPT_DELAY, NULL);
+}
+
+
+====================
+Scheduler statistics
+====================
+
+Preemption delay features adds a new field to scheduler statictics -
+nr_preempt_delayed. This is a per thread statistic that tracks the
+number of times a thread was granted amnesty from preemption when it
+requested for one. "cat /proc/<pid>/task/<tid>/sched" will list this
+number along with other scheduler statistics.
+
+
+=====
+Notes
+=====
+
+1. If the location of shared flag is not aligned to 4-byte boundary,
+ prctl() will terminate with EFAULT.
+2. Userspace app should zero out the sched_delay_req structure before
+ giving kernel the address of this structure. Stale data in this
+ structure could cause unintended requests for preemption delay.
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 41a503c..6c24167 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -852,6 +852,17 @@ config SCHED_MC
making when dealing with multi-core CPU chips at a cost of slightly
increased overhead in some places. If unsure say N here.
+config SCHED_PREEMPT_DELAY
+ def_bool n
+ prompt "Scheduler preemption delay support"
+ ---help---
+ Say Y here if you want to be able to delay scheduler preemption
+ when possible by setting a flag in a memory location after
+ sharing the address of this location with kernel using
+ PR_SET_PREEMPT_DELAY prctl() call. See
+ Documentation/scheduler/sched-preempt-delay.txt for details.
+ If in doubt, say "N".
+
source "kernel/Kconfig.preempt"
config X86_UP_APIC
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 5e344bb..0b2f911 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1116,6 +1116,7 @@ struct sched_statistics {
u64 nr_wakeups_affine_attempts;
u64 nr_wakeups_passive;
u64 nr_wakeups_idle;
+ u64 nr_preempt_delayed;
};
#endif
@@ -1232,6 +1233,14 @@ enum perf_event_task_context {
perf_nr_task_contexts,
};
+#ifdef CONFIG_SCHED_PREEMPT_DELAY
+struct preempt_delay {
+ struct sched_delay_req *delay_req; /* delay request flag pointer */
+ unsigned char delay_granted; /* currently in delay */
+ unsigned char yield_penalty; /* failure to yield penalty */
+};
+#endif
+
struct task_struct {
volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
void *stack;
@@ -1324,6 +1333,9 @@ struct task_struct {
/* Revert to default priority/policy when forking */
unsigned sched_reset_on_fork:1;
unsigned sched_contributes_to_load:1;
+#ifdef CONFIG_SCHED_PREEMPT_DELAY
+ struct preempt_delay sched_preempt_delay;
+#endif
unsigned long atomic_flags; /* Flags needing atomic access. */
@@ -3031,4 +3043,30 @@ static inline unsigned long rlimit_max(unsigned int limit)
return task_rlimit_max(current, limit);
}
+#ifdef CONFIG_SCHED_PREEMPT_DELAY
+static inline void task_init_preempt_delay(struct task_struct *p)
+{
+ memset(&p->sched_preempt_delay, 0, sizeof(struct preempt_delay));
+}
+static inline void task_clear_preempt_yield(struct task_struct *p)
+{
+ p->sched_preempt_delay.yield_penalty = 0;
+}
+extern int preempt_delay_write(struct task_struct *task,
+ unsigned long preempt_delay_addr);
+#define SCHED_SET_PREEMPT_DELAY(a) preempt_delay_write(current, a)
+#define SCHED_GET_PREEMPT_DELAY(a) \
+ put_user((unsigned long)current->sched_preempt_delay.delay_req,\
+ (unsigned long __user *)a)
+#else
+static inline void task_init_preempt_delay(struct task_struct *p)
+{
+}
+static inline void task_clear_preempt_yield(struct task_struct *p)
+{
+}
+#define SCHED_SET_PREEMPT_DELAY(a) (-EINVAL)
+#define SCHED_GET_PREEMPT_DELAY(a) (-EINVAL)
+#endif /* CONFIG_SCHED_PREEMPT_DELAY */
+
#endif
diff --git a/include/linux/sched/sysctl.h b/include/linux/sched/sysctl.h
index 596a0e0..516f74e 100644
--- a/include/linux/sched/sysctl.h
+++ b/include/linux/sched/sysctl.h
@@ -107,4 +107,8 @@ extern int sysctl_numa_balancing(struct ctl_table *table, int write,
void __user *buffer, size_t *lenp,
loff_t *ppos);
+#ifdef CONFIG_SCHED_PREEMPT_DELAY
+extern int sysctl_preempt_delay_available;
+#endif
+
#endif /* _SCHED_SYSCTL_H */
diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h
index 513df75..ecfd2cd 100644
--- a/include/uapi/linux/prctl.h
+++ b/include/uapi/linux/prctl.h
@@ -179,4 +179,7 @@ struct prctl_mm_map {
#define PR_SET_THP_DISABLE 41
#define PR_GET_THP_DISABLE 42
+#define PR_SET_PREEMPT_DELAY 43
+#define PR_GET_PREEMPT_DELAY 44
+
#endif /* _LINUX_PRCTL_H */
diff --git a/include/uapi/linux/sched.h b/include/uapi/linux/sched.h
index b932be9..66a2f67 100644
--- a/include/uapi/linux/sched.h
+++ b/include/uapi/linux/sched.h
@@ -49,4 +49,13 @@
*/
#define SCHED_FLAG_RESET_ON_FORK 0x01
+/*
+ * struct for requesting preemption delay from scheduler
+ */
+struct sched_delay_req {
+ unsigned char nopreempt;
+ unsigned char yield;
+ unsigned char rsvd[2];
+};
+
#endif /* _UAPI_LINUX_SCHED_H */
diff --git a/kernel/fork.c b/kernel/fork.c
index 9b7d746..aea655b 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1672,6 +1672,8 @@ long do_fork(unsigned long clone_flags,
get_task_struct(p);
}
+ task_init_preempt_delay(p);
+
wake_up_new_task(p);
/* forking complete and child started to run, tell ptracer */
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 24beb9b..a926eea 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -4201,6 +4201,7 @@ SYSCALL_DEFINE0(sched_yield)
{
struct rq *rq = this_rq_lock();
+ task_clear_preempt_yield(current);
schedstat_inc(rq, yld_count);
current->sched_class->yield_task(rq);
diff --git a/kernel/sched/debug.c b/kernel/sched/debug.c
index ce33780..618d2ac 100644
--- a/kernel/sched/debug.c
+++ b/kernel/sched/debug.c
@@ -597,6 +597,7 @@ void proc_sched_show_task(struct task_struct *p, struct seq_file *m)
P(se.statistics.nr_wakeups_affine_attempts);
P(se.statistics.nr_wakeups_passive);
P(se.statistics.nr_wakeups_idle);
+ P(se.statistics.nr_preempt_delayed);
{
u64 avg_atom, avg_per_cpu;
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index ef2b104..a880c6f 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -428,6 +428,129 @@ find_matching_se(struct sched_entity **se, struct sched_entity **pse)
#endif /* CONFIG_FAIR_GROUP_SCHED */
+#ifdef CONFIG_SCHED_PREEMPT_DELAY
+int sysctl_preempt_delay_available;
+
+int
+preempt_delay_write(struct task_struct *task, unsigned long preempt_delay_addr)
+{
+ /*
+ * Do not allow write if preemption delay feature is disabled
+ */
+ if (!sysctl_preempt_delay_available)
+ return -EPERM;
+
+ if ((void *)preempt_delay_addr == NULL) {
+ task->sched_preempt_delay.delay_req = NULL;
+ return 0;
+ }
+
+ /*
+ * Validate the pointer. It should be naturally aligned
+ */
+ if (unlikely((preempt_delay_addr % sizeof(u32)) != 0))
+ return -EFAULT;
+ if (unlikely(!access_ok(rw, preempt_delay_addr,
+ sizeof(struct sched_delay_req))))
+ return -EFAULT;
+
+ task->sched_preempt_delay.delay_req =
+ (struct sched_delay_req *) preempt_delay_addr;
+ return 0;
+}
+
+/*
+ * delay_resched_rq(): Check if the task about to be preempted has
+ * requested an additional time slice. If it has, grant it additional
+ * timeslice once.
+ */
+static void
+delay_resched_rq(struct rq *rq)
+{
+ struct task_struct *curr = rq->curr;
+ struct sched_entity *se;
+ struct sched_delay_req *delay_req, delay_flag;
+ int ret;
+
+ if (!sysctl_preempt_delay_available)
+ goto resched_now;
+
+ /*
+ * Check if task is using pre-emption delay feature. If address
+ * for preemption delay request flag is not set, this task is
+ * not using preemption delay feature, we can reschedule without
+ * any delay
+ */
+ delay_req = curr->sched_preempt_delay.delay_req;
+ if (delay_req == NULL)
+ goto resched_now;
+
+ /*
+ * Pre-emption delay will be granted only once. If this task
+ * has already been granted delay, rechedule now
+ */
+ if (curr->sched_preempt_delay.delay_granted) {
+ curr->sched_preempt_delay.delay_granted = 0;
+ goto resched_now;
+ }
+
+ /*
+ * Get the value of preemption delay request flag from userspace.
+ * Task had already passed us the address where the flag is stored
+ * in userspace earlier. If there is a page fault accessing this
+ * flag in userspace, that means userspace has not touched this
+ * flag recently and we can assume no preemption delay is needed.
+ *
+ * If task is not requesting additional timeslice, resched now
+ */
+ pagefault_disable();
+ ret = __copy_from_user_inatomic(&delay_flag, delay_req,
+ sizeof(u32));
+ pagefault_enable();
+ if (ret || !delay_flag.nopreempt)
+ goto resched_now;
+
+ /*
+ * Current thread has requested preemption delay and has not
+ * been granted an extension yet. If this thread failed to yield
+ * processor after being granted amnesty last time, penalize it
+ * by not granting this delay request, otherwise give it an extra
+ * timeslice.
+ */
+ if (curr->sched_preempt_delay.yield_penalty) {
+ curr->sched_preempt_delay.yield_penalty = 0;
+ goto resched_now;
+ }
+
+ se = &curr->se;
+ curr->sched_preempt_delay.delay_granted = 1;
+
+ /*
+ * Set the penalty flag for failing to yield the processor after
+ * being granted immunity. This flag will be cleared in
+ * sched_yield() if the thread indeed calls sched_yield
+ */
+ curr->sched_preempt_delay.yield_penalty = 1;
+
+ /*
+ * Let the thread know it got amnesty and it should call
+ * sched_yield() when it is done to avoid penalty next time
+ * it wants amnesty.
+ */
+ delay_flag.nopreempt = 0;
+ delay_flag.yield = 1;
+ schedstat_inc(curr, se.statistics.nr_preempt_delayed);
+ __copy_to_user_inatomic(delay_req, &delay_flag, sizeof(u32));
+
+ return;
+
+resched_now:
+ resched_curr(rq);
+}
+#else
+#define delay_resched_rq(rq) resched_curr(rq)
+#endif /* CONFIG_SCHED_PREEMPT_DELAY */
+
static __always_inline
void account_cfs_rq_runtime(struct cfs_rq *cfs_rq, u64 delta_exec);
@@ -2951,7 +3074,7 @@ check_preempt_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr)
ideal_runtime = sched_slice(cfs_rq, curr);
delta_exec = curr->sum_exec_runtime - curr->prev_sum_exec_runtime;
if (delta_exec > ideal_runtime) {
- resched_curr(rq_of(cfs_rq));
+ delay_resched_rq(rq_of(cfs_rq));
/*
* The current task ran long enough, ensure it doesn't get
* re-elected due to buddy favours.
@@ -2975,7 +3098,7 @@ check_preempt_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr)
return;
if (delta > ideal_runtime)
- resched_curr(rq_of(cfs_rq));
+ delay_resched_rq(rq_of(cfs_rq));
}
static void
@@ -4792,7 +4915,7 @@ static void check_preempt_wakeup(struct rq *rq, struct task_struct *p, int wake_
return;
preempt:
- resched_curr(rq);
+ delay_resched_rq(rq);
/*
* Only set the backward buddy when the current task is still
* on the rq. This can happen when a wakeup gets interleaved
diff --git a/kernel/sys.c b/kernel/sys.c
index 1eaa2f0..a8b1eff 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -2203,6 +2203,12 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
me->mm->def_flags &= ~VM_NOHUGEPAGE;
up_write(&me->mm->mmap_sem);
break;
+ case PR_SET_PREEMPT_DELAY:
+ error = SCHED_SET_PREEMPT_DELAY(arg2);
+ break;
+ case PR_GET_PREEMPT_DELAY:
+ error = SCHED_GET_PREEMPT_DELAY(arg2);
+ break;
default:
error = -EINVAL;
break;
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 15f2511..c1cd344 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -1104,6 +1104,15 @@ static struct ctl_table kern_table[] = {
.proc_handler = proc_dointvec,
},
#endif
+#ifdef CONFIG_SCHED_PREEMPT_DELAY
+ {
+ .procname = "preempt_delay_available",
+ .data = &sysctl_preempt_delay_available,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+#endif
{ }
};
--
1.9.1
^ permalink raw reply related
* [PATCH v6] selftest: size: Add size test for Linux kernel
From: Tim Bird @ 2014-12-03 18:42 UTC (permalink / raw)
To: Shuah Khan, linux-api@vger.kernel.org
Cc: Thomas Petazzoni, Michael Ellerman,
linux-embedded@vger.kernel.org, linux-kernel@vger.kernel.org,
Josh Triplett
This test shows the amount of memory used by the system.
Note that this is dependent on the user-space that is loaded
when this program runs. Optimally, this program would be
run as the init program itself.
The program is optimized for size itself, to avoid conflating
its own execution with that of the system software.
The code is compiled statically, with no stdlibs. On my x86_64 system,
this results in a statically linked binary of less than 5K.
Signed-off-by: Tim Bird <tim.bird@sonymobile.com>
---
Changes from v5:
- remove #ifdef in Makefile (doh!)
- use variables in build command
- use different num_to_str, with less conversions
Changes from v4:
- make most routines static
- replace strip with gcc -s
- remove explicit reference to _start
- change --static to -static
- remove explicit reference to LIBGCC
- fix test description for ok and not ok paths, for test 1
Changes from v3:
- add more human-readable output
- put libgcc reference into a variable in Makefile
Changes from v2:
- fix copyright string (again!)
- use __builtin_strlen instead of my own strlen
- replace main with _start
Changes from v1:
- add return values to print routines
- add .gitignore file
- use more correct Copyright string in get_size.c
tools/testing/selftests/Makefile | 1 +
tools/testing/selftests/size/.gitignore | 1 +
tools/testing/selftests/size/Makefile | 12 ++++
tools/testing/selftests/size/get_size.c | 100 ++++++++++++++++++++++++++++++++
4 files changed, 114 insertions(+)
create mode 100644 tools/testing/selftests/size/.gitignore
create mode 100644 tools/testing/selftests/size/Makefile
create mode 100644 tools/testing/selftests/size/get_size.c
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index 45f145c..fa91aef 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -15,6 +15,7 @@ TARGETS += user
TARGETS += sysctl
TARGETS += firmware
TARGETS += ftrace
+TARGETS += size
TARGETS_HOTPLUG = cpu-hotplug
TARGETS_HOTPLUG += memory-hotplug
diff --git a/tools/testing/selftests/size/.gitignore b/tools/testing/selftests/size/.gitignore
new file mode 100644
index 0000000..189b781
--- /dev/null
+++ b/tools/testing/selftests/size/.gitignore
@@ -0,0 +1 @@
+get_size
diff --git a/tools/testing/selftests/size/Makefile b/tools/testing/selftests/size/Makefile
new file mode 100644
index 0000000..04dc25e
--- /dev/null
+++ b/tools/testing/selftests/size/Makefile
@@ -0,0 +1,12 @@
+CC = $(CROSS_COMPILE)gcc
+
+all: get_size
+
+get_size: get_size.c
+ $(CC) -static -ffreestanding -nostartfiles -s $< -o $@
+
+run_tests: all
+ ./get_size
+
+clean:
+ $(RM) get_size
diff --git a/tools/testing/selftests/size/get_size.c b/tools/testing/selftests/size/get_size.c
new file mode 100644
index 0000000..2d1af7c
--- /dev/null
+++ b/tools/testing/selftests/size/get_size.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2014 Sony Mobile Communications Inc.
+ *
+ * Licensed under the terms of the GNU GPL License version 2
+ *
+ * Selftest for runtime system size
+ *
+ * Prints the amount of RAM that the currently running system is using.
+ *
+ * This program tries to be as small as possible itself, to
+ * avoid perturbing the system memory utilization with its
+ * own execution. It also attempts to have as few dependencies
+ * on kernel features as possible.
+ *
+ * It should be statically linked, with startup libs avoided.
+ * It uses no library calls, and only the following 3 syscalls:
+ * sysinfo(), write(), and _exit()
+ *
+ * For output, it avoids printf (which in some C libraries
+ * has large external dependencies) by implementing it's own
+ * number output and print routines, and using __builtin_strlen()
+ */
+
+#include <sys/sysinfo.h>
+#include <unistd.h>
+
+#define STDOUT_FILENO 1
+
+static int print(const char *s)
+{
+ return write(STDOUT_FILENO, s, __builtin_strlen(s));
+}
+
+static inline char *num_to_str(unsigned long num, char *buf, int len)
+{
+ unsigned int digit;
+
+ /* put digits in buffer from back to front */
+ buf += len - 1;
+ *buf = 0;
+ do {
+ digit = num % 10;
+ *(--buf) = digit + '0';
+ num /= 10;
+ } while (num > 0);
+
+ return buf;
+}
+
+static int print_num(unsigned long num)
+{
+ char num_buf[30];
+
+ return print(num_to_str(num, num_buf, sizeof(num_buf)));
+}
+
+static int print_k_value(const char *s, unsigned long num, unsigned long units)
+{
+ unsigned long long temp;
+ int ccode;
+
+ print(s);
+
+ temp = num;
+ temp = (temp * units)/1024;
+ num = temp;
+ ccode = print_num(num);
+ print("\n");
+ return ccode;
+}
+
+/* this program has no main(), as startup libraries are not used */
+void _start(void)
+{
+ int ccode;
+ struct sysinfo info;
+ unsigned long used;
+
+ print("Testing system size.\n");
+ print("1..1\n");
+
+ ccode = sysinfo(&info);
+ if (ccode < 0) {
+ print("not ok 1 get runtime memory use\n");
+ print("# could not get sysinfo\n");
+ _exit(ccode);
+ }
+ /* ignore cache complexities for now */
+ used = info.totalram - info.freeram - info.bufferram;
+ print_k_value("ok 1 get runtime memory use # size = ", used,
+ info.mem_unit);
+
+ print("# System runtime memory report (units in Kilobytes):\n");
+ print_k_value("# Total: ", info.totalram, info.mem_unit);
+ print_k_value("# Free: ", info.freeram, info.mem_unit);
+ print_k_value("# Buffer: ", info.bufferram, info.mem_unit);
+ print_k_value("# In use: ", used, info.mem_unit);
+
+ _exit(0);
+}
--
1.8.2.2
^ permalink raw reply related
* Re: [PATCH v5] selftest: size: Add size test for Linux kernel
From: Tim Bird @ 2014-12-03 18:25 UTC (permalink / raw)
To: Geert Uytterhoeven
Cc: Michael Ellerman, Shuah Khan,
linux-api-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Josh Triplett,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-embedded-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Thomas Petazzoni
In-Reply-To: <CAMuHMdVk9QLDRYDs3q7=51=qTMNRY+MQVhkqGE25XqYOkLbCxg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On 12/03/2014 10:00 AM, Geert Uytterhoeven wrote:
> On Wed, Dec 3, 2014 at 5:29 PM, Tim Bird <tim.bird-/MT0OVThwyLZJqsBc5GL+g@public.gmane.org> wrote:
>>>> diff --git a/tools/testing/selftests/size/Makefile b/tools/testing/selftests/size/Makefile
>>>> new file mode 100644
>>>> index 0000000..47f8e9c
>>>> --- /dev/null
>>>> +++ b/tools/testing/selftests/size/Makefile
>>>> @@ -0,0 +1,15 @@
>>>> +#ifndef CC
>>>> + CC = $(CROSS_COMPILE)gcc
>>>> +#endif
>>>
>>> I think the following is preferable:
>>>
>>> CC := $(CROSS_COMPILE)$(CC)
>>>
>>>
>>> It allows optionally setting a custom CC, as well as optionally CROSS_COMPILE.
>>
>> I'm not sure I follow this.
>>
>> If CC is unset, you get only the CROSS_COMPILE prefix.
>> If CC is set to e.g. 'gcc', then you get a nicely formatted toolchain string.
>> But if CC already has the prefix applied, then this will result in
>> having it duplicated, which surely won't work correctly.
>>
>> In the long run, I would hope that a higher level Makefile or environment setting
>> will be setting the toolchain string appropriately (as well as handling build flags)
>> which is why I wanted to use an ifndef (which Thomas correctly pointed out is just
>> wrong).
>>
>> Actually, after getting this tiny program accepted, my next task was working on a
>> proper fix for handling cross compilation in a more generic (not case-by-case) way.
>>
>> CROSS_COMPILE prefix usage looks a bit uncoordinated in the tools directory, but most
>> tests seem to be favoring $(CROSS_COMPILE)gcc.
>>
>> $ cd tools ; mgrep CROSS
>
> [...]
>
>> I agree it's desirable not to hardcode gcc, but we seem to be doing it all over
>> the place already.
>
> Seems like it's time to start integrating the tests with the regular Kbuild
> system, which handles cross-compilation fine...
Where possible, yes. It would be nice to leverage CROSS_COMPILE and CFLAGS, or
a portion thereof, from the Kbuild system (as well a KBUILD_OUTPUT and friends).
It's on my to-do list, after getting my little C program accepted...
-- Tim
^ permalink raw reply
* Re: [PATCH v5] selftest: size: Add size test for Linux kernel
From: Geert Uytterhoeven @ 2014-12-03 18:00 UTC (permalink / raw)
To: Tim Bird
Cc: Michael Ellerman, Shuah Khan, linux-api@vger.kernel.org,
Josh Triplett, linux-kernel@vger.kernel.org,
linux-embedded@vger.kernel.org
In-Reply-To: <547F3A57.7000106@sonymobile.com>
On Wed, Dec 3, 2014 at 5:29 PM, Tim Bird <tim.bird@sonymobile.com> wrote:
>>> diff --git a/tools/testing/selftests/size/Makefile b/tools/testing/selftests/size/Makefile
>>> new file mode 100644
>>> index 0000000..47f8e9c
>>> --- /dev/null
>>> +++ b/tools/testing/selftests/size/Makefile
>>> @@ -0,0 +1,15 @@
>>> +#ifndef CC
>>> + CC = $(CROSS_COMPILE)gcc
>>> +#endif
>>
>> I think the following is preferable:
>>
>> CC := $(CROSS_COMPILE)$(CC)
>>
>>
>> It allows optionally setting a custom CC, as well as optionally CROSS_COMPILE.
>
> I'm not sure I follow this.
>
> If CC is unset, you get only the CROSS_COMPILE prefix.
> If CC is set to e.g. 'gcc', then you get a nicely formatted toolchain string.
> But if CC already has the prefix applied, then this will result in
> having it duplicated, which surely won't work correctly.
>
> In the long run, I would hope that a higher level Makefile or environment setting
> will be setting the toolchain string appropriately (as well as handling build flags)
> which is why I wanted to use an ifndef (which Thomas correctly pointed out is just
> wrong).
>
> Actually, after getting this tiny program accepted, my next task was working on a
> proper fix for handling cross compilation in a more generic (not case-by-case) way.
>
> CROSS_COMPILE prefix usage looks a bit uncoordinated in the tools directory, but most
> tests seem to be favoring $(CROSS_COMPILE)gcc.
>
> $ cd tools ; mgrep CROSS
[...]
> I agree it's desirable not to hardcode gcc, but we seem to be doing it all over
> the place already.
Seems like it's time to start integrating the tests with the regular Kbuild
system, which handles cross-compilation fine...
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply
* [PATCH] Documentation: Add entry for dell-laptop sysfs interface
From: Gabriele Mazzotta @ 2014-12-03 17:41 UTC (permalink / raw)
To: dvhart, mjg59
Cc: linux-api, platform-driver-x86, linux-kernel, libsmbios-devel,
Srinivas_G_Gowda, Michael_E_Brown, pali.rohar, Gabriele Mazzotta
Add the documentation for the new sysfs interface of dell-laptop
that allows to configure the keyboard illumination on Dell systems.
Signed-off-by: Gabriele Mazzotta <gabriele.mzt@gmail.com>
Signed-off-by: Pali Rohár <pali.rohar@gmail.com>
---
.../ABI/testing/sysfs-platform-dell-laptop | 60 ++++++++++++++++++++++
1 file changed, 60 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-platform-dell-laptop
diff --git a/Documentation/ABI/testing/sysfs-platform-dell-laptop b/Documentation/ABI/testing/sysfs-platform-dell-laptop
new file mode 100644
index 0000000..7969443
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-platform-dell-laptop
@@ -0,0 +1,60 @@
+What: /sys/class/leds/dell::kbd_backlight/als_setting
+Date: December 2014
+KernelVersion: 3.19
+Contact: Gabriele Mazzotta <gabriele.mzt@gmail.com>,
+ Pali Rohár <pali.rohar@gmail.com>
+Description:
+ This file allows to control the automatic keyboard
+ illumination mode on some systems that have an ambient
+ light sensor. Write 1 to this file to enable the auto
+ mode, 0 to disable it.
+
+What: /sys/class/leds/dell::kbd_backlight/start_triggers
+Date: December 2014
+KernelVersion: 3.19
+Contact: Gabriele Mazzotta <gabriele.mzt@gmail.com>,
+ Pali Rohár <pali.rohar@gmail.com>
+Description:
+ This file allows to control the input triggers that
+ turn on the keyboard backlight illumination that is
+ disabled because of inactivity.
+ Read the file to see the triggers available. The ones
+ enabled are preceded by '+', those disabled by '-'.
+
+ To enable a trigger, write its name preceded by '+' to
+ this file. To disable a trigger, write its name preceded
+ by '-' instead.
+
+ For example, to enable the keyboard as trigger run:
+ echo +keyboard > /sys/class/leds/dell::kbd_backlight/start_triggers
+ To disable it:
+ echo -keyboard > /sys/class/leds/dell::kbd_backlight/start_triggers
+
+ Note that not all the available triggers can be configured.
+
+What: /sys/class/leds/dell::kbd_backlight/stop_timeout
+Date: December 2014
+KernelVersion: 3.19
+Contact: Gabriele Mazzotta <gabriele.mzt@gmail.com>,
+ Pali Rohár <pali.rohar@gmail.com>
+Description:
+ This file allows to specify the interval after which the
+ keyboard illumination is disabled because of inactivity.
+ The timeouts are expressed in seconds, minutes, hours and
+ days, for which the symbols are 's', 'm', 'h' and 'd'
+ respectively.
+
+ To configure the timeout, write to this file a value along
+ with any the above units. If no unit is specified, the value
+ is assumed to be expressed in seconds.
+
+ For example, to set the timeout to 10 minutes run:
+ echo 10m > /sys/class/leds/dell::kbd_backlight/stop_timeout
+
+ Note that when this file is read, the returned value might be
+ expressed in a different unit than the one used when the timeout
+ was set.
+
+ Also note that only some timeouts are supported and that
+ some systems might fall back to a specific timeout in case
+ an invalid timeout is written to this file.
--
2.1.3
^ permalink raw reply related
* Re: [PATCH v6 0/7] vfs: Non-blockling buffered fs read (page cache only)
From: Milosz Tanski @ 2014-12-03 16:48 UTC (permalink / raw)
To: Andrew Morton
Cc: LKML, Christoph Hellwig, linux-fsdevel@vger.kernel.org,
linux-aio@kvack.org, Mel Gorman, Volker Lendecke, Tejun Heo,
Jeff Moyer, Theodore Ts'o, Al Viro, Linux API,
Michael Kerrisk, linux-arch
In-Reply-To: <20141202144200.a4ca81a46a43563a8874fd8e@linux-foundation.org>
On Tue, Dec 2, 2014 at 5:42 PM, Andrew Morton <akpm@linux-foundation.org> wrote:
>
> On Tue, 2 Dec 2014 17:17:42 -0500 Milosz Tanski <milosz@adfin.com> wrote:
>
> > > There have been several incomplete attempts to implement fincore(). If
> > > we were to complete those attempts, preadv2() could be implemented
> > > using fincore()+pread(). Plus we get fincore(), which is useful for
> > > other (but probably similar) reasons. Probably fincore()+pwrite() could
> > > be used to implement pwritev2(), but I don't know what pwritev2() does
> > > yet.
> > >
> > > Implementing fincore() is more flexible, requires less code and is less
> > > likely to have bugs. So why not go that way? Yes, it's more CPU
> > > intensive, but how much? Is the difference sufficient to justify the
> > > preadv2()/pwritev2() approach?
> >
> > I would like to see a fincore() functionality (for other reasons) I
> > don't think it does the job here. fincore() + preadv() is inherently
> > racy as there's no guarantee that the data becomes uncached between
> > the two calls.
>
> There will always be holes. For example find_get_page() could block on
> lock_page() while some other process is doing IO.
> page_cache_async_readahead() does lots of memory allocation which can
> get blocked for long periods in the page allocator.
> page_cache_async_readahead() can block on synchronous metadata reads,
> etc.
Andrew I think it would helpful if you did read through the patches.
The first 3 are somewhat uninteresting as it's just wiring up the new
syscalls and plumbing the flag argument through. The core of the
RWF_NONBLOCK is patch 4: https://lkml.org/lkml/2014/11/10/463 and if
you strip away the fs specific changes the core of it is very simple.
The core is mostly contained in do_generic_file_read() in filemap.c,
and is very short and easy to understand. It boils down to we read as
much data as we can given what's in the page cache. There's no
fallback to diskio for readpage() in case of missing pages and we bail
before any calls to page_cache_async_readahead(). And to the best of
my knowledge lock_page() does not lock the page, all it does is call
pagecache_get_page() without the FGP_LOCK flag.
I've spent time a decent amount of time looking at this to make sure
we cover all our major bases. It's possible I missed something but the
biggest offenders should be covered and if I missed something I'd love
to cover that as well.
>
> The question is whether a simpler approach such as fincore() will be
> sufficient.
>
> > This may not matter in some cases, but in others (ones
> > that I'm trying to solve) it will introduce unexpected latency.
>
> Details?
Please read my points below and
>
>
> > There's no overlap between prwritev2 and fincore() functionality.
>
> Do we actually need pwritev2()? What's the justification for that?
I'm okay with splitting up the pwritev2 and preadv2 into two
independent patchsets to be considered on their own merits.
>
>
>
> Please let's examine the alternative(s) seriously. It would be mistake
> to add preadv2/pwritev2 if fincore+pread would have sufficed.
What the motivation for my change and also approach is a very common
pattern to async buffered disk IO in userspace server applications. It
comes down to having one thread to handle the network and a thread
pool to perform IO requests. Why a threadpool and not something like a
sendfile() for reads? Many non-trivial applications perform additional
processing (ssl, checksuming, transformation). Unfortunately this has
a inherent increase in average latency due to increased
synchronization penalties (enqueue and notify) but primarily due to
fast requests (already in cache) behind stuck behind slow request.
Here's the illustration of the common architecture:
http://i.imgur.com/f8Pla7j.png. In fact, most apps are even simpler
where they replace the request queue, task worker with a single thread
doing network IO using epoll or such.
preadv2 with RWF_NONBLOCK is analogous to the kernel recvmsg() with
the MSG_NOWAIT flag. It's really frustrating that such capacity
doesn't exist today. As with the user space application design we can
skip the io threadpool and decrease average request latency in many
common workloads (linear reads or zipf data accesses).
preadv2 with RWF_NONBLOCK as implemented does not suffer the same
eviction races as fincore + pread because it's not implemented as two
syscalls. It also has a much lower surface of possible blocking /
locking then fincore + pread because it cannot fallback to reading
from disk, it does not trigger read-ahead, and does not wait for page
lock.
--
Milosz Tanski
CTO
16 East 34th Street, 15th floor
New York, NY 10016
p: 646-253-9055
e: milosz@adfin.com
^ permalink raw reply
* Re: [PATCH v5] selftest: size: Add size test for Linux kernel
From: Tim Bird @ 2014-12-03 16:29 UTC (permalink / raw)
To: Michael Ellerman
Cc: Shuah Khan, linux-api-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Josh Triplett,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-embedded-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <1417578191.16722.11.camel@concordia>
On 12/02/2014 07:43 PM, Michael Ellerman wrote:
> On Tue, 2014-12-02 at 19:36 -0800, Tim Bird wrote:
>> This test shows the amount of memory used by the system.
>> Note that this is dependent on the user-space that is loaded
>> when this program runs. Optimally, this program would be
>> run as the init program itself.
>
> Sorry to only chime in at v5.
>
>> diff --git a/tools/testing/selftests/size/Makefile b/tools/testing/selftests/size/Makefile
>> new file mode 100644
>> index 0000000..47f8e9c
>> --- /dev/null
>> +++ b/tools/testing/selftests/size/Makefile
>> @@ -0,0 +1,15 @@
>> +#ifndef CC
>> + CC = $(CROSS_COMPILE)gcc
>> +#endif
>
> I think the following is preferable:
>
> CC := $(CROSS_COMPILE)$(CC)
>
>
> It allows optionally setting a custom CC, as well as optionally CROSS_COMPILE.
I'm not sure I follow this.
If CC is unset, you get only the CROSS_COMPILE prefix.
If CC is set to e.g. 'gcc', then you get a nicely formatted toolchain string.
But if CC already has the prefix applied, then this will result in
having it duplicated, which surely won't work correctly.
In the long run, I would hope that a higher level Makefile or environment setting
will be setting the toolchain string appropriately (as well as handling build flags)
which is why I wanted to use an ifndef (which Thomas correctly pointed out is just
wrong).
Actually, after getting this tiny program accepted, my next task was working on a
proper fix for handling cross compilation in a more generic (not case-by-case) way.
CROSS_COMPILE prefix usage looks a bit uncoordinated in the tools directory, but most
tests seem to be favoring $(CROSS_COMPILE)gcc.
$ cd tools ; mgrep CROSS
./vm/Makefile:CC = $(CROSS_COMPILE)gcc
./usb/Makefile:CC = $(CROSS_COMPILE)gcc
./testing/selftests/net/Makefile:CC = $(CROSS_COMPILE)gcc
./testing/selftests/vm/Makefile:CC = $(CROSS_COMPILE)gcc
./testing/selftests/efivarfs/Makefile:CC = $(CROSS_COMPILE)gcc
./testing/selftests/size/Makefile: CC = $(CROSS_COMPILE)gcc
./testing/selftests/powerpc/Makefile:CC := $(CROSS_COMPILE)$(CC)
./hv/Makefile:CC = $(CROSS_COMPILE)gcc
./perf/config/feature-checks/Makefile:CC := $(CROSS_COMPILE)gcc -MD
./perf/config/feature-checks/Makefile:PKG_CONFIG := $(CROSS_COMPILE)pkg-config
./lib/api/Makefile:CC = $(CROSS_COMPILE)gcc
./lib/api/Makefile:AR = $(CROSS_COMPILE)ar
./lib/lockdep/Makefile:# Allow setting CC and AR, or setting CROSS_COMPILE as a prefix.
./lib/lockdep/Makefile:$(call allow-override,CC,$(CROSS_COMPILE)gcc)
./lib/lockdep/Makefile:$(call allow-override,AR,$(CROSS_COMPILE)ar)
./lib/lockdep/Makefile:TRACK_CFLAGS = $(subst ','\'',$(CFLAGS)):$(ARCH):$(CROSS_COMPILE)
./lib/traceevent/Makefile:# Allow setting CC and AR, or setting CROSS_COMPILE as a prefix.
./lib/traceevent/Makefile:$(call allow-override,CC,$(CROSS_COMPILE)gcc)
./lib/traceevent/Makefile:$(call allow-override,AR,$(CROSS_COMPILE)ar)
./lib/traceevent/Makefile:TRACK_CFLAGS = $(subst ','\'',$(CFLAGS)):$(ARCH):$(CROSS_COMPILE)
./cgroup/Makefile:CC = $(CROSS_COMPILE)gcc
./power/acpi/Makefile:CROSS = #/usr/i386-linux-uclibc/usr/bin/i386-uclibc-
./power/acpi/Makefile:CC = $(CROSS)gcc
./power/acpi/Makefile:LD = $(CROSS)gcc
./power/acpi/Makefile:STRIP = $(CROSS)strip
./power/cpupower/Makefile:CROSS = #/usr/i386-linux-uclibc/usr/bin/i386-uclibc-
./power/cpupower/Makefile:CC = $(CROSS)gcc
./power/cpupower/Makefile:LD = $(CROSS)gcc
./power/cpupower/Makefile:AR = $(CROSS)ar
./power/cpupower/Makefile:STRIP = $(CROSS)strip
./power/cpupower/Makefile:RANLIB = $(CROSS)ranlib
./power/cpupower/Makefile:export CROSS CC AR STRIP RANLIB CFLAGS LDFLAGS LIB_OBJS
./power/x86/turbostat/Makefile:CC = $(CROSS_COMPILE)gcc
I agree it's desirable not to hardcode gcc, but we seem to be doing it all over
the place already.
-- Tim
^ permalink raw reply
* Re: [PATCH v5] selftest: size: Add size test for Linux kernel
From: Tim Bird @ 2014-12-03 16:13 UTC (permalink / raw)
To: Thomas Petazzoni, Michael Ellerman
Cc: Shuah Khan, linux-api@vger.kernel.org, Josh Triplett,
linux-kernel@vger.kernel.org, linux-embedded@vger.kernel.org
In-Reply-To: <20141203140136.12997331@free-electrons.com>
On 12/03/2014 05:01 AM, Thomas Petazzoni wrote:
> Michael, Tim,
>
> On Wed, 03 Dec 2014 14:43:11 +1100, Michael Ellerman wrote:
>
>>> diff --git a/tools/testing/selftests/size/Makefile b/tools/testing/selftests/size/Makefile
>>> new file mode 100644
>>> index 0000000..47f8e9c
>>> --- /dev/null
>>> +++ b/tools/testing/selftests/size/Makefile
>>> @@ -0,0 +1,15 @@
>>> +#ifndef CC
>>> + CC = $(CROSS_COMPILE)gcc
>>> +#endif
>>
>> I think the following is preferable:
>>
>> CC := $(CROSS_COMPILE)$(CC)
>
> It is even more necessary that #ifndef and #endif don't exist in make.
> They are just comments, and therefore, ignored. Seems like Tim does too
> much C :-)
OK - that's hilarious. Saying 'Oops!' would be too casual for my degree of
embarrassment. :-)
Makefiles do have similar constructs. Those should have been
ifeq ($(CC),)
...
endif
This obviously got through via a failiure in testing - which is somewhat ironic.
Look for a v6 soon. (Geez, when is the merge window coming. I thought this trivial
program would get in pretty easily, but no... that's never the way. Of course
it helps if the submitter is not an idiot.)
-- Tim
^ permalink raw reply
* Re: [PATCH RFC 1/2] virtio_balloon: convert to virtio 1.0 endian-ness
From: Cornelia Huck @ 2014-12-03 14:27 UTC (permalink / raw)
To: Michael S. Tsirkin; +Cc: linux-api, linux-kernel, virtualization
In-Reply-To: <20141203140228.GA12370@redhat.com>
On Wed, 3 Dec 2014 16:02:28 +0200
"Michael S. Tsirkin" <mst@redhat.com> wrote:
> On Tue, Dec 02, 2014 at 07:39:30PM +0100, Cornelia Huck wrote:
> > On Tue, 2 Dec 2014 13:44:06 +0200
> > "Michael S. Tsirkin" <mst@redhat.com> wrote:
> >
> > > balloon device is not part of virtio 1.0 spec. Still, it's easy enough
> > > to make it handle endian-ness exactly as other virtio 1.0 devices: what
> > > we gain from this, is that there's no need to special-case it in virtio
> > > core.
> >
> > Well, the balloon is weird in a number of ways, including its always
> > little-endian config space.
>
> Hmm yes, I forgot about that.
>
> > But I'm not quite sure the spec covers this?
>
> The spec does not cover balloon. It merely includes a reference section
> about legacy balloon device. What to do when that device sets VERSION_1
> is really up to us.
>
> Patchset v8 simply makes balloon tell core it's legacy_only.
> This is I guess fine as far as it goes.
> I was still looking for the best way that involves minimal noise
> in core. This looked like the best way: change 4 lines,
> and drop all special casing in core, but given the config space
> endian-ness mess, I'm not sure anymore.
> I guess we'll just stick to what v8 does for now.
I don't think the legacy_only approach is so bad, let's keep it for now.
^ permalink raw reply
* Re: [PATCH v2 02/19] kbuild: kselftest_install - add a new make target to install selftests
From: Shuah Khan @ 2014-12-03 14:14 UTC (permalink / raw)
To: Michal Marek, gregkh, akpm, davem, keescook, tranmanphong,
dh.herrmann, hughd, bobby.prani, ebiederm, serge.hallyn
Cc: linux-kbuild, linux-kernel, linux-api, netdev,
masami.hiramatsu.pt, Shuah Khan
In-Reply-To: <547EFD6F.2010204@suse.cz>
On 12/03/2014 05:09 AM, Michal Marek wrote:
> On 2014-12-01 17:39, Shuah Khan wrote:
>> On 12/01/2014 08:47 AM, Michal Marek wrote:
>>> On 2014-11-11 21:27, Shuah Khan wrote:
>>>> diff --git a/Makefile b/Makefile
>>>> index 05d67af..ccbd2e1 100644
>>>> --- a/Makefile
>>>> +++ b/Makefile
>>>> @@ -1071,12 +1071,26 @@ headers_check: headers_install
>>>> $(Q)$(MAKE) $(hdr-inst)=arch/$(hdr-arch)/include/uapi/asm $(hdr-dst) HDRCHECK=1
>>>>
>>>> # ---------------------------------------------------------------------------
>>>> -# Kernel selftest
>>>> +# Kernel selftest targets
>>>> +
>>>> +PHONY += __kselftest_configure
>>>> +INSTALL_KSFT_PATH=$(INSTALL_MOD_PATH)/lib/kselftest/$(KERNELRELEASE)
>>>> +export INSTALL_KSFT_PATH
>>>> +KSELFTEST=$(INSTALL_KSFT_PATH)/kselftest.sh
>>>> +export KSELFTEST
>>>
>>> Can this be moved to tools/testing/selftests/Makefile? It's only used in
>>> this part of the tree.
>>
>> I looked into doing that. KERNELRELEASE will have to be exported for
>> tools/testing/selftests/Makefile to use it? Does that sound okay?
>
> In fact, KERNELRELEASE is already exported. So go ahead.
Good.
>
>
>> Also, it might be easier to get this series in, if you can Ack the main
>> Makefile patch (when we are ready i.e), so I can take it through
>> kselftest tree.
>
> Sure. The Makefile change will only consist of redirecting the
> kselftest_install target to tools/testing/selftests, right?
>
Correct. I plan to keep the selftests specific work under the
selftests related Makefiles.
thanks,
-- Shuah
--
Shuah Khan
Sr. Linux Kernel Developer
Samsung Research America (Silicon Valley)
shuahkh@osg.samsung.com | (970) 217-8978
^ permalink raw reply
* Re: [PATCH RFC 1/2] virtio_balloon: convert to virtio 1.0 endian-ness
From: Michael S. Tsirkin @ 2014-12-03 14:02 UTC (permalink / raw)
To: Cornelia Huck; +Cc: linux-api, linux-kernel, virtualization
In-Reply-To: <20141202193930.2b8750f7.cornelia.huck@de.ibm.com>
On Tue, Dec 02, 2014 at 07:39:30PM +0100, Cornelia Huck wrote:
> On Tue, 2 Dec 2014 13:44:06 +0200
> "Michael S. Tsirkin" <mst@redhat.com> wrote:
>
> > balloon device is not part of virtio 1.0 spec. Still, it's easy enough
> > to make it handle endian-ness exactly as other virtio 1.0 devices: what
> > we gain from this, is that there's no need to special-case it in virtio
> > core.
>
> Well, the balloon is weird in a number of ways, including its always
> little-endian config space.
Hmm yes, I forgot about that.
> But I'm not quite sure the spec covers this?
The spec does not cover balloon. It merely includes a reference section
about legacy balloon device. What to do when that device sets VERSION_1
is really up to us.
Patchset v8 simply makes balloon tell core it's legacy_only.
This is I guess fine as far as it goes.
I was still looking for the best way that involves minimal noise
in core. This looked like the best way: change 4 lines,
and drop all special casing in core, but given the config space
endian-ness mess, I'm not sure anymore.
I guess we'll just stick to what v8 does for now.
> >
> > Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
> > ---
> > include/uapi/linux/virtio_balloon.h | 5 +++--
> > drivers/virtio/virtio_balloon.c | 4 ++--
> > 2 files changed, 5 insertions(+), 4 deletions(-)
> >
>
> > struct virtio_balloon_stat {
> > - __u16 tag;
> > - __u64 val;
> > + __virtio16 tag;
> > + __virtio64 val;
> > } __attribute__((packed));
>
> Would the respective fields in the spec need updating? While it is
> actually talking about legacy requirements, the fields are not
> specified as __virtio{16,64}.
>
> Also, is changing the stat fields enough? I've not looked into balloon
> operation, but does the payload need some endianess conversion as well?
^ permalink raw reply
* Re: [PATCH] Documentation: Add entry for dell-laptop sysfs interface
From: Darren Hart @ 2014-12-03 13:18 UTC (permalink / raw)
To: Gabriele Mazzotta
Cc: mjg59, linux-api, platform-driver-x86, linux-kernel,
libsmbios-devel, Srinivas_G_Gowda, Michael_E_Brown, pali.rohar
In-Reply-To: <1417628493-29323-1-git-send-email-gabriele.mzt@gmail.com>
On Wed, Dec 03, 2014 at 06:41:33PM +0100, Gabriele Mazzotta wrote:
> Add the documentation for the new sysfs interface of dell-laptop
> that allows to configure the keyboard illumination on Dell systems.
>
> Signed-off-by: Gabriele Mazzotta <gabriele.mzt@gmail.com>
> Signed-off-by: Pali Rohár <pali.rohar@gmail.com>
If nobody else would prefer to take this in their tree, I'll submit it through
platform-drivers-x86 when I send the corresponding driver patch.
--
Darren Hart
Intel Open Source Technology Center
^ permalink raw reply
* Re: [PATCH v5] selftest: size: Add size test for Linux kernel
From: Thomas Petazzoni @ 2014-12-03 13:01 UTC (permalink / raw)
To: Michael Ellerman
Cc: Tim Bird, Shuah Khan,
linux-api-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Josh Triplett,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-embedded-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <1417578191.16722.11.camel@concordia>
Michael, Tim,
On Wed, 03 Dec 2014 14:43:11 +1100, Michael Ellerman wrote:
> > diff --git a/tools/testing/selftests/size/Makefile b/tools/testing/selftests/size/Makefile
> > new file mode 100644
> > index 0000000..47f8e9c
> > --- /dev/null
> > +++ b/tools/testing/selftests/size/Makefile
> > @@ -0,0 +1,15 @@
> > +#ifndef CC
> > + CC = $(CROSS_COMPILE)gcc
> > +#endif
>
> I think the following is preferable:
>
> CC := $(CROSS_COMPILE)$(CC)
It is even more necessary that #ifndef and #endif don't exist in make.
They are just comments, and therefore, ignored. Seems like Tim does too
much C :-)
Best regards,
Thomas
--
Thomas Petazzoni, CTO, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
^ permalink raw reply
* Re: [PATCH v3 5/5] tty/serial: Add Spreadtrum sc9836-uart driver support
From: Lyra Zhang @ 2014-12-03 12:15 UTC (permalink / raw)
To: Arnd Bergmann
Cc: Murali Karicheri, Chunyan Zhang, Grant Likely, robh+dt@kernel.org,
Catalin Marinas, gregkh@linuxfoundation.org,
ijc+devicetree@hellion.org.uk, jslaby@suse.cz, Kumar Gala,
Mark Rutland, Pawel Moll, Ramkumar Ramachandra,
rrichter@cavium.com, Will Deacon, gnomes@lxorguk.ukuu.org.uk,
Jonathan Corbet, jason@lakedaemon.net, Mark Brown,
Heiko Stübner, florian.vaussard@epfl.ch
In-Reply-To: <3212776.kDbe7yX4eX@wuerfel>
2014-12-03 17:50 GMT+08:00 Arnd Bergmann <arnd@arndb.de>:
> On Wednesday 03 December 2014 17:17:13 Lyra Zhang wrote:
>
>> 2014-11-27 2:29 GMT+08:00 Murali Karicheri <m-karicheri2@ti.com>:
>> > How about sorting this includes? asm/irq.h go first followed linux/ in alphabatical order?
>>
>> There are a few compile warnings if I moved asm/irq.h to the top of
>> all included files.
>
> The order that Murali meant is
>
> - first all linux/*.h headers, sorted alphabetically
> - then all asm/*.h headers, again sorted alphabetically
>
> This is the recommended style in general.
>
>> Warning details are below:
>>
>> In file included from drivers/tty/serial/sprd_serial.c:14:0:
>> ./arch/arm64/include/asm/irq.h:6:39: warning: ‘struct pt_regs’
>> declared inside parameter list [enabled by default]
>> extern void (*handle_arch_irq)(struct pt_regs *);
>> ^
>> ./arch/arm64/include/asm/irq.h:6:39: warning: its scope is only this
>> definition or declaration, which is probably not what you want
>> [enabled by default]
>> ./arch/arm64/include/asm/irq.h:8:54: warning: ‘struct pt_regs’
>> declared inside parameter list [enabled by default]
>> extern void set_handle_irq(void (*handle_irq)(struct pt_regs *));
>>
>
> I would consider this a (minor) bug in asm/irq.h. If you don't mind,
> please submit a patch to add a line with 'struct pt_regs;' to
> asm/irq.h.
OK, I would like to send another separate patch for this.
Best regards,
Chunyan
>
> Arnd
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
^ permalink raw reply
* Re: [PATCH v2 02/19] kbuild: kselftest_install - add a new make target to install selftests
From: Michal Marek @ 2014-12-03 12:09 UTC (permalink / raw)
To: Shuah Khan, gregkh, akpm, davem, keescook, tranmanphong,
dh.herrmann, hughd, bobby.prani, ebiederm, serge.hallyn
Cc: linux-kbuild, linux-kernel, linux-api, netdev,
masami.hiramatsu.pt@hitachi.com >> Masami Hiramatsu
In-Reply-To: <547C99B6.7070903@osg.samsung.com>
On 2014-12-01 17:39, Shuah Khan wrote:
> On 12/01/2014 08:47 AM, Michal Marek wrote:
>> On 2014-11-11 21:27, Shuah Khan wrote:
>>> diff --git a/Makefile b/Makefile
>>> index 05d67af..ccbd2e1 100644
>>> --- a/Makefile
>>> +++ b/Makefile
>>> @@ -1071,12 +1071,26 @@ headers_check: headers_install
>>> $(Q)$(MAKE) $(hdr-inst)=arch/$(hdr-arch)/include/uapi/asm $(hdr-dst) HDRCHECK=1
>>>
>>> # ---------------------------------------------------------------------------
>>> -# Kernel selftest
>>> +# Kernel selftest targets
>>> +
>>> +PHONY += __kselftest_configure
>>> +INSTALL_KSFT_PATH=$(INSTALL_MOD_PATH)/lib/kselftest/$(KERNELRELEASE)
>>> +export INSTALL_KSFT_PATH
>>> +KSELFTEST=$(INSTALL_KSFT_PATH)/kselftest.sh
>>> +export KSELFTEST
>>
>> Can this be moved to tools/testing/selftests/Makefile? It's only used in
>> this part of the tree.
>
> I looked into doing that. KERNELRELEASE will have to be exported for
> tools/testing/selftests/Makefile to use it? Does that sound okay?
In fact, KERNELRELEASE is already exported. So go ahead.
> Also, it might be easier to get this series in, if you can Ack the main
> Makefile patch (when we are ready i.e), so I can take it through
> kselftest tree.
Sure. The Makefile change will only consist of redirecting the
kselftest_install target to tools/testing/selftests, right?
Michal
^ permalink raw reply
* Re: [PATCH v3 5/5] tty/serial: Add Spreadtrum sc9836-uart driver support
From: Lyra Zhang @ 2014-12-03 12:08 UTC (permalink / raw)
To: Russell King - ARM Linux
Cc: Arnd Bergmann, Mark Rutland,
gnomes-qBU/x9rampVanCEyBjwyrvXRex20P6io@public.gmane.org,
Heiko Stübner, Catalin Marinas, Will Deacon,
andrew-g2DYL2Zd6BY@public.gmane.org,
linux-api-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
jslaby-AlSwsSmVLrQ@public.gmane.org, Ramkumar Ramachandra,
lanqing.liu-lxIno14LUO0EEoCn2XhGlw@public.gmane.org,
Wei Qiao (乔伟), Jonathan Corbet, Chunyan Zhang,
zhizhou.zhang, geng.ren-lxIno14LUO0EEoCn2XhGlw@public.gmane.org,
Murali Karicheri,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
linux-serial-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20141203101109.GB11285-l+eeeJia6m9vn6HldHNs0ANdhmdF6hFW@public.gmane.org>
2014-12-03 18:11 GMT+08:00 Russell King - ARM Linux <linux-lFZ/pmaqli4YwGPxGkYEkQ@public.gmane.orgg.uk>:
> On Wed, Dec 03, 2014 at 10:50:17AM +0100, Arnd Bergmann wrote:
>> On Wednesday 03 December 2014 17:17:13 Lyra Zhang wrote:
>>
>> > 2014-11-27 2:29 GMT+08:00 Murali Karicheri <m-karicheri2-l0cyMroinI0@public.gmane.org>:
>> > > How about sorting this includes? asm/irq.h go first followed linux/ in alphabatical order?
>> >
>> > There are a few compile warnings if I moved asm/irq.h to the top of
>> > all included files.
>>
>> The order that Murali meant is
>>
>> - first all linux/*.h headers, sorted alphabetically
>> - then all asm/*.h headers, again sorted alphabetically
>>
>> This is the recommended style in general.
>>
>> > Warning details are below:
>> >
>> > In file included from drivers/tty/serial/sprd_serial.c:14:0:
>> > ./arch/arm64/include/asm/irq.h:6:39: warning: ‘struct pt_regs’
>> > declared inside parameter list [enabled by default]
>> > extern void (*handle_arch_irq)(struct pt_regs *);
>> > ^
>> > ./arch/arm64/include/asm/irq.h:6:39: warning: its scope is only this
>> > definition or declaration, which is probably not what you want
>> > [enabled by default]
>> > ./arch/arm64/include/asm/irq.h:8:54: warning: ‘struct pt_regs’
>> > declared inside parameter list [enabled by default]
>> > extern void set_handle_irq(void (*handle_irq)(struct pt_regs *));
>> >
>>
>> I would consider this a (minor) bug in asm/irq.h. If you don't mind,
>> please submit a patch to add a line with 'struct pt_regs;' to
>> asm/irq.h.
>
> A better question is: why is a modern driver using asm/irq.h in the
> first place.
>
> We used to include that file when platforms defined IRQ numbers
> statically, but modern platforms don't do that, so it shouldn't be
> required anymore.
>
OK, I see. I'll remove it in v4.
I'm very sorry that I didn't check it carefully enough and troubled
many people for this problem.
This file included the asm/irq.h before I revised it for upstreaming,
I also could not find this file was no longer needed to include.
Thank you for your reminder, I'll avoid making the same mistake, and
I'll also share my experience with my colleagues to keep them from
having this mistake in the future.
Best regards,
Chunyan
^ permalink raw reply
* Re: [PATCH v2] media: platform: add VPFE capture driver support for AM437X
From: Hans Verkuil @ 2014-12-03 10:56 UTC (permalink / raw)
To: Lad, Prabhakar, LMML, devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-api
Cc: LKML, Hans Verkuil
In-Reply-To: <1417566590-30529-1-git-send-email-prabhakar.csengg-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
On 12/03/14 01:29, Lad, Prabhakar wrote:
> From: Benoit Parrot <bparrot-l0cyMroinI0@public.gmane.org>
>
> This patch adds Video Processing Front End (VPFE) driver for
> AM437X family of devices
> Driver supports the following:
> - V4L2 API using MMAP buffer access based on videobuf2 api
> - Asynchronous sensor/decoder sub device registration
> - DT support
>
> Signed-off-by: Benoit Parrot <bparrot-l0cyMroinI0@public.gmane.org>
> Signed-off-by: Darren Etheridge <detheridge-l0cyMroinI0@public.gmane.org>
> Signed-off-by: Lad, Prabhakar <prabhakar.csengg-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> ---
> Changes for v2:
> a> Fixed review comments pointed by Hans.
>
> .../devicetree/bindings/media/ti-am437x-vpfe.txt | 61 +
> MAINTAINERS | 9 +
> drivers/media/platform/Kconfig | 1 +
> drivers/media/platform/Makefile | 2 +
> drivers/media/platform/am437x/Kconfig | 11 +
> drivers/media/platform/am437x/Makefile | 2 +
> drivers/media/platform/am437x/am437x-vpfe.c | 2785 ++++++++++++++++++++
> drivers/media/platform/am437x/am437x-vpfe.h | 287 ++
> drivers/media/platform/am437x/am437x-vpfe_regs.h | 140 +
> include/uapi/linux/Kbuild | 1 +
> include/uapi/linux/am437x-vpfe.h | 122 +
> 11 files changed, 3421 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/media/ti-am437x-vpfe.txt
> create mode 100644 drivers/media/platform/am437x/Kconfig
> create mode 100644 drivers/media/platform/am437x/Makefile
> create mode 100644 drivers/media/platform/am437x/am437x-vpfe.c
> create mode 100644 drivers/media/platform/am437x/am437x-vpfe.h
> create mode 100644 drivers/media/platform/am437x/am437x-vpfe_regs.h
> create mode 100644 include/uapi/linux/am437x-vpfe.h
>
> diff --git a/Documentation/devicetree/bindings/media/ti-am437x-vpfe.txt b/Documentation/devicetree/bindings/media/ti-am437x-vpfe.txt
> new file mode 100644
> index 0000000..3932e76
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/ti-am437x-vpfe.txt
> @@ -0,0 +1,61 @@
> +Texas Instruments AM437x CAMERA (VPFE)
> +--------------------------------------
> +
> +The Video Processing Front End (VPFE) is a key component for image capture
> +applications. The capture module provides the system interface and the
> +processing capability to connect RAW image-sensor modules and video decoders
> +to the AM437x device.
> +
> +Required properties:
> +- compatible: must be "ti,am437x-vpfe"
> +- reg: physical base address and length of the registers set for the device;
> +- interrupts: should contain IRQ line for the VPFE;
> +- ti,am437x-vpfe-interface: can be one of the following,
> + 0 - Raw Bayer Interface.
> + 1 - 8 Bit BT656 Interface.
> + 2 - 10 Bit BT656 Interface.
> + 3 - YCbCr 8 Bit Interface.
> + 4 - YCbCr 16 Bit Interface.
> +
> +VPFE supports a single port node with parallel bus. It should contain one
> +'port' child node with child 'endpoint' node. Please refer to the bindings
> +defined in Documentation/devicetree/bindings/media/video-interfaces.txt.
> +
> +Example:
> + vpfe: vpfe@f0034000 {
> + compatible = "ti,am437x-vpfe";
> + reg = <0x48328000 0x2000>;
> + interrupts = <GIC_SPI 50 IRQ_TYPE_LEVEL_HIGH>;
> +
> + pinctrl-names = "default", "sleep";
> + pinctrl-0 = <&vpfe_pins_default>;
> + pinctrl-1 = <&vpfe_pins_sleep>;
> +
> + port {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + vpfe0_ep: endpoint {
> + remote-endpoint = <&ov2659_1>;
> + ti,am437x-vpfe-interface = <0>;
> + bus-width = <8>;
> + hsync-active = <0>;
> + vsync-active = <0>;
> + };
> + };
> + };
> +
> + i2c1: i2c@4802a000 {
> +
> + ov2659@30 {
> + compatible = "ti,ov2659";
> + reg = <0x30>;
> +
> + port {
> + ov2659_1: endpoint {
> + remote-endpoint = <&vpfe0_ep>;
> + bus-width = <8>;
> + mclk-frequency = <12000000>;
> + };
> + };
> + };
> diff --git a/MAINTAINERS b/MAINTAINERS
> index a6288ca..a42d367 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -8537,6 +8537,15 @@ S: Maintained
> F: drivers/media/platform/davinci/
> F: include/media/davinci/
>
> +TI AM437X VPFE DRIVER
> +M: Lad, Prabhakar <prabhakar.csengg-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> +L: linux-media-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> +W: http://linuxtv.org/
> +Q: http://patchwork.linuxtv.org/project/linux-media/list/
> +T: git git://linuxtv.org/mhadli/v4l-dvb-davinci_devices.git
> +S: Maintained
> +F: drivers/media/platform/am437x/
> +
> SIS 190 ETHERNET DRIVER
> M: Francois Romieu <romieu-W8zweXLXuWQS+FvcfC7Uqw@public.gmane.org>
> L: netdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
> index 0c61155..6d94045 100644
> --- a/drivers/media/platform/Kconfig
> +++ b/drivers/media/platform/Kconfig
> @@ -126,6 +126,7 @@ config VIDEO_S3C_CAMIF
> source "drivers/media/platform/soc_camera/Kconfig"
> source "drivers/media/platform/exynos4-is/Kconfig"
> source "drivers/media/platform/s5p-tv/Kconfig"
> +source "drivers/media/platform/am437x/Kconfig"
>
> endif # V4L_PLATFORM_DRIVERS
>
> diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
> index b818afb..7bb6d46 100644
> --- a/drivers/media/platform/Makefile
> +++ b/drivers/media/platform/Makefile
> @@ -49,4 +49,6 @@ obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1/
>
> obj-y += omap/
>
> +obj-$(CONFIG_VIDEO_AM437X_VPFE) += am437x/
> +
> ccflags-y += -I$(srctree)/drivers/media/i2c
> diff --git a/drivers/media/platform/am437x/Kconfig b/drivers/media/platform/am437x/Kconfig
> new file mode 100644
> index 0000000..97dea72
> --- /dev/null
> +++ b/drivers/media/platform/am437x/Kconfig
> @@ -0,0 +1,11 @@
> +config VIDEO_AM437X_VPFE
> + tristate "TI AM437x VPFE video capture driver"
> + depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
> + depends on SOC_AM43XX || COMPILE_TEST
> + select VIDEOBUF2_DMA_CONTIG
> + help
> + Support for AM437x Video Processing Front End based Video
> + Capture Driver.
> +
> + To compile this driver as a module, choose M here. The module
> + will be called ti_vpfe.
> diff --git a/drivers/media/platform/am437x/Makefile b/drivers/media/platform/am437x/Makefile
> new file mode 100644
> index 0000000..3c6b7bd
> --- /dev/null
> +++ b/drivers/media/platform/am437x/Makefile
> @@ -0,0 +1,2 @@
> +ti-vpfe-y := am437x-vpfe.o
> +obj-$(CONFIG_VIDEO_AM437X_VPFE) += ti-vpfe.o
> diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c
> new file mode 100644
> index 0000000..f3db23d
> --- /dev/null
> +++ b/drivers/media/platform/am437x/am437x-vpfe.c
> @@ -0,0 +1,2785 @@
> +/*
> + * TI VPFE capture Driver
> + *
> + * Copyright (C) 2013 - 2014 Texas Instruments, Inc.
> + *
> + * Benoit Parrot <bparrot-l0cyMroinI0@public.gmane.org>
> + * Lad, Prabhakar <prabhakar.csengg-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> + *
> + * This program is free software; you may redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 2 of the License.
> + *
> + * 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
> + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
> + * 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 <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/slab.h>
> +#include <linux/uaccess.h>
> +#include <linux/videodev2.h>
> +
> +#include <media/v4l2-common.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-event.h>
> +#include <media/v4l2-of.h>
> +
> +#include "am437x-vpfe.h"
> +
> +#define VPFE_MODULE_NAME "vpfe"
> +#define VPFE_VERSION "0.1.0"
> +
> +static int debug;
> +module_param(debug, int, 0644);
> +MODULE_PARM_DESC(debug, "Debug level 0-8");
> +
> +#define vpfe_dbg(level, dev, fmt, arg...) \
> + v4l2_dbg(level, debug, &dev->v4l2_dev, fmt, ##arg)
> +#define vpfe_info(dev, fmt, arg...) \
> + v4l2_info(&dev->v4l2_dev, fmt, ##arg)
> +#define vpfe_err(dev, fmt, arg...) \
> + v4l2_err(&dev->v4l2_dev, fmt, ##arg)
> +
> +/* standard information */
> +struct vpfe_standard {
> + v4l2_std_id std_id;
> + unsigned int width;
> + unsigned int height;
> + struct v4l2_fract pixelaspect;
> + int frame_format;
> +};
> +
> +const struct vpfe_standard vpfe_standards[] = {
> + {V4L2_STD_525_60, 720, 480, {11, 10}, 1},
> + {V4L2_STD_625_50, 720, 576, {54, 59}, 1},
> +};
> +
> +struct bus_format {
> + unsigned int width;
> + unsigned int bpp;
> +};
> +
> +/*
> + * struct vpfe_fmt - VPFE media bus format information
> + * @name: V4L2 format description
> + * @code: V4L2 media bus format code
> + * @shifted: V4L2 media bus format code for the same pixel layout but
> + * shifted to be 8 bits per pixel. =0 if format is not shiftable.
> + * @pixelformat: V4L2 pixel format FCC identifier
> + * @width: Bits per pixel (when transferred over a bus)
> + * @bpp: Bytes per pixel (when stored in memory)
> + * @supported: Indicates format supported by subdev
> + */
> +struct vpfe_fmt {
> + const char *name;
> + u32 fourcc;
> + u32 code;
> + struct bus_format l;
> + struct bus_format s;
> + bool supported;
> + u32 index;
> +};
> +
> +static struct vpfe_fmt formats[] = {
> + {
> + .name = "YUV 4:2:2 packed, YCbYCr",
> + .fourcc = V4L2_PIX_FMT_YUYV,
> + .code = MEDIA_BUS_FMT_YUYV8_2X8,
> + .l.width = 10,
> + .l.bpp = 4,
> + .s.width = 8,
> + .s.bpp = 2,
> + .supported = false,
> + }, {
> + .name = "YUV 4:2:2 packed, CbYCrY",
> + .fourcc = V4L2_PIX_FMT_UYVY,
> + .code = MEDIA_BUS_FMT_UYVY8_2X8,
> + .l.width = 10,
> + .l.bpp = 4,
> + .s.width = 8,
> + .s.bpp = 2,
> + .supported = false,
> + }, {
> + .name = "YUV 4:2:2 packed, YCrYCb",
> + .fourcc = V4L2_PIX_FMT_YVYU,
> + .code = MEDIA_BUS_FMT_YVYU8_2X8,
> + .l.width = 10,
> + .l.bpp = 4,
> + .s.width = 8,
> + .s.bpp = 2,
> + .supported = false,
> + }, {
> + .name = "YUV 4:2:2 packed, CrYCbY",
> + .fourcc = V4L2_PIX_FMT_VYUY,
> + .code = MEDIA_BUS_FMT_VYUY8_2X8,
> + .l.width = 10,
> + .l.bpp = 4,
> + .s.width = 8,
> + .s.bpp = 2,
> + .supported = false,
> + }, {
> + .name = "RAW8 BGGR",
> + .fourcc = V4L2_PIX_FMT_SBGGR8,
> + .code = MEDIA_BUS_FMT_SBGGR8_1X8,
> + .l.width = 10,
> + .l.bpp = 2,
> + .s.width = 8,
> + .s.bpp = 1,
> + .supported = false,
> + }, {
> + .name = "RAW8 GBRG",
> + .fourcc = V4L2_PIX_FMT_SGBRG8,
> + .code = MEDIA_BUS_FMT_SGBRG8_1X8,
> + .l.width = 10,
> + .l.bpp = 2,
> + .s.width = 8,
> + .s.bpp = 1,
> + .supported = false,
> + }, {
> + .name = "RAW8 GRBG",
> + .fourcc = V4L2_PIX_FMT_SGRBG8,
> + .code = MEDIA_BUS_FMT_SGRBG8_1X8,
> + .l.width = 10,
> + .l.bpp = 2,
> + .s.width = 8,
> + .s.bpp = 1,
> + .supported = false,
> + }, {
> + .name = "RAW8 RGGB",
> + .fourcc = V4L2_PIX_FMT_SRGGB8,
> + .code = MEDIA_BUS_FMT_SRGGB8_1X8,
> + .l.width = 10,
> + .l.bpp = 2,
> + .s.width = 8,
> + .s.bpp = 1,
> + .supported = false,
> + }, {
> + .name = "RGB565 (LE)",
> + .fourcc = V4L2_PIX_FMT_RGB565,
> + .code = MEDIA_BUS_FMT_RGB565_2X8_LE,
> + .l.width = 10,
> + .l.bpp = 4,
> + .s.width = 8,
> + .s.bpp = 2,
> + .supported = false,
> + }, {
> + .name = "RGB565 (BE)",
> + .fourcc = V4L2_PIX_FMT_RGB565X,
> + .code = MEDIA_BUS_FMT_RGB565_2X8_BE,
> + .l.width = 10,
> + .l.bpp = 4,
> + .s.width = 8,
> + .s.bpp = 2,
> + .supported = false,
> + },
> +};
> +
> +static int
> +__vpfe_get_format(struct vpfe_device *vpfe,
> + struct v4l2_format *format, unsigned int *bpp);
> +
> +static struct vpfe_fmt *find_format_by_code(unsigned int code, bool set)
> +{
> + struct vpfe_fmt *fmt;
> + static u32 index;
> + unsigned int k;
> +
> + for (k = 0; k < ARRAY_SIZE(formats); k++) {
> + fmt = &formats[k];
> + if (fmt->code == code) {
> + if (set) {
> + fmt->supported = true;
> + fmt->index = index++;
> + }
It's ugly and unexpected to set these fields here.
Why not do that where you actually need it?
E.g. instead of calling find_format_by_code(code, true) do:
fmt = find_format_by_code(code);
if (fmt) {
fmt->supported = true;
fmt->index = index++;
}
That's much cleaner.
> + return fmt;
> + }
> + }
> +
> + return NULL;
> +}
> +
> +static struct vpfe_fmt *find_format_by_pix(unsigned int pixelformat)
> +{
> + struct vpfe_fmt *fmt;
> + unsigned int k;
> +
> + for (k = 0; k < ARRAY_SIZE(formats); k++) {
> + fmt = &formats[k];
> + if (fmt->fourcc == pixelformat)
> + return fmt;
> + }
> +
> + return NULL;
> +}
> +
> +static void
> +mbus_to_pix(struct vpfe_device *vpfe,
> + const struct v4l2_mbus_framefmt *mbus,
> + struct v4l2_pix_format *pix, unsigned int *bpp)
> +{
> + struct vpfe_subdev_info *sdinfo = vpfe->current_subdev;
> + unsigned int bus_width = sdinfo->vpfe_param.bus_width;
> + struct vpfe_fmt *fmt;
> +
> + memset(pix, 0, sizeof(*pix));
> + pix->width = mbus->width;
> + pix->height = mbus->height;
> +
> + fmt = find_format_by_code(mbus->code, false);
> + if (WARN_ON(fmt == NULL)) {
> + pr_err("Invalid mbus code set\n");
> + *bpp = 1;
> + return;
> + }
> +
> + pix->colorspace = mbus->colorspace;
> + pix->field = mbus->field;
Use the v4l2_fill_pix_format helper (media/v4l2-mediabus.h).
That will also fill in the new ycbcr_enc and quantization fields.
> + pix->pixelformat = fmt->fourcc;
> + *bpp = (bus_width == 10) ? fmt->l.bpp : fmt->s.bpp;
> +
> + pix->bytesperline = pix->width * *bpp;
> + /* pitch should be 32 bytes aligned */
> + pix->bytesperline = ALIGN(pix->bytesperline, 32);
> + pix->sizeimage = pix->bytesperline * pix->height;
> +}
> +
> +static int
> +pix_to_mbus(struct vpfe_device *vpfe,
> + struct v4l2_pix_format *pix,
> + struct v4l2_mbus_framefmt *mbus)
> +{
> + struct vpfe_fmt *fmt;
> +
> + memset(mbus, 0, sizeof(*mbus));
> + mbus->width = pix->width;
> + mbus->height = pix->height;
> +
> + fmt = find_format_by_pix(pix->pixelformat);
> + if (!fmt) {
> + /* default to first entry */
> + vpfe_dbg(3, vpfe, "Invalid pixel code: %x, default used instead\n",
> + pix->pixelformat);
> + fmt = &formats[0];
> + }
> +
> + mbus->code = fmt->code;
> + mbus->colorspace = pix->colorspace;
> + mbus->field = pix->field;
Use v4l2_fill_mbus_format().
> +
> + return 0;
> +}
> +
> +/* Print Four-character-code (FOURCC) */
> +static char *print_fourcc(u32 fmt)
> +{
> + static char code[5];
> +
> + code[0] = (unsigned char)(fmt & 0xff);
> + code[1] = (unsigned char)((fmt >> 8) & 0xff);
> + code[2] = (unsigned char)((fmt >> 16) & 0xff);
> + code[3] = (unsigned char)((fmt >> 24) & 0xff);
> + code[4] = '\0';
> +
> + return code;
> +}
> +
> +static int
> +cmp_v4l2_format(const struct v4l2_format *lhs, const struct v4l2_format *rhs)
> +{
> + return lhs->type == rhs->type &&
> + lhs->fmt.pix.width == rhs->fmt.pix.width &&
> + lhs->fmt.pix.height == rhs->fmt.pix.height &&
> + lhs->fmt.pix.pixelformat == rhs->fmt.pix.pixelformat &&
> + lhs->fmt.pix.field == rhs->fmt.pix.field &&
> + lhs->fmt.pix.colorspace == rhs->fmt.pix.colorspace;
> +}
> +
> +static inline u32 vpfe_reg_read(struct vpfe_ccdc *ccdc, u32 offset)
> +{
> + return ioread32(ccdc->ccdc_cfg.base_addr + offset);
> +}
> +
> +static inline void vpfe_reg_write(struct vpfe_ccdc *ccdc, u32 val, u32 offset)
> +{
> + iowrite32(val, ccdc->ccdc_cfg.base_addr + offset);
> +}
> +
> +static inline struct vpfe_device *to_vpfe(struct vpfe_ccdc *ccdc)
> +{
> + return container_of(ccdc, struct vpfe_device, ccdc);
> +}
> +
> +static inline struct vpfe_cap_buffer *to_vpfe_buffer(struct vb2_buffer *vb)
> +{
> + return container_of(vb, struct vpfe_cap_buffer, vb);
> +}
> +
> +static inline void vpfe_pcr_enable(struct vpfe_ccdc *ccdc, int flag)
> +{
> + vpfe_reg_write(ccdc, !!flag, VPFE_PCR);
> +}
> +
> +static void vpfe_config_enable(struct vpfe_ccdc *ccdc, int flag)
> +{
> + unsigned int cfg;
> +
> + if (!flag) {
> + cfg = vpfe_reg_read(ccdc, VPFE_CONFIG);
> + cfg &= ~(VPFE_CONFIG_EN_ENABLE << VPFE_CONFIG_EN_SHIFT);
> + } else {
> + cfg = VPFE_CONFIG_EN_ENABLE << VPFE_CONFIG_EN_SHIFT;
> + }
> +
> + vpfe_reg_write(ccdc, cfg, VPFE_CONFIG);
> +}
> +
> +static void vpfe_ccdc_setwin(struct vpfe_ccdc *ccdc,
> + struct v4l2_rect *image_win,
> + enum ccdc_frmfmt frm_fmt,
> + int bpp)
> +{
> + int horz_start, horz_nr_pixels;
> + int vert_start, vert_nr_lines;
> + int val, mid_img;
> +
> + /*
> + * ppc - per pixel count. indicates how many pixels per cell
> + * output to SDRAM. example, for ycbcr, it is one y and one c, so 2.
> + * raw capture this is 1
> + */
> + horz_start = image_win->left * bpp;
> + horz_nr_pixels = (image_win->width * bpp) - 1;
> + vpfe_reg_write(ccdc, (horz_start << VPFE_HORZ_INFO_SPH_SHIFT) |
> + horz_nr_pixels, VPFE_HORZ_INFO);
> +
> + vert_start = image_win->top;
> +
> + if (frm_fmt == CCDC_FRMFMT_INTERLACED) {
> + vert_nr_lines = (image_win->height >> 1) - 1;
> + vert_start >>= 1;
> + /* Since first line doesn't have any data */
> + vert_start += 1;
> + /* configure VDINT0 */
> + val = (vert_start << VPFE_VDINT_VDINT0_SHIFT);
> + } else {
> + /* Since first line doesn't have any data */
> + vert_start += 1;
> + vert_nr_lines = image_win->height - 1;
> + /*
> + * configure VDINT0 and VDINT1. VDINT1 will be at half
> + * of image height
> + */
> + mid_img = vert_start + (image_win->height / 2);
> + val = (vert_start << VPFE_VDINT_VDINT0_SHIFT) |
> + (mid_img & VPFE_VDINT_VDINT1_MASK);
> + }
> +
> + vpfe_reg_write(ccdc, val, VPFE_VDINT);
> +
> + vpfe_reg_write(ccdc, (vert_start << VPFE_VERT_START_SLV0_SHIFT) |
> + vert_start, VPFE_VERT_START);
> + vpfe_reg_write(ccdc, vert_nr_lines, VPFE_VERT_LINES);
> +}
> +
> +static void vpfe_reg_dump(struct vpfe_ccdc *ccdc)
> +{
> + struct vpfe_device *vpfe = to_vpfe(ccdc);
> +
> + vpfe_dbg(3, vpfe, "ALAW: 0x%x\n", vpfe_reg_read(ccdc, VPFE_ALAW));
> + vpfe_dbg(3, vpfe, "CLAMP: 0x%x\n", vpfe_reg_read(ccdc, VPFE_CLAMP));
> + vpfe_dbg(3, vpfe, "DCSUB: 0x%x\n", vpfe_reg_read(ccdc, VPFE_DCSUB));
> + vpfe_dbg(3, vpfe, "BLKCMP: 0x%x\n", vpfe_reg_read(ccdc, VPFE_BLKCMP));
> + vpfe_dbg(3, vpfe, "COLPTN: 0x%x\n", vpfe_reg_read(ccdc, VPFE_COLPTN));
> + vpfe_dbg(3, vpfe, "SDOFST: 0x%x\n", vpfe_reg_read(ccdc, VPFE_SDOFST));
> + vpfe_dbg(3, vpfe, "SYN_MODE: 0x%x\n",
> + vpfe_reg_read(ccdc, VPFE_SYNMODE));
> + vpfe_dbg(3, vpfe, "HSIZE_OFF: 0x%x\n",
> + vpfe_reg_read(ccdc, VPFE_HSIZE_OFF));
> + vpfe_dbg(3, vpfe, "HORZ_INFO: 0x%x\n",
> + vpfe_reg_read(ccdc, VPFE_HORZ_INFO));
> + vpfe_dbg(3, vpfe, "VERT_START: 0x%x\n",
> + vpfe_reg_read(ccdc, VPFE_VERT_START));
> + vpfe_dbg(3, vpfe, "VERT_LINES: 0x%x\n",
> + vpfe_reg_read(ccdc, VPFE_VERT_LINES));
> +}
> +
> +static int
> +vpfe_ccdc_validate_param(struct vpfe_ccdc *ccdc,
> + struct vpfe_ccdc_config_params_raw *ccdcparam)
> +{
> + struct vpfe_device *vpfe = to_vpfe(ccdc);
> + u8 max_gamma, max_data;
> +
> + if (!ccdcparam->alaw.enable)
> + return 0;
> +
> + max_gamma = ccdc_gamma_width_max_bit(ccdcparam->alaw.gamma_wd);
> + max_data = ccdc_data_size_max_bit(ccdcparam->data_sz);
> +
> + if (ccdcparam->alaw.gamma_wd > VPFE_CCDC_GAMMA_BITS_09_0 ||
> + ccdcparam->alaw.gamma_wd < VPFE_CCDC_GAMMA_BITS_15_6 ||
> + max_gamma > max_data) {
> + vpfe_dbg(1, vpfe, "Invalid data line select\n");
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static void
> +vpfe_ccdc_update_raw_params(struct vpfe_ccdc *ccdc,
> + struct vpfe_ccdc_config_params_raw *raw_params)
> +{
> + struct vpfe_ccdc_config_params_raw *config_params =
> + &ccdc->ccdc_cfg.bayer.config_params;
> +
> + config_params = raw_params;
> +}
> +
> +/*
> + * vpfe_ccdc_restore_defaults()
> + * This function will write defaults to all CCDC registers
> + */
> +static void vpfe_ccdc_restore_defaults(struct vpfe_ccdc *ccdc)
> +{
> + int i;
> +
> + /* Disable CCDC */
> + vpfe_pcr_enable(ccdc, 0);
> +
> + /* set all registers to default value */
> + for (i = 4; i <= 0x94; i += 4)
> + vpfe_reg_write(ccdc, 0, i);
> +
> + vpfe_reg_write(ccdc, VPFE_NO_CULLING, VPFE_CULLING);
> + vpfe_reg_write(ccdc, VPFE_CCDC_GAMMA_BITS_11_2, VPFE_ALAW);
> +}
> +
> +static int vpfe_ccdc_close(struct vpfe_ccdc *ccdc, struct device *dev)
> +{
> + int dma_cntl, i, pcr;
> +
> + /* If the CCDC module is still busy wait for it to be done */
> + for (i = 0; i < 10; i++) {
> + usleep_range(5000, 6000);
> + pcr = vpfe_reg_read(ccdc, VPFE_PCR);
> + if (!pcr)
> + break;
> +
> + /* make sure it it is disabled */
> + vpfe_pcr_enable(ccdc, 0);
> + }
> +
> + /* Disable CCDC by resetting all register to default POR values */
> + vpfe_ccdc_restore_defaults(ccdc);
> +
> + /* if DMA_CNTL overflow bit is set. Clear it
> + * It appears to take a while for this to become quiescent ~20ms
> + */
> + for (i = 0; i < 10; i++) {
> + dma_cntl = vpfe_reg_read(ccdc, VPFE_DMA_CNTL);
> + if (!(dma_cntl & VPFE_DMA_CNTL_OVERFLOW))
> + break;
> +
> + /* Clear the overflow bit */
> + vpfe_reg_write(ccdc, dma_cntl, VPFE_DMA_CNTL);
> + usleep_range(5000, 6000);
> + }
> +
> + /* Disabled the module at the CONFIG level */
> + vpfe_config_enable(ccdc, 0);
> +
> + pm_runtime_put_sync(dev);
> +
> + return 0;
> +}
> +
> +static int vpfe_ccdc_set_params(struct vpfe_ccdc *ccdc, void __user *params)
> +{
> + struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc);
> + struct vpfe_ccdc_config_params_raw raw_params;
> + int x;
> +
> + if (ccdc->ccdc_cfg.if_type != VPFE_RAW_BAYER)
> + return -EINVAL;
> +
> + x = copy_from_user(&raw_params, params, sizeof(raw_params));
> + if (x) {
> + vpfe_dbg(1, vpfe,
> + "vpfe_ccdc_set_params: error in copying ccdc params, %d\n",
> + x);
> + return -EFAULT;
> + }
> +
> + if (!vpfe_ccdc_validate_param(ccdc, &raw_params)) {
> + vpfe_ccdc_update_raw_params(ccdc, &raw_params);
> + return 0;
> + }
> +
> + return -EINVAL;
> +}
> +
> +/*
> + * vpfe_ccdc_config_ycbcr()
> + * This function will configure CCDC for YCbCr video capture
> + */
> +static void vpfe_ccdc_config_ycbcr(struct vpfe_ccdc *ccdc)
> +{
> + struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc);
> + struct ccdc_params_ycbcr *params = &ccdc->ccdc_cfg.ycbcr;
> + u32 syn_mode;
> +
> + vpfe_dbg(3, vpfe, "vpfe_ccdc_config_ycbcr:\n");
> + /*
> + * first restore the CCDC registers to default values
> + * This is important since we assume default values to be set in
> + * a lot of registers that we didn't touch
> + */
> + vpfe_ccdc_restore_defaults(ccdc);
> +
> + /*
> + * configure pixel format, frame format, configure video frame
> + * format, enable output to SDRAM, enable internal timing generator
> + * and 8bit pack mode
> + */
> + syn_mode = (((params->pix_fmt & VPFE_SYN_MODE_INPMOD_MASK) <<
> + VPFE_SYN_MODE_INPMOD_SHIFT) |
> + ((params->frm_fmt & VPFE_SYN_FLDMODE_MASK) <<
> + VPFE_SYN_FLDMODE_SHIFT) | VPFE_VDHDEN_ENABLE |
> + VPFE_WEN_ENABLE | VPFE_DATA_PACK_ENABLE);
> +
> + /* setup BT.656 sync mode */
> + if (params->bt656_enable) {
> + vpfe_reg_write(ccdc, VPFE_REC656IF_BT656_EN, VPFE_REC656IF);
> +
> + /*
> + * configure the FID, VD, HD pin polarity,
> + * fld,hd pol positive, vd negative, 8-bit data
> + */
> + syn_mode |= VPFE_SYN_MODE_VD_POL_NEGATIVE;
> + if (ccdc->ccdc_cfg.if_type == VPFE_BT656_10BIT)
> + syn_mode |= VPFE_SYN_MODE_10BITS;
> + else
> + syn_mode |= VPFE_SYN_MODE_8BITS;
> + } else {
> + /* y/c external sync mode */
> + syn_mode |= (((params->fid_pol & VPFE_FID_POL_MASK) <<
> + VPFE_FID_POL_SHIFT) |
> + ((params->hd_pol & VPFE_HD_POL_MASK) <<
> + VPFE_HD_POL_SHIFT) |
> + ((params->vd_pol & VPFE_VD_POL_MASK) <<
> + VPFE_VD_POL_SHIFT));
> + }
> + vpfe_reg_write(ccdc, syn_mode, VPFE_SYNMODE);
> +
> + /* configure video window */
> + vpfe_ccdc_setwin(ccdc, ¶ms->win,
> + params->frm_fmt, params->bytesperpixel);
> +
> + /*
> + * configure the order of y cb cr in SDRAM, and disable latch
> + * internal register on vsync
> + */
> + if (ccdc->ccdc_cfg.if_type == VPFE_BT656_10BIT)
> + vpfe_reg_write(ccdc,
> + (params->pix_order << VPFE_CCDCFG_Y8POS_SHIFT) |
> + VPFE_LATCH_ON_VSYNC_DISABLE |
> + VPFE_CCDCFG_BW656_10BIT, VPFE_CCDCFG);
> + else
> + vpfe_reg_write(ccdc,
> + (params->pix_order << VPFE_CCDCFG_Y8POS_SHIFT) |
> + VPFE_LATCH_ON_VSYNC_DISABLE, VPFE_CCDCFG);
> +
> + /*
> + * configure the horizontal line offset. This should be a
> + * on 32 byte boundary. So clear LSB 5 bits
> + */
> + vpfe_reg_write(ccdc, params->bytesperline, VPFE_HSIZE_OFF);
> +
> + /* configure the memory line offset */
> + if (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED)
> + /* two fields are interleaved in memory */
> + vpfe_reg_write(ccdc, VPFE_SDOFST_FIELD_INTERLEAVED,
> + VPFE_SDOFST);
> +}
> +
> +static void
> +vpfe_ccdc_config_black_clamp(struct vpfe_ccdc *ccdc,
> + struct vpfe_ccdc_black_clamp *bclamp)
> +{
> + u32 val;
> +
> + if (!bclamp->enable) {
> + /* configure DCSub */
> + val = (bclamp->dc_sub) & VPFE_BLK_DC_SUB_MASK;
> + vpfe_reg_write(ccdc, val, VPFE_DCSUB);
> + vpfe_reg_write(ccdc, VPFE_CLAMP_DEFAULT_VAL, VPFE_CLAMP);
> + return;
> + }
> + /*
> + * Configure gain, Start pixel, No of line to be avg,
> + * No of pixel/line to be avg, & Enable the Black clamping
> + */
> + val = ((bclamp->sgain & VPFE_BLK_SGAIN_MASK) |
> + ((bclamp->start_pixel & VPFE_BLK_ST_PXL_MASK) <<
> + VPFE_BLK_ST_PXL_SHIFT) |
> + ((bclamp->sample_ln & VPFE_BLK_SAMPLE_LINE_MASK) <<
> + VPFE_BLK_SAMPLE_LINE_SHIFT) |
> + ((bclamp->sample_pixel & VPFE_BLK_SAMPLE_LN_MASK) <<
> + VPFE_BLK_SAMPLE_LN_SHIFT) | VPFE_BLK_CLAMP_ENABLE);
> + vpfe_reg_write(ccdc, val, VPFE_CLAMP);
> + /* If Black clamping is enable then make dcsub 0 */
> + vpfe_reg_write(ccdc, VPFE_DCSUB_DEFAULT_VAL, VPFE_DCSUB);
> +}
> +
> +static void
> +vpfe_ccdc_config_black_compense(struct vpfe_ccdc *ccdc,
> + struct vpfe_ccdc_black_compensation *bcomp)
> +{
> + u32 val;
> +
> + val = ((bcomp->b & VPFE_BLK_COMP_MASK) |
> + ((bcomp->gb & VPFE_BLK_COMP_MASK) <<
> + VPFE_BLK_COMP_GB_COMP_SHIFT) |
> + ((bcomp->gr & VPFE_BLK_COMP_MASK) <<
> + VPFE_BLK_COMP_GR_COMP_SHIFT) |
> + ((bcomp->r & VPFE_BLK_COMP_MASK) <<
> + VPFE_BLK_COMP_R_COMP_SHIFT));
> + vpfe_reg_write(ccdc, val, VPFE_BLKCMP);
> +}
> +
> +/*
> + * vpfe_ccdc_config_raw()
> + * This function will configure CCDC for Raw capture mode
> + */
> +static void vpfe_ccdc_config_raw(struct vpfe_ccdc *ccdc)
> +{
> + struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc);
> + struct vpfe_ccdc_config_params_raw *config_params =
> + &ccdc->ccdc_cfg.bayer.config_params;
> + struct ccdc_params_raw *params = &ccdc->ccdc_cfg.bayer;
> + unsigned int syn_mode;
> + unsigned int val;
> +
> + vpfe_dbg(3, vpfe, "vpfe_ccdc_config_raw:\n");
> +
> + /* Reset CCDC */
> + vpfe_ccdc_restore_defaults(ccdc);
> +
> + /* Disable latching function registers on VSYNC */
> + vpfe_reg_write(ccdc, VPFE_LATCH_ON_VSYNC_DISABLE, VPFE_CCDCFG);
> +
> + /*
> + * Configure the vertical sync polarity(SYN_MODE.VDPOL),
> + * horizontal sync polarity (SYN_MODE.HDPOL), frame id polarity
> + * (SYN_MODE.FLDPOL), frame format(progressive or interlace),
> + * data size(SYNMODE.DATSIZ), &pixel format (Input mode), output
> + * SDRAM, enable internal timing generator
> + */
> + syn_mode = (((params->vd_pol & VPFE_VD_POL_MASK) << VPFE_VD_POL_SHIFT) |
> + ((params->hd_pol & VPFE_HD_POL_MASK) << VPFE_HD_POL_SHIFT) |
> + ((params->fid_pol & VPFE_FID_POL_MASK) <<
> + VPFE_FID_POL_SHIFT) | ((params->frm_fmt &
> + VPFE_FRM_FMT_MASK) << VPFE_FRM_FMT_SHIFT) |
> + ((config_params->data_sz & VPFE_DATA_SZ_MASK) <<
> + VPFE_DATA_SZ_SHIFT) | ((params->pix_fmt &
> + VPFE_PIX_FMT_MASK) << VPFE_PIX_FMT_SHIFT) |
> + VPFE_WEN_ENABLE | VPFE_VDHDEN_ENABLE);
> +
> + /* Enable and configure aLaw register if needed */
> + if (config_params->alaw.enable) {
> + val = ((config_params->alaw.gamma_wd &
> + VPFE_ALAW_GAMMA_WD_MASK) | VPFE_ALAW_ENABLE);
> + vpfe_reg_write(ccdc, val, VPFE_ALAW);
> + vpfe_dbg(3, vpfe, "\nWriting 0x%x to ALAW...\n", val);
> + }
> +
> + /* Configure video window */
> + vpfe_ccdc_setwin(ccdc, ¶ms->win, params->frm_fmt,
> + params->bytesperpixel);
> +
> + /* Configure Black Clamp */
> + vpfe_ccdc_config_black_clamp(ccdc, &config_params->blk_clamp);
> +
> + /* Configure Black level compensation */
> + vpfe_ccdc_config_black_compense(ccdc, &config_params->blk_comp);
> +
> + /* If data size is 8 bit then pack the data */
> + if ((config_params->data_sz == VPFE_CCDC_DATA_8BITS) ||
> + config_params->alaw.enable)
> + syn_mode |= VPFE_DATA_PACK_ENABLE;
> +
> + /*
> + * Configure Horizontal offset register. If pack 8 is enabled then
> + * 1 pixel will take 1 byte
> + */
> + vpfe_reg_write(ccdc, params->bytesperline, VPFE_HSIZE_OFF);
> +
> + vpfe_dbg(3, vpfe, "Writing %d (%x) to HSIZE_OFF\n",
> + params->bytesperline, params->bytesperline);
> +
> + /* Set value for SDOFST */
> + if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) {
> + if (params->image_invert_enable) {
> + /* For interlace inverse mode */
> + vpfe_reg_write(ccdc, VPFE_INTERLACED_IMAGE_INVERT,
> + VPFE_SDOFST);
> + } else {
> + /* For interlace non inverse mode */
> + vpfe_reg_write(ccdc, VPFE_INTERLACED_NO_IMAGE_INVERT,
> + VPFE_SDOFST);
> + }
> + } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) {
> + vpfe_reg_write(ccdc, VPFE_PROGRESSIVE_NO_IMAGE_INVERT,
> + VPFE_SDOFST);
> + }
> +
> + vpfe_reg_write(ccdc, syn_mode, VPFE_SYNMODE);
> +
> + vpfe_reg_dump(ccdc);
> +}
> +
> +static inline int
> +vpfe_ccdc_set_buftype(struct vpfe_ccdc *ccdc,
> + enum ccdc_buftype buf_type)
> +{
> + if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER)
> + ccdc->ccdc_cfg.bayer.buf_type = buf_type;
> + else
> + ccdc->ccdc_cfg.ycbcr.buf_type = buf_type;
> +
> + return 0;
> +}
> +
> +static inline enum ccdc_buftype vpfe_ccdc_get_buftype(struct vpfe_ccdc *ccdc)
> +{
> + if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER)
> + return ccdc->ccdc_cfg.bayer.buf_type;
> +
> + return ccdc->ccdc_cfg.ycbcr.buf_type;
> +}
> +
> +static int vpfe_ccdc_set_pixel_format(struct vpfe_ccdc *ccdc, u32 pixfmt)
> +{
> + struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc);
> +
> + vpfe_dbg(1, vpfe, "vpfe_ccdc_set_pixel_format: if_type: %d, pixfmt:%s\n",
> + ccdc->ccdc_cfg.if_type, print_fourcc(pixfmt));
> +
> + if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) {
> + ccdc->ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW;
> + /*
> + * Need to clear it in case it was left on
> + * after the last capture.
> + */
> + ccdc->ccdc_cfg.bayer.config_params.alaw.enable = 0;
> +
> + switch (pixfmt) {
> + case V4L2_PIX_FMT_SBGGR8:
> + ccdc->ccdc_cfg.bayer.config_params.alaw.enable = 1;
> + break;
> +
> + case V4L2_PIX_FMT_YUYV:
> + case V4L2_PIX_FMT_UYVY:
> + case V4L2_PIX_FMT_YUV420:
> + case V4L2_PIX_FMT_NV12:
> + case V4L2_PIX_FMT_RGB565X:
> + break;
> +
> + case V4L2_PIX_FMT_SBGGR16:
> + default:
> + return -EINVAL;
> + }
> + } else {
> + switch (pixfmt) {
> + case V4L2_PIX_FMT_YUYV:
> + ccdc->ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR;
> + break;
> +
> + case V4L2_PIX_FMT_UYVY:
> + ccdc->ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY;
> + break;
> +
> + default:
> + return -EINVAL;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static u32 vpfe_ccdc_get_pixel_format(struct vpfe_ccdc *ccdc)
> +{
> + u32 pixfmt;
> +
> + if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) {
> + pixfmt = V4L2_PIX_FMT_YUYV;
> + } else {
> + if (ccdc->ccdc_cfg.ycbcr.pix_order == CCDC_PIXORDER_YCBYCR)
> + pixfmt = V4L2_PIX_FMT_YUYV;
> + else
> + pixfmt = V4L2_PIX_FMT_UYVY;
> + }
> +
> + return pixfmt;
> +}
> +
> +static int
> +vpfe_ccdc_set_image_window(struct vpfe_ccdc *ccdc,
> + struct v4l2_rect *win, unsigned int bpp)
> +{
> + if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) {
> + ccdc->ccdc_cfg.bayer.win = *win;
> + ccdc->ccdc_cfg.bayer.bytesperpixel = bpp;
> + ccdc->ccdc_cfg.bayer.bytesperline = ALIGN(win->width * bpp, 32);
> + } else {
> + ccdc->ccdc_cfg.ycbcr.win = *win;
> + ccdc->ccdc_cfg.ycbcr.bytesperpixel = bpp;
> + ccdc->ccdc_cfg.ycbcr.bytesperline = ALIGN(win->width * bpp, 32);
> + }
> +
> + return 0;
> +}
> +
> +static inline void
> +vpfe_ccdc_get_image_window(struct vpfe_ccdc *ccdc,
> + struct v4l2_rect *win)
> +{
> + if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER)
> + *win = ccdc->ccdc_cfg.bayer.win;
> + else
> + *win = ccdc->ccdc_cfg.ycbcr.win;
> +}
> +
> +static inline unsigned int vpfe_ccdc_get_line_length(struct vpfe_ccdc *ccdc)
> +{
> + if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER)
> + return ccdc->ccdc_cfg.bayer.bytesperline;
> +
> + return ccdc->ccdc_cfg.ycbcr.bytesperline;
> +}
> +
> +static inline int
> +vpfe_ccdc_set_frame_format(struct vpfe_ccdc *ccdc,
> + enum ccdc_frmfmt frm_fmt)
> +{
> + if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER)
> + ccdc->ccdc_cfg.bayer.frm_fmt = frm_fmt;
> + else
> + ccdc->ccdc_cfg.ycbcr.frm_fmt = frm_fmt;
> +
> + return 0;
> +}
> +
> +static inline enum ccdc_frmfmt
> +vpfe_ccdc_get_frame_format(struct vpfe_ccdc *ccdc)
> +{
> + if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER)
> + return ccdc->ccdc_cfg.bayer.frm_fmt;
> +
> + return ccdc->ccdc_cfg.ycbcr.frm_fmt;
> +}
> +
> +static inline int vpfe_ccdc_getfid(struct vpfe_ccdc *ccdc)
> +{
> + return (vpfe_reg_read(ccdc, VPFE_SYNMODE) >> 15) & 1;
> +}
> +
> +static inline void vpfe_set_sdr_addr(struct vpfe_ccdc *ccdc, unsigned long addr)
> +{
> + vpfe_reg_write(ccdc, addr & 0xffffffe0, VPFE_SDR_ADDR);
> +}
> +
> +static int vpfe_ccdc_set_hw_if_params(struct vpfe_ccdc *ccdc,
> + struct vpfe_hw_if_param *params)
> +{
> + struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc);
> +
> + ccdc->ccdc_cfg.if_type = params->if_type;
> +
> + switch (params->if_type) {
> + case VPFE_BT656:
> + case VPFE_YCBCR_SYNC_16:
> + case VPFE_YCBCR_SYNC_8:
> + case VPFE_BT656_10BIT:
> + ccdc->ccdc_cfg.ycbcr.vd_pol = params->vdpol;
> + ccdc->ccdc_cfg.ycbcr.hd_pol = params->hdpol;
> + break;
> +
> + case VPFE_RAW_BAYER:
> + ccdc->ccdc_cfg.bayer.vd_pol = params->vdpol;
> + ccdc->ccdc_cfg.bayer.hd_pol = params->hdpol;
> + if (params->bus_width == 10)
> + ccdc->ccdc_cfg.bayer.config_params.data_sz =
> + VPFE_CCDC_DATA_10BITS;
> + else
> + ccdc->ccdc_cfg.bayer.config_params.data_sz =
> + VPFE_CCDC_DATA_8BITS;
> + vpfe_dbg(1, vpfe, "params.bus_width: %d\n",
> + params->bus_width);
> + vpfe_dbg(1, vpfe, "config_params.data_sz: %d\n",
> + ccdc->ccdc_cfg.bayer.config_params.data_sz);
> + break;
> +
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static void vpfe_clear_intr(struct vpfe_ccdc *ccdc, int vdint)
> +{
> + unsigned int vpfe_int_status;
> +
> + vpfe_int_status = vpfe_reg_read(ccdc, VPFE_IRQ_STS);
> +
> + switch (vdint) {
> + /* VD0 interrupt */
> + case VPFE_VDINT0:
> + vpfe_int_status &= ~VPFE_VDINT0;
> + vpfe_int_status |= VPFE_VDINT0;
> + break;
> +
> + /* VD1 interrupt */
> + case VPFE_VDINT1:
> + vpfe_int_status &= ~VPFE_VDINT1;
> + vpfe_int_status |= VPFE_VDINT1;
> + break;
> +
> + /* VD2 interrupt */
> + case VPFE_VDINT2:
> + vpfe_int_status &= ~VPFE_VDINT2;
> + vpfe_int_status |= VPFE_VDINT2;
> + break;
> +
> + /* Clear all interrupts */
> + default:
> + vpfe_int_status &= ~(VPFE_VDINT0 |
> + VPFE_VDINT1 |
> + VPFE_VDINT2);
> + vpfe_int_status |= (VPFE_VDINT0 |
> + VPFE_VDINT1 |
> + VPFE_VDINT2);
> + break;
> + }
> + /* Clear specific VDINT from the status register */
> + vpfe_reg_write(ccdc, vpfe_int_status, VPFE_IRQ_STS);
> +
> + vpfe_int_status = vpfe_reg_read(ccdc, VPFE_IRQ_STS);
> +
> + /* Acknowledge that we are done with all interrupts */
> + vpfe_reg_write(ccdc, 1, VPFE_IRQ_EOI);
> +}
> +
> +static void vpfe_ccdc_config_defaults(struct vpfe_ccdc *ccdc)
> +{
> + ccdc->ccdc_cfg.if_type = VPFE_RAW_BAYER;
> +
> + ccdc->ccdc_cfg.ycbcr.pix_fmt = CCDC_PIXFMT_YCBCR_8BIT;
> + ccdc->ccdc_cfg.ycbcr.frm_fmt = CCDC_FRMFMT_INTERLACED;
> + ccdc->ccdc_cfg.ycbcr.fid_pol = VPFE_PINPOL_POSITIVE;
> + ccdc->ccdc_cfg.ycbcr.vd_pol = VPFE_PINPOL_POSITIVE;
> + ccdc->ccdc_cfg.ycbcr.hd_pol = VPFE_PINPOL_POSITIVE;
> + ccdc->ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY;
> + ccdc->ccdc_cfg.ycbcr.buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED;
> +
> + ccdc->ccdc_cfg.ycbcr.win.left = 0;
> + ccdc->ccdc_cfg.ycbcr.win.top = 0;
> + ccdc->ccdc_cfg.ycbcr.win.width = 720;
> + ccdc->ccdc_cfg.ycbcr.win.height = 576;
> + ccdc->ccdc_cfg.ycbcr.bt656_enable = 1;
> +
> + ccdc->ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW;
> + ccdc->ccdc_cfg.bayer.frm_fmt = CCDC_FRMFMT_PROGRESSIVE;
> + ccdc->ccdc_cfg.bayer.fid_pol = VPFE_PINPOL_POSITIVE;
> + ccdc->ccdc_cfg.bayer.vd_pol = VPFE_PINPOL_POSITIVE;
> + ccdc->ccdc_cfg.bayer.hd_pol = VPFE_PINPOL_POSITIVE;
> +
> + ccdc->ccdc_cfg.bayer.win.left = 0;
> + ccdc->ccdc_cfg.bayer.win.top = 0;
> + ccdc->ccdc_cfg.bayer.win.width = 800;
> + ccdc->ccdc_cfg.bayer.win.height = 600;
> + ccdc->ccdc_cfg.bayer.config_params.data_sz = VPFE_CCDC_DATA_8BITS;
> + ccdc->ccdc_cfg.bayer.config_params.alaw.gamma_wd =
> + VPFE_CCDC_GAMMA_BITS_09_0;
> +}
> +
> +/*
> + * vpfe_get_ccdc_image_format - Get image parameters based on CCDC settings
> + */
> +static int vpfe_get_ccdc_image_format(struct vpfe_device *vpfe,
> + struct v4l2_format *f)
> +{
> + struct v4l2_rect image_win;
> + enum ccdc_buftype buf_type;
> + enum ccdc_frmfmt frm_fmt;
> +
> + memset(f, 0, sizeof(*f));
> + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> + vpfe_ccdc_get_image_window(&vpfe->ccdc, &image_win);
> + f->fmt.pix.width = image_win.width;
> + f->fmt.pix.height = image_win.height;
> + f->fmt.pix.bytesperline = vpfe_ccdc_get_line_length(&vpfe->ccdc);
> + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline *
> + f->fmt.pix.height;
> + buf_type = vpfe_ccdc_get_buftype(&vpfe->ccdc);
> + f->fmt.pix.pixelformat = vpfe_ccdc_get_pixel_format(&vpfe->ccdc);
> + frm_fmt = vpfe_ccdc_get_frame_format(&vpfe->ccdc);
> +
> + if (frm_fmt == CCDC_FRMFMT_PROGRESSIVE) {
> + f->fmt.pix.field = V4L2_FIELD_NONE;
> + } else if (frm_fmt == CCDC_FRMFMT_INTERLACED) {
> + if (buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) {
> + f->fmt.pix.field = V4L2_FIELD_INTERLACED;
> + } else if (buf_type == CCDC_BUFTYPE_FLD_SEPARATED) {
> + f->fmt.pix.field = V4L2_FIELD_SEQ_TB;
> + } else {
> + vpfe_err(vpfe, "Invalid buf_type\n");
> + return -EINVAL;
> + }
> + } else {
> + vpfe_err(vpfe, "Invalid frm_fmt\n");
> + return -EINVAL;
> + }
> + return 0;
> +}
> +
> +static int vpfe_config_ccdc_image_format(struct vpfe_device *vpfe)
> +{
> + enum ccdc_frmfmt frm_fmt = CCDC_FRMFMT_INTERLACED;
> + int ret;
> +
> + vpfe_dbg(2, vpfe, "vpfe_config_ccdc_image_format\n");
> +
> + vpfe_dbg(1, vpfe, "pixelformat: %s\n",
> + print_fourcc(vpfe->fmt.fmt.pix.pixelformat));
> +
> + if (vpfe_ccdc_set_pixel_format(&vpfe->ccdc,
> + vpfe->fmt.fmt.pix.pixelformat) < 0) {
> + vpfe_err(vpfe, "couldn't set pix format in ccdc\n");
> + return -EINVAL;
> + }
> +
> + /* configure the image window */
> + vpfe_ccdc_set_image_window(&vpfe->ccdc, &vpfe->crop, vpfe->bpp);
> +
> + switch (vpfe->fmt.fmt.pix.field) {
> + case V4L2_FIELD_INTERLACED:
> + /* do nothing, since it is default */
> + ret = vpfe_ccdc_set_buftype(
> + &vpfe->ccdc,
> + CCDC_BUFTYPE_FLD_INTERLEAVED);
> + break;
> +
> + case V4L2_FIELD_NONE:
> + frm_fmt = CCDC_FRMFMT_PROGRESSIVE;
> + /* buffer type only applicable for interlaced scan */
> + break;
> +
> + case V4L2_FIELD_SEQ_TB:
> + ret = vpfe_ccdc_set_buftype(
> + &vpfe->ccdc,
> + CCDC_BUFTYPE_FLD_SEPARATED);
> + break;
> +
> + default:
> + return -EINVAL;
> + }
> +
> + if (ret)
> + return ret;
> +
> + return vpfe_ccdc_set_frame_format(&vpfe->ccdc, frm_fmt);
> +}
> +
> +/*
> + * vpfe_config_image_format()
> + * For a given standard, this functions sets up the default
> + * pix format & crop values in the vpfe device and ccdc. It first
> + * starts with defaults based values from the standard table.
> + * It then checks if sub device support g_mbus_fmt and then override the
> + * values based on that.Sets crop values to match with scan resolution
> + * starting at 0,0. It calls vpfe_config_ccdc_image_format() set the
> + * values in ccdc
> + */
> +static int vpfe_config_image_format(struct vpfe_device *vpfe,
> + v4l2_std_id std_id)
> +{
> + struct v4l2_pix_format *pix = &vpfe->fmt.fmt.pix;
> + int i, ret;
> +
> + for (i = 0; i < ARRAY_SIZE(vpfe_standards); i++) {
> + if (vpfe_standards[i].std_id & std_id) {
> + vpfe->std_info.active_pixels =
> + vpfe_standards[i].width;
> + vpfe->std_info.active_lines =
> + vpfe_standards[i].height;
> + vpfe->std_info.frame_format =
> + vpfe_standards[i].frame_format;
> + vpfe->std_index = i;
> +
> + break;
> + }
> + }
> +
> + if (i == ARRAY_SIZE(vpfe_standards)) {
> + vpfe_err(vpfe, "standard not supported\n");
> + return -EINVAL;
> + }
> +
> + vpfe->crop.top = vpfe->crop.left = 0;
> + vpfe->crop.width = vpfe->std_info.active_pixels;
> + vpfe->crop.height = vpfe->std_info.active_lines;
> + pix->width = vpfe->crop.width;
> + pix->height = vpfe->crop.height;
> + pix->pixelformat = V4L2_PIX_FMT_YUYV;
> +
> + /* first field and frame format based on standard frame format */
> + if (vpfe->std_info.frame_format)
> + pix->field = V4L2_FIELD_INTERLACED;
> + else
> + pix->field = V4L2_FIELD_NONE;
> +
> + ret = __vpfe_get_format(vpfe, &vpfe->fmt, &vpfe->bpp);
> + if (ret)
> + return ret;
> +
> + /* Update the crop window based on found values */
> + vpfe->crop.width = pix->width;
> + vpfe->crop.height = pix->height;
> +
> + return vpfe_config_ccdc_image_format(vpfe);
> +}
> +
> +static int vpfe_initialize_device(struct vpfe_device *vpfe)
> +{
> + struct vpfe_subdev_info *sdinfo;
> + int ret;
> +
> + sdinfo = &vpfe->cfg->sub_devs[0];
> + sdinfo->sd = vpfe->sd[0];
> + vpfe->current_input = 0;
> + vpfe->std_index = 0;
> + /* Configure the default format information */
> + ret = vpfe_config_image_format(vpfe,
> + vpfe_standards[vpfe->std_index].std_id);
> + if (ret)
> + return ret;
> +
> + pm_runtime_get_sync(vpfe->pdev);
> +
> + vpfe_config_enable(&vpfe->ccdc, 1);
> +
> + vpfe_ccdc_restore_defaults(&vpfe->ccdc);
> +
> + /* Clear all VPFE interrupts */
> + vpfe_clear_intr(&vpfe->ccdc, -1);
> +
> + return ret;
> +}
> +
> +/*
> + * vpfe_release : This function is based on the vb2_fop_release
> + * helper function.
> + * It has been augmented to handle module power management,
> + * by disabling/enabling h/w module fcntl clock when necessary.
> + */
> +static int vpfe_release(struct file *file)
> +{
> + struct vpfe_device *vpfe = video_drvdata(file);
> + int ret;
> +
> + vpfe_dbg(2, vpfe, "vpfe_release\n");
> +
> + ret = _vb2_fop_release(file, NULL);
> +
> + if (v4l2_fh_is_singular_file(file)) {
> + mutex_lock(&vpfe->lock);
> + vpfe_ccdc_close(&vpfe->ccdc, vpfe->pdev);
> + v4l2_fh_release(file);
> + mutex_unlock(&vpfe->lock);
> + }
> +
> + return ret;
> +}
> +
> +/*
> + * vpfe_open : This function is based on the v4l2_fh_open helper function.
> + * It has been augmented to handle module power management,
> + * by disabling/enabling h/w module fcntl clock when necessary.
> + */
> +
> +static int vpfe_open(struct file *file)
> +{
> + struct vpfe_device *vpfe = video_drvdata(file);
> + int ret;
> +
> + ret = v4l2_fh_open(file);
> + if (ret) {
> + vpfe_err(vpfe, "v4l2_fh_open failed\n");
> + return ret;
> + }
> +
> + if (!v4l2_fh_is_singular_file(file))
> + return 0;
> +
> + mutex_lock(&vpfe->lock);
> + if (vpfe_initialize_device(vpfe)) {
> + mutex_unlock(&vpfe->lock);
> + v4l2_fh_release(file);
> + return -ENODEV;
> + }
> + mutex_unlock(&vpfe->lock);
> +
> + return 0;
> +}
> +
> +/**
> + * vpfe_schedule_next_buffer: set next buffer address for capture
> + * @vpfe : ptr to vpfe device
> + *
> + * This function will get next buffer from the dma queue and
> + * set the buffer address in the vpfe register for capture.
> + * the buffer is marked active
> + *
> + * Assumes caller is holding vpfe->dma_queue_lock already
> + */
> +static inline void vpfe_schedule_next_buffer(struct vpfe_device *vpfe)
> +{
> + vpfe->next_frm = list_entry(vpfe->dma_queue.next,
> + struct vpfe_cap_buffer, list);
> + list_del(&vpfe->next_frm->list);
> +
> + vpfe_set_sdr_addr(&vpfe->ccdc,
> + vb2_dma_contig_plane_dma_addr(&vpfe->next_frm->vb, 0));
> +}
> +
> +static inline void vpfe_schedule_bottom_field(struct vpfe_device *vpfe)
> +{
> + unsigned long addr;
> +
> + addr = vb2_dma_contig_plane_dma_addr(&vpfe->next_frm->vb, 0) +
> + vpfe->field_off;
> +
> + vpfe_set_sdr_addr(&vpfe->ccdc, addr);
> +}
> +
> +/*
> + * vpfe_process_buffer_complete: process a completed buffer
> + * @vpfe : ptr to vpfe device
> + *
> + * This function time stamp the buffer and mark it as DONE. It also
> + * wake up any process waiting on the QUEUE and set the next buffer
> + * as current
> + */
> +static inline void vpfe_process_buffer_complete(struct vpfe_device *vpfe)
> +{
> + v4l2_get_timestamp(&vpfe->cur_frm->vb.v4l2_buf.timestamp);
> + vpfe->cur_frm->vb.v4l2_buf.field = vpfe->fmt.fmt.pix.field;
> + vpfe->cur_frm->vb.v4l2_buf.sequence = vpfe->sequence++;
> + vb2_buffer_done(&vpfe->cur_frm->vb, VB2_BUF_STATE_DONE);
> + vpfe->cur_frm = vpfe->next_frm;
> +}
> +
> +/*
> + * vpfe_isr : ISR handler for vpfe capture (VINT0)
> + * @irq: irq number
> + * @dev_id: dev_id ptr
> + *
> + * It changes status of the captured buffer, takes next buffer from the queue
> + * and sets its address in VPFE registers
> + */
> +static irqreturn_t vpfe_isr(int irq, void *dev)
> +{
> + struct vpfe_device *vpfe = (struct vpfe_device *)dev;
> + enum v4l2_field field;
> + int intr_status;
> + int fid;
> +
> + intr_status = vpfe_reg_read(&vpfe->ccdc, VPFE_IRQ_STS);
> +
> + if (intr_status & VPFE_VDINT0) {
> + field = vpfe->fmt.fmt.pix.field;
> +
> + if (field == V4L2_FIELD_NONE) {
> + /* handle progressive frame capture */
> + if (vpfe->cur_frm != vpfe->next_frm)
> + vpfe_process_buffer_complete(vpfe);
> + goto next_intr;
> + }
> +
> + /* interlaced or TB capture check which field
> + we are in hardware */
> + fid = vpfe_ccdc_getfid(&vpfe->ccdc);
> +
> + /* switch the software maintained field id */
> + vpfe->field ^= 1;
> + if (fid == vpfe->field) {
> + /* we are in-sync here,continue */
> + if (fid == 0) {
> + /*
> + * One frame is just being captured. If the
> + * next frame is available, release the
> + * current frame and move on
> + */
> + if (vpfe->cur_frm != vpfe->next_frm)
> + vpfe_process_buffer_complete(vpfe);
> + /*
> + * based on whether the two fields are stored
> + * interleave or separately in memory,
> + * reconfigure the CCDC memory address
> + */
> + if (field == V4L2_FIELD_SEQ_TB)
> + vpfe_schedule_bottom_field(vpfe);
> +
> + goto next_intr;
> + }
> + /*
> + * if one field is just being captured configure
> + * the next frame get the next frame from the empty
> + * queue if no frame is available hold on to the
> + * current buffer
> + */
> + spin_lock(&vpfe->dma_queue_lock);
> + if (!list_empty(&vpfe->dma_queue) &&
> + vpfe->cur_frm == vpfe->next_frm)
> + vpfe_schedule_next_buffer(vpfe);
> + spin_unlock(&vpfe->dma_queue_lock);
> + } else if (fid == 0) {
> + /*
> + * out of sync. Recover from any hardware out-of-sync.
> + * May loose one frame
> + */
> + vpfe->field = fid;
> + }
> + }
> +
> +next_intr:
> + if (intr_status & VPFE_VDINT1) {
> + spin_lock(&vpfe->dma_queue_lock);
> + if (vpfe->fmt.fmt.pix.field == V4L2_FIELD_NONE &&
> + !list_empty(&vpfe->dma_queue) &&
> + vpfe->cur_frm == vpfe->next_frm)
> + vpfe_schedule_next_buffer(vpfe);
> + spin_unlock(&vpfe->dma_queue_lock);
> + }
> +
> + vpfe_clear_intr(&vpfe->ccdc, intr_status);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static inline void vpfe_detach_irq(struct vpfe_device *vpfe)
> +{
> + unsigned int intr = VPFE_VDINT0;
> + enum ccdc_frmfmt frame_format;
> +
> + frame_format = vpfe_ccdc_get_frame_format(&vpfe->ccdc);
> + if (frame_format == CCDC_FRMFMT_PROGRESSIVE)
> + intr |= VPFE_VDINT1;
> +
> + vpfe_reg_write(&vpfe->ccdc, intr, VPFE_IRQ_EN_CLR);
> +}
> +
> +static inline void vpfe_attach_irq(struct vpfe_device *vpfe)
> +{
> + unsigned int intr = VPFE_VDINT0;
> + enum ccdc_frmfmt frame_format;
> +
> + frame_format = vpfe_ccdc_get_frame_format(&vpfe->ccdc);
> + if (frame_format == CCDC_FRMFMT_PROGRESSIVE)
> + intr |= VPFE_VDINT1;
> +
> + vpfe_reg_write(&vpfe->ccdc, intr, VPFE_IRQ_EN_SET);
> +}
> +
> +static int vpfe_querycap(struct file *file, void *priv,
> + struct v4l2_capability *cap)
> +{
> + struct vpfe_device *vpfe = video_drvdata(file);
> +
> + vpfe_dbg(2, vpfe, "vpfe_querycap\n");
> +
> + strlcpy(cap->driver, VPFE_MODULE_NAME, sizeof(cap->driver));
> + strlcpy(cap->card, "TI AM437x VPFE", sizeof(cap->card));
> + snprintf(cap->bus_info, sizeof(cap->bus_info),
> + "platform:%s", vpfe->v4l2_dev.name);
> + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
> + V4L2_CAP_READWRITE;
> + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
> +
> + return 0;
> +}
> +
> +/* get the format set at output pad of the adjacent subdev */
> +static int __vpfe_get_format(struct vpfe_device *vpfe,
> + struct v4l2_format *format, unsigned int *bpp)
> +{
> + struct v4l2_mbus_framefmt mbus_fmt;
> + struct vpfe_subdev_info *sdinfo;
> + struct v4l2_subdev_format fmt;
> + int ret;
> +
> + sdinfo = vpfe->current_subdev;
> + if (!sdinfo->sd)
> + return -EINVAL;
> +
> + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> + fmt.pad = 0;
> +
> + ret = v4l2_subdev_call(sdinfo->sd, pad, get_fmt, NULL, &fmt);
> + if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV)
> + return ret;
> +
> + if (!ret) {
> + v4l2_fill_pix_format(&format->fmt.pix, &fmt.format);
> + mbus_to_pix(vpfe, &fmt.format, &format->fmt.pix, bpp);
> + } else {
> + ret = v4l2_device_call_until_err(&vpfe->v4l2_dev,
> + sdinfo->grp_id,
> + video, g_mbus_fmt,
> + &mbus_fmt);
> + if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV)
> + return ret;
> + v4l2_fill_pix_format(&format->fmt.pix, &mbus_fmt);
> + mbus_to_pix(vpfe, &mbus_fmt, &format->fmt.pix, bpp);
> + }
> +
> + format->type = vpfe->fmt.type;
> +
> + vpfe_dbg(1, vpfe,
> + "%s size %dx%d (%s) bytesperline= %d, size= %d, bpp= %d\n",
> + __func__, format->fmt.pix.width, format->fmt.pix.height,
> + print_fourcc(format->fmt.pix.pixelformat),
> + format->fmt.pix.bytesperline, format->fmt.pix.sizeimage, *bpp);
> +
> + return 0;
> +}
> +
> +/* set the format at output pad of the adjacent subdev */
> +static int __vpfe_set_format(struct vpfe_device *vpfe,
> + struct v4l2_format *format, unsigned int *bpp)
> +{
> + struct vpfe_subdev_info *sdinfo;
> + struct v4l2_subdev_format fmt;
> + int ret;
> +
> + vpfe_dbg(2, vpfe, "__vpfe_set_format\n");
> +
> + sdinfo = vpfe->current_subdev;
> + if (!sdinfo->sd)
> + return -EINVAL;
> +
> + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> + fmt.pad = 0;
> +
> + ret = pix_to_mbus(vpfe, &format->fmt.pix, &fmt.format);
> + if (ret)
> + return ret;
> +
> + ret = v4l2_subdev_call(sdinfo->sd, pad, set_fmt, NULL, &fmt);
> + if (ret == -ENOIOCTLCMD)
> + return -EINVAL;
> +
> + format->type = vpfe->fmt.type;
> +
> + /* convert mbus_format to v4l2_format */
> + v4l2_fill_pix_format(&format->fmt.pix, &fmt.format);
> + mbus_to_pix(vpfe, &fmt.format, &format->fmt.pix, bpp);
> + vpfe_dbg(1, vpfe, "__vpfe_set_format size %dx%d (%s) bytesperline = %d, sizeimage = %d, bpp = %d\n",
> + format->fmt.pix.width, format->fmt.pix.height,
> + print_fourcc(format->fmt.pix.pixelformat),
> + format->fmt.pix.bytesperline, format->fmt.pix.sizeimage, *bpp);
> +
> + return 0;
> +}
> +
> +static int vpfe_g_fmt(struct file *file, void *priv,
> + struct v4l2_format *fmt)
> +{
> + struct vpfe_device *vpfe = video_drvdata(file);
> +
> + vpfe_dbg(2, vpfe, "vpfe_g_fmt\n");
> +
> + *fmt = vpfe->fmt;
> +
> + return 0;
> +}
> +
> +static int vpfe_enum_fmt(struct file *file, void *priv,
> + struct v4l2_fmtdesc *f)
> +{
> + struct vpfe_device *vpfe = video_drvdata(file);
> + struct vpfe_subdev_info *sdinfo;
> + struct vpfe_fmt *fmt = NULL;
> + unsigned int k;
> +
> + vpfe_dbg(2, vpfe, "vpfe_enum_format index:%d\n",
> + f->index);
> +
> + sdinfo = vpfe->current_subdev;
> + if (!sdinfo->sd)
> + return -EINVAL;
> +
> + if (f->index > ARRAY_SIZE(formats))
> + return -EINVAL;
> +
> + for (k = 0; k < ARRAY_SIZE(formats); k++) {
> + if (formats[k].index == f->index) {
> + fmt = &formats[k];
> + break;
> + }
> + }
> + if (!fmt)
> + return -EINVAL;
> +
> + strncpy(f->description, fmt->name, sizeof(f->description) - 1);
> + f->pixelformat = fmt->fourcc;
> + f->type = vpfe->fmt.type;
> +
> + vpfe_dbg(1, vpfe, "vpfe_enum_format: mbus index: %d code: %x pixelformat: %s [%s]\n",
> + f->index, fmt->code, print_fourcc(fmt->fourcc), fmt->name);
> +
> + return 0;
> +}
> +
> +static int vpfe_try_fmt(struct file *file, void *priv,
> + struct v4l2_format *fmt)
> +{
> + struct vpfe_device *vpfe = video_drvdata(file);
> + unsigned int bpp;
> +
> + vpfe_dbg(2, vpfe, "vpfe_try_fmt\n");
> +
> + return __vpfe_get_format(vpfe, fmt, &bpp);
> +}
> +
> +static int vpfe_s_fmt(struct file *file, void *priv,
> + struct v4l2_format *fmt)
> +{
> + struct vpfe_device *vpfe = video_drvdata(file);
> + struct v4l2_format format;
> + unsigned int bpp;
> + int ret;
> +
> + vpfe_dbg(2, vpfe, "vpfe_s_fmt\n");
> +
> + /* If streaming is started, return error */
> + if (vb2_is_busy(&vpfe->buffer_queue)) {
> + vpfe_err(vpfe, "%s device busy\n", __func__);
> + return -EBUSY;
> + }
> +
> + ret = vpfe_try_fmt(file, priv, fmt);
> + if (ret)
> + return ret;
> +
> +
> + if (!cmp_v4l2_format(fmt, &format)) {
> + /* Sensor format is different from the requested format
> + * so we need to change it
> + */
> + ret = __vpfe_set_format(vpfe, fmt, &bpp);
> + if (ret)
> + return ret;
> + } else /* Just make sure all of the fields are consistent */
> + *fmt = format;
> +
> + /* First detach any IRQ if currently attached */
> + vpfe_detach_irq(vpfe);
> + vpfe->fmt = *fmt;
> + vpfe->bpp = bpp;
> +
> + /* Update the crop window based on found values */
> + vpfe->crop.width = fmt->fmt.pix.width;
> + vpfe->crop.height = fmt->fmt.pix.height;
> +
> + /* set image capture parameters in the ccdc */
> + return vpfe_config_ccdc_image_format(vpfe);
> +}
> +
> +static int vpfe_enum_size(struct file *file, void *priv,
> + struct v4l2_frmsizeenum *fsize)
> +{
> + struct vpfe_device *vpfe = video_drvdata(file);
> + struct v4l2_subdev_frame_size_enum fse;
> + struct vpfe_subdev_info *sdinfo;
> + struct v4l2_mbus_framefmt mbus;
> + struct v4l2_pix_format pix;
Zero this pix struct so that there are no random values.
Same for fse and mbus.
> + struct vpfe_fmt *fmt;
> + int ret;
> +
> + vpfe_dbg(2, vpfe, "vpfe_enum_size\n");
> +
> + /* check for valid format */
> + fmt = find_format_by_pix(fsize->pixel_format);
> + if (!fmt) {
> + vpfe_dbg(3, vpfe, "Invalid pixel code: %x, default used instead\n",
> + fsize->pixel_format);
> + return -EINVAL;
> + }
> +
> + memset(fsize->reserved, 0x00, sizeof(fsize->reserved));
> +
> + sdinfo = vpfe->current_subdev;
> + if (!sdinfo->sd)
> + return -EINVAL;
> +
> + /* Construct pix from parameter and use default for the rest */
> + pix.pixelformat = fsize->pixel_format;
> + pix.width = 640;
> + pix.height = 480;
> + pix.colorspace = V4L2_COLORSPACE_SRGB;
> + pix.field = V4L2_FIELD_NONE;
> + ret = pix_to_mbus(vpfe, &pix, &mbus);
> + if (ret)
> + return ret;
> +
> + fse.index = fsize->index;
> + fse.pad = 0;
> + fse.code = mbus.code;
> + ret = v4l2_subdev_call(sdinfo->sd, pad, enum_frame_size, NULL, &fse);
> + if (ret)
> + return -EINVAL;
> +
> + vpfe_dbg(1, vpfe, "vpfe_enum_size: index: %d code: %x W:[%d,%d] H:[%d,%d]\n",
> + fse.index, fse.code, fse.min_width, fse.max_width,
> + fse.min_height, fse.max_height);
> +
> + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
> + fsize->discrete.width = fse.max_width;
> + fsize->discrete.height = fse.max_height;
> +
> + vpfe_dbg(1, vpfe, "vpfe_enum_size: index: %d pixformat: %s size: %dx%d\n",
> + fsize->index, print_fourcc(fsize->pixel_format),
> + fsize->discrete.width, fsize->discrete.height);
> +
> + return 0;
> +}
> +
> +/*
> + * vpfe_get_subdev_input_index - Get subdev index and subdev input index for a
> + * given app input index
> + */
> +static int
> +vpfe_get_subdev_input_index(struct vpfe_device *vpfe,
> + int *subdev_index,
> + int *subdev_input_index,
> + int app_input_index)
> +{
> + struct vpfe_config *cfg = vpfe->cfg;
> + struct vpfe_subdev_info *sdinfo;
> + int i, j = 0;
> +
> + for (i = 0; i < ARRAY_SIZE(vpfe->cfg->asd); i++) {
> + sdinfo = &cfg->sub_devs[i];
> + if (app_input_index < (j + 1)) {
> + *subdev_index = i;
> + *subdev_input_index = app_input_index - j;
> + return 0;
> + }
> + j++;
> + }
> + return -EINVAL;
> +}
> +
> +/*
> + * vpfe_get_app_input - Get app input index for a given subdev input index
> + * driver stores the input index of the current sub device and translate it
> + * when application request the current input
> + */
> +static int vpfe_get_app_input_index(struct vpfe_device *vpfe,
> + int *app_input_index)
> +{
> + struct vpfe_config *cfg = vpfe->cfg;
> + struct vpfe_subdev_info *sdinfo;
> + int i, j = 0;
> +
> + for (i = 0; i < ARRAY_SIZE(vpfe->cfg->asd); i++) {
> + sdinfo = &cfg->sub_devs[i];
> + if (!strcmp(sdinfo->name, vpfe->current_subdev->name)) {
> + if (vpfe->current_input >= 1)
> + return -1;
> + *app_input_index = j + vpfe->current_input;
> + return 0;
> + }
> + j++;
> + }
> + return -EINVAL;
> +}
> +
> +static int vpfe_enum_input(struct file *file, void *priv,
> + struct v4l2_input *inp)
> +{
> + struct vpfe_device *vpfe = video_drvdata(file);
> + struct vpfe_subdev_info *sdinfo;
> + int subdev, index;
> +
> + vpfe_dbg(2, vpfe, "vpfe_enum_input\n");
> +
> + if (vpfe_get_subdev_input_index(vpfe,
> + &subdev,
> + &index,
> + inp->index) < 0) {
> + vpfe_dbg(1, vpfe,
> + "input information not found for the subdev\n");
> + return -EINVAL;
> + }
> + sdinfo = &vpfe->cfg->sub_devs[subdev];
> + *inp = sdinfo->inputs[index];
> +
> + return 0;
> +}
> +
> +static int vpfe_g_input(struct file *file, void *priv, unsigned int *index)
> +{
> + struct vpfe_device *vpfe = video_drvdata(file);
> +
> + vpfe_dbg(2, vpfe, "vpfe_g_input\n");
> +
> + return vpfe_get_app_input_index(vpfe, index);
> +}
> +
> +/* Assumes caller is holding vpfe_dev->lock */
> +static int vpfe_set_input(struct vpfe_device *vpfe, unsigned int index)
> +{
> + int subdev_index = 0, inp_index = 0;
> + struct vpfe_subdev_info *sdinfo;
> + struct vpfe_route *route;
> + u32 input, output;
> + int ret;
> +
> + vpfe_dbg(2, vpfe, "vpfe_set_input: index: %d\n", index);
> +
> + /* If streaming is started, return error */
> + if (vb2_is_busy(&vpfe->buffer_queue)) {
> + vpfe_err(vpfe, "%s device busy\n", __func__);
> + return -EBUSY;
> + }
> + ret = vpfe_get_subdev_input_index(vpfe,
> + &subdev_index,
> + &inp_index,
> + index);
> + if (ret < 0) {
> + vpfe_err(vpfe, "invalid input index: %d\n", index);
> + goto get_out;
> + }
> +
> + sdinfo = &vpfe->cfg->sub_devs[subdev_index];
> + sdinfo->sd = vpfe->sd[subdev_index];
> + route = &sdinfo->routes[inp_index];
> + if (route && sdinfo->can_route) {
> + input = route->input;
> + output = route->output;
> + if (sdinfo->sd) {
> + ret = v4l2_subdev_call(sdinfo->sd, video,
> + s_routing, input, output, 0);
> + if (ret) {
> + vpfe_err(vpfe, "s_routing failed\n");
> + ret = -EINVAL;
> + goto get_out;
> + }
> + }
> +
> + }
> +
> + vpfe->current_subdev = sdinfo;
> + if (sdinfo->sd)
> + vpfe->v4l2_dev.ctrl_handler = sdinfo->sd->ctrl_handler;
> + vpfe->current_input = index;
> + vpfe->std_index = 0;
> +
> + /* set the bus/interface parameter for the sub device in ccdc */
> + ret = vpfe_ccdc_set_hw_if_params(&vpfe->ccdc, &sdinfo->vpfe_param);
> + if (ret)
> + return ret;
> +
> + /* set the default image parameters in the device */
> + return vpfe_config_image_format(vpfe,
> + vpfe_standards[vpfe->std_index].std_id);
> +
> +get_out:
> + return ret;
> +}
> +
> +static int vpfe_s_input(struct file *file, void *priv, unsigned int index)
> +{
> + struct vpfe_device *vpfe = video_drvdata(file);
> +
> + vpfe_dbg(2, vpfe,
> + "vpfe_s_input: index: %d\n", index);
> +
> + return vpfe_set_input(vpfe, index);
> +}
> +
> +static int vpfe_querystd(struct file *file, void *priv, v4l2_std_id *std_id)
> +{
> + struct vpfe_device *vpfe = video_drvdata(file);
> + struct vpfe_subdev_info *sdinfo;
> +
> + vpfe_dbg(2, vpfe, "vpfe_querystd\n");
> +
> + sdinfo = vpfe->current_subdev;
> + if (!(sdinfo->inputs[0].capabilities & V4L2_IN_CAP_STD))
> + return -ENODATA;
> +
> + /* Call querystd function of decoder device */
> + return v4l2_device_call_until_err(&vpfe->v4l2_dev, sdinfo->grp_id,
> + video, querystd, std_id);
> +}
> +
> +static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id std_id)
> +{
> + struct vpfe_device *vpfe = video_drvdata(file);
> + struct vpfe_subdev_info *sdinfo;
> + int ret;
> +
> + vpfe_dbg(2, vpfe, "vpfe_s_std\n");
> +
> + sdinfo = vpfe->current_subdev;
> + if (!(sdinfo->inputs[0].capabilities & V4L2_IN_CAP_STD))
> + return -ENODATA;
> +
> + /* If streaming is started, return error */
> + if (vb2_is_busy(&vpfe->buffer_queue)) {
> + vpfe_err(vpfe, "%s device busy\n", __func__);
> + ret = -EBUSY;
> + return ret;
> + }
> +
> + ret = v4l2_device_call_until_err(&vpfe->v4l2_dev, sdinfo->grp_id,
> + video, s_std, std_id);
> + if (ret < 0) {
> + vpfe_err(vpfe, "Failed to set standard\n");
> + return ret;
> + }
> + ret = vpfe_config_image_format(vpfe, std_id);
> +
> + return ret;
> +}
> +
> +static int vpfe_g_std(struct file *file, void *priv, v4l2_std_id *std_id)
> +{
> + struct vpfe_device *vpfe = video_drvdata(file);
> + struct vpfe_subdev_info *sdinfo;
> +
> + vpfe_dbg(2, vpfe, "vpfe_g_std\n");
> +
> + sdinfo = vpfe->current_subdev;
> + if (sdinfo->inputs[0].capabilities != V4L2_IN_CAP_STD)
> + return -ENODATA;
> +
> + *std_id = vpfe_standards[vpfe->std_index].std_id;
> +
> + return 0;
> +}
> +
> +/*
> + * vpfe_calculate_offsets : This function calculates buffers offset
> + * for top and bottom field
> + */
> +static void vpfe_calculate_offsets(struct vpfe_device *vpfe)
> +{
> + struct v4l2_rect image_win;
> +
> + vpfe_dbg(2, vpfe, "vpfe_calculate_offsets\n");
> +
> + vpfe_ccdc_get_image_window(&vpfe->ccdc, &image_win);
> + vpfe->field_off = image_win.height * image_win.width;
> +}
> +
> +/*
> + * vpfe_queue_setup - Callback function for buffer setup.
> + * @vq: vb2_queue ptr
> + * @fmt: v4l2 format
> + * @nbuffers: ptr to number of buffers requested by application
> + * @nplanes:: contains number of distinct video planes needed to hold a frame
> + * @sizes[]: contains the size (in bytes) of each plane.
> + * @alloc_ctxs: ptr to allocation context
> + *
> + * This callback function is called when reqbuf() is called to adjust
> + * the buffer count and buffer size
> + */
> +static int vpfe_queue_setup(struct vb2_queue *vq,
> + const struct v4l2_format *fmt,
> + unsigned int *nbuffers, unsigned int *nplanes,
> + unsigned int sizes[], void *alloc_ctxs[])
> +{
> + struct vpfe_device *vpfe = vb2_get_drv_priv(vq);
> +
> + if (fmt && fmt->fmt.pix.sizeimage < vpfe->fmt.fmt.pix.sizeimage)
> + return -EINVAL;
> +
> + if (vq->num_buffers + *nbuffers < 3)
> + *nbuffers = 3 - vq->num_buffers;
> +
> + *nplanes = 1;
> + sizes[0] = fmt ? fmt->fmt.pix.sizeimage : vpfe->fmt.fmt.pix.sizeimage;
> + alloc_ctxs[0] = vpfe->alloc_ctx;
> +
> + vpfe_dbg(1, vpfe,
> + "nbuffers=%d, size=%u\n", *nbuffers, sizes[0]);
> +
> + /* Calculate field offset */
> + vpfe_calculate_offsets(vpfe);
> +
> + return 0;
> +}
> +
> +/*
> + * vpfe_buffer_prepare : callback function for buffer prepare
> + * @vb: ptr to vb2_buffer
> + *
> + * This is the callback function for buffer prepare when vb2_qbuf()
> + * function is called. The buffer is prepared and user space virtual address
> + * or user address is converted into physical address
> + */
> +static int vpfe_buffer_prepare(struct vb2_buffer *vb)
> +{
> + struct vpfe_device *vpfe = vb2_get_drv_priv(vb->vb2_queue);
> +
> + vb2_set_plane_payload(vb, 0, vpfe->fmt.fmt.pix.sizeimage);
> +
> + if (vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0))
> + return -EINVAL;
> +
> + vb->v4l2_buf.field = vpfe->fmt.fmt.pix.field;
> +
> + return 0;
> +}
> +
> +/*
> + * vpfe_buffer_queue : Callback function to add buffer to DMA queue
> + * @vb: ptr to vb2_buffer
> + */
> +static void vpfe_buffer_queue(struct vb2_buffer *vb)
> +{
> + struct vpfe_device *vpfe = vb2_get_drv_priv(vb->vb2_queue);
> + struct vpfe_cap_buffer *buf = to_vpfe_buffer(vb);
> + unsigned long flags = 0;
> +
> + /* add the buffer to the DMA queue */
> + spin_lock_irqsave(&vpfe->dma_queue_lock, flags);
> + list_add_tail(&buf->list, &vpfe->dma_queue);
> + spin_unlock_irqrestore(&vpfe->dma_queue_lock, flags);
> +}
> +
> +/*
> + * vpfe_start_streaming : Starts the DMA engine for streaming
> + * @vb: ptr to vb2_buffer
> + * @count: number of buffers
> + */
> +static int vpfe_start_streaming(struct vb2_queue *vq, unsigned int count)
> +{
> + struct vpfe_device *vpfe = vb2_get_drv_priv(vq);
> + struct vpfe_cap_buffer *buf, *tmp;
> + struct vpfe_subdev_info *sdinfo;
> + unsigned long flags;
> + unsigned long addr;
> + int ret;
> +
> + spin_lock_irqsave(&vpfe->dma_queue_lock, flags);
> +
> + vpfe->field = 0;
> + vpfe->sequence = 0;
> +
> + sdinfo = vpfe->current_subdev;
> +
> + vpfe_attach_irq(vpfe);
> +
> + if (vpfe->ccdc.ccdc_cfg.if_type == VPFE_RAW_BAYER)
> + vpfe_ccdc_config_raw(&vpfe->ccdc);
> + else
> + vpfe_ccdc_config_ycbcr(&vpfe->ccdc);
> +
> + /* Get the next frame from the buffer queue */
> + vpfe->next_frm = list_entry(vpfe->dma_queue.next,
> + struct vpfe_cap_buffer, list);
> + vpfe->cur_frm = vpfe->next_frm;
> + /* Remove buffer from the buffer queue */
> + list_del(&vpfe->cur_frm->list);
> + spin_unlock_irqrestore(&vpfe->dma_queue_lock, flags);
> +
> + addr = vb2_dma_contig_plane_dma_addr(&vpfe->cur_frm->vb, 0);
> +
> + vpfe_set_sdr_addr(&vpfe->ccdc, (unsigned long)(addr));
> +
> + vpfe_pcr_enable(&vpfe->ccdc, 1);
> +
> + ret = v4l2_subdev_call(sdinfo->sd, video, s_stream, 1);
> + if (ret < 0) {
> + vpfe_err(vpfe, "Error in attaching interrupt handle\n");
> + goto err;
> + }
> +
> + return 0;
> +
> +err:
> + list_for_each_entry_safe(buf, tmp, &vpfe->dma_queue, list) {
> + list_del(&buf->list);
> + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED);
> + }
> + spin_unlock_irqrestore(&vpfe->dma_queue_lock, flags);
> +
> + return ret;
> +}
> +
> +/*
> + * vpfe_stop_streaming : Stop the DMA engine
> + * @vq: ptr to vb2_queue
> + *
> + * This callback stops the DMA engine and any remaining buffers
> + * in the DMA queue are released.
> + */
> +static void vpfe_stop_streaming(struct vb2_queue *vq)
> +{
> + struct vpfe_device *vpfe = vb2_get_drv_priv(vq);
> + struct vpfe_subdev_info *sdinfo;
> + unsigned long flags;
> + int ret;
> +
> + vpfe_pcr_enable(&vpfe->ccdc, 0);
> +
> + vpfe_detach_irq(vpfe);
> +
> + sdinfo = vpfe->current_subdev;
> + ret = v4l2_subdev_call(sdinfo->sd, video, s_stream, 0);
> + if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV)
> + vpfe_dbg(1, vpfe, "stream off failed in subdev\n");
> +
> + /* release all active buffers */
> + spin_lock_irqsave(&vpfe->dma_queue_lock, flags);
> + if (vpfe->cur_frm == vpfe->next_frm) {
> + vb2_buffer_done(&vpfe->cur_frm->vb, VB2_BUF_STATE_ERROR);
> + } else {
> + if (vpfe->cur_frm != NULL)
> + vb2_buffer_done(&vpfe->cur_frm->vb,
> + VB2_BUF_STATE_ERROR);
> + if (vpfe->next_frm != NULL)
> + vb2_buffer_done(&vpfe->next_frm->vb,
> + VB2_BUF_STATE_ERROR);
> + }
> +
> + while (!list_empty(&vpfe->dma_queue)) {
> + vpfe->next_frm = list_entry(vpfe->dma_queue.next,
> + struct vpfe_cap_buffer, list);
> + list_del(&vpfe->next_frm->list);
> + vb2_buffer_done(&vpfe->next_frm->vb, VB2_BUF_STATE_ERROR);
> + }
> + spin_unlock_irqrestore(&vpfe->dma_queue_lock, flags);
> +}
> +
> +static int vpfe_cropcap(struct file *file, void *priv,
> + struct v4l2_cropcap *crop)
> +{
> + struct vpfe_device *vpfe = video_drvdata(file);
> +
> + vpfe_dbg(2, vpfe, "vpfe_cropcap\n");
> +
> + if (vpfe->std_index >= ARRAY_SIZE(vpfe_standards))
> + return -EINVAL;
> +
> + memset(crop, 0, sizeof(struct v4l2_cropcap));
> +
> + crop->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> + crop->defrect.width = vpfe_standards[vpfe->std_index].width;
> + crop->bounds.width = crop->defrect.width;
> + crop->defrect.height = vpfe_standards[vpfe->std_index].height;
> + crop->bounds.height = crop->defrect.height;
> + crop->pixelaspect = vpfe_standards[vpfe->std_index].pixelaspect;
> +
> + return 0;
> +}
> +
> +static int
> +vpfe_g_selection(struct file *file, void *fh, struct v4l2_selection *s)
> +{
> + struct vpfe_device *vpfe = video_drvdata(file);
> +
> + switch (s->target) {
> + case V4L2_SEL_TGT_COMPOSE_DEFAULT:
> + case V4L2_SEL_TGT_COMPOSE_BOUNDS:
> + case V4L2_SEL_TGT_CROP_BOUNDS:
> + case V4L2_SEL_TGT_CROP_DEFAULT:
> + s->r.left = s->r.top = 0;
> + s->r.width = vpfe->crop.width;
> + s->r.height = vpfe->crop.height;
> + break;
> +
> + case V4L2_SEL_TGT_COMPOSE:
Why is COMPOSE supported when AFAICT only crop is implemented?
> + case V4L2_SEL_TGT_CROP:
> + s->r = vpfe->crop;
> + break;
> +
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b)
> +{
> + if (a->left < b->left || a->top < b->top)
> + return 0;
> +
> + if (a->left + a->width > b->left + b->width)
> + return 0;
> +
> + if (a->top + a->height > b->top + b->height)
> + return 0;
> +
> + return 1;
> +}
> +
> +static int
> +vpfe_s_selection(struct file *file, void *fh, struct v4l2_selection *s)
> +{
> + struct vpfe_device *vpfe = video_drvdata(file);
> + struct v4l2_rect cr = vpfe->crop;
> + struct v4l2_rect r = s->r;
> +
> + /* If streaming is started, return error */
> + if (vb2_is_busy(&vpfe->buffer_queue)) {
> + vpfe_err(vpfe, "%s device busy\n", __func__);
> + return -EBUSY;
> + }
> +
> + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
> + (s->target != V4L2_SEL_TGT_COMPOSE &&
Same question.
> + s->target != V4L2_SEL_TGT_CROP))
> + return -EINVAL;
> +
> + v4l_bound_align_image(&r.width, 0, cr.width, 0,
> + &r.height, 0, cr.height, 0, 0);
> +
> + r.left = clamp_t(unsigned int, r.left, 0, cr.width - r.width);
> + r.top = clamp_t(unsigned int, r.top, 0, cr.height - r.height);
> +
> + if (s->flags & V4L2_SEL_FLAG_LE && !enclosed_rectangle(&r, &s->r))
> + return -ERANGE;
> +
> + if (s->flags & V4L2_SEL_FLAG_GE && !enclosed_rectangle(&s->r, &r))
> + return -ERANGE;
> +
> + s->r = vpfe->crop = r;
> +
> + vpfe_ccdc_set_image_window(&vpfe->ccdc, &r, vpfe->bpp);
> + vpfe->fmt.fmt.pix.width = r.width;
> + vpfe->fmt.fmt.pix.height = r.height;
> + vpfe->fmt.fmt.pix.bytesperline = vpfe_ccdc_get_line_length(&vpfe->ccdc);
> + vpfe->fmt.fmt.pix.sizeimage = vpfe->fmt.fmt.pix.bytesperline *
> + vpfe->fmt.fmt.pix.height;
> +
> + vpfe_dbg(1, vpfe, "cropped (%d,%d)/%dx%d of %dx%d\n",
> + r.left, r.top, r.width, r.height, cr.width, cr.height);
> +
> + return 0;
> +}
> +
> +static long vpfe_ioctl_default(struct file *file, void *priv,
> + bool valid_prio, unsigned int cmd, void *param)
> +{
> + struct vpfe_device *vpfe = video_drvdata(file);
> + int ret;
> +
> + vpfe_dbg(2, vpfe, "vpfe_ioctl_default\n");
> +
> + if (!valid_prio) {
> + vpfe_err(vpfe, "%s device busy\n", __func__);
> + return -EBUSY;
> + }
> +
> + /* If streaming is started, return error */
> + if (vb2_is_busy(&vpfe->buffer_queue)) {
> + vpfe_err(vpfe, "%s device busy\n", __func__);
> + return -EBUSY;
> + }
> +
> + switch (cmd) {
> + case VIDIOC_AM437X_CCDC_CFG:
> + ret = vpfe_ccdc_set_params(&vpfe->ccdc, param);
> + if (ret) {
> + vpfe_dbg(2, vpfe,
> + "Error setting parameters in CCDC\n");
> + return ret;
> + }
> + ret = vpfe_get_ccdc_image_format(vpfe,
> + &vpfe->fmt);
> + if (ret < 0) {
> + vpfe_dbg(2, vpfe,
> + "Invalid image format at CCDC\n");
> + return ret;
> + }
> + break;
> +
> + default:
> + ret = -ENOTTY;
> + break;
> + }
> +
> + return ret;
> +}
> +
> +static const struct vb2_ops vpfe_video_qops = {
> + .wait_prepare = vb2_ops_wait_prepare,
> + .wait_finish = vb2_ops_wait_finish,
> + .queue_setup = vpfe_queue_setup,
> + .buf_prepare = vpfe_buffer_prepare,
> + .buf_queue = vpfe_buffer_queue,
> + .start_streaming = vpfe_start_streaming,
> + .stop_streaming = vpfe_stop_streaming,
> +};
> +
> +/* vpfe capture driver file operations */
> +static const struct v4l2_file_operations vpfe_fops = {
> + .owner = THIS_MODULE,
> + .open = vpfe_open,
> + .release = vpfe_release,
> + .read = vb2_fop_read,
> + .poll = vb2_fop_poll,
> + .unlocked_ioctl = video_ioctl2,
> + .mmap = vb2_fop_mmap,
> +
> +};
> +
> +/* vpfe capture ioctl operations */
> +static const struct v4l2_ioctl_ops vpfe_ioctl_ops = {
> + .vidioc_querycap = vpfe_querycap,
> + .vidioc_enum_fmt_vid_cap = vpfe_enum_fmt,
> + .vidioc_g_fmt_vid_cap = vpfe_g_fmt,
> + .vidioc_s_fmt_vid_cap = vpfe_s_fmt,
> + .vidioc_try_fmt_vid_cap = vpfe_try_fmt,
> +
> + .vidioc_enum_framesizes = vpfe_enum_size,
> +
> + .vidioc_enum_input = vpfe_enum_input,
> + .vidioc_g_input = vpfe_g_input,
> + .vidioc_s_input = vpfe_s_input,
> +
> + .vidioc_querystd = vpfe_querystd,
> + .vidioc_s_std = vpfe_s_std,
> + .vidioc_g_std = vpfe_g_std,
> +
> + .vidioc_reqbufs = vb2_ioctl_reqbufs,
> + .vidioc_create_bufs = vb2_ioctl_create_bufs,
> + .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
> + .vidioc_querybuf = vb2_ioctl_querybuf,
> + .vidioc_qbuf = vb2_ioctl_qbuf,
> + .vidioc_dqbuf = vb2_ioctl_dqbuf,
> + .vidioc_expbuf = vb2_ioctl_expbuf,
> + .vidioc_streamon = vb2_ioctl_streamon,
> + .vidioc_streamoff = vb2_ioctl_streamoff,
> +
> + .vidioc_log_status = v4l2_ctrl_log_status,
> + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
> + .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
> +
> + .vidioc_cropcap = vpfe_cropcap,
> + .vidioc_g_selection = vpfe_g_selection,
> + .vidioc_s_selection = vpfe_s_selection,
> +
> + .vidioc_default = vpfe_ioctl_default,
> +};
> +
> +static int
> +vpfe_async_bound(struct v4l2_async_notifier *notifier,
> + struct v4l2_subdev *subdev,
> + struct v4l2_async_subdev *asd)
> +{
> + struct vpfe_device *vpfe = container_of(notifier->v4l2_dev,
> + struct vpfe_device, v4l2_dev);
> + struct v4l2_subdev_mbus_code_enum mbus_code;
> + struct vpfe_subdev_info *sdinfo;
> + bool found = false;
> + int i, j;
> +
> + vpfe_dbg(1, vpfe, "vpfe_async_bound\n");
> +
> + for (i = 0; i < ARRAY_SIZE(vpfe->cfg->asd); i++) {
> + sdinfo = &vpfe->cfg->sub_devs[i];
> +
> + if (!strcmp(sdinfo->name, subdev->name)) {
> + vpfe->sd[i] = subdev;
> + vpfe_info(vpfe,
> + "v4l2 sub device %s registered\n",
> + subdev->name);
> + vpfe->sd[i]->grp_id =
> + sdinfo->grp_id;
> + /* update tvnorms from the sub devices */
> + for (j = 0; j < 1; j++)
> + vpfe->video_dev->tvnorms |=
> + sdinfo->inputs[j].std;
> +
> + found = true;
> + break;
> + }
> + }
> +
> + if (!found) {
> + vpfe_info(vpfe, "sub device (%s) not matched\n", subdev->name);
> + return -EINVAL;
> + }
> +
> + for (j = 0; ; ++j) {
> + int ret;
> +
> + memset(&mbus_code, 0, sizeof(mbus_code));
> + mbus_code.index = j;
> + ret = v4l2_subdev_call(subdev, pad, enum_mbus_code,
> + NULL, &mbus_code);
> + if (ret)
> + break;
> +
> + find_format_by_code(mbus_code.code, true);
> + }
> +
> + return 0;
> +}
> +
> +static int vpfe_probe_complete(struct vpfe_device *vpfe)
> +{
> + struct video_device *vdev;
> + struct vb2_queue *q;
> + int err;
> +
> + spin_lock_init(&vpfe->dma_queue_lock);
> + mutex_init(&vpfe->lock);
> +
> + vpfe->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +
> + /* set first sub device as current one */
> + vpfe->current_subdev = &vpfe->cfg->sub_devs[0];
> + vpfe->v4l2_dev.ctrl_handler = vpfe->sd[0]->ctrl_handler;
> +
> + err = vpfe_set_input(vpfe, 0);
> + if (err)
> + goto probe_out;
> +
> + /* Initialize videobuf2 queue as per the buffer type */
> + vpfe->alloc_ctx = vb2_dma_contig_init_ctx(vpfe->pdev);
> + if (IS_ERR(vpfe->alloc_ctx)) {
> + vpfe_err(vpfe, "Failed to get the context\n");
> + err = PTR_ERR(vpfe->alloc_ctx);
> + goto probe_out;
> + }
> +
> + q = &vpfe->buffer_queue;
> + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ;
> + q->drv_priv = vpfe;
> + q->ops = &vpfe_video_qops;
> + q->mem_ops = &vb2_dma_contig_memops;
> + q->buf_struct_size = sizeof(struct vpfe_cap_buffer);
> + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
> + q->lock = &vpfe->lock;
> + q->min_buffers_needed = 1;
> +
> + err = vb2_queue_init(q);
> + if (err) {
> + vpfe_err(vpfe, "vb2_queue_init() failed\n");
> + vb2_dma_contig_cleanup_ctx(vpfe->alloc_ctx);
> + goto probe_out;
> + }
> +
> + INIT_LIST_HEAD(&vpfe->dma_queue);
> +
> + vdev = vpfe->video_dev;
> + strlcpy(vdev->name, VPFE_MODULE_NAME, sizeof(vdev->name));
> + vdev->release = video_device_release;
> + vdev->fops = &vpfe_fops;
> + vdev->ioctl_ops = &vpfe_ioctl_ops;
> + vdev->v4l2_dev = &vpfe->v4l2_dev;
> + vdev->vfl_dir = VFL_DIR_RX;
> + vdev->queue = q;
> + vdev->lock = &vpfe->lock;
> + video_set_drvdata(vdev, vpfe);
> + err = video_register_device(vpfe->video_dev, VFL_TYPE_GRABBER, -1);
> + if (err) {
> + vpfe_err(vpfe,
> + "Unable to register video device.\n");
> + goto probe_out;
> + }
> +
> + return 0;
> +
> +probe_out:
> + v4l2_device_unregister(&vpfe->v4l2_dev);
> + return err;
> +}
> +
> +static int vpfe_async_complete(struct v4l2_async_notifier *notifier)
> +{
> + struct vpfe_device *vpfe = container_of(notifier->v4l2_dev,
> + struct vpfe_device, v4l2_dev);
> +
> + return vpfe_probe_complete(vpfe);
> +}
> +
> +static struct vpfe_config *
> +vpfe_get_pdata(struct platform_device *pdev)
> +{
> + struct device_node *endpoint = NULL, *rem = NULL;
> + struct v4l2_of_endpoint bus_cfg;
> + struct vpfe_subdev_info *sdinfo;
> + struct vpfe_config *pdata;
> + unsigned int flags;
> + unsigned int i;
> + int err;
> +
> + dev_dbg(&pdev->dev, "vpfe_get_pdata\n");
> +
> + if (!IS_ENABLED(CONFIG_OF) || !pdev->dev.of_node)
> + return pdev->dev.platform_data;
> +
> + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
> + if (!pdata)
> + return NULL;
> +
> + for (i = 0; ; i++) {
> + endpoint = of_graph_get_next_endpoint(pdev->dev.of_node,
> + endpoint);
> + if (!endpoint)
> + break;
> +
> + sdinfo = &pdata->sub_devs[i];
> + sdinfo->grp_id = 0;
> +
> + /* we only support camera */
> + sdinfo->inputs[0].index = i;
> + strcpy(sdinfo->inputs[0].name, "Camera");
> + sdinfo->inputs[0].type = V4L2_INPUT_TYPE_CAMERA;
> + sdinfo->inputs[0].std = V4L2_STD_ALL;
> + sdinfo->inputs[0].capabilities = V4L2_IN_CAP_STD;
> +
> + sdinfo->can_route = 0;
> + sdinfo->routes = NULL;
> +
> + of_property_read_u32(endpoint, "ti,am437x-vpfe-interface",
> + &sdinfo->vpfe_param.if_type);
> + if (sdinfo->vpfe_param.if_type < 0 ||
> + sdinfo->vpfe_param.if_type > 4) {
> + sdinfo->vpfe_param.if_type = VPFE_RAW_BAYER;
> + }
> +
> + err = v4l2_of_parse_endpoint(endpoint, &bus_cfg);
> + if (err) {
> + dev_err(&pdev->dev, "Could not parse the endpoint\n");
> + goto done;
> + }
> +
> + sdinfo->vpfe_param.bus_width = bus_cfg.bus.parallel.bus_width;
> +
> + if (sdinfo->vpfe_param.bus_width < 8 ||
> + sdinfo->vpfe_param.bus_width > 16) {
> + dev_err(&pdev->dev, "Invalid bus width.\n");
> + goto done;
> + }
> +
> + flags = bus_cfg.bus.parallel.flags;
> +
> + if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
> + sdinfo->vpfe_param.hdpol = 1;
> +
> + if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
> + sdinfo->vpfe_param.vdpol = 1;
> +
> + rem = of_graph_get_remote_port_parent(endpoint);
> + if (!rem) {
> + dev_err(&pdev->dev, "Remote device at %s not found\n",
> + endpoint->full_name);
> + goto done;
> + }
> +
> + strncpy(sdinfo->name, rem->name, sizeof(sdinfo->name));
> +
> + pdata->asd[i] = devm_kzalloc(&pdev->dev,
> + sizeof(struct v4l2_async_subdev),
> + GFP_KERNEL);
> + pdata->asd[i]->match_type = V4L2_ASYNC_MATCH_OF;
> + pdata->asd[i]->match.of.node = rem;
> + of_node_put(endpoint);
> + of_node_put(rem);
> + }
> +
> + of_node_put(endpoint);
> + return pdata;
> +
> +done:
> + of_node_put(endpoint);
> + of_node_put(rem);
> + return NULL;
> +}
> +
> +/*
> + * vpfe_probe : This function creates device entries by register
> + * itself to the V4L2 driver and initializes fields of each
> + * device objects
> + */
> +static int vpfe_probe(struct platform_device *pdev)
> +{
> + struct vpfe_config *vpfe_cfg = vpfe_get_pdata(pdev);
> + struct vpfe_device *vpfe;
> + struct vpfe_ccdc *ccdc;
> + struct resource *res;
> + int ret;
> +
> + if (!vpfe_cfg) {
> + dev_err(&pdev->dev, "No platform data\n");
> + return -EINVAL;
> + }
> +
> + vpfe = devm_kzalloc(&pdev->dev, sizeof(*vpfe), GFP_KERNEL);
> + if (!vpfe)
> + return -ENOMEM;
> +
> + vpfe->pdev = &pdev->dev;
> + vpfe->cfg = vpfe_cfg;
> + ccdc = &vpfe->ccdc;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + ccdc->ccdc_cfg.base_addr = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(ccdc->ccdc_cfg.base_addr))
> + return PTR_ERR(ccdc->ccdc_cfg.base_addr);
> +
> + vpfe->irq = platform_get_irq(pdev, 0);
> + if (vpfe->irq <= 0) {
> + dev_err(&pdev->dev, "No IRQ resource\n");
> + return -ENODEV;
> + }
> +
> + ret = devm_request_irq(vpfe->pdev, vpfe->irq, vpfe_isr, 0,
> + "vpfe_capture0", vpfe);
> + if (ret) {
> + dev_err(&pdev->dev, "Unable to request interrupt\n");
> + return -EINVAL;
> + }
> +
> + vpfe->video_dev = video_device_alloc();
> + if (!vpfe->video_dev) {
> + dev_err(&pdev->dev, "Unable to alloc video device\n");
> + return -ENOMEM;
> + }
> +
> + ret = v4l2_device_register(&pdev->dev, &vpfe->v4l2_dev);
> + if (ret) {
> + vpfe_err(vpfe,
> + "Unable to register v4l2 device.\n");
> + goto probe_out_video_release;
> + }
> +
> + /* set the driver data in platform device */
> + platform_set_drvdata(pdev, vpfe);
> + /* Enabling module functional clock */
> + pm_runtime_enable(&pdev->dev);
> +
> + /* for now just enable it here instead of waiting for the open */
> + pm_runtime_get_sync(&pdev->dev);
> +
> + vpfe_ccdc_config_defaults(ccdc);
> +
> + pm_runtime_put_sync(&pdev->dev);
> +
> + vpfe->sd = devm_kzalloc(&pdev->dev, sizeof(struct v4l2_subdev *) *
> + ARRAY_SIZE(vpfe->cfg->asd), GFP_KERNEL);
> + if (!vpfe->sd) {
> + ret = -ENOMEM;
> + goto probe_out_v4l2_unregister;
> + }
> +
> + vpfe->notifier.subdevs = vpfe->cfg->asd;
> + vpfe->notifier.num_subdevs = ARRAY_SIZE(vpfe->cfg->asd);
> + vpfe->notifier.bound = vpfe_async_bound;
> + vpfe->notifier.complete = vpfe_async_complete;
> + ret = v4l2_async_notifier_register(&vpfe->v4l2_dev,
> + &vpfe->notifier);
> + if (ret) {
> + vpfe_err(vpfe, "Error registering async notifier\n");
> + ret = -EINVAL;
> + goto probe_out_v4l2_unregister;
> + }
> +
> + return 0;
> +
> +probe_out_v4l2_unregister:
> + v4l2_device_unregister(&vpfe->v4l2_dev);
> +probe_out_video_release:
> + if (!video_is_registered(vpfe->video_dev))
> + video_device_release(vpfe->video_dev);
> + return ret;
> +}
> +
> +/*
> + * vpfe_remove : It un-register device from V4L2 driver
> + */
> +static int vpfe_remove(struct platform_device *pdev)
> +{
> + struct vpfe_device *vpfe = platform_get_drvdata(pdev);
> +
> + vpfe_dbg(2, vpfe, "vpfe_remove\n");
> +
> + pm_runtime_disable(&pdev->dev);
> +
> + v4l2_async_notifier_unregister(&vpfe->notifier);
> + v4l2_device_unregister(&vpfe->v4l2_dev);
> + video_unregister_device(vpfe->video_dev);
> +
> + return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +
> +static void vpfe_save_context(struct vpfe_ccdc *ccdc)
> +{
> + ccdc->ccdc_ctx[VPFE_PCR >> 2] = vpfe_reg_read(ccdc, VPFE_PCR);
> + ccdc->ccdc_ctx[VPFE_SYNMODE >> 2] = vpfe_reg_read(ccdc, VPFE_SYNMODE);
> + ccdc->ccdc_ctx[VPFE_SDOFST >> 2] = vpfe_reg_read(ccdc, VPFE_SDOFST);
> + ccdc->ccdc_ctx[VPFE_SDR_ADDR >> 2] = vpfe_reg_read(ccdc, VPFE_SDR_ADDR);
> + ccdc->ccdc_ctx[VPFE_CLAMP >> 2] = vpfe_reg_read(ccdc, VPFE_CLAMP);
> + ccdc->ccdc_ctx[VPFE_DCSUB >> 2] = vpfe_reg_read(ccdc, VPFE_DCSUB);
> + ccdc->ccdc_ctx[VPFE_COLPTN >> 2] = vpfe_reg_read(ccdc, VPFE_COLPTN);
> + ccdc->ccdc_ctx[VPFE_BLKCMP >> 2] = vpfe_reg_read(ccdc, VPFE_BLKCMP);
> + ccdc->ccdc_ctx[VPFE_VDINT >> 2] = vpfe_reg_read(ccdc, VPFE_VDINT);
> + ccdc->ccdc_ctx[VPFE_ALAW >> 2] = vpfe_reg_read(ccdc, VPFE_ALAW);
> + ccdc->ccdc_ctx[VPFE_REC656IF >> 2] = vpfe_reg_read(ccdc, VPFE_REC656IF);
> + ccdc->ccdc_ctx[VPFE_CCDCFG >> 2] = vpfe_reg_read(ccdc, VPFE_CCDCFG);
> + ccdc->ccdc_ctx[VPFE_CULLING >> 2] = vpfe_reg_read(ccdc, VPFE_CULLING);
> + ccdc->ccdc_ctx[VPFE_HD_VD_WID >> 2] = vpfe_reg_read(ccdc,
> + VPFE_HD_VD_WID);
> + ccdc->ccdc_ctx[VPFE_PIX_LINES >> 2] = vpfe_reg_read(ccdc,
> + VPFE_PIX_LINES);
> + ccdc->ccdc_ctx[VPFE_HORZ_INFO >> 2] = vpfe_reg_read(ccdc,
> + VPFE_HORZ_INFO);
> + ccdc->ccdc_ctx[VPFE_VERT_START >> 2] = vpfe_reg_read(ccdc,
> + VPFE_VERT_START);
> + ccdc->ccdc_ctx[VPFE_VERT_LINES >> 2] = vpfe_reg_read(ccdc,
> + VPFE_VERT_LINES);
> + ccdc->ccdc_ctx[VPFE_HSIZE_OFF >> 2] = vpfe_reg_read(ccdc,
> + VPFE_HSIZE_OFF);
> +}
> +
> +static int vpfe_suspend(struct device *dev)
> +{
> + struct platform_device *pdev = to_platform_device(dev);
> + struct vpfe_device *vpfe = platform_get_drvdata(pdev);
> + struct vpfe_ccdc *ccdc = &vpfe->ccdc;
> +
> + /* if streaming has not started we dont care */
> + if (!vb2_start_streaming_called(&vpfe->buffer_queue))
> + return 0;
> +
> + pm_runtime_get_sync(dev);
> + vpfe_config_enable(ccdc, 1);
> +
> + /* Save VPFE context */
> + vpfe_save_context(ccdc);
> +
> + /* Disable CCDC */
> + vpfe_pcr_enable(ccdc, 0);
> + vpfe_config_enable(ccdc, 0);
> +
> + /* Disable both master and slave clock */
> + pm_runtime_put_sync(dev);
> +
> + /* Select sleep pin state */
> + pinctrl_pm_select_sleep_state(dev);
> +
> + return 0;
> +}
> +
> +static void vpfe_restore_context(struct vpfe_ccdc *ccdc)
> +{
> + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_SYNMODE >> 2], VPFE_SYNMODE);
> + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_CULLING >> 2], VPFE_CULLING);
> + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_SDOFST >> 2], VPFE_SDOFST);
> + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_SDR_ADDR >> 2], VPFE_SDR_ADDR);
> + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_CLAMP >> 2], VPFE_CLAMP);
> + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_DCSUB >> 2], VPFE_DCSUB);
> + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_COLPTN >> 2], VPFE_COLPTN);
> + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_BLKCMP >> 2], VPFE_BLKCMP);
> + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_VDINT >> 2], VPFE_VDINT);
> + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_ALAW >> 2], VPFE_ALAW);
> + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_REC656IF >> 2], VPFE_REC656IF);
> + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_CCDCFG >> 2], VPFE_CCDCFG);
> + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_PCR >> 2], VPFE_PCR);
> + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_HD_VD_WID >> 2],
> + VPFE_HD_VD_WID);
> + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_PIX_LINES >> 2],
> + VPFE_PIX_LINES);
> + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_HORZ_INFO >> 2],
> + VPFE_HORZ_INFO);
> + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_VERT_START >> 2],
> + VPFE_VERT_START);
> + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_VERT_LINES >> 2],
> + VPFE_VERT_LINES);
> + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_HSIZE_OFF >> 2],
> + VPFE_HSIZE_OFF);
> +}
> +
> +static int vpfe_resume(struct device *dev)
> +{
> + struct platform_device *pdev = to_platform_device(dev);
> + struct vpfe_device *vpfe = platform_get_drvdata(pdev);
> + struct vpfe_ccdc *ccdc = &vpfe->ccdc;
> +
> + /* if streaming has not started we dont care */
> + if (!vb2_start_streaming_called(&vpfe->buffer_queue))
> + return 0;
> +
> + /* Enable both master and slave clock */
> + pm_runtime_get_sync(dev);
> + vpfe_config_enable(ccdc, 1);
> +
> + /* Restore VPFE context */
> + vpfe_restore_context(ccdc);
> +
> + vpfe_config_enable(ccdc, 0);
> + pm_runtime_put_sync(dev);
> +
> + /* Select default pin state */
> + pinctrl_pm_select_default_state(dev);
> +
> + return 0;
> +}
> +
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(vpfe_pm_ops, vpfe_suspend, vpfe_resume);
> +
> +static const struct of_device_id vpfe_of_match[] = {
> + { .compatible = "ti,am437x-vpfe", },
> + { /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, vpfe_of_match);
> +
> +static struct platform_driver vpfe_driver = {
> + .probe = vpfe_probe,
> + .remove = vpfe_remove,
> + .driver = {
> + .name = VPFE_MODULE_NAME,
> + .owner = THIS_MODULE,
> + .pm = &vpfe_pm_ops,
> + .of_match_table = of_match_ptr(vpfe_of_match),
> + },
> +};
> +
> +module_platform_driver(vpfe_driver);
> +
> +MODULE_AUTHOR("Texas Instruments");
> +MODULE_DESCRIPTION("TI AM437x VPFE driver");
> +MODULE_LICENSE("GPL");
> +MODULE_VERSION(VPFE_VERSION);
Regards,
Hans
^ permalink raw reply
* Re: [PATCH v17 1/7] mm: support madvise(MADV_FREE)
From: Michal Hocko @ 2014-12-03 10:13 UTC (permalink / raw)
To: Minchan Kim
Cc: Andrew Morton, linux-kernel, linux-mm, Michael Kerrisk, linux-api,
Hugh Dickins, Johannes Weiner, Rik van Riel, KOSAKI Motohiro,
Mel Gorman, Jason Evans, zhangyanfei, Kirill A. Shutemov,
Kirill A. Shutemov
In-Reply-To: <20141203000026.GA30217@bbox>
On Wed 03-12-14 09:00:26, Minchan Kim wrote:
> On Tue, Dec 02, 2014 at 11:01:25AM +0100, Michal Hocko wrote:
> > On Mon 01-12-14 08:56:52, Minchan Kim wrote:
> > [...]
> > > From 2edd6890f92fa4943ce3c452194479458582d88c Mon Sep 17 00:00:00 2001
> > > From: Minchan Kim <minchan@kernel.org>
> > > Date: Mon, 1 Dec 2014 08:53:55 +0900
> > > Subject: [PATCH] madvise.2: Document MADV_FREE
> > >
> > > Signed-off-by: Minchan Kim <minchan@kernel.org>
> > > ---
> > > man2/madvise.2 | 13 +++++++++++++
> > > 1 file changed, 13 insertions(+)
> > >
> > > diff --git a/man2/madvise.2 b/man2/madvise.2
> > > index 032ead7..33aa936 100644
> > > --- a/man2/madvise.2
> > > +++ b/man2/madvise.2
> > > @@ -265,6 +265,19 @@ file (see
> > > .BR MADV_DODUMP " (since Linux 3.4)"
> > > Undo the effect of an earlier
> > > .BR MADV_DONTDUMP .
> > > +.TP
> > > +.BR MADV_FREE " (since Linux 3.19)"
> > > +Gives the VM system the freedom to free pages, and tells the system that
> > > +information in the specified page range is no longer important.
> > > +This is an efficient way of allowing
> > > +.BR malloc (3)
> >
> > This might be rather misleading. Only some malloc implementations are
> > using this feature (jemalloc, right?). So either be specific about which
> > implementation or do not add it at all.
>
> Make sense. I don't think it's a good idea to say specific example
> in man page, which is rather arguable and limit the idea.
>
> >
> > > +to free pages anywhere in the address space, while keeping the address space
> > > +valid. The next time that the page is referenced, the page might be demand
> > > +zeroed, or might contain the data that was there before the MADV_FREE call.
> > > +References made to that address space range will not make the VM system page the
> > > +information back in from backing store until the page is modified again.
> >
> > I am not sure I understand the last sentence. So say I did MADV_FREE and
> > the reclaim has dropped that page. I know that the file backed mappings
> > are not supported yet but assume they were for a second... Now, I do
> > read from that location again what is the result?
>
> Zero page.
OK, it felt strange at first but now that I am thinking about it some
more it starts making sense. So the semantic is: Either zero page
(disconnected from the backing store) or the original content after
madvise(MADV_FREE). The page gets connected to the backing store after
it gets modified again. If this is the case then the sentence in the man
page makes perfect sense.
What made me confused was that I expected file backed pages would get a
fresh page from the origin but this would be awkward I guess.
> > If we consider anon mappings then the backing store is misleading as
> > well because memory was dropped and so always newly allocated.
>
> When I read the sentence at first, I thought backing store means swap
> so I don't have any trouble to understand it. But I agree your opinion.
> Target for man page is not a kernel developer but application developer.
>
> > I would rather drop the whole sentence and rather see an explanation
> > what is the difference between to MADV_DONT_NEED.
> > "
> > Unlike MADV_DONT_NEED the memory is freed lazily e.g. when the VM system
> > is under memory pressure.
> > "
>
> It's a good idea but I don't think it's enough. At least we should explan
> cancel of delay free logic(ie, write). So, How about this?
>
> MADV_FREE " (since Linux 3.19)"
>
> Gives the VM system the freedom to free pages, and tells the system that
> it's okay to free pages if the VM system has reasons(e.g., memory pressure).
> So, it looks like delayed MADV_DONTNEED.
> The next time that the page is referenced, the page might be demand
> zeroed if the VM system freed the page. Otherwise, it might contain the data
> that was there before the MADV_FREE call if the VM system didn't free the page.
> New write in the page after the MADV_FREE call makes the VM system not free
> the page any more.
Dunno, I guess the original content was slightly better. Or the
following wording from UNIX man pages is even more descriptive
(http://www.lehman.cuny.edu/cgi-bin/man-cgi?madvise+3)
"
Tell the kernel that contents in the specified address range are no
longer important and the range will be overwritten. When there is
demand for memory, the system will free pages associated with the
specified address range. In this instance, the next time a page in the
address range is referenced, it will contain all zeroes. Otherwise,
it will con- tain the data that was there prior to the MADV_FREE
call. References made to the address range will not make the system read
from backing store (swap space) until the page is modified again.
This value cannot be used on mappings that have underlying file objects.
"
I would just clarify the last sentence with addition
(MAP_PRIVATE|MAP_ANONYMOUS mappings in this implementation). The
difference to MADV_DONTNEED is more complicated now so I wouldn't make
the text even more confusing.
Anyway the confusion started on my end so feel free to stick with the
BSD wording (modulo malloc note which is really confusing as the default
glibc allocator doesn't do that AFAIK).
> It works only with private anonymous pages (see mmap(2)).
--
Michal Hocko
SUSE Labs
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* Re: [PATCH v3 5/5] tty/serial: Add Spreadtrum sc9836-uart driver support
From: Russell King - ARM Linux @ 2014-12-03 10:11 UTC (permalink / raw)
To: Arnd Bergmann
Cc: Lyra Zhang, Mark Rutland, gnomes@lxorguk.ukuu.org.uk,
Heiko Stübner, Mark Brown, Catalin Marinas, Will Deacon,
andrew@lunn.ch, linux-api@vger.kernel.org, jslaby@suse.cz,
Ramkumar Ramachandra, lanqing.liu@spreadtrum.com,
Wei Qiao (乔伟), Jonathan Corbet, Chunyan Zhang,
zhizhou.zhang, geng.ren@spreadtrum.com, Murali Karicheri,
linux-arm-kernel@lists.infradead.org
In-Reply-To: <3212776.kDbe7yX4eX@wuerfel>
On Wed, Dec 03, 2014 at 10:50:17AM +0100, Arnd Bergmann wrote:
> On Wednesday 03 December 2014 17:17:13 Lyra Zhang wrote:
>
> > 2014-11-27 2:29 GMT+08:00 Murali Karicheri <m-karicheri2@ti.com>:
> > > How about sorting this includes? asm/irq.h go first followed linux/ in alphabatical order?
> >
> > There are a few compile warnings if I moved asm/irq.h to the top of
> > all included files.
>
> The order that Murali meant is
>
> - first all linux/*.h headers, sorted alphabetically
> - then all asm/*.h headers, again sorted alphabetically
>
> This is the recommended style in general.
>
> > Warning details are below:
> >
> > In file included from drivers/tty/serial/sprd_serial.c:14:0:
> > ./arch/arm64/include/asm/irq.h:6:39: warning: ‘struct pt_regs’
> > declared inside parameter list [enabled by default]
> > extern void (*handle_arch_irq)(struct pt_regs *);
> > ^
> > ./arch/arm64/include/asm/irq.h:6:39: warning: its scope is only this
> > definition or declaration, which is probably not what you want
> > [enabled by default]
> > ./arch/arm64/include/asm/irq.h:8:54: warning: ‘struct pt_regs’
> > declared inside parameter list [enabled by default]
> > extern void set_handle_irq(void (*handle_irq)(struct pt_regs *));
> >
>
> I would consider this a (minor) bug in asm/irq.h. If you don't mind,
> please submit a patch to add a line with 'struct pt_regs;' to
> asm/irq.h.
A better question is: why is a modern driver using asm/irq.h in the
first place.
We used to include that file when platforms defined IRQ numbers
statically, but modern platforms don't do that, so it shouldn't be
required anymore.
--
FTTC broadband for 0.8mile line: currently at 9.5Mbps down 400kbps up
according to speedtest.net.
^ permalink raw reply
* Re: [PATCH v8 0/8] TPM 2.0 support
From: Jarkko Sakkinen @ 2014-12-03 10:10 UTC (permalink / raw)
To: Peter Hüwe
Cc: Ashley Lai, Marcel Selhorst,
tpmdd-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
josh.triplett-ral2JQCrhuEAvxtiuMwx3w,
christophe.ricard-Re5JQEeQqe8AvxtiuMwx3w,
jason.gunthorpe-ePGOBjL8dl3ta4EC/59zMFaTQe2KTcn/,
linux-api-u79uwXL29TY76Z2rM5mHXA,
trousers-tech-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
In-Reply-To: <201412022355.34544.PeterHuewe-Mmb7MZpHnFY@public.gmane.org>
On Tue, Dec 02, 2014 at 11:55:34PM +0100, Peter Hüwe wrote:
> Hi Jarkko,
>
>
> Am Dienstag, 2. Dezember 2014, 23:31:12 schrieb Jarkko Sakkinen:
> > This patch set enables TPM2 protocol and provides drivers for FIFO and
> > CRB interfaces. This patch set does not export any sysfs attributes for
> > TPM 2.0 because existing sysfs attributes have three non-trivial issues:
> >
>
>
> which tree are you basing your patches against?
> Can you use
> https://github.com/PeterHuewe/linux-tpmdd for-james
> please?
Yes, just started preparing v9, will be ready in few hours. My apologies
for the compilications!
> Thanks,
> Peter
/Jarkko
^ permalink raw reply
* Re: [PATCH v3 5/5] tty/serial: Add Spreadtrum sc9836-uart driver support
From: Arnd Bergmann @ 2014-12-03 9:50 UTC (permalink / raw)
To: Lyra Zhang
Cc: Murali Karicheri, Chunyan Zhang, Grant Likely,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org, Catalin Marinas,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org,
ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg@public.gmane.org,
jslaby-AlSwsSmVLrQ@public.gmane.org, Kumar Gala, Mark Brown,
Mark Rutland, Pawel Moll, Ramkumar Ramachandra,
rrichter-YGCgFSpz5w/QT0dZR+AlfA@public.gmane.org, Will Deacon,
gnomes-qBU/x9rampVanCEyBjwyrvXRex20P6io@public.gmane.org,
Jonathan Corbet, jason-NLaQJdtUoK4Be96aLqz0jA@public.gmane.org,
Mark Brown, Heiko Stübner, shawn.guo
In-Reply-To: <CAAfSe-u+g0mYGi9Adrd+YwwZ=q0-s79yCg_paB1UAVqFoufXEA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On Wednesday 03 December 2014 17:17:13 Lyra Zhang wrote:
> 2014-11-27 2:29 GMT+08:00 Murali Karicheri <m-karicheri2-l0cyMroinI0@public.gmane.org>:
> > How about sorting this includes? asm/irq.h go first followed linux/ in alphabatical order?
>
> There are a few compile warnings if I moved asm/irq.h to the top of
> all included files.
The order that Murali meant is
- first all linux/*.h headers, sorted alphabetically
- then all asm/*.h headers, again sorted alphabetically
This is the recommended style in general.
> Warning details are below:
>
> In file included from drivers/tty/serial/sprd_serial.c:14:0:
> ./arch/arm64/include/asm/irq.h:6:39: warning: ‘struct pt_regs’
> declared inside parameter list [enabled by default]
> extern void (*handle_arch_irq)(struct pt_regs *);
> ^
> ./arch/arm64/include/asm/irq.h:6:39: warning: its scope is only this
> definition or declaration, which is probably not what you want
> [enabled by default]
> ./arch/arm64/include/asm/irq.h:8:54: warning: ‘struct pt_regs’
> declared inside parameter list [enabled by default]
> extern void set_handle_irq(void (*handle_irq)(struct pt_regs *));
>
I would consider this a (minor) bug in asm/irq.h. If you don't mind,
please submit a patch to add a line with 'struct pt_regs;' to
asm/irq.h.
Arnd
^ permalink raw reply
* Re: Why not make kdbus use CUSE?
From: Richard Yao @ 2014-12-03 9:22 UTC (permalink / raw)
To: Greg Kroah-Hartman
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-api-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20141202172612.GA8958-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 4958 bytes --]
On 12/02/2014 12:26 PM, Greg Kroah-Hartman wrote:
> On Tue, Dec 02, 2014 at 07:22:11AM -0500, Richard Yao wrote:
>> Assuming that this dance succeeds, the FUSE process could then make a
>> readonly file in itself, open it read only, unlink it, put the data into
>> the file and send the file descriptor via UNIX domain socket while
>> refusing further writes. If it has its own user/group, the file should
>> be safe from prying eyes.
>>
>> This is not as good as a memfd and also suffers from the race that
>> O_TMPFILE was meant to close, but it should be able to function as a
>> decent fallback.
>
> We can't knowingly create and advocate for broken code, sorry.
>
>> This would preserve portability across not only
>> different versions of Linux, but also other POSIX systems.
>
> I honestly do not care about any other system than Linux, so I don't see
> why this would ever be an issue.
If you tie your userland to the most recent kernel and then want to
bisect an old bug, you will have a problem. You could try to find
another userland that supports the older kernels, but it would be *much*
easier if you could just use your current userland with it because then
the bare minimum must change. Writing portable software is the way to do
that.
Why burn the bridges that allow us to look backward when we have such a
need?
>> Keeping the code in userspace would allow us to apply SELinux policies
>> to it, which is something that we would lose if it were go to into the
>> kernel.
>
> On the contrary, the kdbusfs implementation gives you better security
> model support than before, it ties directly into the LSM hooks, see the
> add-on patches from some other developers that bring full support of LSM
> to the codebase.
If a bug in kdbusfs that allows arbitrary code execution is exploited in
the wild, would kdbus be more secure than a userland version?
>> That said, it is still not clear to me that dbus must be inside the
>> kernel to be able to perform multicast and zero copy using memfd.
>
> It seems you have yet to read my introductory email for the patch
> series.
Allow me to be more specific:
> - performance: fewer process context switches, fewer copies, fewer
> syscalls, larger memory chunks via memfd. This is really important
> for a whole class of userspace programs that are ported from other
> operating systems that are run on tiny ARM systems that rely on
> hundreds of thousands of messages passed at boot time, and at
> "critical" times in their user interaction loops.
What are some examples of these programs? Are any of them examples of
good software design?
> - security: the peers which communicate do not have to trust each other,
> as the only trustworthy compoenent in the game is the kernel which
> adds metadata and ensures that all data passed as payload is either
> copied or sealed, so that the receiver can parse the data without
> having to protect against changing memory while parsing buffers.
What keeps userspace from passing around memfds?
> - more metadata can be attached to messages than in userspace
How much metadata can be attached in either case? is there some inherit
aspect of the existing syscall API that prevents userspace from
attaching more? Why do we want to attach more?
> - being in the kernle closes a lot of races which can't be fixed with
> the current userspace solutions. For example, with kdbus, there is a
> way a client can disconnect from a bus, but do so only if no further
> messages present in its queue, which is crucial for implementing
> race-free "exit-on-idle" services
Is the current dbus daemon not supporting this this only thing
preventing us from doing it in userspace?
> Of course, some of the bits above could be implemented in userspace
> alone, for example with more sophisticated memory management APIs, but
> this is usually done by losing out on the other details. For example,
> for many of the memory management APIs, it's hard to not require the
> communicating peers to fully trust each other. And we _really_ don't
> want peers to have to trust each other.
Does being in the kernel solve this in a way that using memfds in
userspace does not?
> Another benefit of having this in the kernel, rather than as a userspace
> daemon, is that you can now easily use the bus from the initrd, or up to
> the very end when the system shuts down. On current userspace D-Bus,
> this is not really possible, as this requires passing the bus instance
> around between initrd and the "real" system. Such a transition of all
> fds also requires keeping full state of what has already been read from
> the connection fds. kdbus makes this much simpler, as we can change the
> ownership of the bus, just by passing one fd over from one part to the
> other.
Why do we want to start D-Bus inside the initramfs?
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 884 bytes --]
^ permalink raw reply
* Re: [PATCH v3 5/5] tty/serial: Add Spreadtrum sc9836-uart driver support
From: Lyra Zhang @ 2014-12-03 9:17 UTC (permalink / raw)
To: Murali Karicheri
Cc: Mark Rutland, gnomes@lxorguk.ukuu.org.uk, Heiko Stübner,
Mark Brown, Catalin Marinas, Will Deacon, andrew@lunn.ch,
linux-api@vger.kernel.org, jslaby@suse.cz, Ramkumar Ramachandra,
lanqing.liu@spreadtrum.com, Arnd Bergmann, Jonathan Corbet,
Chunyan Zhang, zhizhou.zhang, geng.ren@spreadtrum.com,
linux-arm-kernel@lists.infradead.org,
linux-serial@vger.kernel.org, Grant Likely
In-Reply-To: <54761BFC.4020705@ti.com>
2014-11-27 2:29 GMT+08:00 Murali Karicheri <m-karicheri2@ti.com>:
>
> On 11/25/2014 07:16 AM, Chunyan Zhang wrote:
>>
>> Add a full sc9836-uart driver for SC9836 SoC which is based on the
>> spreadtrum sharkl64 platform.
>> This driver also support earlycon.
>>
>> [...]
>>
>> +++ b/drivers/tty/serial/sprd_serial.c
>> @@ -0,0 +1,752 @@
>> +/*
>> + * Copyright (C) 2012 Spreadtrum Communications Inc.
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#include<linux/module.h>
>> +#include<linux/tty.h>
>> +#include<linux/ioport.h>
>> +#include<linux/console.h>
>> +#include<linux/platform_device.h>
>> +#include<linux/tty_flip.h>
>> +#include<linux/serial_core.h>
>> +#include<linux/serial.h>
>> +#include<linux/delay.h>
>> +#include<linux/io.h>
>> +#include<asm/irq.h>
>> +#include<linux/slab.h>
>> +#include<linux/of.h>
>> +#include<linux/kernel.h>
>> +#include<linux/slab.h>
>> +#include<linux/clk.h>
>
> How about sorting this includes? asm/irq.h go first followed linux/ in alphabatical order?
There are a few compile warnings if I moved asm/irq.h to the top of
all included files.
Warning details are below:
In file included from drivers/tty/serial/sprd_serial.c:14:0:
./arch/arm64/include/asm/irq.h:6:39: warning: ‘struct pt_regs’
declared inside parameter list [enabled by default]
extern void (*handle_arch_irq)(struct pt_regs *);
^
./arch/arm64/include/asm/irq.h:6:39: warning: its scope is only this
definition or declaration, which is probably not what you want
[enabled by default]
./arch/arm64/include/asm/irq.h:8:54: warning: ‘struct pt_regs’
declared inside parameter list [enabled by default]
extern void set_handle_irq(void (*handle_irq)(struct pt_regs *));
Thanks,
Chunyan
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v3 3/5] arm64: dts: Add support for Spreadtrum SC9836 SoC in dts and Makefile
From: Lyra Zhang @ 2014-12-03 9:16 UTC (permalink / raw)
To: Mark Rutland
Cc: Chunyan Zhang, grant.likely@linaro.org, robh+dt@kernel.org,
Catalin Marinas, gregkh@linuxfoundation.org,
ijc+devicetree@hellion.org.uk, jslaby@suse.cz,
galak@codeaurora.org, broonie@linaro.org, m-karicheri2@ti.com,
Pawel Moll, artagnon@gmail.com, rrichter@cavium.com, Will Deacon,
arnd@arndb.de, gnomes@lxorguk.ukuu.org.uk, corbet@lwn.net,
jason@lakedaemon.net, broonie@kernel.org,
"heiko@sntech.de" <heiko@
In-Reply-To: <20141127115042.GE857@leverpostej>
>
>> + gic: interrupt-controller@12001000 {
>> + compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic";
>> + #interrupt-cells = <3>;
>> + interrupt-controller;
>> + reg = <0 0x12001000 0 0x1000>,
>> + <0 0x12002000 0 0x1000>,
>> + <0 0x12004000 0 0x2000>,
>> + <0 0x12006000 0 0x2000>;
>> + };
>
> I believe GICC should be 8KiB here.
>
Yes, I'll correct it in next patch.
>> +
>> + psci {
>> + compatible = "arm,psci-0.2";
>> + method = "smc";
>> + };
>
> Do you have a complete PSCI 0.2 implementation (e.g. are all the
> mandatory functions implemented)?
>
I was using the latest boot-wrapper as the bootloader for test, I'll
add a compatible string "arm,psci" in v4.
We'll use u-boot on real hardware, the support for PSCI in our u-boot
is in progress.
We're intending to implement psci-0.1 at first, and psci-0.2 for the next step.
> I take it CPUs enter the kernel at EL2?
>
yes, we've just do like this.
> How have you tested this?
I'll test it on the SC9836 FPGA board before next submitting.
Thanks for your patient review.
Chunyan
^ permalink raw reply
* Re: [PATCH v6 0/7] vfs: Non-blockling buffered fs read (page cache only)
From: Volker Lendecke @ 2014-12-03 9:10 UTC (permalink / raw)
To: Andrew Morton
Cc: Milosz Tanski, LKML, Christoph Hellwig,
linux-fsdevel@vger.kernel.org, linux-aio@kvack.org, Mel Gorman,
Tejun Heo, Jeff Moyer, Theodore Ts'o, Al Viro, Linux API,
Michael Kerrisk, linux-arch
In-Reply-To: <20141202144200.a4ca81a46a43563a8874fd8e@linux-foundation.org>
On Tue, Dec 02, 2014 at 02:42:00PM -0800, Andrew Morton wrote:
> The question is whether a simpler approach such as fincore() will be
> sufficient.
For many use cases in Samba, fincore will probably be
enough. But Windows clients become more and more
multi-threaded, so Samba sees multiple parallel active read
requests. Samba's core SMB processing engine is single
threaded, and if due to that race we get blocked, more than
one data stream will be affected. We might make it an option
to use the fincore alternative, but I don't see it as a
default. The default will be the strict threadpool.
With best regards,
Volker Lendecke
--
SerNet GmbH, Bahnhofsallee 1b, 37081 Göttingen
phone: +49-551-370000-0, fax: +49-551-370000-9
AG Göttingen, HRB 2816, GF: Dr. Johannes Loxen
http://www.sernet.de, mailto:kontakt@sernet.de
--
To unsubscribe, send a message with 'unsubscribe linux-aio' in
the body to majordomo@kvack.org. For more info on Linux AIO,
see: http://www.kvack.org/aio/
Don't email: <a href=mailto:"aart@kvack.org">aart@kvack.org</a>
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox