* [PATCH 3/4] Adds project quota support for ext4
From: Li Xi @ 2014-09-24 14:04 UTC (permalink / raw)
To: linux-fsdevel, linux-ext4, linux-api, tytso, adilger, jack, viro,
hch, dmonakhov
In-Reply-To: <1411567470-31799-1-git-send-email-lixi@ddn.com>
This patch adds mount options for enabling/disabling project quota
accounting and enforcement. A new specific inode is also used for
project quota accounting.
Signed-off-by: Li Xi <lixi@ddn.com>
Signed-off-by: Dmitry Monakhov <dmonakhov@openvz.org>
---
fs/ext4/ext4.h | 8 +++-
fs/ext4/super.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++-----
2 files changed, 118 insertions(+), 13 deletions(-)
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 092f60c..f8be9bf 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -217,6 +217,7 @@ struct ext4_io_submit {
#define EXT4_UNDEL_DIR_INO 6 /* Undelete directory inode */
#define EXT4_RESIZE_INO 7 /* Reserved group descriptors inode */
#define EXT4_JOURNAL_INO 8 /* Journal inode */
+#define EXT4_PRJ_QUOTA_INO 9 /* Project quota inode */
/* First non-reserved inode for old ext4 filesystems */
#define EXT4_GOOD_OLD_FIRST_INO 11
@@ -991,6 +992,7 @@ struct ext4_inode_info {
#define EXT4_MOUNT_DIOREAD_NOLOCK 0x400000 /* Enable support for dio read nolocking */
#define EXT4_MOUNT_JOURNAL_CHECKSUM 0x800000 /* Journal checksums */
#define EXT4_MOUNT_JOURNAL_ASYNC_COMMIT 0x1000000 /* Journal Async Commit */
+#define EXT4_MOUNT_PRJQUOTA 0x2000000 /* Project quota support */
#define EXT4_MOUNT_DELALLOC 0x8000000 /* Delalloc support */
#define EXT4_MOUNT_DATA_ERR_ABORT 0x10000000 /* Abort on file data write */
#define EXT4_MOUNT_BLOCK_VALIDITY 0x20000000 /* Block validity checking */
@@ -1166,7 +1168,8 @@ struct ext4_super_block {
__le32 s_grp_quota_inum; /* inode for tracking group quota */
__le32 s_overhead_clusters; /* overhead blocks/clusters in fs */
__le32 s_backup_bgs[2]; /* groups with sparse_super2 SBs */
- __le32 s_reserved[106]; /* Padding to the end of the block */
+ __le32 s_prj_quota_inum; /* inode for tracking project quota */
+ __le32 s_reserved[105]; /* Padding to the end of the block */
__le32 s_checksum; /* crc32c(superblock) */
};
@@ -1181,7 +1184,7 @@ struct ext4_super_block {
#define EXT4_MF_FS_ABORTED 0x0002 /* Fatal error detected */
/* Number of quota types we support */
-#define EXT4_MAXQUOTAS 2
+#define EXT4_MAXQUOTAS 3
/*
* fourth extended-fs super-block data in memory
@@ -1372,6 +1375,7 @@ static inline int ext4_valid_inum(struct super_block *sb, unsigned long ino)
ino == EXT4_BOOT_LOADER_INO ||
ino == EXT4_JOURNAL_INO ||
ino == EXT4_RESIZE_INO ||
+ ino == EXT4_PRJ_QUOTA_INO ||
(ino >= EXT4_FIRST_INO(sb) &&
ino <= le32_to_cpu(EXT4_SB(sb)->s_es->s_inodes_count));
}
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 8c3e87b..5cb5055 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1048,8 +1048,8 @@ static int bdev_try_to_free_page(struct super_block *sb, struct page *page,
}
#ifdef CONFIG_QUOTA
-#define QTYPE2NAME(t) ((t) == USRQUOTA ? "user" : "group")
-#define QTYPE2MOPT(on, t) ((t) == USRQUOTA?((on)##USRJQUOTA):((on)##GRPJQUOTA))
+static char *quotatypes[] = INITQFNAMES;
+#define QTYPE2NAME(t) (quotatypes[t])
static int ext4_write_dquot(struct dquot *dquot);
static int ext4_acquire_dquot(struct dquot *dquot);
@@ -1160,10 +1160,11 @@ enum {
Opt_journal_path, Opt_journal_checksum, Opt_journal_async_commit,
Opt_abort, Opt_data_journal, Opt_data_ordered, Opt_data_writeback,
Opt_data_err_abort, Opt_data_err_ignore,
- Opt_usrjquota, Opt_grpjquota, Opt_offusrjquota, Opt_offgrpjquota,
+ Opt_usrjquota, Opt_grpjquota, Opt_prjjquota,
+ Opt_offusrjquota, Opt_offgrpjquota, Opt_offprjjquota,
Opt_jqfmt_vfsold, Opt_jqfmt_vfsv0, Opt_jqfmt_vfsv1, Opt_quota,
Opt_noquota, Opt_barrier, Opt_nobarrier, Opt_err,
- Opt_usrquota, Opt_grpquota, Opt_i_version,
+ Opt_usrquota, Opt_grpquota, Opt_prjquota, Opt_i_version,
Opt_stripe, Opt_delalloc, Opt_nodelalloc, Opt_mblk_io_submit,
Opt_nomblk_io_submit, Opt_block_validity, Opt_noblock_validity,
Opt_inode_readahead_blks, Opt_journal_ioprio,
@@ -1214,6 +1215,8 @@ static const match_table_t tokens = {
{Opt_usrjquota, "usrjquota=%s"},
{Opt_offgrpjquota, "grpjquota="},
{Opt_grpjquota, "grpjquota=%s"},
+ {Opt_offprjjquota, "prjjquota="},
+ {Opt_prjjquota, "prjjquota=%s"},
{Opt_jqfmt_vfsold, "jqfmt=vfsold"},
{Opt_jqfmt_vfsv0, "jqfmt=vfsv0"},
{Opt_jqfmt_vfsv1, "jqfmt=vfsv1"},
@@ -1221,6 +1224,7 @@ static const match_table_t tokens = {
{Opt_noquota, "noquota"},
{Opt_quota, "quota"},
{Opt_usrquota, "usrquota"},
+ {Opt_prjquota, "prjquota"},
{Opt_barrier, "barrier=%u"},
{Opt_barrier, "barrier"},
{Opt_nobarrier, "nobarrier"},
@@ -1433,6 +1437,8 @@ static const struct mount_opts {
MOPT_SET | MOPT_Q},
{Opt_grpquota, EXT4_MOUNT_QUOTA | EXT4_MOUNT_GRPQUOTA,
MOPT_SET | MOPT_Q},
+ {Opt_prjquota, EXT4_MOUNT_QUOTA | EXT4_MOUNT_PRJQUOTA,
+ MOPT_SET | MOPT_Q},
{Opt_noquota, (EXT4_MOUNT_QUOTA | EXT4_MOUNT_USRQUOTA |
EXT4_MOUNT_GRPQUOTA), MOPT_CLEAR | MOPT_Q},
{Opt_usrjquota, 0, MOPT_Q},
@@ -1461,10 +1467,14 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token,
return set_qf_name(sb, USRQUOTA, &args[0]);
else if (token == Opt_grpjquota)
return set_qf_name(sb, GRPQUOTA, &args[0]);
+ else if (token == Opt_prjjquota)
+ return set_qf_name(sb, PRJQUOTA, &args[0]);
else if (token == Opt_offusrjquota)
return clear_qf_name(sb, USRQUOTA);
else if (token == Opt_offgrpjquota)
return clear_qf_name(sb, GRPQUOTA);
+ else if (token == Opt_offprjjquota)
+ return clear_qf_name(sb, PRJQUOTA);
#endif
switch (token) {
case Opt_noacl:
@@ -1690,19 +1700,28 @@ static int parse_options(char *options, struct super_block *sb,
}
#ifdef CONFIG_QUOTA
if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA) &&
- (test_opt(sb, USRQUOTA) || test_opt(sb, GRPQUOTA))) {
+ (test_opt(sb, USRQUOTA) ||
+ test_opt(sb, GRPQUOTA) ||
+ test_opt(sb, PRJQUOTA))) {
ext4_msg(sb, KERN_ERR, "Cannot set quota options when QUOTA "
"feature is enabled");
return 0;
}
- if (sbi->s_qf_names[USRQUOTA] || sbi->s_qf_names[GRPQUOTA]) {
+ if (sbi->s_qf_names[USRQUOTA] ||
+ sbi->s_qf_names[GRPQUOTA] ||
+ sbi->s_qf_names[PRJQUOTA]) {
if (test_opt(sb, USRQUOTA) && sbi->s_qf_names[USRQUOTA])
clear_opt(sb, USRQUOTA);
if (test_opt(sb, GRPQUOTA) && sbi->s_qf_names[GRPQUOTA])
clear_opt(sb, GRPQUOTA);
- if (test_opt(sb, GRPQUOTA) || test_opt(sb, USRQUOTA)) {
+ if (test_opt(sb, PRJQUOTA) && sbi->s_qf_names[PRJQUOTA])
+ clear_opt(sb, PRJQUOTA);
+
+ if (test_opt(sb, GRPQUOTA) ||
+ test_opt(sb, USRQUOTA) ||
+ test_opt(sb, PRJQUOTA)) {
ext4_msg(sb, KERN_ERR, "old and new quota "
"format mixing");
return 0;
@@ -1763,6 +1782,9 @@ static inline void ext4_show_quota_options(struct seq_file *seq,
if (sbi->s_qf_names[GRPQUOTA])
seq_printf(seq, ",grpjquota=%s", sbi->s_qf_names[GRPQUOTA]);
+
+ if (sbi->s_qf_names[PRJQUOTA])
+ seq_printf(seq, ",prjjquota=%s", sbi->s_qf_names[PRJQUOTA]);
#endif
}
@@ -2833,6 +2855,13 @@ static int ext4_feature_set_ok(struct super_block *sb, int readonly)
"without CONFIG_QUOTA");
return 0;
}
+ if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_PROJECT) &&
+ !readonly) {
+ ext4_msg(sb, KERN_ERR,
+ "Filesystem with project quota feature cannot be"
+ "mounted RDWR without CONFIG_QUOTA");
+ return 0;
+ }
#endif /* CONFIG_QUOTA */
return 1;
}
@@ -5052,6 +5081,46 @@ restore_opts:
return err;
}
+static int ext4_statfs_project(struct super_block *sb,
+ kprojid_t projid, struct kstatfs *buf)
+{
+ struct kqid qid;
+ struct dquot *dquot;
+ u64 limit;
+ u64 curblock;
+
+ qid = make_kqid_projid(projid);
+ dquot = dqget(sb, qid);
+ if (!dquot)
+ return -ESRCH;
+ spin_lock(&dq_data_lock);
+
+ limit = dquot->dq_dqb.dqb_bsoftlimit ?
+ dquot->dq_dqb.dqb_bsoftlimit :
+ dquot->dq_dqb.dqb_bhardlimit;
+ if (limit && buf->f_blocks * buf->f_bsize > limit) {
+ curblock = dquot->dq_dqb.dqb_curspace / buf->f_bsize;
+ buf->f_blocks = limit / buf->f_bsize;
+ buf->f_bfree = buf->f_bavail =
+ (buf->f_blocks > curblock) ?
+ (buf->f_blocks - curblock) : 0;
+ }
+
+ limit = dquot->dq_dqb.dqb_isoftlimit ?
+ dquot->dq_dqb.dqb_isoftlimit :
+ dquot->dq_dqb.dqb_ihardlimit;
+ if (limit && buf->f_files > limit) {
+ buf->f_files = limit;
+ buf->f_ffree =
+ (buf->f_files > dquot->dq_dqb.dqb_curinodes) ?
+ (buf->f_ffree - dquot->dq_dqb.dqb_curinodes) : 0;
+ }
+
+ spin_unlock(&dq_data_lock);
+ dqput(dquot);
+ return 0;
+}
+
static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf)
{
struct super_block *sb = dentry->d_sb;
@@ -5060,6 +5129,8 @@ static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf)
ext4_fsblk_t overhead = 0, resv_blocks;
u64 fsid;
s64 bfree;
+ struct inode *inode = dentry->d_inode;
+ int err = 0;
resv_blocks = EXT4_C2B(sbi, atomic64_read(&sbi->s_resv_clusters));
if (!test_opt(sb, MINIX_DF))
@@ -5084,7 +5155,18 @@ static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf)
buf->f_fsid.val[0] = fsid & 0xFFFFFFFFUL;
buf->f_fsid.val[1] = (fsid >> 32) & 0xFFFFFFFFUL;
- return 0;
+ if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_PROJECT) &&
+ ext4_test_inode_flag(inode, EXT4_INODE_PROJINHERIT) &&
+ sb_has_quota_usage_enabled(sb, PRJQUOTA) &&
+ sb_has_quota_limits_enabled(sb, PRJQUOTA)) {
+ err = ext4_statfs_project(sb, EXT4_I(inode)->i_projid, buf);
+ if (err) {
+ ext4_warning(sb, "Cannot get quota of project %u\n",
+ from_kprojid(&init_user_ns,
+ EXT4_I(inode)->i_projid));
+ }
+ }
+ return err;
}
/* Helper function for writing quotas on sync - we need to start transaction
@@ -5164,7 +5246,9 @@ static int ext4_mark_dquot_dirty(struct dquot *dquot)
/* Are we journaling quotas? */
if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA) ||
- sbi->s_qf_names[USRQUOTA] || sbi->s_qf_names[GRPQUOTA]) {
+ sbi->s_qf_names[USRQUOTA] ||
+ sbi->s_qf_names[GRPQUOTA] ||
+ sbi->s_qf_names[PRJQUOTA]) {
dquot_mark_dquot_dirty(dquot);
return ext4_write_dquot(dquot);
} else {
@@ -5248,10 +5332,14 @@ static int ext4_quota_enable(struct super_block *sb, int type, int format_id,
struct inode *qf_inode;
unsigned long qf_inums[EXT4_MAXQUOTAS] = {
le32_to_cpu(EXT4_SB(sb)->s_es->s_usr_quota_inum),
- le32_to_cpu(EXT4_SB(sb)->s_es->s_grp_quota_inum)
+ le32_to_cpu(EXT4_SB(sb)->s_es->s_grp_quota_inum),
+ le32_to_cpu(EXT4_SB(sb)->s_es->s_prj_quota_inum)
};
BUG_ON(!EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA));
+ BUG_ON(type == PRJQUOTA &&
+ (!EXT4_HAS_RO_COMPAT_FEATURE(sb,
+ EXT4_FEATURE_RO_COMPAT_PROJECT)));
if (!qf_inums[type])
return -EPERM;
@@ -5276,11 +5364,16 @@ static int ext4_enable_quotas(struct super_block *sb)
int type, err = 0;
unsigned long qf_inums[EXT4_MAXQUOTAS] = {
le32_to_cpu(EXT4_SB(sb)->s_es->s_usr_quota_inum),
- le32_to_cpu(EXT4_SB(sb)->s_es->s_grp_quota_inum)
+ le32_to_cpu(EXT4_SB(sb)->s_es->s_grp_quota_inum),
+ le32_to_cpu(EXT4_SB(sb)->s_es->s_prj_quota_inum)
};
sb_dqopt(sb)->flags |= DQUOT_QUOTA_SYS_FILE;
for (type = 0; type < EXT4_MAXQUOTAS; type++) {
+ if (type == PRJQUOTA &&
+ (!EXT4_HAS_RO_COMPAT_FEATURE(sb,
+ EXT4_FEATURE_RO_COMPAT_PROJECT)))
+ continue;
if (qf_inums[type]) {
err = ext4_quota_enable(sb, type, QFMT_VFS_V1,
DQUOT_USAGE_ENABLED);
@@ -5305,6 +5398,10 @@ static int ext4_quota_on_sysfile(struct super_block *sb, int type,
if (!EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA))
return -EINVAL;
+ if (type == PRJQUOTA &&
+ (!EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_PROJECT)))
+ return -EINVAL;
+
/*
* USAGE was enabled at mount time. Only need to enable LIMITS now.
*/
@@ -5345,6 +5442,10 @@ static int ext4_quota_off_sysfile(struct super_block *sb, int type)
if (!EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA))
return -EINVAL;
+ if (type == PRJQUOTA &&
+ (!EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_PROJECT)))
+ return -EINVAL;
+
/* Disable only the limits. */
return dquot_disable(sb, type, DQUOT_LIMITS_ENABLED);
}
--
1.7.1
^ permalink raw reply related
* [PATCH 4/4] Adds ioctl interface support for ext4 project
From: Li Xi @ 2014-09-24 14:04 UTC (permalink / raw)
To: linux-fsdevel, linux-ext4, linux-api, tytso, adilger, jack, viro,
hch, dmonakhov
In-Reply-To: <1411567470-31799-1-git-send-email-lixi@ddn.com>
This patch adds ioctl interface for setting/getting project of ext4.
Signed-off-by: Li Xi <lixi@ddn.com>
---
Documentation/filesystems/ext4.txt | 4 ++
fs/ext4/ext4.h | 2 +
fs/ext4/ioctl.c | 85 ++++++++++++++++++++++++++++++++++++
3 files changed, 91 insertions(+), 0 deletions(-)
diff --git a/Documentation/filesystems/ext4.txt b/Documentation/filesystems/ext4.txt
index 919a329..9c98e62 100644
--- a/Documentation/filesystems/ext4.txt
+++ b/Documentation/filesystems/ext4.txt
@@ -609,6 +609,10 @@ EXT4_IOC_SWAP_BOOT Swap i_blocks and associated attributes
The data blocks of the previous boot loader
will be associated with the given inode.
+ EXT4_IOC_GETPROJECT Get project ID associated with inode.
+
+ EXT4_IOC_SETPROJECT Set Project ID associated with inode.
+
..............................................................................
References
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index f8be9bf..51946fd 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -617,6 +617,8 @@ enum {
#define EXT4_IOC_RESIZE_FS _IOW('f', 16, __u64)
#define EXT4_IOC_SWAP_BOOT _IO('f', 17)
#define EXT4_IOC_PRECACHE_EXTENTS _IO('f', 18)
+#define EXT4_IOC_GETPROJECT _IOR('f', 19, long)
+#define EXT4_IOC_SETPROJECT _IOW('f', 20, long)
#if defined(__KERNEL__) && defined(CONFIG_COMPAT)
/*
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index 0f2252e..93b7ff4 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -14,6 +14,8 @@
#include <linux/compat.h>
#include <linux/mount.h>
#include <linux/file.h>
+#include <linux/quotaops.h>
+#include <linux/quota.h>
#include <asm/uaccess.h>
#include "ext4_jbd2.h"
#include "ext4.h"
@@ -611,6 +613,89 @@ resizefs_out:
case EXT4_IOC_PRECACHE_EXTENTS:
return ext4_ext_precache(inode);
+ case EXT4_IOC_GETPROJECT:
+ {
+ __u32 projid;
+
+ projid = (__u32)from_kprojid(&init_user_ns,
+ EXT4_I(inode)->i_projid);
+ return put_user(projid, (__u32 __user *) arg);
+ }
+ case EXT4_IOC_SETPROJECT:
+ {
+ __u32 projid;
+ int err;
+ handle_t *handle;
+ kprojid_t kprojid;
+ struct ext4_iloc iloc;
+ struct ext4_inode *raw_inode;
+
+ struct dquot *transfer_to[EXT4_MAXQUOTAS] = { };
+
+ /* Make sure caller can change project. */
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+
+ if (get_user(projid, (__u32 __user *) arg))
+ return -EFAULT;
+
+ kprojid = make_kprojid(&init_user_ns, (projid_t)projid);
+
+ if (projid_eq(kprojid, EXT4_I(inode)->i_projid))
+ return 0;
+
+ err = mnt_want_write_file(filp);
+ if (err)
+ return err;
+
+ err = -EPERM;
+ mutex_lock(&inode->i_mutex);
+ /* Is it quota file? Do not allow user to mess with it */
+ if (IS_NOQUOTA(inode))
+ goto project_out;
+
+ dquot_initialize(inode);
+
+ handle = ext4_journal_start(inode, EXT4_HT_QUOTA,
+ EXT4_QUOTA_INIT_BLOCKS(sb) +
+ EXT4_QUOTA_DEL_BLOCKS(sb) + 3);
+ if (IS_ERR(handle)) {
+ err = PTR_ERR(handle);
+ goto project_out;
+ }
+
+ err = ext4_reserve_inode_write(handle, inode, &iloc);
+ if (err)
+ goto project_stop;
+
+ raw_inode = ext4_raw_inode(&iloc);
+ if ((EXT4_INODE_SIZE(inode->i_sb) <=
+ EXT4_GOOD_OLD_INODE_SIZE) ||
+ (!EXT4_FITS_IN_INODE(raw_inode, ei, i_projid))) {
+ err = -EFBIG;
+ goto project_stop;
+ }
+
+ transfer_to[PRJQUOTA] = dqget(sb, make_kqid_projid(kprojid));
+ if (!transfer_to[PRJQUOTA])
+ goto project_set;
+
+ err = __dquot_transfer(inode, transfer_to);
+ dqput(transfer_to[PRJQUOTA]);
+ if (err)
+ goto project_stop;
+
+project_set:
+ EXT4_I(inode)->i_projid = kprojid;
+ inode->i_ctime = ext4_current_time(inode);
+ err = ext4_mark_iloc_dirty(handle, inode, &iloc);
+project_stop:
+ ext4_journal_stop(handle);
+project_out:
+ mutex_unlock(&inode->i_mutex);
+ mnt_drop_write_file(filp);
+ return err;
+ }
default:
return -ENOTTY;
}
--
1.7.1
^ permalink raw reply related
* Re: [PATCH] usb: gadget: f_rndis: fix usb_interface_descriptor for rndis
From: Lars Melin @ 2014-09-24 14:22 UTC (permalink / raw)
To: hs-ynQEQJNshbs
Cc: linux-usb-u79uwXL29TY76Z2rM5mHXA, Felipe Balbi,
Greg Kroah-Hartman, linux-kernel-u79uwXL29TY76Z2rM5mHXA,
netdev-u79uwXL29TY76Z2rM5mHXA, linux-api-u79uwXL29TY76Z2rM5mHXA,
Andrzej Pietrasiewicz, Michal Nazarewicz, Kyungmin Park,
Dan Carpenter, Macpaul Lin
In-Reply-To: <5422C33F.400-ynQEQJNshbs@public.gmane.org>
On 2014-09-24 20:12, Heiko Schocher wrote:
> Hello Lars,
>
> Am 24.09.2014 14:25, schrieb Lars Melin:
>> On 2014-09-24 13:48, Heiko Schocher wrote:
>>> use the values for RNDIS over Ethernet as defined in
>>> http://www.usb.org/developers/defined_class
>>> (search for RDNIS):
>>>
>>> - baseclass: 0xef (miscellaneous)
>>> - subclass: 0x04
>>> - protocol: 0x01
>>>
>> That is usb class, it is not the same thing as communication device
>> class.
>>> --- a/include/uapi/linux/usb/cdc.h
>>> +++ b/include/uapi/linux/usb/cdc.h
>>> @@ -12,6 +12,7 @@
>>> #include <linux/types.h>
>>> #define USB_CDC_SUBCLASS_ACM 0x02
>>> +#define USB_CDC_SUBCLASS_RNDIS 0x04
>> No, no, no.
>> There is no CDC_SUBCLASS_RNDIS and you can not define one over an
>> already used cdc subclass number, 0x04 is Multi-Channel Control Model
>
> Ah, ok, so I have to define this values in a new header file, as there
> is no current file for the USB_CLASS_MISC defines? Or is there a proper
> place for them?
>
> BTW: where do I find the "cdc subclass number, 0x04 is Multi-Channel
> Control Model" define?
>
> bye,
> Heiko
You can still find the original specification usbcdc11.pdf on the net if
you google for it, it has been pulled from usb.org where you could
download it until a few years ago.
It is old but covers a lot of what you need to know.
Linux has afaik only the cdc.h definition file, everything else is coded
by class/subclass in respectively drivers when needed.
02/02/ff or e0/01/03 are the most common interface attribute for
rndis, both of them together with a data interface with attributes
0a/00/00.
Please check the whitelisting in drivers/net/usb/rndis_host.c and also
blacklistings in other net drivers under the same path, it should give
you an idea how to bind an interface to a specific driver by interface
attributes and/or usb vid:pid.
You should be able to do the same for your particular device.
^ permalink raw reply
* [GIT PULL] MFD: Immutable branche between Charger, Power and Regulator
From: Lee Jones @ 2014-09-24 14:34 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: Samuel Ortiz, linux-kernel, linux-api, linux-pm,
Sebastian Reichel, Dmitry Eremin-Solenikov, David Woodhouse,
Liam Girdwood, Mark Brown, Rob Herring, Pawel Moll, Mark Rutland,
Ian Campbell, Kumar Gala, Kyungmin Park, Marek Szyprowski,
Bartlomiej Zolnierkiewicz, devicetree, Andrew Morton
In-Reply-To: <1411472603-11821-1-git-send-email-k.kozlowski@samsung.com>
Mark, Sebastian,
The following changes since commit 52addcf9d6669fa439387610bc65c92fa0980cef:
Linux 3.17-rc2 (2014-08-25 15:36:20 -0700)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git tags/mfd-power-charger-regulator-v3.18
for you to fetch changes up to d6cc1f5824cbca392d099f3bb0c441efd9e54de9:
Documentation: charger: max14577: Document exported sysfs entry (2014-09-24 15:25:56 +0100)
----------------------------------------------------------------
Immutable branch between MFD, Power, Charger and Regulator for v3.18
----------------------------------------------------------------
Krzysztof Kozlowski (8):
mfd: max14577: Add defines for MAX77836 charger
mfd: max14577: Map charger device to its own of_node
charger: max14577: Add support for MAX77836 charger
regulator/mfd: max14577: Export symbols for calculating charger current
charger: max14577: Configure battery-dependent settings from DTS and sysfs
power: max17040: Add ID for MAX77836 Fuel Gauge block
devicetree: mfd: max14577: Add device tree bindings document
Documentation: charger: max14577: Document exported sysfs entry
Documentation/ABI/testing/sysfs-class-power | 14 +
Documentation/devicetree/bindings/mfd/max14577.txt | 146 ++++++++
drivers/mfd/max14577.c | 100 +++++-
drivers/power/Kconfig | 5 +-
drivers/power/max14577_charger.c | 370 +++++++++++++++++++--
drivers/power/max17040_battery.c | 3 +-
drivers/regulator/max14577.c | 80 +----
include/linux/mfd/max14577-private.h | 95 ++++--
include/linux/mfd/max14577.h | 30 ++
9 files changed, 704 insertions(+), 139 deletions(-)
--
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
^ permalink raw reply
* Re: [PATCH v4 0/4] quota: add project quota support
From: Theodore Ts'o @ 2014-09-24 15:42 UTC (permalink / raw)
To: Jan Kara
Cc: Li Xi, linux-fsdevel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Ext4 Developers List, linux-api-u79uwXL29TY76Z2rM5mHXA,
Andreas Dilger,
viro-RmSDqhL/yNMiFSDQTTA3OLVCufUGDwFn@public.gmane.org,
hch-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org, Dmitry Monakhov
In-Reply-To: <20140924125206.GA26916-+0h/O2h83AeN3ZZ/Hiejyg@public.gmane.org>
On Wed, Sep 24, 2014 at 02:52:06PM +0200, Jan Kara wrote:
> Whitespace got mangled in your patches so much they are hardly readable.
> Please resend them so that it is properly preserved. I'm not sure how other
> guys using gmail do this so you might need to ask them... Thanks!
And it would be nice if they are chained together so that people with
proper mail-threading readers can easily find all of your patches.
Probably the best way to do this is to configure your a local mail
transfer agent on your Linux desktop/laptop, and configure it to use
smart mail-router with authenticated SMTP to:
Host: smtp.gmail.com
Port: 465
Authentication: Yes
Username: #YOU#@gmail.com
Password: #YOUR_PASSWORD#
Security: SSL
Then use "git send-email" to post your patches, and it should all be
good.
(I'm guessing your cutting and pasting your patches into the mail send
window, and that unfortunately tends not to work well.)
Cheers,
- Ted
^ permalink raw reply
* Re: [RFC PATCH net-next v2 0/5] netns: allow to identify peer netns
From: Cong Wang @ 2014-09-24 16:01 UTC (permalink / raw)
To: Nicolas Dichtel
Cc: netdev, containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-api-u79uwXL29TY76Z2rM5mHXA, David Miller, Eric W. Biederman,
Stephen Hemminger, Andrew Morton, Andy Lutomirski
In-Reply-To: <54228D87.3070309-pdR9zngts4EAvxtiuMwx3w@public.gmane.org>
On Wed, Sep 24, 2014 at 2:23 AM, Nicolas Dichtel
<nicolas.dichtel-pdR9zngts4EAvxtiuMwx3w@public.gmane.org> wrote:
> Le 23/09/2014 21:22, Cong Wang a écrit :
>
>> On Tue, Sep 23, 2014 at 6:20 AM, Nicolas Dichtel
>> <nicolas.dichtel-pdR9zngts4EAvxtiuMwx3w@public.gmane.org> wrote:
>>>
>>>
>>> Here is a small screenshot to show how it can be used by userland:
>>> $ ip netns add foo
>>> $ ip netns del foo
>>> $ ip netns
>>> $ touch /var/run/netns/init_net
>>> $ mount --bind /proc/1/ns/net /var/run/netns/init_net
>>> $ ip netns add foo
>>> $ ip netns
>>> foo (id: 3)
>>> init_net (id: 1)
>>> $ ip netns exec foo ip netns
>>> foo (id: 3)
>>> init_net (id: 1)
>>> $ ip netns exec foo ip link add ipip1 link-netnsid 1 type ipip remote
>>> 10.16.0.121 local 10.16.0.249
>>> $ ip netns exec foo ip l ls ipip1
>>> 6: ipip1@NONE: <POINTOPOINT,NOARP> mtu 1480 qdisc noop state DOWN mode
>>> DEFAULT group default
>>> link/ipip 10.16.0.249 peer 10.16.0.121 link-netnsid 1
>>>
>>> The parameter link-netnsid shows us where the interface sends and
>>> receives
>>> packets (and thus we know where encapsulated addresses are set).
>>>
>>
>> So ipip1 is shown in netns foo but functioning in netns init_net? Getting
>> the
>> id of init_net in foo depends on your mount namespace, /var/run/netns/ may
>> not visible inside foo, in this case, link-netnsid is meaningless. It
>> is not your
>> fault, network namespace already heavily relies on mount namespace (sysfs
>> needs to be remount otherwise you can not create device with the same
>> name.)
>>
>> On the other hand, what's the problem you are trying to solve? AFAIK,
>> the ifindex
>> issue is purely in output, IOW, the device still functions correctly
>> even through
>> its link ifindex is not correct after moving to another namespace. If
>> not, it is bug
>> we need to fix.
>>
> The problem is explained here:
> http://thread.gmane.org/gmane.linux.network/315933/focus=316064
> and here:
> http://thread.gmane.org/gmane.linux.kernel.containers/28301/focus=4239
>
Please, summarize the discussion in your changelog, instead of pointing
to a long thread.
And clearly you missed my question above: how do you get netns id
without sharing /var/run/netns/ ?
^ permalink raw reply
* Re: [PATCH 1/4] Adds general codes to enforces project quota limits
From: Jan Kara @ 2014-09-24 16:08 UTC (permalink / raw)
To: Li Xi
Cc: linux-fsdevel, linux-ext4, linux-api, tytso, adilger, jack, viro,
hch, dmonakhov
In-Reply-To: <1411567470-31799-2-git-send-email-lixi@ddn.com>
On Wed 24-09-14 22:04:27, Li Xi wrote:
> This patch adds support for a new quota type PRJQUOTA for project quota
> enforcement. Also a new method get_projid() is added into dquot_operations
> structure.
>
> Signed-off-by: Li Xi <lixi@ddn.com>
> Signed-off-by: Dmitry Monakhov <dmonakhov@openvz.org>
> ---
> fs/quota/dquot.c | 16 ++++++++++++++--
> fs/quota/quota.c | 5 ++++-
> fs/quota/quotaio_v2.h | 6 ++++--
> include/linux/quota.h | 2 ++
> include/uapi/linux/quota.h | 6 ++++--
> 5 files changed, 28 insertions(+), 7 deletions(-)
>
> diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
> index f2d0eee..8d0c270 100644
> --- a/fs/quota/dquot.c
> +++ b/fs/quota/dquot.c
> @@ -1154,8 +1154,8 @@ static int need_print_warning(struct dquot_warn *warn)
> return uid_eq(current_fsuid(), warn->w_dq_id.uid);
> case GRPQUOTA:
> return in_group_p(warn->w_dq_id.gid);
> - case PRJQUOTA: /* Never taken... Just make gcc happy */
> - return 0;
> + case PRJQUOTA:
> + return 1;
> }
> return 0;
> }
> @@ -1394,6 +1394,9 @@ static void __dquot_initialize(struct inode *inode, int type)
> /* First get references to structures we might need. */
> for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
> struct kqid qid;
> + kprojid_t projid;
> + int rc;
> +
> got[cnt] = NULL;
> if (type != -1 && cnt != type)
> continue;
> @@ -1413,6 +1416,15 @@ static void __dquot_initialize(struct inode *inode, int type)
> case GRPQUOTA:
> qid = make_kqid_gid(inode->i_gid);
> break;
> + case PRJQUOTA:
> + /* Project ID is not supported */
> + if (!inode->i_sb->dq_op->get_projid)
> + continue;
> + rc = inode->i_sb->dq_op->get_projid(inode, &projid);
> + if (rc)
> + continue;
> + qid = make_kqid_projid(projid);
> + break;
> }
> got[cnt] = dqget(sb, qid);
> }
Looking into dquot.c you seem to be missing a change to do_get_dqblk()
where setting of di->d_flags needs to handle PRJQUOTA now as well.
> diff --git a/fs/quota/quota.c b/fs/quota/quota.c
> index 7562164..cce7371 100644
> --- a/fs/quota/quota.c
> +++ b/fs/quota/quota.c
> @@ -30,7 +30,10 @@ static int check_quotactl_permission(struct super_block *sb, int type, int cmd,
> case Q_XGETQSTATV:
> case Q_XQUOTASYNC:
> break;
> - /* allow to query information for dquots we "own" */
> + /*
> + * allow to query information for dquots we "own"
> + * always allow quota check for project quota
The last sentense should probably be:
"always allow querying project quota"
> + */
> case Q_GETQUOTA:
> case Q_XGETQUOTA:
> if ((type == USRQUOTA && uid_eq(current_euid(), make_kuid(current_user_ns(), id))) ||
But looking at the code, you need to add there
|| type == PRJQUOTA
don't you?
Honza
--
Jan Kara <jack@suse.cz>
SUSE Labs, CR
^ permalink raw reply
* Re: [PATCH 1/4] Adds general codes to enforces project quota limits
From: Christoph Hellwig @ 2014-09-24 16:14 UTC (permalink / raw)
To: Li Xi
Cc: linux-fsdevel-u79uwXL29TY76Z2rM5mHXA,
linux-ext4-u79uwXL29TY76Z2rM5mHXA,
linux-api-u79uwXL29TY76Z2rM5mHXA, tytso-3s7WtUTddSA,
adilger-m1MBpc4rdrD3fQ9qLvQP4Q, jack-AlSwsSmVLrQ,
viro-RmSDqhL/yNMiFSDQTTA3OLVCufUGDwFn, hch-wEGCiKHe2LqWVfeAwA7xHQ,
dmonakhov-GEFAQzZX7r8dnm+yROfE0A
In-Reply-To: <1411567470-31799-2-git-send-email-lixi-LfVdkaOWEx8@public.gmane.org>
> -#define MAXQUOTAS 2
> +#define MAXQUOTAS 3
This bloats every single inode in the system. I think this would be a
good opportunity to move i_dquot into the per-filesystem structures
and provide a little get_dquots methods to get at them from the
quota code.
^ permalink raw reply
* Re: [RFC PATCH net-next v2 0/5] netns: allow to identify peer netns
From: Cong Wang @ 2014-09-24 16:15 UTC (permalink / raw)
To: Nicolas Dichtel
Cc: netdev, containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Andy Lutomirski, Stephen Hemminger, Eric W. Biederman,
linux-api-u79uwXL29TY76Z2rM5mHXA, Andrew Morton, David Miller
In-Reply-To: <CAHA+R7NfJYzCsZx0E9YVXKVCQbCm_thPSi+80tix8Z9nVA82Ug-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On Wed, Sep 24, 2014 at 9:01 AM, Cong Wang <cwang-xCSkyg8dI+0RB7SZvlqPiA@public.gmane.org> wrote:
>
> And clearly you missed my question above: how do you get netns id
> without sharing /var/run/netns/ ?
OK, I found it:
> Ids are stored in the parent user namespace. These ids are valid only inside
> this user namespace. The user can retrieve these ids via a new netlink messages,
> but only if peer netns are in the same user namespace.
So your example is confusing, perhaps you need some other way to show the ID's
instead of binding to ip netns output which is basically ls
/var/run/netns/. We don't
want an inner netns know anything outside, IOW, we don't share /var/run/netns/.
I think in this case your ID's are still available, but aren't you
providing a new way
for the inner netns device to escape which we are trying to avoid?
^ permalink raw reply
* Re: [PATCH v5 1/3] drm/rockchip: Add basic drm driver
From: Daniel Kurtz @ 2014-09-24 16:20 UTC (permalink / raw)
To: Mark yao
Cc: Heiko Stübner, Boris BREZILLON, David Airlie, Rob Clark,
Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Randy Dunlap, Grant Likely, Greg Kroah-Hartman, John Stultz,
Rom Lemarchand, devicetree, linux-doc,
linux-kernel@vger.kernel.org, dri-devel, linux-api,
linux-rockchip, Douglas Anderson, Stéphane Marchesin
In-Reply-To: <1411524746-14635-1-git-send-email-mark.yao@rock-chips.com>
Hi Mark,
Please review comments inline...
On Wed, Sep 24, 2014 at 10:12 AM, Mark yao <mark.yao@rock-chips.com> wrote:
> This patch adds the basic structure of a DRM Driver for Rockchip Socs.
>
> Signed-off-by: Mark yao <mark.yao@rock-chips.com>
> ---
> Changes in v2:
> - use the component framework to defer main drm driver probe
> until all VOP devices have been probed.
> - use dma-mapping API with ARM_DMA_USE_IOMMU, create dma mapping by
> master device and each vop device can shared the drm dma mapping.
> - use drm_crtc_init_with_planes and drm_universal_plane_init.
> - remove unnecessary middle layers.
> - add cursor set, move funcs to rockchip drm crtc.
> - use vop reset at first init
> - reference framebuffer when used and unreference when swap out vop
>
> Changes in v3:
> - change "crtc->fb" to "crtc->primary-fb"
> Adviced by Daniel Vetter
> - init cursor plane with universal api, remove unnecessary cursor set,move
>
> Changes in v4:
> Adviced by David Herrmann
> - remove drm_platform_*() usage, use register drm device directly.
> Adviced by Rob Clark
> - remove special mmap ioctl, do userspace mmap with normal mmap() or mmap offset
>
> Changes in v5:
> Adviced by Arnd Bergmann
> - doing DMA start with a 32-bit masks with dma_mask and dma_coherent_mark
> - fix some incorrect dependencies.
> Adviced by Boris BREZILLON
> - fix some mistake and bugs.
> Adviced by Daniel Vetter
> - drop all special ioctl and use generic kms ioctl instead.
> Adviced by Rob Clark
> - use unlocked api for drm_fb_helper_restore_fbdev_mode.
> - remove unused rockchip_gem_prime_import_sg_table.
>
> drivers/gpu/drm/Kconfig | 2 +
> drivers/gpu/drm/Makefile | 1 +
> drivers/gpu/drm/rockchip/Kconfig | 17 +
> drivers/gpu/drm/rockchip/Makefile | 8 +
> drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 509 +++++++++
> drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 120 +++
> drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 201 ++++
> drivers/gpu/drm/rockchip/rockchip_drm_fb.h | 28 +
> drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c | 230 +++++
> drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h | 20 +
> drivers/gpu/drm/rockchip/rockchip_drm_gem.c | 341 ++++++
> drivers/gpu/drm/rockchip/rockchip_drm_gem.h | 55 +
> drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 1373 +++++++++++++++++++++++++
> drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 187 ++++
> 14 files changed, 3092 insertions(+)
> create mode 100644 drivers/gpu/drm/rockchip/Kconfig
> create mode 100644 drivers/gpu/drm/rockchip/Makefile
> create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fb.h
> create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
> create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
> create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_gem.c
> create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_gem.h
> create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index b066bb3..7c4c3c6 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -171,6 +171,8 @@ config DRM_SAVAGE
>
> source "drivers/gpu/drm/exynos/Kconfig"
>
> +source "drivers/gpu/drm/rockchip/Kconfig"
> +
> source "drivers/gpu/drm/vmwgfx/Kconfig"
>
> source "drivers/gpu/drm/gma500/Kconfig"
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index 4a55d59..d03387a 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -52,6 +52,7 @@ obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/
> obj-$(CONFIG_DRM_VIA) +=via/
> obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
> obj-$(CONFIG_DRM_EXYNOS) +=exynos/
> +obj-$(CONFIG_DRM_ROCKCHIP) +=rockchip/
> obj-$(CONFIG_DRM_GMA500) += gma500/
> obj-$(CONFIG_DRM_UDL) += udl/
> obj-$(CONFIG_DRM_AST) += ast/
> diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
> new file mode 100644
> index 0000000..87255f7
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/Kconfig
> @@ -0,0 +1,17 @@
> +config DRM_ROCKCHIP
> + tristate "DRM Support for Rockchip"
> + depends on DRM && ROCKCHIP_IOMMU && ARM_DMA_USE_IOMMU && IOMMU_API
> + select DRM_KMS_HELPER
> + select DRM_KMS_FB_HELPER
> + select DRM_PANEL
> + select FB_CFB_FILLRECT
> + select FB_CFB_COPYAREA
> + select FB_CFB_IMAGEBLIT
> + select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE
> + select VIDEOMODE_HELPERS
> + help
> + Choose this option if you have a Rockchip soc chipset.
> + This driver provides kernel mode setting and buffer
> + management to userspace. This driver does not provides
> + 2D or 3D acceleration; acceleration is performed by other
> + IP found on the SoC.
> diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
> new file mode 100644
> index 0000000..b3a5193
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/Makefile
> @@ -0,0 +1,8 @@
> +#
> +# Makefile for the drm device driver. This driver provides support for the
> +# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
> +
> +rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \
> + rockchip_drm_gem.o rockchip_drm_vop.o
> +
> +obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> new file mode 100644
> index 0000000..0ca4a6b
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> @@ -0,0 +1,509 @@
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:Mark Yao <mark.yao@rock-chips.com>
> + *
> + * based on exynos_drm_drv.c
> + *
> + * 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 <asm/dma-iommu.h>
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_fb_helper.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/of_graph.h>
> +#include <linux/component.h>
> +
> +#include "rockchip_drm_drv.h"
> +#include "rockchip_drm_fb.h"
> +#include "rockchip_drm_fbdev.h"
> +#include "rockchip_drm_gem.h"
> +
> +#define DRIVER_NAME "rockchip"
> +#define DRIVER_DESC "RockChip Soc DRM"
> +#define DRIVER_DATE "20140818"
> +#define DRIVER_MAJOR 1
> +#define DRIVER_MINOR 0
> +
> +/*
> + * Attach a (component) device to the shared drm dma mapping from master drm
> + * device. This is used by the VOPs to map GEM buffers to a common DMA
> + * mapping.
> + */
> +int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
> + struct device *dev)
> +{
> + struct dma_iommu_mapping *mapping = drm_dev->dev->archdata.mapping;
> + int ret;
> +
> + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
> + if (ret)
> + return ret;
> +
> + dma_set_max_seg_size(dev, 0xffffffffu);
> +
> + return arm_iommu_attach_device(dev, mapping);
> +}
> +
> +void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
> + struct device *dev)
> +{
> + arm_iommu_detach_device(drm_dev->dev);
> +}
> +
> +static int rockchip_drm_load(struct drm_device *drm_dev, unsigned long flags)
> +{
> + struct rockchip_drm_private *private;
> + struct dma_iommu_mapping *mapping;
> + struct device *dev = drm_dev->dev;
> + int ret;
> +
> + private = devm_kzalloc(drm_dev->dev, sizeof(*private), GFP_KERNEL);
> + if (!private)
> + return -ENOMEM;
> +
> + drm_dev->dev_private = private;
> +
> + drm_mode_config_init(drm_dev);
> +
> + rockchip_drm_mode_config_init(drm_dev);
> +
> + dev->dma_parms = devm_kzalloc(dev, sizeof(*dev->dma_parms),
> + GFP_KERNEL);
> + if (!dev->dma_parms) {
> + ret = -ENOMEM;
> + goto err_config_cleanup;
> + }
> +
> + /* TODO(djkurtz): fetch the mapping start/size from somewhere */
> + mapping = arm_iommu_create_mapping(&platform_bus_type, 0x10000000,
> + SZ_1G);
> + if (IS_ERR(mapping)) {
> + ret = PTR_ERR(mapping);
> + goto err_config_cleanup;
> + }
> +
> + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
> + if (ret)
> + goto err_config_cleanup;
goto err_release_mapping;
> +
> + dma_set_max_seg_size(dev, 0xffffffffu);
> +
> + ret = arm_iommu_attach_device(dev, mapping);
> + if (ret)
> + goto err_release_mapping;
> +
> + /* Try to bind all sub drivers. */
> + ret = component_bind_all(dev, drm_dev);
> + if (ret)
> + goto err_detach_device;
> +
> + /* init kms poll for handling hpd */
> + drm_kms_helper_poll_init(drm_dev);
> +
> + /*
> + * enable drm irq mode.
> + * - with irq_enabled = true, we can use the vblank feature.
> + */
> + drm_dev->irq_enabled = true;
> +
> + /*
> + * with vblank_disable_allowed = true, vblank interrupt will be disabled
> + * by drm timer once a current process gives up ownership of
> + * vblank event.(after drm_vblank_put function is called)
> + */
> + drm_dev->vblank_disable_allowed = true;
> +
> + ret = drm_vblank_init(drm_dev, ROCKCHIP_MAX_CRTC);
> + if (ret)
> + goto err_kms_helper_poll_fini;
> +
> + rockchip_drm_fbdev_init(drm_dev);
> +
> + /* force connectors detection */
> + drm_helper_hpd_irq_event(drm_dev);
> +
> + return 0;
> +
> +err_kms_helper_poll_fini:
> + drm_kms_helper_poll_fini(drm_dev);
> + component_unbind_all(dev, drm_dev);
> +err_detach_device:
> + arm_iommu_detach_device(dev);
> +err_release_mapping:
> + arm_iommu_release_mapping(dev->archdata.mapping);
> +err_config_cleanup:
> + drm_mode_config_cleanup(drm_dev);
> + drm_dev->dev_private = NULL;
> + return ret;
> +}
> +
> +static int rockchip_drm_unload(struct drm_device *drm_dev)
> +{
> + struct device *dev = drm_dev->dev;
> +
> + drm_kms_helper_poll_fini(drm_dev);
> + component_unbind_all(dev, drm_dev);
> + arm_iommu_detach_device(dev);
> + arm_iommu_release_mapping(dev->archdata.mapping);
> + drm_mode_config_cleanup(drm_dev);
> + drm_dev->dev_private = NULL;
> +
> + return 0;
> +}
> +
> +static int rockchip_drm_suspend(struct drm_device *dev, pm_message_t state)
> +{
> + struct drm_connector *connector;
> +
> + drm_modeset_lock_all(dev);
> + list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> + int old_dpms = connector->dpms;
> +
> + if (connector->funcs->dpms)
> + connector->funcs->dpms(connector, DRM_MODE_DPMS_OFF);
> +
> + /* Set the old mode back to the connector for resume */
> + connector->dpms = old_dpms;
> + }
> + drm_modeset_unlock_all(dev);
> +
> + return 0;
> +}
> +
> +static int rockchip_drm_resume(struct drm_device *dev)
> +{
> + struct drm_connector *connector;
> +
> + drm_modeset_lock_all(dev);
> + list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> + if (connector->funcs->dpms)
> + connector->funcs->dpms(connector, connector->dpms);
> + }
> + drm_modeset_unlock_all(dev);
> +
> + drm_helper_resume_force_mode(dev);
> +
> + return 0;
> +}
> +
> +void rockchip_drm_lastclose(struct drm_device *dev)
> +{
> + struct rockchip_drm_private *priv = dev->dev_private;
> +
> + if (priv->fb_helper)
> + drm_fb_helper_restore_fbdev_mode_unlocked(priv->fb_helper);
> +}
> +
> +static const struct file_operations rockchip_drm_driver_fops = {
> + .owner = THIS_MODULE,
> + .open = drm_open,
> + .mmap = rockchip_drm_gem_mmap,
> + .poll = drm_poll,
> + .read = drm_read,
> + .unlocked_ioctl = drm_ioctl,
> +#ifdef CONFIG_COMPAT
> + .compat_ioctl = drm_compat_ioctl,
> +#endif
> + .release = drm_release,
> +};
> +
> +const struct vm_operations_struct rockchip_drm_vm_ops = {
> + .open = drm_gem_vm_open,
> + .close = drm_gem_vm_close,
> +};
> +
> +static struct drm_driver rockchip_drm_driver = {
> + .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME,
> + .load = rockchip_drm_load,
> + .unload = rockchip_drm_unload,
> + .lastclose = rockchip_drm_lastclose,
> + .suspend = rockchip_drm_suspend,
> + .resume = rockchip_drm_resume,
Looking at drm_sysfs.c, the .suspend/.resume callbacks are only
invoked if the driver does not set DRIVER_MODESET in .driver_features.
Since this driver does have DRIVER_MODESET, let's just drop these two items.
The platform_driver provides its own suspend/resume callbacks, instead.
> + .get_vblank_counter = drm_vblank_count,
> + .enable_vblank = rockchip_drm_crtc_enable_vblank,
> + .disable_vblank = rockchip_drm_crtc_disable_vblank,
> + .gem_vm_ops = &rockchip_drm_vm_ops,
> + .gem_free_object = rockchip_gem_free_object,
> + .dumb_create = rockchip_gem_dumb_create,
> + .dumb_map_offset = rockchip_gem_dumb_map_offset,
> + .dumb_destroy = drm_gem_dumb_destroy,
> + .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
> + .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
> + .gem_prime_import = drm_gem_prime_import,
> + .gem_prime_export = drm_gem_prime_export,
> + .gem_prime_get_sg_table = rockchip_gem_prime_get_sg_table,
> + .gem_prime_vmap = rockchip_gem_prime_vmap,
> + .gem_prime_vunmap = rockchip_gem_prime_vunmap,
> + .gem_prime_mmap = rockchip_gem_prime_mmap,
> + .ioctls = rockchip_ioctls,
> + .num_ioctls = ARRAY_SIZE(rockchip_ioctls),
> + .fops = &rockchip_drm_driver_fops,
> + .name = DRIVER_NAME,
> + .desc = DRIVER_DESC,
> + .date = DRIVER_DATE,
> + .major = DRIVER_MAJOR,
> + .minor = DRIVER_MINOR,
> +};
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int rockchip_drm_sys_suspend(struct device *dev)
> +{
> + struct drm_device *drm_dev = dev_get_drvdata(dev);
> + pm_message_t message;
> +
> + if (pm_runtime_suspended(dev))
> + return 0;
> +
> + message.event = PM_EVENT_SUSPEND;
> +
> + return rockchip_drm_suspend(drm_dev, message);
> +}
> +
> +static int rockchip_drm_sys_resume(struct device *dev)
> +{
> + struct drm_device *drm_dev = dev_get_drvdata(dev);
> +
> + if (pm_runtime_suspended(dev))
> + return 0;
> +
> + return rockchip_drm_resume(drm_dev);
> +}
> +#endif
> +
> +static const struct dev_pm_ops rockchip_drm_pm_ops = {
> + SET_SYSTEM_SLEEP_PM_OPS(rockchip_drm_sys_suspend,
> + rockchip_drm_sys_resume)
> +};
Looks like this is not actually hooked up.
Perhaps you meant to add this to rockchip_drm_platform_driver, see below...
> +
> +int rockchip_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc,
> + struct device_node *np)
> +{
> + struct rockchip_drm_private *priv = drm->dev_private;
> + struct device_node *port;
> + int pipe;
> +
> + if (priv->num_pipe >= ROCKCHIP_MAX_CRTC)
> + return -EINVAL;
> +
> + port = of_get_child_by_name(np, "port");
> + if (!port) {
> + dev_err(drm->dev, "no port node found in %s\n",
> + np->full_name);
> + return -ENXIO;
> + }
> + pipe = priv->num_pipe++;
> + crtc->port = port;
> +
> + priv->crtc[pipe] = crtc;
> +
> + return pipe;
> +}
> +
> +void rockchip_drm_remove_crtc(struct drm_device *drm, int pipe)
> +{
> + struct rockchip_drm_private *priv = drm->dev_private;
> +
> + priv->num_pipe--;
> + of_node_put(priv->crtc[pipe]->port);
> + priv->crtc[pipe] = NULL;
> +}
> +
> +struct drm_crtc *rockchip_find_crtc(struct drm_device *drm, int pipe)
> +{
> + struct rockchip_drm_private *priv = drm->dev_private;
> +
> + if (pipe < ROCKCHIP_MAX_CRTC && priv->crtc[pipe])
> + return priv->crtc[pipe];
> +
> + return NULL;
> +}
> +
> +/*
> + * @node: device tree node containing encoder input ports
> + * @encoder: drm_encoder
> + */
> +int rockchip_drm_encoder_get_mux_id(struct device_node *node,
> + struct drm_encoder *encoder)
> +{
> + struct device_node *ep = NULL;
> + struct drm_crtc *crtc = encoder->crtc;
> + struct of_endpoint endpoint;
> + struct device_node *port;
> + int ret;
> +
> + if (!node || !crtc)
> + return -EINVAL;
> +
> + do {
> + ep = of_graph_get_next_endpoint(node, ep);
> + if (!ep)
> + break;
> +
> + port = of_graph_get_remote_port(ep);
> + of_node_put(port);
> + if (port == crtc->port) {
> + ret = of_graph_parse_endpoint(ep, &endpoint);
> + return ret ? ret : endpoint.id;
nit:
return ret ?: endpoint.id;
> + }
> + } while (ep);
> +
> + return -EINVAL;
> +}
> +
> +static int compare_of(struct device *dev, void *data)
> +{
> + struct device_node *np = data;
> +
> + return dev->of_node == np;
> +}
> +
> +static void rockchip_add_endpoints(struct device *dev,
> + struct component_match **match,
> + struct device_node *port)
> +{
> + struct device_node *ep, *remote;
> +
> + for_each_child_of_node(port, ep) {
> + remote = of_graph_get_remote_port_parent(ep);
> + if (!remote || !of_device_is_available(remote)) {
> + of_node_put(remote);
> + continue;
> + } else if (!of_device_is_available(remote->parent)) {
> + dev_warn(dev, "parent device of %s is not available\n",
> + remote->full_name);
> + of_node_put(remote);
> + continue;
> + }
> +
> + component_match_add(dev, match, compare_of, remote);
> + of_node_put(remote);
> + }
> +}
> +
> +static int rockchip_drm_bind(struct device *dev)
> +{
> + struct drm_device *drm;
> + int ret;
> +
> + drm = drm_dev_alloc(&rockchip_drm_driver, dev);
> + if (!drm)
> + return -ENOMEM;
> +
> + ret = drm_dev_set_unique(drm, "%s", dev_name(dev));
> + if (ret)
> + goto err_free;
> +
> + ret = drm_dev_register(drm, 0);
> + if (ret)
> + goto err_free;
> +
> + dev_set_drvdata(dev, drm);
> +
> + return 0;
> +
> +err_free:
> + drm_dev_unref(drm);
> + return ret;
> +}
> +
> +static void rockchip_drm_unbind(struct device *dev)
> +{
> + struct drm_device *drm = dev_get_drvdata(dev);
> +
> + drm_dev_unregister(drm);
> + drm_dev_unref(drm);
> +}
> +
> +static const struct component_master_ops rockchip_drm_ops = {
> + .bind = rockchip_drm_bind,
> + .unbind = rockchip_drm_unbind,
> +};
> +
> +static int rockchip_drm_platform_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct component_match *match = NULL;
> + struct device_node *np = dev->of_node;
> + struct device_node *port;
> + int i;
> + int ret;
> +
> + if (!np)
> + return -ENODEV;
> + /*
> + * Bind the crtc ports first, so that
> + * drm_of_find_possible_crtcs called from encoder .bind callbacks
> + * works as expected.
> + */
> + for (i = 0;; i++) {
> + port = of_parse_phandle(np, "ports", i);
> + if (!port)
> + break;
I think you want to make sure that the parent node of this 'port' (ie,
the VOP node) is not disabled...
if (!of_device_is_available(port->parent)) {
of_node_put(port);
continue;
}
> +
> + component_match_add(dev, &match, compare_of, port->parent);
> + of_node_put(port);
> + }
> +
> + if (i == 0) {
> + dev_err(dev, "missing 'ports' property\n");
> + return -ENODEV;
> + }
> + /*
> + * For each bound crtc, bind the encoders attached to its
> + * remote endpoint.
> + */
> + for (i = 0;; i++) {
> + port = of_parse_phandle(np, "ports", i);
> + if (!port)
> + break;
> +
Here, too:
if (!of_device_is_available(port->parent)) {
of_node_put(port);
continue;
}
> + rockchip_add_endpoints(dev, &match, port);
> + of_node_put(port);
> + }
> +
> + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
> + if (ret)
> + return ret;
> +
> + return component_master_add_with_match(dev, &rockchip_drm_ops, match);
> +}
> +
> +static int rockchip_drm_platform_remove(struct platform_device *pdev)
> +{
> + component_master_del(&pdev->dev, &rockchip_drm_ops);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id rockchip_drm_dt_ids[] = {
> + { .compatible = "rockchip,display-subsystem", },
> + { /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, rockchip_drm_dt_ids);
> +
> +static struct platform_driver rockchip_drm_platform_driver = {
> + .probe = rockchip_drm_platform_probe,
> + .remove = rockchip_drm_platform_remove,
> + .driver = {
> + .owner = THIS_MODULE,
> + .name = "rockchip-drm",
> + .of_match_table = rockchip_drm_dt_ids,
.pm = &rockchip_drm_pm_ops,
> + },
> +};
> +
> +module_platform_driver(rockchip_drm_platform_driver);
> +
> +MODULE_AUTHOR("Mark Yao <mark.yao@rock-chips.com>");
> +MODULE_DESCRIPTION("ROCKCHIP DRM Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> new file mode 100644
> index 0000000..154b3ec
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> @@ -0,0 +1,120 @@
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:Mark Yao <mark.yao@rock-chips.com>
> + *
> + * based on exynos_drm_drv.h
> + *
> + * 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.
> + */
> +
> +#ifndef _ROCKCHIP_DRM_DRV_H
> +#define _ROCKCHIP_DRM_DRV_H
> +
> +#include <linux/module.h>
> +#include <linux/component.h>
> +
> +#define ROCKCHIP_MAX_FB_BUFFER 4
This is only used to size the array of gem object pointers used by a
framebuffer .
What framebuffer pixel format uses 4 planes? I think the max is 3?
> +#define ROCKCHIP_MAX_CONNECTOR 2
> +
> +struct drm_device;
> +struct drm_connector;
> +
> +/*
> + * display output interface supported by rockchip lcdc
s/lcdc/vop
> + */
> +#define ROCKCHIP_OUTFACE_P888 0
> +#define ROCKCHIP_OUTFACE_P666 1
> +#define ROCKCHIP_OUTFACE_P565 2
> +/* for use special outface */
> +#define ROCKCHIP_OUTFACE_AAAA 15
"outface" is creative. It is actually an English word that means "to
disconcert an opponent by bold confrontation", I do not think that is
what you mean here :-).
Rename these to ROCKCHIP_OUT_MODE_, to be consistent with the
corresponding field (dsp_out_mode) of the VOP_DSP_CTRL0 register to
which these are written.
Also, since these are register values written to VOP_DSP_CTRL0, so
move them to the VOP header file.
> +
> +#define ROCKCHIP_COLOR_SWAP_RG 0x1
> +#define ROCKCHIP_COLOR_SWAP_RB 0x2
> +#define ROCKCHIP_COLOR_SWAP_GB 0x4
> +
> +/*
> + * Special mode info for rockchip
> + *
> + * @out_type: lcd controller need to know the sceen type.
> + */
> +struct rockchip_display_mode {
> + int out_type;
> +};
> +
> +#define ROCKCHIP_EVENT_HOTPLUG 1
> +
> +enum rockchip_plane_type {
> + ROCKCHIP_WIN0,
> + ROCKCHIP_WIN1,
> + ROCKCHIP_WIN2,
> + ROCKCHIP_WIN3,
> + ROCKCHIP_CURSOR,
> + ROCKCHIP_MAX_PLANE,
> +};
> +
> +/* This enumerates device type. */
> +enum rockchip_drm_device_type {
> + ROCKCHIP_DEVICE_TYPE_NONE,
> + ROCKCHIP_DEVICE_TYPE_CRTC,
> + ROCKCHIP_DEVICE_TYPE_CONNECTOR,
> +};
> +
> +/* this enumerates display type. */
> +enum rockchip_drm_output_type {
> + ROCKCHIP_DISPLAY_TYPE_NONE = 0,
To match the enum name, use ROCKCHIP_OUTPUT_TYPE_*.
Also, no need to explicitly set the first one to 0.
However, see below. I don't think we to modify the drm_display_mode
to include an output type.
> + /* RGB Interface. */
> + ROCKCHIP_DISPLAY_TYPE_RGB,
> + /* LVDS Interface. */
> + ROCKCHIP_DISPLAY_TYPE_LVDS,
> + /* EDP Interface. */
> + ROCKCHIP_DISPLAY_TYPE_EDP,
> + /* MIPI Interface. */
> + ROCKCHIP_DISPLAY_TYPE_MIPI,
> + /* HDMI Interface. */
> + ROCKCHIP_DISPLAY_TYPE_HDMI,
> +};
> +
> +enum rockchip_crtc_type {
> + ROCKCHIP_CRTC_VOPB,
> + ROCKCHIP_CRTC_VOPL,
> + ROCKCHIP_MAX_CRTC,
> +};
None of the following defined above are used and they can all be removed:
ROCKCHIP_EVENT_HOTPLUG
rockchip_plane_type
rockchip_drm_device_type
ROCKCHIP_COLOR_SWAP_*
rockchip_crtc_type
> +
> +/*
> + * Rockchip drm private structure.
> + *
> + * @num_pipe: number of pipes for this device.
> + */
> +struct rockchip_drm_private {
> + struct drm_fb_helper *fb_helper;
> + /*
> + * created crtc object would be contained at this array and
> + * this array is used to be aware of which crtc did it request vblank.
> + */
Move this comment up to the struct comment, above "num_pipe".
@crtc: array of enabled CRTCs, used to map from "pipe" to drm_crtc
> + struct drm_crtc *crtc[ROCKCHIP_MAX_CRTC];
> +
> + unsigned int num_pipe;
> +};
> +
> +int rockchip_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc,
> + struct device_node *port);
> +void rockchip_drm_remove_crtc(struct drm_device *drm, int pipe);
> +struct drm_crtc *rockchip_find_crtc(struct drm_device *drm, int pipe);
For consistency, this should probably be "rockchip_drm_find_crtc",
although you can probably drop the "_drm" from all of these function
names if you wanted to make them shorter.
Or, perhaps use: rk_drm_*()
> +int rockchip_drm_encoder_get_mux_id(struct device_node *node,
> + struct drm_encoder *encoder);
> +void rockchip_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe);
> +void rockchip_drm_crtc_cancel_pending_flip(struct drm_device *dev);
> +int rockchip_drm_crtc_enable_vblank(struct drm_device *dev, int pipe);
> +void rockchip_drm_crtc_disable_vblank(struct drm_device *dev, int pipe);
> +int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
> + struct device *dev);
> +void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
> + struct device *dev);
A blank line here helps.
> +#endif /* _ROCKCHIP_DRM_DRV_H_ */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> new file mode 100644
> index 0000000..482f7b8
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> @@ -0,0 +1,201 @@
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:Mark Yao <mark.yao@rock-chips.com>
> + *
> + * 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/kernel.h>
> +#include <drm/drm.h>
> +#include <drm/drmP.h>
> +#include <drm/drm_fb_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +
> +#include "rockchip_drm_drv.h"
> +#include "rockchip_drm_gem.h"
> +
> +#define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
> +
> +struct rockchip_drm_fb {
> + struct drm_framebuffer fb;
> + struct drm_gem_object *obj[ROCKCHIP_MAX_FB_BUFFER];
> +};
> +
> +struct drm_gem_object *rockchip_fb_get_gem_obj(struct drm_framebuffer *fb,
> + unsigned int plane)
> +{
> + struct rockchip_drm_fb *rk_fb = to_rockchip_fb(fb);
> +
> + if (plane >= ROCKCHIP_MAX_FB_BUFFER)
> + return NULL;
> +
> + return rk_fb->obj[plane];
> +}
> +
> +static void rockchip_drm_fb_destroy(struct drm_framebuffer *fb)
> +{
> + struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb);
> + struct drm_gem_object *obj;
> + int i;
> +
> + for (i = 0; i < ROCKCHIP_MAX_FB_BUFFER; i++) {
> + obj = rockchip_fb->obj[i];
> + if (obj)
> + drm_gem_object_unreference_unlocked(obj);
> + }
> +
> + drm_framebuffer_cleanup(fb);
> + kfree(rockchip_fb);
> +}
> +
> +static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb,
> + struct drm_file *file_priv,
> + unsigned int *handle)
> +{
> + struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb);
> +
> + return drm_gem_handle_create(file_priv,
> + rockchip_fb->obj[0], handle);
> +}
> +
> +static struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
> + .destroy = rockchip_drm_fb_destroy,
> + .create_handle = rockchip_drm_fb_create_handle,
> +};
> +
> +static struct rockchip_drm_fb *
> +rockchip_fb_alloc(struct drm_device *dev, struct drm_mode_fb_cmd2 *mode_cmd,
> + struct drm_gem_object **obj, unsigned int num_planes)
> +{
> + struct rockchip_drm_fb *rockchip_fb;
> + int ret;
> + int i;
> +
> + rockchip_fb = kzalloc(sizeof(*rockchip_fb), GFP_KERNEL);
> + if (!rockchip_fb)
> + return ERR_PTR(-ENOMEM);
> +
> + drm_helper_mode_fill_fb_struct(&rockchip_fb->fb, mode_cmd);
> +
> + for (i = 0; i < num_planes; i++)
> + rockchip_fb->obj[i] = obj[i];
> +
> + ret = drm_framebuffer_init(dev, &rockchip_fb->fb,
> + &rockchip_drm_fb_funcs);
> + if (ret) {
> + dev_err(dev->dev, "Failed to initialize framebuffer: %d\n",
> + ret);
> + kfree(rockchip_fb);
> + return ERR_PTR(ret);
> + }
> +
> + return rockchip_fb;
> +}
> +
> +static struct drm_framebuffer *
> +rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
> + struct drm_mode_fb_cmd2 *mode_cmd)
> +{
> + struct rockchip_drm_fb *rockchip_fb;
> + struct drm_gem_object *objs[ROCKCHIP_MAX_FB_BUFFER];
> + struct drm_gem_object *obj;
> + unsigned int hsub;
> + unsigned int vsub;
> + int num_planes;
> + int ret;
> + int i;
> +
> + hsub = drm_format_horz_chroma_subsampling(mode_cmd->pixel_format);
> + vsub = drm_format_vert_chroma_subsampling(mode_cmd->pixel_format);
> + num_planes = min(drm_format_num_planes(mode_cmd->pixel_format),
> + ROCKCHIP_MAX_FB_BUFFER);
> +
> + for (i = 0; i < num_planes; i++) {
> + unsigned int width = mode_cmd->width / (i ? hsub : 1);
> + unsigned int height = mode_cmd->height / (i ? vsub : 1);
> + unsigned int min_size;
> +
> + obj = drm_gem_object_lookup(dev, file_priv,
> + mode_cmd->handles[i]);
> + if (!obj) {
> + dev_err(dev->dev, "Failed to lookup GEM object\n");
> + ret = -ENXIO;
> + goto err_gem_object_unreference;
> + }
> +
> + min_size = (height - 1) * mode_cmd->pitches[i] +
> + mode_cmd->offsets[i] +
> + width * drm_format_plane_cpp(mode_cmd->pixel_format, i);
> +
> + if (obj->size < min_size) {
> + drm_gem_object_unreference_unlocked(obj);
> + ret = -EINVAL;
> + goto err_gem_object_unreference;
> + }
> + objs[i] = obj;
> + }
> +
> + rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, objs, i);
> + if (IS_ERR(rockchip_fb)) {
> + ret = PTR_ERR(rockchip_fb);
> + goto err_gem_object_unreference;
> + }
> +
> + return &rockchip_fb->fb;
> +
> +err_gem_object_unreference:
> + for (i--; i >= 0; i--)
> + drm_gem_object_unreference_unlocked(objs[i]);
> + return ERR_PTR(ret);
> +}
> +
> +static void rockchip_drm_output_poll_changed(struct drm_device *dev)
> +{
> + struct rockchip_drm_private *private = dev->dev_private;
> + struct drm_fb_helper *fb_helper = private->fb_helper;
> +
> + if (fb_helper)
> + drm_fb_helper_hotplug_event(fb_helper);
> +}
> +
> +static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = {
> + .fb_create = rockchip_user_fb_create,
> + .output_poll_changed = rockchip_drm_output_poll_changed,
> +};
> +
> +struct drm_framebuffer *
> +rockchip_drm_framebuffer_init(struct drm_device *dev,
> + struct drm_mode_fb_cmd2 *mode_cmd,
> + struct drm_gem_object *obj)
> +{
> + struct rockchip_drm_fb *rockchip_fb;
> +
> + rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, &obj, 1);
> + if (IS_ERR(rockchip_fb))
> + return NULL;
> +
> + return &rockchip_fb->fb;
> +}
> +
> +void rockchip_drm_mode_config_init(struct drm_device *dev)
> +{
> + dev->mode_config.min_width = 0;
> + dev->mode_config.min_height = 0;
> +
> + /*
> + * set max width and height as default value(4096x4096).
> + * this value would be used to check framebuffer size limitation
> + * at drm_mode_addfb().
> + */
> + dev->mode_config.max_width = 4096;
> + dev->mode_config.max_height = 4096;
> +
> + dev->mode_config.funcs = &rockchip_drm_mode_config_funcs;
> +}
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.h b/drivers/gpu/drm/rockchip/rockchip_drm_fb.h
> new file mode 100644
> index 0000000..09574d4
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.h
> @@ -0,0 +1,28 @@
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:Mark Yao <mark.yao@rock-chips.com>
> + *
> + * 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.
> + */
> +
> +#ifndef _ROCKCHIP_DRM_FB_H
> +#define _ROCKCHIP_DRM_FB_H
> +
> +struct drm_framebuffer *
> +rockchip_drm_framebuffer_init(struct drm_device *dev,
> + struct drm_mode_fb_cmd2 *mode_cmd,
> + struct drm_gem_object *obj);
> +void rockchip_drm_framebuffer_fini(struct drm_framebuffer *fb);
> +
> +void rockchip_drm_mode_config_init(struct drm_device *dev);
> +
> +struct drm_gem_object *rockchip_fb_get_gem_obj(struct drm_framebuffer *fb,
> + unsigned int plane);
> +#endif /* _ROCKCHIP_DRM_FB_H */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
> new file mode 100644
> index 0000000..ae563f5
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
> @@ -0,0 +1,230 @@
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:Mark Yao <mark.yao@rock-chips.com>
> + *
> + * 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 <drm/drm.h>
> +#include <drm/drmP.h>
> +#include <drm/drm_fb_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +
> +#include "rockchip_drm_drv.h"
> +#include "rockchip_drm_gem.h"
> +#include "rockchip_drm_fb.h"
> +
> +#define PREFERRED_BPP 32
> +#define to_rockchip_fbdev(x) container_of(x, struct rockchip_fbdev, helper)
> +
> +struct rockchip_fbdev {
> + struct drm_fb_helper helper;
> + struct drm_gem_object *bo;
> +};
> +
> +static int rockchip_fbdev_mmap(struct fb_info *info,
> + struct vm_area_struct *vma)
> +{
> + struct drm_fb_helper *helper = info->par;
> + struct rockchip_fbdev *fbdev = to_rockchip_fbdev(helper);
> +
> + return rockchip_gem_mmap(fbdev->bo, vma);
> +}
> +
> +static struct fb_ops rockchip_drm_fbdev_ops = {
> + .owner = THIS_MODULE,
> + .fb_mmap = rockchip_fbdev_mmap,
> + .fb_fillrect = cfb_fillrect,
> + .fb_copyarea = cfb_copyarea,
> + .fb_imageblit = cfb_imageblit,
> + .fb_check_var = drm_fb_helper_check_var,
> + .fb_set_par = drm_fb_helper_set_par,
> + .fb_blank = drm_fb_helper_blank,
> + .fb_pan_display = drm_fb_helper_pan_display,
> + .fb_setcmap = drm_fb_helper_setcmap,
> +};
> +
> +static int rockchip_drm_fbdev_create(struct drm_fb_helper *helper,
> + struct drm_fb_helper_surface_size *sizes)
> +{
> + struct rockchip_fbdev *fbdev = to_rockchip_fbdev(helper);
> + struct drm_mode_fb_cmd2 mode_cmd = { 0 };
> + struct drm_device *dev = helper->dev;
> + struct rockchip_gem_object *rk_obj;
> + struct drm_framebuffer *fb;
> + unsigned int bytes_per_pixel;
> + unsigned long offset;
> + struct fb_info *fbi;
> + size_t size;
> + int ret;
> +
> + bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8);
> +
> + mode_cmd.width = sizes->surface_width;
> + mode_cmd.height = sizes->surface_height;
> + mode_cmd.pitches[0] = sizes->surface_width * bytes_per_pixel;
> + mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
> + sizes->surface_depth);
> +
> + size = mode_cmd.pitches[0] * mode_cmd.height;
> +
> + rk_obj = rockchip_gem_create_object(dev, size);
> + if (IS_ERR(rk_obj))
> + return -ENOMEM;
> +
> + fbdev->bo = &rk_obj->base;
> +
> + fbi = framebuffer_alloc(0, dev->dev);
> + if (!fbi) {
> + dev_err(dev->dev, "Failed to allocate framebuffer info.\n");
> + ret = -ENOMEM;
> + goto err_rockchip_gem_free_object;
> + }
> +
> + helper->fb = rockchip_drm_framebuffer_init(dev, &mode_cmd, fbdev->bo);
> + if (IS_ERR(helper->fb)) {
> + dev_err(dev->dev, "Failed to allocate DRM framebuffer.\n");
> + ret = PTR_ERR(helper->fb);
> + goto err_framebuffer_release;
> + }
> +
> + helper->fbdev = fbi;
> +
> + fbi->par = helper;
> + fbi->flags = FBINFO_FLAG_DEFAULT;
> + fbi->fbops = &rockchip_drm_fbdev_ops;
> +
> + ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
> + if (ret) {
> + dev_err(dev->dev, "Failed to allocate color map.\n");
> + goto err_drm_framebuffer_unref;
> + }
> +
> + fb = helper->fb;
> + drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);
> + drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height);
> +
> + offset = fbi->var.xoffset * bytes_per_pixel;
> + offset += fbi->var.yoffset * fb->pitches[0];
> +
> + dev->mode_config.fb_base = 0;
> + fbi->screen_base = rk_obj->kvaddr + offset;
> + fbi->screen_size = rk_obj->base.size;
> + fbi->fix.smem_len = rk_obj->base.size;
> +
> + DRM_DEBUG_KMS("FB [%dx%d]-%d kvaddr=%p offset=%ld size=%d\n",
> + fb->width, fb->height, fb->depth, rk_obj->kvaddr,
> + offset, size);
> + return 0;
> +
> +err_drm_framebuffer_unref:
> + drm_framebuffer_unreference(helper->fb);
> +err_framebuffer_release:
> + framebuffer_release(fbi);
> +err_rockchip_gem_free_object:
> + rockchip_gem_free_object(&rk_obj->base);
> + return ret;
> +}
> +
> +static struct drm_fb_helper_funcs rockchip_drm_fb_helper_funcs = {
> + .fb_probe = rockchip_drm_fbdev_create,
> +};
> +
> +int rockchip_drm_fbdev_init(struct drm_device *dev)
> +{
> + struct rockchip_drm_private *private = dev->dev_private;
> + struct rockchip_fbdev *fbdev;
> + struct drm_fb_helper *helper;
> + unsigned int num_crtc;
> + int ret;
> +
> + if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector)
> + return -EINVAL;
> +
> + if (private->fb_helper) {
> + DRM_ERROR("no allow to reinit fbdev\n");
> + return -EINVAL;
> + }
> +
> + num_crtc = dev->mode_config.num_crtc;
> +
> + fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
> + if (!fbdev)
> + return -ENOMEM;
> +
> + fbdev->helper.funcs = &rockchip_drm_fb_helper_funcs;
> + helper = &fbdev->helper;
> +
> + ret = drm_fb_helper_init(dev, helper, num_crtc, ROCKCHIP_MAX_CONNECTOR);
> + if (ret < 0) {
> + dev_err(dev->dev, "Failed to initialize drm fb helper.\n");
> + goto err_free;
> + }
> +
> + ret = drm_fb_helper_single_add_all_connectors(helper);
> + if (ret < 0) {
> + dev_err(dev->dev, "Failed to add connectors.\n");
> + goto err_drm_fb_helper_fini;
> + }
> +
> + /* disable all the possible outputs/crtcs before entering KMS mode */
> + drm_helper_disable_unused_functions(dev);
> +
> + ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP);
> + if (ret < 0) {
> + dev_err(dev->dev, "Failed to set initial hw configuration.\n");
> + goto err_drm_fb_helper_fini;
> + }
> +
> + private->fb_helper = helper;
> +
> + return 0;
> +
> +err_drm_fb_helper_fini:
> + drm_fb_helper_fini(helper);
> +err_free:
> + kfree(fbdev);
> + return ret;
> +}
> +
> +void rockchip_drm_fbdev_fini(struct drm_device *dev)
> +{
> + struct rockchip_drm_private *private = dev->dev_private;
> + struct drm_fb_helper *helper;
> + struct rockchip_fbdev *fbdev;
> +
> + if (!private || !private->fb_helper)
> + return;
> +
> + helper = private->fb_helper;
> + fbdev = to_rockchip_fbdev(helper);
> +
> + if (helper->fbdev) {
> + struct fb_info *info;
> + int ret;
> +
> + info = helper->fbdev;
> + ret = unregister_framebuffer(info);
> + if (ret < 0)
> + DRM_DEBUG_KMS("failed unregister_framebuffer()\n");
> +
> + if (info->cmap.len)
> + fb_dealloc_cmap(&info->cmap);
> +
> + framebuffer_release(info);
> + }
> +
> + if (helper->fb)
> + drm_framebuffer_unreference(helper->fb);
> +
> + drm_fb_helper_fini(helper);
> + kfree(fbdev);
> + private->fb_helper = NULL;
> +}
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
> new file mode 100644
> index 0000000..5edcf6a
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
> @@ -0,0 +1,20 @@
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:Mark Yao <mark.yao@rock-chips.com>
> + *
> + * 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.
> + */
> +
> +#ifndef _ROCKCHIP_DRM_FBDEV_H
> +#define _ROCKCHIP_DRM_FBDEV_H
> +
> +int rockchip_drm_fbdev_init(struct drm_device *dev);
> +
> +#endif /* _ROCKCHIP_DRM_FBDEV_H */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
> new file mode 100644
> index 0000000..8e7a91c
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
> @@ -0,0 +1,341 @@
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:Mark Yao <mark.yao@rock-chips.com>
> + *
> + * 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 <drm/drm.h>
> +#include <drm/drmP.h>
> +#include <drm/drm_vma_manager.h>
> +
> +#include <linux/anon_inodes.h>
> +#include <linux/dma-attrs.h>
> +
> +#include "rockchip_drm_drv.h"
> +#include "rockchip_drm_gem.h"
> +
> +static int rockchip_gem_alloc_buf(struct rockchip_gem_object *rk_obj)
> +{
> + struct drm_gem_object *obj = &rk_obj->base;
> + struct drm_device *drm = obj->dev;
> +
> + init_dma_attrs(&rk_obj->dma_attrs);
> + dma_set_attr(DMA_ATTR_WRITE_COMBINE, &rk_obj->dma_attrs);
> +
> + /* TODO(djkurtz): Use DMA_ATTR_NO_KERNEL_MAPPING except for fbdev */
> + rk_obj->kvaddr = dma_alloc_attrs(drm->dev, obj->size,
> + &rk_obj->dma_addr, GFP_KERNEL,
> + &rk_obj->dma_attrs);
> + if (IS_ERR(rk_obj->kvaddr)) {
> + int ret = PTR_ERR(rk_obj->kvaddr);
> +
> + DRM_ERROR("failed to allocate %#x byte dma buffer, %d",
> + obj->size, ret);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static void rockchip_gem_free_buf(struct rockchip_gem_object *rk_obj)
> +{
> + struct drm_gem_object *obj = &rk_obj->base;
> + struct drm_device *drm = obj->dev;
> +
> + dma_free_attrs(drm->dev, obj->size, rk_obj->kvaddr, rk_obj->dma_addr,
> + &rk_obj->dma_attrs);
> +}
> +
> +/* drm driver mmap file operations */
> +int rockchip_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
> +{
> + struct drm_file *priv = filp->private_data;
> + struct drm_device *dev = priv->minor->dev;
> + struct drm_gem_object *obj;
> + struct drm_vma_offset_node *node;
> + int ret;
> +
> + if (drm_device_is_unplugged(dev))
> + return -ENODEV;
> +
> + mutex_lock(&dev->struct_mutex);
> +
> + node = drm_vma_offset_exact_lookup(dev->vma_offset_manager,
> + vma->vm_pgoff,
> + vma_pages(vma));
> + if (!node) {
> + mutex_unlock(&dev->struct_mutex);
> + DRM_ERROR("failed to find vma node.\n");
> + return -EINVAL;
> + } else if (!drm_vma_node_is_allowed(node, filp)) {
> + mutex_unlock(&dev->struct_mutex);
> + return -EACCES;
> + }
> +
> + obj = container_of(node, struct drm_gem_object, vma_node);
> + ret = rockchip_gem_mmap(obj, vma);
> +
> + mutex_unlock(&dev->struct_mutex);
> +
> + return ret;
> +}
> +
> +int rockchip_drm_gem_mmap_buffer(struct file *filp,
> + struct vm_area_struct *vma)
> +{
> + struct drm_gem_object *obj = filp->private_data;
> +
> + return rockchip_gem_mmap(obj, vma);
> +}
> +
> +static const struct file_operations rockchip_drm_gem_fops = {
> + .mmap = rockchip_drm_gem_mmap_buffer,
> +};
> +
> +struct rockchip_gem_object *
> + rockchip_gem_create_object(struct drm_device *drm, unsigned int size)
> +{
> + struct rockchip_gem_object *rk_obj;
> + struct drm_gem_object *obj;
> + struct file *filp;
> + int ret;
> +
> + size = round_up(size, PAGE_SIZE);
> +
> + rk_obj = kzalloc(sizeof(*rk_obj), GFP_KERNEL);
> + if (!rk_obj)
> + return ERR_PTR(-ENOMEM);
> +
> + obj = &rk_obj->base;
> +
> + drm_gem_private_object_init(drm, obj, size);
> +
> + filp = anon_inode_getfile("rockchip_gem", &rockchip_drm_gem_fops,
> + obj, 0);
> + if (IS_ERR(filp)) {
> + DRM_ERROR("failed to create anon file object.\n");
> + ret = PTR_ERR(filp);
> + goto err_free_rk_obj;
> + }
> + filp->f_mode = FMODE_READ | FMODE_WRITE;
> + obj->filp = filp;
> +
> + ret = drm_gem_create_mmap_offset(obj);
> + if (ret)
> + goto err_free_obj;
> +
> + ret = rockchip_gem_alloc_buf(rk_obj);
> + if (ret)
> + goto err_free_mmap_offset;
> +
> + return rk_obj;
> +
> +err_free_mmap_offset:
> + drm_gem_free_mmap_offset(obj);
> +err_free_obj:
> + drm_gem_object_release(obj);
> +err_free_rk_obj:
> + kfree(rk_obj);
> + return ERR_PTR(ret);
> +}
> +
> +/*
> + * rockchip_gem_free_object - (struct drm_driver)->gem_free_object callback
> + * function
> + */
> +void rockchip_gem_free_object(struct drm_gem_object *obj)
> +{
> + struct rockchip_gem_object *rk_obj;
> +
> + drm_gem_free_mmap_offset(obj);
> +
> + rk_obj = to_rockchip_obj(obj);
> +
> + rockchip_gem_free_buf(rk_obj);
> + drm_gem_free_mmap_offset(obj);
> +
> + drm_gem_object_release(obj);
> +
> + kfree(rk_obj);
> +}
> +
> +int rockchip_gem_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
> +{
> + struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
> + struct drm_device *drm = obj->dev;
> + unsigned long vm_size;
> +
> + vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
> + vm_size = vma->vm_end - vma->vm_start;
> +
> + if (vm_size > obj->size)
> + return -EINVAL;
> +
> + return dma_mmap_attrs(drm->dev, vma, rk_obj->kvaddr, rk_obj->dma_addr,
> + obj->size, &rk_obj->dma_attrs);
> +}
> +
> +/*
> + * rockchip_gem_create_with_handle - allocate an object with the given
> + * size and create a gem handle on it
> + *
> + * returns a struct rockchip_gem_object* on success or ERR_PTR values
> + * on failure.
> + */
> +static struct rockchip_gem_object *
> +rockchip_gem_create_with_handle(struct drm_file *file_priv,
> + struct drm_device *drm, unsigned int size,
> + unsigned int *handle)
> +{
> + struct rockchip_gem_object *rk_obj;
> + struct drm_gem_object *obj;
> + int ret;
> +
> + rk_obj = rockchip_gem_create_object(drm, size);
> + if (IS_ERR(rk_obj))
> + return NULL;
> +
> + obj = &rk_obj->base;
> +
> + /*
> + * allocate a id of idr table where the obj is registered
> + * and handle has the id what user can see.
> + */
> + ret = drm_gem_handle_create(file_priv, obj, handle);
> + if (ret)
> + goto err_handle_create;
> +
> + /* drop reference from allocate - handle holds it now. */
> + drm_gem_object_unreference_unlocked(obj);
> +
> + return rk_obj;
> +
> +err_handle_create:
> + rockchip_gem_free_object(obj);
> +
> + return ERR_PTR(ret);
> +}
> +
> +int rockchip_gem_dumb_map_offset(struct drm_file *file_priv,
> + struct drm_device *dev, uint32_t handle,
> + uint64_t *offset)
> +{
> + struct drm_gem_object *obj;
> + int ret = 0;
> +
> + mutex_lock(&dev->struct_mutex);
> +
> + /*
> + * get offset of memory allocated for drm framebuffer.
> + * - this callback would be called by user application
> + * with DRM_IOCTL_MODE_MAP_DUMB command.
> + */
> +
> + obj = drm_gem_object_lookup(dev, file_priv, handle);
> + if (!obj) {
> + DRM_ERROR("failed to lookup gem object.\n");
> + ret = -EINVAL;
> + goto unlock;
> + }
> +
> + ret = drm_gem_create_mmap_offset(obj);
> + if (ret)
> + goto out;
> +
> + *offset = drm_vma_node_offset_addr(&obj->vma_node);
> + DRM_DEBUG_KMS("offset = 0x%lx\n", (unsigned long)*offset);
> +
> +out:
> + drm_gem_object_unreference(obj);
> +unlock:
> + mutex_unlock(&dev->struct_mutex);
> + return ret;
> +}
> +
> +/*
> + * rockchip_gem_dumb_create - (struct drm_driver)->dumb_create callback
> + * function
> + *
> + * This aligns the pitch and size arguments to the minimum required. wrap
> + * this into your own function if you need bigger alignment.
> + */
> +int rockchip_gem_dumb_create(struct drm_file *file_priv,
> + struct drm_device *dev,
> + struct drm_mode_create_dumb *args)
> +{
> + struct rockchip_gem_object *rk_obj;
> + int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
> +
> + if (args->pitch < min_pitch)
> + args->pitch = min_pitch;
> +
> + if (args->size < args->pitch * args->height)
> + args->size = args->pitch * args->height;
> +
> + rk_obj = rockchip_gem_create_with_handle(file_priv, dev, args->size,
> + &args->handle);
> +
> + return PTR_ERR_OR_ZERO(rk_obj);
> +}
> +
> +/*
> + * Allocate a sg_table for this GEM object.
> + * Note: Both the table's contents, and the sg_table itself must be freed by
> + * the caller.
> + * Returns a pointer to the newly allocated sg_table, or an ERR_PTR() error.
> + */
> +struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj)
> +{
> + struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
> + struct drm_device *drm = obj->dev;
> + struct sg_table *sgt = NULL;
> + int ret;
> +
> + sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
> + if (!sgt)
> + return ERR_PTR(-ENOMEM);
> +
> + ret = dma_get_sgtable_attrs(drm->dev, sgt, rk_obj->kvaddr,
> + rk_obj->dma_addr, obj->size,
> + &rk_obj->dma_attrs);
> + if (ret) {
> + DRM_ERROR("failed to allocate sgt, %d\n", ret);
> + kfree(sgt);
> + return ERR_PTR(ret);
> + }
> +
> + return sgt;
> +}
> +
> +void *rockchip_gem_prime_vmap(struct drm_gem_object *obj)
> +{
> + struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
> +
> + return rk_obj->kvaddr;
> +}
> +
> +void rockchip_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
> +{
> + /* Nothing to do */
> +}
> +
> +int rockchip_gem_prime_mmap(struct drm_gem_object *obj,
> + struct vm_area_struct *vma)
> +{
> + struct drm_device *dev = obj->dev;
> + int ret;
> +
> + mutex_lock(&dev->struct_mutex);
> + ret = drm_gem_mmap_obj(obj, obj->size, vma);
> + mutex_unlock(&dev->struct_mutex);
> +
> + return ret;
> +}
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.h b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h
> new file mode 100644
> index 0000000..e37b921
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h
> @@ -0,0 +1,55 @@
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:Mark Yao <mark.yao@rock-chips.com>
> + *
> + * 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.
> + */
> +
> +#ifndef _ROCKCHIP_DRM_GEM_H
> +#define _ROCKCHIP_DRM_GEM_H
> +
> +#define to_rockchip_obj(x) container_of(x, struct rockchip_gem_object, base)
> +
> +struct rockchip_gem_object {
> + struct drm_gem_object base;
> + unsigned int flags;
> +
> + void *kvaddr;
> + dma_addr_t dma_addr;
> + struct dma_attrs dma_attrs;
> +};
> +
> +struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj);
> +struct drm_gem_object *
> +rockchip_gem_prime_import_sg_table(struct drm_device *dev, size_t size,
> + struct sg_table *sgt);
> +void *rockchip_gem_prime_vmap(struct drm_gem_object *obj);
> +void rockchip_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
> +int rockchip_gem_prime_mmap(struct drm_gem_object *obj,
> + struct vm_area_struct *vma);
> +
> +/* drm driver mmap file operations */
> +int rockchip_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma);
> +
> +/* mmap a gem object to userspace. */
> +int rockchip_gem_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma);
> +
> +struct rockchip_gem_object *
> + rockchip_gem_create_object(struct drm_device *drm, unsigned int size);
> +
> +void rockchip_gem_free_object(struct drm_gem_object *obj);
> +
> +int rockchip_gem_dumb_create(struct drm_file *file_priv,
> + struct drm_device *dev,
> + struct drm_mode_create_dumb *args);
> +int rockchip_gem_dumb_map_offset(struct drm_file *file_priv,
> + struct drm_device *dev, uint32_t handle,
> + uint64_t *offset);
> +#endif /* _ROCKCHIP_DRM_GEM_H */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> new file mode 100644
> index 0000000..24c3946
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> @@ -0,0 +1,1373 @@
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:Mark Yao <mark.yao@rock-chips.com>
> + *
> + * 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 <drm/drm.h>
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +
> +#include <linux/kernel.h>
> +#include <linux/platform_device.h>
> +#include <linux/clk.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/component.h>
> +
> +#include <linux/reset.h>
> +#include <linux/iommu.h>
> +#include <linux/delay.h>
> +
> +#include <video/of_display_timing.h>
> +#include <video/of_videomode.h>
> +
> +#include "rockchip_drm_drv.h"
> +#include "rockchip_drm_fbdev.h"
> +#include "rockchip_drm_gem.h"
> +#include "rockchip_drm_fb.h"
> +#include "rockchip_drm_vop.h"
> +
> +#define VOP_DEFAULT_FRAMERATE 60
> +#define VOP_MAX_WIN_SUPPORT 5
> +#define VOP_DEFAULT_CURSOR 1
> +#define VOP_REG(off, _mask, s) \
> + {.offset = off, \
> + .mask = _mask, \
> + .shift = s,}
> +
> +#define __REG_SET(x, off, mask, shift, v) \
> + vop_mask_write(x, off, (mask) << shift, (v) << shift)
> +
> +#define REG_SET(x, base, reg, v) \
> + __REG_SET(x, base + reg.offset, reg.mask, reg.shift, v)
> +
> +#define VOP_WIN_SET(x, win, name, v) \
> + REG_SET(x, win->base, win->phy->name, v)
> +#define VOP_CTRL_SET(x, name, v) \
> + REG_SET(x, 0, (x)->data->ctrl->name, v)
> +
> +#define VOP_WIN_GET_YRGBADDR(ctx, win) \
> + vop_readl(ctx, win->base + win->phy->yrgb_mst.offset)
> +
> +#define to_vop_ctx(x) container_of(x, struct vop_context, crtc)
> +#define to_rockchip_plane(x) container_of(x, struct rockchip_plane, base)
> +
> +struct rockchip_plane {
> + int id;
> + struct drm_plane base;
> + const struct vop_win *win;
> + struct vop_context *ctx;
Isn't ctx just: to_vop_ctx(base->crtc)
> +
> + uint32_t pending_yrgb_mst;
> + struct drm_framebuffer *front_fb;
> + struct drm_framebuffer *pending_fb;
> + bool enabled;
> +};
> +
> +struct vop_context {
> + struct device *dev;
> + struct drm_device *drm_dev;
> + struct drm_crtc crtc;
> + struct drm_pending_vblank_event *event;
> + struct vop_driver *drv;
> + unsigned int dpms;
> + unsigned int win_mask;
> + wait_queue_head_t wait_vsync_queue;
> + atomic_t wait_vsync_event;
> +
> + struct workqueue_struct *vsync_wq;
> + struct work_struct vsync_work;
> +
> + /* mutex vsync_ work */
> + struct mutex vsync_mutex;
> + bool vsync_work_pending;
> +
> + struct vop_driver_data *data;
> +
> + uint32_t *regsbak;
> + void __iomem *regs;
> +
> + /* physical map length of vop register */
> + uint32_t len;
> +
> + /* one time only one process allowed to config the register */
> + spinlock_t reg_lock;
> + /* lock vop irq reg */
> + spinlock_t irq_lock;
> +
> + unsigned int irq;
> +
> + /* vop AHP clk */
> + struct clk *hclk;
> + /* vop dclk */
> + struct clk *dclk;
> + /* vop share memory frequency */
> + struct clk *aclk;
> + uint32_t pixclock;
> +
> + int pipe;
> + bool clk_on;
> +};
> +
> +enum vop_data_format {
> + VOP_FMT_ARGB8888 = 0,
> + VOP_FMT_RGB888,
> + VOP_FMT_RGB565,
> + VOP_FMT_YUV420SP = 4,
> + VOP_FMT_YUV422SP,
> + VOP_FMT_YUV444SP,
> +};
> +
> +struct vop_reg_data {
> + uint32_t offset;
> + uint32_t value;
> +};
> +
> +struct vop_reg {
> + uint32_t offset;
> + uint32_t shift;
> + uint32_t mask;
> +};
> +
> +struct vop_ctrl {
> + struct vop_reg standby;
> + struct vop_reg gate_en;
> + struct vop_reg mmu_en;
> + struct vop_reg rgb_en;
> + struct vop_reg edp_en;
> + struct vop_reg hdmi_en;
> + struct vop_reg mipi_en;
> + struct vop_reg out_mode;
> + struct vop_reg dither_down;
> + struct vop_reg dither_up;
> + struct vop_reg pin_pol;
> +
> + struct vop_reg htotal_pw;
> + struct vop_reg hact_st_end;
> + struct vop_reg vtotal_pw;
> + struct vop_reg vact_st_end;
> + struct vop_reg hpost_st_end;
> + struct vop_reg vpost_st_end;
> +};
> +
> +struct vop_win_phy {
> + const uint32_t *data_formats;
> + uint32_t nformats;
> +
> + struct vop_reg enable;
> + struct vop_reg format;
> + struct vop_reg act_info;
> + struct vop_reg dsp_info;
> + struct vop_reg dsp_st;
> + struct vop_reg yrgb_mst;
> + struct vop_reg uv_mst;
> + struct vop_reg yrgb_vir;
> + struct vop_reg uv_vir;
> +
> + struct vop_reg dst_alpha_ctl;
> + struct vop_reg src_alpha_ctl;
> +};
> +
> +struct vop_win {
> + uint32_t base;
> + const struct vop_win_phy *phy;
> +};
> +
> +struct vop_driver_data {
> + const void *init_table;
> + int table_size;
> + const struct vop_ctrl *ctrl;
> + const struct vop_win *win[VOP_MAX_WIN_SUPPORT];
> +};
> +
> +static const uint32_t formats_01[] = {
> + DRM_FORMAT_XRGB8888,
> + DRM_FORMAT_ARGB8888,
> + DRM_FORMAT_RGB888,
> + DRM_FORMAT_RGB565,
> + DRM_FORMAT_NV12,
> + DRM_FORMAT_NV16,
> + DRM_FORMAT_NV24,
> +};
> +
> +static const uint32_t formats_234[] = {
> + DRM_FORMAT_XRGB8888,
> + DRM_FORMAT_ARGB8888,
> + DRM_FORMAT_RGB888,
> + DRM_FORMAT_RGB565,
> +};
> +
> +static const struct vop_win_phy win01_data = {
> + .data_formats = formats_01,
> + .nformats = ARRAY_SIZE(formats_01),
> + .enable = VOP_REG(WIN0_CTRL0, 0x1, 0),
> + .format = VOP_REG(WIN0_CTRL0, 0x7, 1),
> + .act_info = VOP_REG(WIN0_ACT_INFO, 0x1fff1fff, 0),
> + .dsp_info = VOP_REG(WIN0_DSP_INFO, 0x1fff1fff, 0),
> + .dsp_st = VOP_REG(WIN0_DSP_ST, 0x1fff1fff, 0),
> + .yrgb_mst = VOP_REG(WIN0_YRGB_MST, 0xffffffff, 0),
> + .uv_mst = VOP_REG(WIN0_CBR_MST, 0xffffffff, 0),
> + .yrgb_vir = VOP_REG(WIN0_VIR, 0x3fff, 0),
> + .uv_vir = VOP_REG(WIN0_VIR, 0x3fff, 16),
> + .src_alpha_ctl = VOP_REG(WIN0_SRC_ALPHA_CTRL, 0xff, 0),
> + .dst_alpha_ctl = VOP_REG(WIN0_DST_ALPHA_CTRL, 0xff, 0),
> +};
> +
> +static const struct vop_win_phy win23_data = {
> + .data_formats = formats_234,
> + .nformats = ARRAY_SIZE(formats_234),
> + .enable = VOP_REG(WIN2_CTRL0, 0x1, 0),
> + .format = VOP_REG(WIN2_CTRL0, 0x7, 1),
> + .dsp_info = VOP_REG(WIN2_DSP_INFO0, 0x0fff0fff, 0),
> + .dsp_st = VOP_REG(WIN2_DSP_ST0, 0x1fff1fff, 0),
> + .yrgb_mst = VOP_REG(WIN2_MST0, 0xffffffff, 0),
> + .yrgb_vir = VOP_REG(WIN2_VIR0_1, 0x1fff, 0),
> + .src_alpha_ctl = VOP_REG(WIN2_SRC_ALPHA_CTRL, 0xff, 0),
> + .dst_alpha_ctl = VOP_REG(WIN2_DST_ALPHA_CTRL, 0xff, 0),
> +};
> +
> +static const struct vop_win_phy cursor_data = {
> + .data_formats = formats_234,
> + .nformats = ARRAY_SIZE(formats_234),
> + .enable = VOP_REG(HWC_CTRL0, 0x1, 0),
> + .format = VOP_REG(HWC_CTRL0, 0x7, 1),
> + .dsp_st = VOP_REG(HWC_DSP_ST, 0x1fff1fff, 0),
> + .yrgb_mst = VOP_REG(HWC_MST, 0xffffffff, 0),
> +};
> +
> +static const struct vop_win win0 = {
> + .base = 0,
> + .phy = &win01_data,
> +};
> +
> +static const struct vop_win win1 = {
> + .base = 0x40,
> + .phy = &win01_data,
> +};
> +
> +static const struct vop_win win2 = {
> + .base = 0,
> + .phy = &win23_data,
> +};
> +
> +static const struct vop_win win3 = {
> + .base = 0x50,
> + .phy = &win23_data,
> +};
> +
> +static const struct vop_win win_cursor = {
> + .base = 0,
> + .phy = &cursor_data,
> +};
> +
> +static const struct vop_ctrl ctrl_data = {
> + .standby = VOP_REG(SYS_CTRL, 0x1, 22),
> + .gate_en = VOP_REG(SYS_CTRL, 0x1, 23),
> + .mmu_en = VOP_REG(SYS_CTRL, 0x1, 20),
> + .rgb_en = VOP_REG(SYS_CTRL, 0x1, 12),
> + .hdmi_en = VOP_REG(SYS_CTRL, 0x1, 13),
> + .edp_en = VOP_REG(SYS_CTRL, 0x1, 14),
> + .mipi_en = VOP_REG(SYS_CTRL, 0x1, 15),
> + .dither_down = VOP_REG(DSP_CTRL1, 0xf, 1),
> + .dither_up = VOP_REG(DSP_CTRL1, 0x1, 6),
> + .out_mode = VOP_REG(DSP_CTRL0, 0xf, 0),
> + .pin_pol = VOP_REG(DSP_CTRL0, 0xf, 4),
> + .htotal_pw = VOP_REG(DSP_HTOTAL_HS_END, 0x1fff1fff, 0),
> + .hact_st_end = VOP_REG(DSP_HACT_ST_END, 0x1fff1fff, 0),
> + .vtotal_pw = VOP_REG(DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
> + .vact_st_end = VOP_REG(DSP_VACT_ST_END, 0x1fff1fff, 0),
> + .hpost_st_end = VOP_REG(POST_DSP_HACT_INFO, 0x1fff1fff, 0),
> + .vpost_st_end = VOP_REG(POST_DSP_VACT_INFO, 0x1fff1fff, 0),
> +};
> +
> +static const struct vop_reg_data vop_init_reg_table[] = {
> + {SYS_CTRL, 0x00801000},
> + {DSP_CTRL0, 0x00000000},
> + {WIN0_CTRL0, 0x00000080},
> + {WIN1_CTRL0, 0x00000080},
> +};
> +
> +static const struct vop_driver_data rockchip_rk3288_vop = {
> + .init_table = vop_init_reg_table,
> + .table_size = ARRAY_SIZE(vop_init_reg_table),
> + .ctrl = &ctrl_data,
> + .win[0] = &win0,
> + .win[1] = &win1,
> + .win[2] = &win2,
> + .win[3] = &win3,
> + .win[4] = &win_cursor,
> +};
> +
> +static const struct of_device_id vop_driver_dt_match[] = {
> + { .compatible = "rockchip,rk3288-vop",
> + .data = (void *)&rockchip_rk3288_vop },
> + {},
> +};
> +
> +static inline void vop_writel(struct vop_context *ctx,
> + uint32_t offset, uint32_t v)
> +{
> + writel(v, ctx->regs + offset);
> + ctx->regsbak[offset >> 2] = v;
> +}
> +
> +static inline uint32_t vop_readl(struct vop_context *ctx, uint32_t offset)
> +{
> + return readl(ctx->regs + offset);
> +}
> +
> +static inline void vop_cfg_done(struct vop_context *ctx)
> +{
> + writel(0x01, ctx->regs + REG_CFG_DONE);
> +}
> +
> +static inline void vop_mask_write(struct vop_context *ctx,
> + uint32_t offset, uint32_t mask, uint32_t v)
> +{
> + if (mask) {
> + uint32_t cached_val = ctx->regsbak[offset >> 2];
> +
> + cached_val = (cached_val & ~mask) | v;
> + writel(cached_val, ctx->regs + offset);
> + ctx->regsbak[offset >> 2] = cached_val;
> + }
> +}
> +
> +static inline struct vop_driver_data *vop_get_driver_data(struct device *dev)
> +{
> + const struct of_device_id *of_id =
> + of_match_device(vop_driver_dt_match, dev);
> +
> + return (struct vop_driver_data *)of_id->data;
> +}
> +
> +static enum vop_data_format vop_convert_format(uint32_t format)
> +{
> + switch (format) {
> + case DRM_FORMAT_XRGB8888:
> + case DRM_FORMAT_ARGB8888:
> + return VOP_FMT_ARGB8888;
> + case DRM_FORMAT_RGB888:
> + return VOP_FMT_RGB888;
> + case DRM_FORMAT_RGB565:
> + return VOP_FMT_RGB565;
> + case DRM_FORMAT_NV12:
> + return VOP_FMT_YUV420SP;
> + case DRM_FORMAT_NV16:
> + return VOP_FMT_YUV422SP;
> + case DRM_FORMAT_NV24:
> + return VOP_FMT_YUV444SP;
> + default:
> + DRM_ERROR("unsupport format[%08x]\n", format);
> + return -EINVAL;
> + }
> +}
> +
> +static bool is_alpha_support(uint32_t format)
> +{
> + switch (format) {
> + case DRM_FORMAT_ARGB8888:
> + return true;
> + default:
> + return false;
> + }
> +}
> +
> +/* TODO(djkurtz): move generic 'setup slave rk_iommu' code somewhere common */
> +int vop_iommu_init(struct vop_context *ctx)
> +{
> + struct device *dev = ctx->dev;
> + struct device_node *np = dev->of_node;
> + struct platform_device *pd;
> + int count;
> + int ret;
> + struct of_phandle_args args;
> +
> + /* Each VOP must have exactly one iommu node, with no args */
> + count = of_count_phandle_with_args(np, "iommus", "#iommu-cells");
> + if (count != 1) {
> + dev_err(dev, "of_count_phandle_with_args(%s) => %d\n",
> + np->full_name, count);
> + return -EINVAL;
> + }
> +
> + ret = of_parse_phandle_with_args(np, "iommus", "#iommu-cells", 0,
> + &args);
> + if (ret) {
> + dev_err(dev, "of_parse_phandle_with_args(%s) => %d\n",
> + np->full_name, ret);
> + return ret;
> + }
> + if (args.args_count != 0) {
> + dev_err(dev, "incorrect number of iommu params found for %s (found %d, expected 0)\n",
> + args.np->full_name, args.args_count);
> + return -EINVAL;
> + }
> +
> + pd = of_find_device_by_node(args.np);
> + of_node_put(args.np);
> + if (!pd) {
> + dev_err(dev, "iommu %s not found\n", args.np->full_name);
> + return -EPROBE_DEFER;
> + }
> +
> + /* TODO(djkurtz): handle multiple slave iommus for a single master */
> + dev->archdata.iommu = &pd->dev;
> +
> + ret = rockchip_drm_dma_attach_device(ctx->drm_dev, dev);
> + if (ret) {
> + dev_err(dev, "failed to attach to drm dma mapping, %d\n", ret);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static void vop_iommu_fini(struct vop_context *ctx)
> +{
> + rockchip_drm_dma_detach_device(ctx->drm_dev, ctx->dev);
> +}
> +
> +static int rockchip_plane_get_size(int start, unsigned length, unsigned last)
This function can use some documentation to define the variables which are
somewhat misleadingly named:
start: starting pixel in screen coordinates
length: length of buffer "row" in pixels
end: one pixel past end of buffer in screen coordinates
last: width of screen, or one pixel past end of screen
size: number of pixels of buffer to display on screen
> +{
> + int end = start + length;
> + int size = 0;
> +
> + if (start <= 0) {
> + if (end > 0)
> + size = min_t(unsigned, end, last);
> + } else if (start <= last) {
start < last
/* if (start == last), we are starting 1 pixel past the end of the screen,
so no need to compute size :-), it will be 0. */
> + size = min_t(unsigned, last - start, length);
> + }
> +
> + return size;
> +}
> +
> +static int vop_clk_enable(struct vop_context *ctx)
> +{
> + int ret;
> +
> + if (!ctx->clk_on) {
> + ret = clk_prepare_enable(ctx->hclk);
I saw a code review recently that suggested to do
"clk_prepare/_unprepare()" in probe()/remove(), and just do
clk_enable()/clk_disable() at runtime.
> + if (ret < 0) {
> + dev_err(ctx->dev, "failed to enable hclk\n");
> + return ret;
> + }
> +
> + ret = clk_prepare_enable(ctx->dclk);
> + if (ret < 0) {
> + dev_err(ctx->dev, "failed to enable dclk\n");
> + goto err_dclk;
> + }
> +
> + ret = clk_prepare_enable(ctx->aclk);
> + if (ret < 0) {
> + dev_err(ctx->dev, "failed to enable aclk\n");
> + goto err_aclk;
> + }
> + ctx->clk_on = true;
> + }
> +
> + return ret;
> +err_aclk:
> + clk_disable_unprepare(ctx->aclk);
this should be dclk
> +err_dclk:
> + clk_disable_unprepare(ctx->hclk);
> + return ret;
> +}
> +
> +static void vop_clk_disable(struct vop_context *ctx)
> +{
> + if (ctx->clk_on) {
> + clk_disable_unprepare(ctx->dclk);
> + clk_disable_unprepare(ctx->hclk);
> + clk_disable_unprepare(ctx->aclk);
disable in inverse order from enable:
aclk
dclk
hclk
> + ctx->clk_on = false;
> + }
> +}
> +
> +static void vop_power_on(struct vop_context *ctx)
> +{
> + if (vop_clk_enable(ctx) < 0) {
> + dev_err(ctx->dev, "failed to enable clks\n");
> + return;
> + }
> +
> + spin_lock(&ctx->reg_lock);
> +
> + VOP_CTRL_SET(ctx, standby, 0);
> +
> + spin_unlock(&ctx->reg_lock);
> +}
> +
> +static void vop_power_off(struct vop_context *ctx)
> +{
> + spin_lock(&ctx->reg_lock);
> +
> + VOP_CTRL_SET(ctx, standby, 1);
> +
> + spin_unlock(&ctx->reg_lock);
> +
> + vop_clk_disable(ctx);
> +}
> +
> +static int rockchip_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
> + struct drm_framebuffer *fb, int crtc_x,
> + int crtc_y, unsigned int crtc_w,
> + unsigned int crtc_h, uint32_t src_x,
> + uint32_t src_y, uint32_t src_w, uint32_t src_h)
> +{
> + struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
> + const struct vop_win *win = rockchip_plane->win;
> + struct vop_context *ctx = to_vop_ctx(crtc);
> + struct drm_gem_object *obj;
> + struct rockchip_gem_object *rk_obj;
> + unsigned long offset;
> + unsigned int actual_w;
> + unsigned int actual_h;
> + unsigned int dsp_stx;
> + unsigned int dsp_sty;
> + unsigned int y_vir_stride;
> + dma_addr_t yrgb_mst;
> + enum vop_data_format format;
> + uint32_t val;
> + bool is_alpha;
> +
> + if (!win) {
> + DRM_ERROR("can't find win data for vop, failed\n");
> + return -EINVAL;
> + }
There should be no need for this error checking.
> +
> + obj = rockchip_fb_get_gem_obj(fb, 0);
> + if (!obj) {
> + DRM_ERROR("fail to get rockchip gem object from framebuffer\n");
> + return -EINVAL;
> + }
Nor this.
> +
> + rk_obj = to_rockchip_obj(obj);
> +
> + yrgb_mst = rk_obj->dma_addr;
> + if (yrgb_mst <= 0)
> + return -ENOMEM;
Nor this:
I don't think dma_addr_t can be less than 0.
Also, dma_addr_t == 0 could be a valid iova.
src_* are actually 16.16 fixed point 'subpixel' units, but crtc_x is in pixels.
However, since you aren't implementing sub-pixel positioning, you can do:
if (src_x & 0xffff || src_y & 0xffff || src_w & 0xffff || src_h & 0xffff)
return -EINVAL;
And, since you aren't implementing plane scaling:
src_w >>= 16;
src_h >>= 16;
src_x >>= 16;
src_y >>= 16;
if (crtc_w != src_w || crtc_h != src_h)
return -EINVAL;
> +
> + actual_w = rockchip_plane_get_size(crtc_x,
> + crtc_w, crtc->mode.hdisplay);
> + actual_h = rockchip_plane_get_size(crtc_y,
> + crtc_h, crtc->mode.vdisplay);
crtc_w and crtc_h seem to imply the width & height of the crtc, but
really they are they are
desired width and height of the framebuffer in crtc pixel units.
actual_w and actual_h are the number of pixels of this framebuffer that will
actually be shown on the crtc. What happens if one of them is 0? Perhaps we
should just return -EINVAL. Otherwise you might compute -1 below when setting
act_info/dsp_info, and end up programming a very large number.
> + if (crtc_x < 0) {
> + if (actual_w)
> + src_x -= crtc_x;
> + crtc_x = 0;
> + }
> +
> + if (crtc_y < 0) {
> + if (actual_h)
> + src_y -= crtc_y;
> + crtc_y = 0;
> + }
> +
> + dsp_stx = crtc_x + crtc->mode.htotal - crtc->mode.hsync_start;
> + dsp_sty = crtc_y + crtc->mode.vtotal - crtc->mode.vsync_start;
The additions herer are because you have the vblank at the beginning
of the frame.
Can you simplify this by moving vblank to the end of the frame? (see below)
> +
> + offset = src_x * (fb->bits_per_pixel >> 3);
> + offset += src_y * fb->pitches[0];
> +
> + y_vir_stride = fb->pitches[0] / (fb->bits_per_pixel >> 3);
> + is_alpha = is_alpha_support(fb->pixel_format);
> + format = vop_convert_format(fb->pixel_format);
This function would return < 0 if the format not supported. Check this earlier.
Although, it should not be possible to get an invalid format, since it is
already verified by drm core.
> +
> + spin_lock(&ctx->reg_lock);
> +
> + VOP_WIN_SET(ctx, win, format, format);
> + VOP_WIN_SET(ctx, win, yrgb_vir, y_vir_stride);
> + yrgb_mst += offset;
> + VOP_WIN_SET(ctx, win, yrgb_mst, yrgb_mst);
> + VOP_WIN_SET(ctx, win, act_info,
> + ((actual_h - 1) << 16) | (actual_w - 1));
Nit: You should probably be masking the individual h/w fields to 13-bits each
before or'ing them together.
Same with dsp_st.
> + VOP_WIN_SET(ctx, win, dsp_info,
> + ((actual_h - 1) << 16) | (actual_w - 1));
> + VOP_WIN_SET(ctx, win, dsp_st, (dsp_sty << 16) | dsp_stx);
> + if (is_alpha) {
> + VOP_WIN_SET(ctx, win, dst_alpha_ctl,
> + DST_FACTOR_M0(ALPHA_SRC_INVERSE));
> + val = SRC_ALPHA_EN(1) | SRC_COLOR_M0(ALPHA_SRC_PRE_MUL) |
> + SRC_ALPHA_M0(ALPHA_STRAIGHT) |
> + SRC_BLEND_M0(ALPHA_PER_PIX) |
> + SRC_ALPHA_CAL_M0(ALPHA_NO_SATURATION) |
> + SRC_FACTOR_M0(ALPHA_ONE);
> + VOP_WIN_SET(ctx, win, src_alpha_ctl, val);
> + } else {
> + VOP_WIN_SET(ctx, win, src_alpha_ctl, SRC_ALPHA_EN(0));
> + }
> +
> + VOP_WIN_SET(ctx, win, enable, 1);
> +
> + spin_unlock(&ctx->reg_lock);
> +
> + mutex_lock(&ctx->vsync_mutex);
> +
> + /*
> + * Because the buffer set to vop take effect at frame start time,
> + * we need make sure old buffer is not in use before we release
> + * it.
> + * reference the framebuffer, and unference it when it swap out of vop.
> + */
> + if (fb != rockchip_plane->front_fb) {
> + drm_framebuffer_reference(fb);
> + rockchip_plane->pending_fb = fb;
> + rockchip_plane->pending_yrgb_mst = yrgb_mst;
> + ctx->vsync_work_pending = true;
> + }
> + rockchip_plane->enabled = true;
> +
> + mutex_unlock(&ctx->vsync_mutex);
> +
> + spin_lock(&ctx->reg_lock);
> + vop_cfg_done(ctx);
> + spin_unlock(&ctx->reg_lock);
> +
> + return 0;
> +}
> +
> +static int rockchip_disable_plane(struct drm_plane *plane)
> +{
> + struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
> + struct vop_context *ctx = rockchip_plane->ctx;
> + const struct vop_win *win = rockchip_plane->win;
> +
> + spin_lock(&ctx->reg_lock);
> +
> + VOP_WIN_SET(ctx, win, enable, 0);
> + vop_cfg_done(ctx);
> +
> + spin_unlock(&ctx->reg_lock);
> +
> + mutex_lock(&ctx->vsync_mutex);
> +
> + /*
> + * clear the pending framebuffer and set vsync_work_pending true,
> + * so that the framebuffer will unref at the next vblank.
> + */
> + if (rockchip_plane->pending_fb) {
> + drm_framebuffer_unreference(rockchip_plane->pending_fb);
> + rockchip_plane->pending_fb = NULL;
> + }
> +
> + rockchip_plane->enabled = false;
> + ctx->vsync_work_pending = true;
> +
> + mutex_unlock(&ctx->vsync_mutex);
> +
> + return 0;
> +}
> +
> +static void rockchip_plane_destroy(struct drm_plane *plane)
> +{
> + struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
> + struct vop_context *ctx = rockchip_plane->ctx;
> +
> + rockchip_disable_plane(plane);
> + drm_plane_cleanup(plane);
> + ctx->win_mask &= ~(1 << rockchip_plane->id);
> + kfree(rockchip_plane);
> +}
> +
> +static const struct drm_plane_funcs rockchip_plane_funcs = {
> + .update_plane = rockchip_update_plane,
> + .disable_plane = rockchip_disable_plane,
> + .destroy = rockchip_plane_destroy,
> +};
> +
> +struct drm_plane *rockchip_plane_init(struct vop_context *ctx,
> + unsigned long possible_crtcs,
> + enum drm_plane_type type)
> +{
> + struct rockchip_plane *rockchip_plane;
> + struct vop_driver_data *vop_data = ctx->data;
> + const struct vop_win *win;
> + int i;
> + int err;
> +
> + rockchip_plane = kzalloc(sizeof(*rockchip_plane), GFP_KERNEL);
> + if (!rockchip_plane)
> + return NULL;
Let's propagate errors from this function, like this...
return ERR_PTR(-ENOMEM);
> +
> + for (i = 0; i < VOP_MAX_WIN_SUPPORT; i++) {
> + if (!(ctx->win_mask & (1 << i))) {
> + win = vop_data->win[i];
> + break;
> + }
> + }
The caller already knows for which window index they are init'ing the plane.
Just have the caller pass it in.
> +
> + if (VOP_MAX_WIN_SUPPORT == i) {
> + DRM_ERROR("failed to find win\n");
> + kfree(rockchip_plane);
> + return NULL;
> + }
> +
> + ctx->win_mask |= (1 << i);
> + rockchip_plane->id = i;
> + rockchip_plane->win = win;
> + rockchip_plane->ctx = ctx;
> +
> + err = drm_universal_plane_init(ctx->drm_dev, &rockchip_plane->base,
> + possible_crtcs, &rockchip_plane_funcs,
> + win->phy->data_formats,
> + win->phy->nformats, type);
> + if (err) {
> + DRM_ERROR("failed to initialize plane\n");
> + kfree(rockchip_plane);
> + return NULL;
return ERR_PTR(err);
> + }
> +
> + return &rockchip_plane->base;
> +}
> +
> +int rockchip_drm_crtc_enable_vblank(struct drm_device *dev, int pipe)
> +{
> + struct vop_context *ctx = to_vop_ctx(rockchip_find_crtc(dev, pipe));
> + unsigned long flags;
> +
> + if (ctx->dpms != DRM_MODE_DPMS_ON)
> + return -EPERM;
> +
> + spin_lock_irqsave(&ctx->irq_lock, flags);
> +
> + vop_mask_write(ctx, INTR_CTRL0, LINE_FLAG_INTR_MASK,
> + LINE_FLAG_INTR_EN(1));
> +
> + spin_unlock_irqrestore(&ctx->irq_lock, flags);
> +
> + return 0;
> +}
> +
> +void rockchip_drm_crtc_disable_vblank(struct drm_device *dev, int pipe)
> +{
> + struct vop_context *ctx = to_vop_ctx(rockchip_find_crtc(dev, pipe));
> + unsigned long flags;
> +
> + if (ctx->dpms != DRM_MODE_DPMS_ON)
> + return;
> + spin_lock_irqsave(&ctx->irq_lock, flags);
> + vop_mask_write(ctx, INTR_CTRL0, LINE_FLAG_INTR_MASK,
> + LINE_FLAG_INTR_EN(0));
> + spin_unlock_irqrestore(&ctx->irq_lock, flags);
> +}
> +
> +static void rockchip_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
> +{
> + struct vop_context *ctx = to_vop_ctx(crtc);
> +
> + DRM_DEBUG_KMS("crtc[%d] mode[%d]\n", crtc->base.id, mode);
> +
> + if (ctx->dpms == mode) {
> + DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
> + return;
> + }
> + if (mode > DRM_MODE_DPMS_ON) {
> + /* wait for the completion of page flip. */
> + if (!wait_event_timeout(ctx->wait_vsync_queue,
> + !atomic_read(&ctx->wait_vsync_event),
> + HZ/20))
> + DRM_DEBUG_KMS("vblank wait timed out.\n");
> + drm_vblank_off(crtc->dev, ctx->pipe);
> + }
> +
> + switch (mode) {
> + case DRM_MODE_DPMS_ON:
> + vop_power_on(ctx);
> + break;
> + case DRM_MODE_DPMS_STANDBY:
> + case DRM_MODE_DPMS_SUSPEND:
> + case DRM_MODE_DPMS_OFF:
> + vop_power_off(ctx);
> + break;
> + default:
> + DRM_DEBUG_KMS("unspecified mode %d\n", mode);
> + break;
> + }
> +
> + ctx->dpms = mode;
> +}
> +
> +static void rockchip_drm_crtc_prepare(struct drm_crtc *crtc)
> +{
> + rockchip_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
> +}
> +
> +static bool rockchip_drm_crtc_mode_fixup(struct drm_crtc *crtc,
> + const struct drm_display_mode *mode,
> + struct drm_display_mode *adjusted_mode)
> +{
> + /* just do dummy now */
> +
> + return true;
> +}
> +
> +static int rockchip_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
> + struct drm_framebuffer *old_fb);
> +
> +static int rockchip_drm_crtc_mode_set(struct drm_crtc *crtc,
> + struct drm_display_mode *mode,
> + struct drm_display_mode *adjusted_mode,
> + int x, int y,
> + struct drm_framebuffer *fb)
> +{
> + struct vop_context *ctx = to_vop_ctx(crtc);
> + u16 hsync_len = adjusted_mode->hsync_end - adjusted_mode->hsync_start;
> + u16 left_margin = adjusted_mode->htotal - adjusted_mode->hsync_end;
> + u16 vsync_len = adjusted_mode->vsync_end - adjusted_mode->vsync_start;
> + u16 upper_margin = adjusted_mode->vtotal - adjusted_mode->vsync_end;
This moves the vblank/hblank to the front of the frame rather than the end.
Is that necessary on this hardware?
If you leave the active area origin at (0,0), it makes it a little bit simpler
to locate overlays.
> + u16 hdisplay = adjusted_mode->hdisplay;
> + u16 vdisplay = adjusted_mode->vdisplay;
> + u16 htotal = adjusted_mode->htotal;
> + u16 vtotal = adjusted_mode->vtotal;
> + struct rockchip_display_mode *priv_mode =
> + (void *)adjusted_mode->private;
> + unsigned long flags;
> + int ret;
> + uint32_t val;
> +
> + /* nothing to do if we haven't set the mode yet */
> + if (adjusted_mode->htotal == 0 || adjusted_mode->vtotal == 0)
> + return -EINVAL;
How is it possible to be given a mode with htotal or vtotal == 0?
How could we be doing a mode set without a mode to set?
Can you do these verification checks to mode_fixup() instead?
> +
> + if (!priv_mode) {
> + DRM_ERROR("fail to found display output type[%d]\n",
> + priv_mode->out_type);
> + return -EINVAL;
> + }
> +
> + ret = rockchip_drm_crtc_mode_set_base(crtc, x, y, fb);
> + if (ret)
> + return ret;
> +
> + switch (priv_mode->out_type) {
> + case ROCKCHIP_DISPLAY_TYPE_RGB:
> + case ROCKCHIP_DISPLAY_TYPE_LVDS:
> + VOP_CTRL_SET(ctx, rgb_en, 1);
> + VOP_CTRL_SET(ctx, out_mode, ROCKCHIP_OUTFACE_P888);
> + break;
> + case ROCKCHIP_DISPLAY_TYPE_EDP:
> + VOP_CTRL_SET(ctx, edp_en, 1);
> + VOP_CTRL_SET(ctx, out_mode, ROCKCHIP_OUTFACE_AAAA);
> + break;
> + case ROCKCHIP_DISPLAY_TYPE_HDMI:
> + VOP_CTRL_SET(ctx, out_mode, ROCKCHIP_OUTFACE_AAAA);
> + VOP_CTRL_SET(ctx, hdmi_en, 1);
> + break;
> + default:
> + DRM_ERROR("unsupport out type[%d]\n", priv_mode->out_type);
> + return -EINVAL;
> + };
This switch is directing the vop output to a particular output (edp,
lvds, hdmi, etc)
based on a private field added to the drm_display_mode by the down-pipe encoder.
However, on other hardware, it is possible to drive multiple outputs
from the same crtc.
This is a useful feature, for mirroring one framebuffer to multiple displays.
Is that possible for rk3288 too?
It looks like it should be possible to set multiple rgb_en, edp_en, hdmi_en, etc
bits at the same time.
If so, then we can't really make the encoder selection part of the mode.
Instead, what we can try to do is implement this output selection step by
providing a helper function from the vop for the encoders to call in their own
mode_set() callback.
Also, vop can expose another helper for allowing the encoder to select the vop
pixel output format that it requires (ie, LVDS panel encoder can select 565).
> +
> + val = 0x8;
> + val |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0;
> + val |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ? (1 << 1) : 0;
> + VOP_CTRL_SET(ctx, pin_pol, val);
> +
> + VOP_CTRL_SET(ctx, htotal_pw, (htotal << 16) | hsync_len);
> + val = (hsync_len + left_margin) << 16;
> + val |= hsync_len + left_margin + hdisplay;
> + VOP_CTRL_SET(ctx, hact_st_end, val);
> + VOP_CTRL_SET(ctx, hpost_st_end, val);
> +
> + VOP_CTRL_SET(ctx, vtotal_pw, (vtotal << 16) | vsync_len);
> + val = (vsync_len + upper_margin) << 16;
> + val |= vsync_len + upper_margin + vdisplay;
> + VOP_CTRL_SET(ctx, vact_st_end, val);
> + VOP_CTRL_SET(ctx, vpost_st_end, val);
> +
> + spin_lock_irqsave(&ctx->irq_lock, flags);
> +
> + vop_mask_write(ctx, INTR_CTRL0, DSP_LINE_NUM_MASK,
> + DSP_LINE_NUM(vsync_len + upper_margin + vdisplay));
> +
> + spin_unlock_irqrestore(&ctx->irq_lock, flags);
> +
> + clk_set_rate(ctx->dclk, adjusted_mode->clock * 1000);
When we are changing modes, can we just change the dclk like this while a
previous frame is already being scanned out using the old clock?
Shouldn't we synchronize changing clocks:
- schedule any current scanout to stop at next vblank
- wait for that vblank
- disable clock
- program new mode
- program new framebuffer(s) to match that new mode.
- start clock
Or maybe like what you do in initial before turning on the iommu:
- disable dclk
- do a dclk & ahb reset
- program new mode
- program new fb(s)
- enable dclk.
> +
> + return 0;
> +}
> +
> +static int rockchip_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
> + struct drm_framebuffer *old_fb)
> +{
> + unsigned int crtc_w;
> + unsigned int crtc_h;
> + int ret;
> +
> + crtc_w = crtc->primary->fb->width - crtc->x;
> + crtc_h = crtc->primary->fb->height - crtc->y;
> +
> + ret = rockchip_update_plane(crtc->primary, crtc, crtc->primary->fb, 0,
> + 0, crtc_w, crtc_h, crtc->x, crtc->y, crtc_w,
> + crtc_h);
rockchip_update_plane expects its src_x, src_y, src_h, src_w to be in 16.16
'sub-pixel' units, so you need to << 16 the last 4 parameters here.
It might be more convenient to split rockchip_update_plane() into two functions:
(1) rockchip_update_plane() which takes 16.16 src_ and is used for the
drm_plane_funcs.update_plane. This function converts src_* from 16.16 to
pixels, and then calls:
(2) a helper function which just takes src_ in whole pixel units. The helper
function you can call from rockchip_drm_crtc_mode_set_base() and
rockchip_drm_crtc_page_flip(). In fact, since we do not support scaling,
the helper probably does not even need src_w and src_h arguments.
> + if (ret < 0) {
> + DRM_ERROR("fail to update plane\n");
> + return -EINVAL;
return ret;
> + }
> +
> + return 0;
> +}
> +
> +static void rockchip_drm_crtc_commit(struct drm_crtc *crtc)
> +{
> + /* just do dummy now */
> +}
> +
> +static const struct drm_crtc_helper_funcs rockchip_crtc_helper_funcs = {
> + .dpms = rockchip_drm_crtc_dpms,
> + .prepare = rockchip_drm_crtc_prepare,
> + .mode_fixup = rockchip_drm_crtc_mode_fixup,
> + .mode_set = rockchip_drm_crtc_mode_set,
> + .mode_set_base = rockchip_drm_crtc_mode_set_base,
> + .commit = rockchip_drm_crtc_commit,
> +};
> +
> +static int rockchip_drm_crtc_page_flip(struct drm_crtc *crtc,
> + struct drm_framebuffer *fb,
> + struct drm_pending_vblank_event *event,
> + uint32_t page_flip_flags)
> +{
> + struct drm_device *dev = crtc->dev;
> + struct vop_context *ctx = to_vop_ctx(crtc);
> + struct drm_framebuffer *old_fb = crtc->primary->fb;
> + unsigned int crtc_w;
> + unsigned int crtc_h;
> + int ret;
> +
> + /* when the page flip is requested, crtc's dpms should be on */
> + if (ctx->dpms > DRM_MODE_DPMS_ON) {
> + DRM_DEBUG("failed page flip request at dpms[%d].\n", ctx->dpms);
> + return 0;
> + }
> +
> + ret = drm_vblank_get(dev, ctx->pipe);
> + if (ret) {
> + DRM_DEBUG("failed to acquire vblank counter\n");
> + return ret;
> + }
> +
> + spin_lock_irq(&dev->event_lock);
> + if (ctx->event) {
> + spin_unlock_irq(&dev->event_lock);
> + DRM_ERROR("already pending flip!\n");
> + return -EBUSY;
> + }
> + ctx->event = event;
> + atomic_set(&ctx->wait_vsync_event, 1);
> + spin_unlock_irq(&dev->event_lock);
> +
> + crtc->primary->fb = fb;
> + crtc_w = crtc->primary->fb->width - crtc->x;
> + crtc_h = crtc->primary->fb->height - crtc->y;
> +
> + ret = rockchip_update_plane(crtc->primary, crtc, fb, 0, 0, crtc_w,
> + crtc_h, crtc->x, crtc->y, crtc_w, crtc_h);
> + if (ret) {
> + crtc->primary->fb = old_fb;
> +
> + spin_lock_irq(&dev->event_lock);
> + drm_vblank_put(dev, ctx->pipe);
> + atomic_set(&ctx->wait_vsync_event, 0);
> + ctx->event = NULL;
> + spin_unlock_irq(&dev->event_lock);
> + }
> +
> + return ret;
> +}
> +
> +void rockchip_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe)
> +{
> + struct rockchip_drm_private *dev_priv = dev->dev_private;
> + struct drm_crtc *drm_crtc = dev_priv->crtc[pipe];
> + struct vop_context *ctx;
> + unsigned long flags;
> +
> + if (!drm_crtc)
> + return;
Can you add a comment describing when this can happen?
> +
> + ctx = to_vop_ctx(drm_crtc);
> +
> + spin_lock_irqsave(&dev->event_lock, flags);
> +
> + if (ctx->event) {
> + drm_send_vblank_event(dev, -1, ctx->event);
> + drm_vblank_put(dev, pipe);
> + atomic_set(&ctx->wait_vsync_event, 0);
> + wake_up(&ctx->wait_vsync_queue);
> + ctx->event = NULL;
> + }
> +
> + spin_unlock_irqrestore(&dev->event_lock, flags);
> +}
> +
> +void rockchip_drm_crtc_cancel_pending_flip(struct drm_device *dev)
> +{
> + int i;
> +
> + for (i = 0; i < dev->num_crtcs; i++)
> + rockchip_drm_crtc_finish_pageflip(dev, i);
> +}
> +
> +static void rockchip_drm_crtc_destroy(struct drm_crtc *crtc)
> +{
> + struct vop_context *ctx = to_vop_ctx(crtc);
> + struct rockchip_drm_private *private = crtc->dev->dev_private;
> +
> + private->crtc[ctx->pipe] = NULL;
> + drm_crtc_cleanup(crtc);
> +}
> +
> +static const struct drm_crtc_funcs rockchip_crtc_funcs = {
> + .set_config = drm_crtc_helper_set_config,
> + .page_flip = rockchip_drm_crtc_page_flip,
> + .destroy = rockchip_drm_crtc_destroy,
> +};
> +
> +static void rockchip_vsync_worker(struct work_struct *work)
> +{
> + struct vop_context *ctx = container_of(work, struct vop_context,
> + vsync_work);
> + struct drm_device *drm = ctx->drm_dev;
> + struct rockchip_drm_private *dev_priv = drm->dev_private;
> + struct drm_crtc *crtc = dev_priv->crtc[ctx->pipe];
> + struct rockchip_plane *rockchip_plane;
> + struct drm_plane *plane;
> + uint32_t yrgb_mst;
> +
> + mutex_lock(&ctx->vsync_mutex);
> +
> + ctx->vsync_work_pending = false;
> +
> + list_for_each_entry(plane, &drm->mode_config.plane_list, head) {
> + rockchip_plane = to_rockchip_plane(plane);
> +
> + if (rockchip_plane->ctx != ctx)
> + continue;
> + if (rockchip_plane->enabled && !rockchip_plane->pending_fb)
> + continue;
> + if (!rockchip_plane->enabled && !rockchip_plane->front_fb)
> + continue;
> + /*
> + * make sure the yrgb_mst take effect, so that
> + * we can unreference the old framebuffer.
> + */
> + yrgb_mst = VOP_WIN_GET_YRGBADDR(ctx, rockchip_plane->win);
> + if (rockchip_plane->pending_yrgb_mst != yrgb_mst) {
> + /*
> + * some plane no complete, unref at next vblank
> + */
> + ctx->vsync_work_pending = true;
> + continue;
> + }
> +
> + /*
> + * drm_framebuffer_unreference maybe call iommu unmap,
> + * and iommu not allow unmap buffer at irq context,
> + * so we do drm_framebuffer_unreference at queue_work.
> + */
> + if (rockchip_plane->front_fb)
> + drm_framebuffer_unreference(rockchip_plane->front_fb);
> +
> + rockchip_plane->front_fb = rockchip_plane->pending_fb;
> + rockchip_plane->pending_fb = NULL;
> +
> + /*
> + * if primary plane flip complete, sending the event to
> + * userspace
> + */
> + if (&rockchip_plane->base == crtc->primary)
> + rockchip_drm_crtc_finish_pageflip(ctx->drm_dev,
> + ctx->pipe);
> + }
> +
> + mutex_unlock(&ctx->vsync_mutex);
> +}
> +
> +static irqreturn_t rockchip_vop_isr(int irq, void *data)
> +{
> + struct vop_context *ctx = data;
> + uint32_t intr0_reg;
> + unsigned long flags;
> +
> + intr0_reg = vop_readl(ctx, INTR_CTRL0);
> + if (intr0_reg & LINE_FLAG_INTR) {
> + spin_lock_irqsave(&ctx->irq_lock, flags);
> + vop_writel(ctx, INTR_CTRL0, intr0_reg | LINE_FLAG_INTR_CLR);
> + spin_unlock_irqrestore(&ctx->irq_lock, flags);
> + } else {
> + return IRQ_NONE;
> + }
> +
> + drm_handle_vblank(ctx->drm_dev, ctx->pipe);
> + if (ctx->vsync_work_pending)
> + queue_work(ctx->vsync_wq, &ctx->vsync_work);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int vop_create_crtc(struct vop_context *ctx)
> +{
> + struct device *dev = ctx->dev;
> + struct drm_device *drm_dev = ctx->drm_dev;
> + struct drm_plane *primary, *cursor;
> + unsigned long possible_crtcs;
> + struct drm_crtc *crtc;
> + int ret;
> + int nr;
> +
> + ctx->win_mask = 0;
> + crtc = &ctx->crtc;
> +
> + ret = rockchip_drm_add_crtc(drm_dev, crtc, dev->of_node);
> + if (ret < 0)
> + return ret;
> + ctx->pipe = ret;
> +
> + possible_crtcs = (1 << ctx->pipe);
> +
> + primary = rockchip_plane_init(ctx, possible_crtcs,
> + DRM_PLANE_TYPE_PRIMARY);
> + if (!primary) {
> + DRM_ERROR("fail to init primary plane\n");
> + return -EINVAL;
> + }
> +
> + for (nr = 1; nr < ROCKCHIP_MAX_PLANE; nr++) {
> + if (nr == VOP_DEFAULT_CURSOR) {
> + cursor = rockchip_plane_init(ctx, possible_crtcs,
> + DRM_PLANE_TYPE_CURSOR);
> + if (!cursor) {
> + DRM_ERROR("fail to init cursor plane\n");
> + return -EINVAL;
> + }
> + } else {
> + struct drm_plane *plane;
> +
> + plane = rockchip_plane_init(ctx, possible_crtcs,
> + DRM_PLANE_TYPE_OVERLAY);
> + if (!plane) {
> + DRM_ERROR("fail to init overlay plane\n");
> + return -EINVAL;
> + }
> + }
> + }
> +
> + drm_crtc_init_with_planes(drm_dev, crtc, primary, cursor,
> + &rockchip_crtc_funcs);
> + drm_crtc_helper_add(crtc, &rockchip_crtc_helper_funcs);
> +
> + return 0;
> +}
> +
> +static int rockchip_vop_initial(struct vop_context *ctx)
> +{
> + struct vop_driver_data *vop_data = ctx->data;
> + const struct vop_reg_data *init_table = vop_data->init_table;
> + struct reset_control *rst;
> + int i, ret;
> +
> + ctx->hclk = devm_clk_get(ctx->dev, "hclk_vop");
> + if (IS_ERR(ctx->hclk)) {
> + dev_err(ctx->dev, "failed to get hclk source\n");
> + return PTR_ERR(ctx->hclk);
> + }
> + ctx->aclk = devm_clk_get(ctx->dev, "aclk_vop");
> + if (IS_ERR(ctx->aclk)) {
> + dev_err(ctx->dev, "failed to get aclk source\n");
> + return PTR_ERR(ctx->aclk);
> + }
> + ctx->dclk = devm_clk_get(ctx->dev, "dclk_vop");
> + if (IS_ERR(ctx->dclk)) {
> + dev_err(ctx->dev, "failed to get dclk source\n");
> + return PTR_ERR(ctx->dclk);
> + }
> +
> + ret = vop_clk_enable(ctx);
> + if (ret < 0)
> + return ret;
if anything fails after this, remember to disable the clk...
Ok, that is enough for now :-)
Thanks,
-djk
> +
> + /*
> + * do hclk_reset, reset all vop registers.
> + */
> + rst = devm_reset_control_get(ctx->dev, "ahb");
> + if (IS_ERR(rst)) {
> + dev_err(ctx->dev, "failed to get ahb reset\n");
> + return PTR_ERR(rst);
> + }
> + reset_control_assert(rst);
> + usleep_range(10, 20);
> + reset_control_deassert(rst);
> +
> + memcpy(ctx->regsbak, ctx->regs, ctx->len);
> +
> + for (i = 0; i < vop_data->table_size; i++)
> + vop_writel(ctx, init_table[i].offset, init_table[i].value);
> +
> + for (i = 0; i < VOP_MAX_WIN_SUPPORT; i++)
> + VOP_WIN_SET(ctx, vop_data->win[i], enable, 0);
> +
> + vop_cfg_done(ctx);
> +
> + /*
> + * do dclk_reset, let all win config take affect, and then we can enable
> + * iommu safe.
> + */
> + rst = devm_reset_control_get(ctx->dev, "dclk");
> + if (IS_ERR(rst)) {
> + dev_err(ctx->dev, "failed to get dclk reset\n");
> + return PTR_ERR(rst);
> + }
> + reset_control_assert(rst);
> + usleep_range(10, 20);
> + reset_control_deassert(rst);
> +
> + ctx->dpms = DRM_MODE_DPMS_ON;
> +
> + return 0;
> +}
> +
> +static int vop_bind(struct device *dev, struct device *master, void *data)
> +{
> + struct platform_device *pdev = to_platform_device(dev);
> + struct vop_driver_data *vop_data = vop_get_driver_data(dev);
> + struct drm_device *drm_dev = data;
> + struct vop_context *ctx;
> + struct resource *res;
> + int ret;
> +
> + if (!vop_data)
> + return -ENODEV;
> +
> + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
> + if (!ctx)
> + return -ENOMEM;
> +
> + ctx->dev = dev;
> + ctx->data = vop_data;
> + ctx->drm_dev = drm_dev;
> + dev_set_drvdata(dev, ctx);
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + ctx->len = resource_size(res);
> + ctx->regs = devm_ioremap_resource(dev, res);
> + if (IS_ERR(ctx->regs))
> + return PTR_ERR(ctx->regs);
> +
> + ctx->regsbak = devm_kzalloc(dev, ctx->len, GFP_KERNEL);
> + if (!ctx->regsbak)
> + return -ENOMEM;
> +
> + ret = rockchip_vop_initial(ctx);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "cannot initial vop dev - err %d\n", ret);
> + return ret;
> + }
> +
> + ctx->irq = platform_get_irq(pdev, 0);
> + if (ctx->irq < 0) {
> + dev_err(dev, "cannot find irq for vop\n");
> + return ctx->irq;
> + }
> +
> + spin_lock_init(&ctx->reg_lock);
> + spin_lock_init(&ctx->irq_lock);
> +
> + init_waitqueue_head(&ctx->wait_vsync_queue);
> + atomic_set(&ctx->wait_vsync_event, 0);
> +
> + ret = vop_iommu_init(ctx);
> + if (ret) {
> + DRM_ERROR("Failed to setup iommu, %d\n", ret);
> + return ret;
> + }
> +
> + ctx->vsync_wq = create_singlethread_workqueue("vsync");
> + if (!ctx->vsync_wq) {
> + dev_err(dev, "failed to create workqueue\n");
> + return -EINVAL;
> + }
> + INIT_WORK(&ctx->vsync_work, rockchip_vsync_worker);
> +
> + mutex_init(&ctx->vsync_mutex);
> + pm_runtime_enable(&pdev->dev);
> +
> + ret = devm_request_irq(dev, ctx->irq, rockchip_vop_isr,
> + IRQF_SHARED, dev_name(dev), ctx);
> + if (ret) {
> + dev_err(dev, "cannot requeset irq%d - err %d\n", ctx->irq, ret);
> + return ret;
> + }
> +
> + return vop_create_crtc(ctx);
> +}
> +
> +static void vop_unbind(struct device *dev, struct device *master,
> + void *data)
> +{
> + struct drm_device *drm_dev = data;
> + struct vop_context *ctx = dev_get_drvdata(dev);
> + struct drm_crtc *crtc = &ctx->crtc;
> +
> + drm_crtc_cleanup(crtc);
> + pm_runtime_disable(dev);
> + rockchip_drm_remove_crtc(drm_dev, ctx->pipe);
> +
> + vop_iommu_fini(ctx);
> +}
> +
> +static const struct component_ops vop_component_ops = {
> + .bind = vop_bind,
> + .unbind = vop_unbind,
> +};
> +
> +static int vop_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct vop_context *ctx;
> +
> + if (!dev->of_node) {
> + dev_err(dev, "can't find vop devices\n");
> + return -ENODEV;
> + }
> +
> + platform_set_drvdata(pdev, ctx);
> +
> + return component_add(dev, &vop_component_ops);
> +}
> +
> +static int vop_remove(struct platform_device *pdev)
> +{
> + component_del(&pdev->dev, &vop_component_ops);
> +
> + return 0;
> +}
> +
> +struct platform_driver rockchip_vop_platform_driver = {
> + .probe = vop_probe,
> + .remove = vop_remove,
> + .driver = {
> + .name = "rockchip-vop",
> + .owner = THIS_MODULE,
> + .of_match_table = of_match_ptr(vop_driver_dt_match),
> + },
> +};
> +
> +module_platform_driver(rockchip_vop_platform_driver);
> +
> +MODULE_AUTHOR("Mark Yao <mark.yao@rock-chips.com>");
> +MODULE_DESCRIPTION("ROCKCHIP VOP Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> new file mode 100644
> index 0000000..2343760
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> @@ -0,0 +1,187 @@
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:Mark Yao <mark.yao@rock-chips.com>
> + *
> + * 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.
> + */
> +
> +#ifndef _ROCKCHIP_DRM_VOP_H
> +#define _ROCKCHIP_DRM_VOP_H
> +
> +/* register definition */
> +#define REG_CFG_DONE 0x0000
> +#define VERSION_INFO 0x0004
> +#define SYS_CTRL 0x0008
> +#define SYS_CTRL1 0x000c
> +#define DSP_CTRL0 0x0010
> +#define DSP_CTRL1 0x0014
> +#define DSP_BG 0x0018
> +#define MCU_CTRL 0x001c
> +#define INTR_CTRL0 0x0020
> +#define INTR_CTRL1 0x0024
> +#define WIN0_CTRL0 0x0030
> +#define WIN0_CTRL1 0x0034
> +#define WIN0_COLOR_KEY 0x0038
> +#define WIN0_VIR 0x003c
> +#define WIN0_YRGB_MST 0x0040
> +#define WIN0_CBR_MST 0x0044
> +#define WIN0_ACT_INFO 0x0048
> +#define WIN0_DSP_INFO 0x004c
> +#define WIN0_DSP_ST 0x0050
> +#define WIN0_SCL_FACTOR_YRGB 0x0054
> +#define WIN0_SCL_FACTOR_CBR 0x0058
> +#define WIN0_SCL_OFFSET 0x005c
> +#define WIN0_SRC_ALPHA_CTRL 0x0060
> +#define WIN0_DST_ALPHA_CTRL 0x0064
> +#define WIN0_FADING_CTRL 0x0068
> +/* win1 register */
> +#define WIN1_CTRL0 0x0070
> +#define WIN1_CTRL1 0x0074
> +#define WIN1_COLOR_KEY 0x0078
> +#define WIN1_VIR 0x007c
> +#define WIN1_YRGB_MST 0x0080
> +#define WIN1_CBR_MST 0x0084
> +#define WIN1_ACT_INFO 0x0088
> +#define WIN1_DSP_INFO 0x008c
> +#define WIN1_DSP_ST 0x0090
> +#define WIN1_SCL_FACTOR_YRGB 0x0094
> +#define WIN1_SCL_FACTOR_CBR 0x0098
> +#define WIN1_SCL_OFFSET 0x009c
> +#define WIN1_SRC_ALPHA_CTRL 0x00a0
> +#define WIN1_DST_ALPHA_CTRL 0x00a4
> +#define WIN1_FADING_CTRL 0x00a8
> +/* win2 register */
> +#define WIN2_CTRL0 0x00b0
> +#define WIN2_CTRL1 0x00b4
> +#define WIN2_VIR0_1 0x00b8
> +#define WIN2_VIR2_3 0x00bc
> +#define WIN2_MST0 0x00c0
> +#define WIN2_DSP_INFO0 0x00c4
> +#define WIN2_DSP_ST0 0x00c8
> +#define WIN2_COLOR_KEY 0x00cc
> +#define WIN2_MST1 0x00d0
> +#define WIN2_DSP_INFO1 0x00d4
> +#define WIN2_DSP_ST1 0x00d8
> +#define WIN2_SRC_ALPHA_CTRL 0x00dc
> +#define WIN2_MST2 0x00e0
> +#define WIN2_DSP_INFO2 0x00e4
> +#define WIN2_DSP_ST2 0x00e8
> +#define WIN2_DST_ALPHA_CTRL 0x00ec
> +#define WIN2_MST3 0x00f0
> +#define WIN2_DSP_INFO3 0x00f4
> +#define WIN2_DSP_ST3 0x00f8
> +#define WIN2_FADING_CTRL 0x00fc
> +/* win3 register */
> +#define WIN3_CTRL0 0x0100
> +#define WIN3_CTRL1 0x0104
> +#define WIN3_VIR0_1 0x0108
> +#define WIN3_VIR2_3 0x010c
> +#define WIN3_MST0 0x0110
> +#define WIN3_DSP_INFO0 0x0114
> +#define WIN3_DSP_ST0 0x0118
> +#define WIN3_COLOR_KEY 0x011c
> +#define WIN3_MST1 0x0120
> +#define WIN3_DSP_INFO1 0x0124
> +#define WIN3_DSP_ST1 0x0128
> +#define WIN3_SRC_ALPHA_CTRL 0x012c
> +#define WIN3_MST2 0x0130
> +#define WIN3_DSP_INFO2 0x0134
> +#define WIN3_DSP_ST2 0x0138
> +#define WIN3_DST_ALPHA_CTRL 0x013c
> +#define WIN3_MST3 0x0140
> +#define WIN3_DSP_INFO3 0x0144
> +#define WIN3_DSP_ST3 0x0148
> +#define WIN3_FADING_CTRL 0x014c
> +/* hwc register */
> +#define HWC_CTRL0 0x0150
> +#define HWC_CTRL1 0x0154
> +#define HWC_MST 0x0158
> +#define HWC_DSP_ST 0x015c
> +#define HWC_SRC_ALPHA_CTRL 0x0160
> +#define HWC_DST_ALPHA_CTRL 0x0164
> +#define HWC_FADING_CTRL 0x0168
> +/* post process register */
> +#define POST_DSP_HACT_INFO 0x0170
> +#define POST_DSP_VACT_INFO 0x0174
> +#define POST_SCL_FACTOR_YRGB 0x0178
> +#define POST_SCL_CTRL 0x0180
> +#define POST_DSP_VACT_INFO_F1 0x0184
> +#define DSP_HTOTAL_HS_END 0x0188
> +#define DSP_HACT_ST_END 0x018c
> +#define DSP_VTOTAL_VS_END 0x0190
> +#define DSP_VACT_ST_END 0x0194
> +#define DSP_VS_ST_END_F1 0x0198
> +#define DSP_VACT_ST_END_F1 0x019c
> +/* register definition end */
> +
> +/* interrupt define */
> +#define DSP_HOLD_VALID_INTR (1 << 0)
> +#define FS_INTR (1 << 1)
> +#define LINE_FLAG_INTR (1 << 2)
> +#define BUS_ERROR_INTR (1 << 3)
> +
> +#define DSP_HOLD_VALID_INTR_EN(x) ((x) << 4)
> +#define FS_INTR_EN(x) ((x) << 5)
> +#define LINE_FLAG_INTR_EN(x) ((x) << 6)
> +#define BUS_ERROR_INTR_EN(x) ((x) << 7)
> +#define DSP_HOLD_VALID_INTR_MASK (1 << 4)
> +#define FS_INTR_EN_MASK (1 << 5)
> +#define LINE_FLAG_INTR_MASK (1 << 6)
> +#define BUS_ERROR_INTR_MASK (1 << 7)
> +
> +#define DSP_HOLD_VALID_INTR_CLR (1 << 8)
> +#define FS_INTR_EN_CLR (1 << 9)
> +#define LINE_FLAG_INTR_CLR (1 << 10)
> +#define BUS_ERROR_INTR_CLR (1 << 11)
> +#define DSP_LINE_NUM(x) (((x) & 0x1fff) << 12)
> +#define DSP_LINE_NUM_MASK (0x1fff << 12)
> +
> +/* src alpha ctrl define */
> +#define SRC_FADING_VALUE(x) (((x) & 0xff) << 24)
> +#define SRC_GLOBAL_ALPHA(x) (((x) & 0xff) << 16)
> +#define SRC_FACTOR_M0(x) (((x) & 0x7) << 6)
> +#define SRC_ALPHA_CAL_M0(x) (((x) & 0x1) << 5)
> +#define SRC_BLEND_M0(x) (((x) & 0x3) << 3)
> +#define SRC_ALPHA_M0(x) (((x) & 0x1) << 2)
> +#define SRC_COLOR_M0(x) (((x) & 0x1) << 1)
> +#define SRC_ALPHA_EN(x) (((x) & 0x1) << 0)
> +/* dst alpha ctrl define */
> +#define DST_FACTOR_M0(x) (((x) & 0x7) << 6)
> +
> +enum alpha_mode {
> + ALPHA_STRAIGHT,
> + ALPHA_INVERSE,
> +};
> +
> +enum global_blend_mode {
> + ALPHA_GLOBAL,
> + ALPHA_PER_PIX,
> + ALPHA_PER_PIX_GLOBAL,
> +};
> +
> +enum alpha_cal_mode {
> + ALPHA_SATURATION,
> + ALPHA_NO_SATURATION,
> +};
> +
> +enum color_mode {
> + ALPHA_SRC_PRE_MUL,
> + ALPHA_SRC_NO_PRE_MUL,
> +};
> +
> +enum factor_mode {
> + ALPHA_ZERO,
> + ALPHA_ONE,
> + ALPHA_SRC,
> + ALPHA_SRC_INVERSE,
> + ALPHA_SRC_GLOBAL,
> +};
> +
> +#endif /* _ROCKCHIP_DRM_VOP_H */
> --
> 1.7.9.5
>
^ permalink raw reply
* Re: [PATCH 4/4] Adds ioctl interface support for ext4 project
From: Jan Kara @ 2014-09-24 16:25 UTC (permalink / raw)
To: Li Xi
Cc: linux-fsdevel, linux-ext4, linux-api, tytso, adilger, jack, viro,
hch, dmonakhov, xfs
In-Reply-To: <1411567470-31799-5-git-send-email-lixi@ddn.com>
On Wed 24-09-14 22:04:30, Li Xi wrote:
> This patch adds ioctl interface for setting/getting project of ext4.
The patch looks good to me. I was just wondering whether it won't be
useful to add an ioctl() which isn't ext4 specific. We could just extend
->setattr() to allow setting of project ID (most filesystems would just
return -EOPNOTSUPP but ext4 and xfs could do the right thing) and then call
->setattr from the generic ioctl. That way userspace won't have to care
about filesystem type when setting project ID... What do others think?
Honza
> Signed-off-by: Li Xi <lixi@ddn.com>
> ---
> Documentation/filesystems/ext4.txt | 4 ++
> fs/ext4/ext4.h | 2 +
> fs/ext4/ioctl.c | 85 ++++++++++++++++++++++++++++++++++++
> 3 files changed, 91 insertions(+), 0 deletions(-)
>
> diff --git a/Documentation/filesystems/ext4.txt b/Documentation/filesystems/ext4.txt
> index 919a329..9c98e62 100644
> --- a/Documentation/filesystems/ext4.txt
> +++ b/Documentation/filesystems/ext4.txt
> @@ -609,6 +609,10 @@ EXT4_IOC_SWAP_BOOT Swap i_blocks and associated attributes
> The data blocks of the previous boot loader
> will be associated with the given inode.
>
> + EXT4_IOC_GETPROJECT Get project ID associated with inode.
> +
> + EXT4_IOC_SETPROJECT Set Project ID associated with inode.
> +
> ..............................................................................
>
> References
> diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
> index f8be9bf..51946fd 100644
> --- a/fs/ext4/ext4.h
> +++ b/fs/ext4/ext4.h
> @@ -617,6 +617,8 @@ enum {
> #define EXT4_IOC_RESIZE_FS _IOW('f', 16, __u64)
> #define EXT4_IOC_SWAP_BOOT _IO('f', 17)
> #define EXT4_IOC_PRECACHE_EXTENTS _IO('f', 18)
> +#define EXT4_IOC_GETPROJECT _IOR('f', 19, long)
> +#define EXT4_IOC_SETPROJECT _IOW('f', 20, long)
>
> #if defined(__KERNEL__) && defined(CONFIG_COMPAT)
> /*
> diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
> index 0f2252e..93b7ff4 100644
> --- a/fs/ext4/ioctl.c
> +++ b/fs/ext4/ioctl.c
> @@ -14,6 +14,8 @@
> #include <linux/compat.h>
> #include <linux/mount.h>
> #include <linux/file.h>
> +#include <linux/quotaops.h>
> +#include <linux/quota.h>
> #include <asm/uaccess.h>
> #include "ext4_jbd2.h"
> #include "ext4.h"
> @@ -611,6 +613,89 @@ resizefs_out:
> case EXT4_IOC_PRECACHE_EXTENTS:
> return ext4_ext_precache(inode);
>
> + case EXT4_IOC_GETPROJECT:
> + {
> + __u32 projid;
> +
> + projid = (__u32)from_kprojid(&init_user_ns,
> + EXT4_I(inode)->i_projid);
> + return put_user(projid, (__u32 __user *) arg);
> + }
> + case EXT4_IOC_SETPROJECT:
> + {
> + __u32 projid;
> + int err;
> + handle_t *handle;
> + kprojid_t kprojid;
> + struct ext4_iloc iloc;
> + struct ext4_inode *raw_inode;
> +
> + struct dquot *transfer_to[EXT4_MAXQUOTAS] = { };
> +
> + /* Make sure caller can change project. */
> + if (!capable(CAP_SYS_ADMIN))
> + return -EACCES;
> +
> + if (get_user(projid, (__u32 __user *) arg))
> + return -EFAULT;
> +
> + kprojid = make_kprojid(&init_user_ns, (projid_t)projid);
> +
> + if (projid_eq(kprojid, EXT4_I(inode)->i_projid))
> + return 0;
> +
> + err = mnt_want_write_file(filp);
> + if (err)
> + return err;
> +
> + err = -EPERM;
> + mutex_lock(&inode->i_mutex);
> + /* Is it quota file? Do not allow user to mess with it */
> + if (IS_NOQUOTA(inode))
> + goto project_out;
> +
> + dquot_initialize(inode);
> +
> + handle = ext4_journal_start(inode, EXT4_HT_QUOTA,
> + EXT4_QUOTA_INIT_BLOCKS(sb) +
> + EXT4_QUOTA_DEL_BLOCKS(sb) + 3);
> + if (IS_ERR(handle)) {
> + err = PTR_ERR(handle);
> + goto project_out;
> + }
> +
> + err = ext4_reserve_inode_write(handle, inode, &iloc);
> + if (err)
> + goto project_stop;
> +
> + raw_inode = ext4_raw_inode(&iloc);
> + if ((EXT4_INODE_SIZE(inode->i_sb) <=
> + EXT4_GOOD_OLD_INODE_SIZE) ||
> + (!EXT4_FITS_IN_INODE(raw_inode, ei, i_projid))) {
> + err = -EFBIG;
> + goto project_stop;
> + }
> +
> + transfer_to[PRJQUOTA] = dqget(sb, make_kqid_projid(kprojid));
> + if (!transfer_to[PRJQUOTA])
> + goto project_set;
> +
> + err = __dquot_transfer(inode, transfer_to);
> + dqput(transfer_to[PRJQUOTA]);
> + if (err)
> + goto project_stop;
> +
> +project_set:
> + EXT4_I(inode)->i_projid = kprojid;
> + inode->i_ctime = ext4_current_time(inode);
> + err = ext4_mark_iloc_dirty(handle, inode, &iloc);
> +project_stop:
> + ext4_journal_stop(handle);
> +project_out:
> + mutex_unlock(&inode->i_mutex);
> + mnt_drop_write_file(filp);
> + return err;
> + }
> default:
> return -ENOTTY;
> }
> --
> 1.7.1
>
--
Jan Kara <jack@suse.cz>
SUSE Labs, CR
^ permalink raw reply
* Re: [PATCH 4/4] Adds ioctl interface support for ext4 project
From: Christoph Hellwig @ 2014-09-24 16:26 UTC (permalink / raw)
To: Jan Kara
Cc: Li Xi, linux-fsdevel, linux-ext4, linux-api, tytso, adilger, viro,
hch, dmonakhov, xfs
In-Reply-To: <20140924162507.GC27000@quack.suse.cz>
On Wed, Sep 24, 2014 at 06:25:07PM +0200, Jan Kara wrote:
> On Wed 24-09-14 22:04:30, Li Xi wrote:
> > This patch adds ioctl interface for setting/getting project of ext4.
> The patch looks good to me. I was just wondering whether it won't be
> useful to add an ioctl() which isn't ext4 specific. We could just extend
> ->setattr() to allow setting of project ID (most filesystems would just
> return -EOPNOTSUPP but ext4 and xfs could do the right thing) and then call
> ->setattr from the generic ioctl. That way userspace won't have to care
> about filesystem type when setting project ID... What do others think?
Absolutely. In general I also wonder why this patch doesn't implement
the full XFS API. Maybe there is a reason it was considered and
rejected, but it would be helpful to document why.
^ permalink raw reply
* Re: [RFC PATCH net-next v2 0/5] netns: allow to identify peer netns
From: Nicolas Dichtel @ 2014-09-24 16:27 UTC (permalink / raw)
To: Cong Wang
Cc: netdev, containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Andy Lutomirski, Stephen Hemminger, Eric W. Biederman,
linux-api-u79uwXL29TY76Z2rM5mHXA, Andrew Morton, David Miller
In-Reply-To: <CAHA+R7NfJYzCsZx0E9YVXKVCQbCm_thPSi+80tix8Z9nVA82Ug-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
Le 24/09/2014 18:01, Cong Wang a écrit :
> On Wed, Sep 24, 2014 at 2:23 AM, Nicolas Dichtel
> <nicolas.dichtel@6wind.com> wrote:
>> Le 23/09/2014 21:22, Cong Wang a écrit :
>>
>>> On Tue, Sep 23, 2014 at 6:20 AM, Nicolas Dichtel
>>> <nicolas.dichtel@6wind.com> wrote:
>>>>
>>>>
>>>> Here is a small screenshot to show how it can be used by userland:
>>>> $ ip netns add foo
>>>> $ ip netns del foo
>>>> $ ip netns
>>>> $ touch /var/run/netns/init_net
>>>> $ mount --bind /proc/1/ns/net /var/run/netns/init_net
>>>> $ ip netns add foo
>>>> $ ip netns
>>>> foo (id: 3)
>>>> init_net (id: 1)
>>>> $ ip netns exec foo ip netns
>>>> foo (id: 3)
>>>> init_net (id: 1)
>>>> $ ip netns exec foo ip link add ipip1 link-netnsid 1 type ipip remote
>>>> 10.16.0.121 local 10.16.0.249
>>>> $ ip netns exec foo ip l ls ipip1
>>>> 6: ipip1@NONE: <POINTOPOINT,NOARP> mtu 1480 qdisc noop state DOWN mode
>>>> DEFAULT group default
>>>> link/ipip 10.16.0.249 peer 10.16.0.121 link-netnsid 1
>>>>
>>>> The parameter link-netnsid shows us where the interface sends and
>>>> receives
>>>> packets (and thus we know where encapsulated addresses are set).
>>>>
>>>
>>> So ipip1 is shown in netns foo but functioning in netns init_net? Getting
>>> the
>>> id of init_net in foo depends on your mount namespace, /var/run/netns/ may
>>> not visible inside foo, in this case, link-netnsid is meaningless. It
>>> is not your
>>> fault, network namespace already heavily relies on mount namespace (sysfs
>>> needs to be remount otherwise you can not create device with the same
>>> name.)
>>>
>>> On the other hand, what's the problem you are trying to solve? AFAIK,
>>> the ifindex
>>> issue is purely in output, IOW, the device still functions correctly
>>> even through
>>> its link ifindex is not correct after moving to another namespace. If
>>> not, it is bug
>>> we need to fix.
>>>
>> The problem is explained here:
>> http://thread.gmane.org/gmane.linux.network/315933/focus=316064
>> and here:
>> http://thread.gmane.org/gmane.linux.kernel.containers/28301/focus=4239
>>
>
> Please, summarize the discussion in your changelog, instead of pointing
> to a long thread.
The thread is long, but the mail in focus contains the information. Here is a
copy and paste:
What I'm trying to solve is to have full info in netlink messages sent by the
kernel, thus beeing able to identify a peer netns (and this is close from what
audit guys are trying to have). Theorically, messages sent by the kernel can be
reused as is to have the same configuration. This is not the case with x-netns
devices. Here is an example, with ip tunnels:
$ ip netns add 1
$ ip link add ipip1 type ipip remote 10.16.0.121 local 10.16.0.249 dev eth0
$ ip -d link ls ipip1
8: ipip1 <at> eth0: <POINTOPOINT,NOARP> mtu 1480 qdisc noop state DOWN mode DEFAULT
group default
link/ipip 10.16.0.249 peer 10.16.0.121 promiscuity 0
ipip remote 10.16.0.121 local 10.16.0.249 dev eth0 ttl inherit pmtudisc
$ ip link set ipip1 netns 1
$ ip netns exec 1 ip -d link ls ipip1
8: ipip1 <at> tunl0: <POINTOPOINT,NOARP,M-DOWN> mtu 1480 qdisc noop state DOWN mode
DEFAULT group default
link/ipip 10.16.0.249 peer 10.16.0.121 promiscuity 0
ipip remote 10.16.0.121 local 10.16.0.249 dev tunl0 ttl inherit pmtudisc
Now informations got with 'ip link' are wrong and incomplete:
- the link dev is now tunl0 instead of eth0, because we only got an ifindex
from the kernel without any netns informations.
- the encapsulation addresses are not part of this netns but the user doesn't
known that (still because netns info is missing). These IPv4 addresses may
exist into this netns.
- it's not possible to create the same netdevice with these infos.
Hope it's more clear now.
>
> And clearly you missed my question above: how do you get netns id
> without sharing /var/run/netns/ ?
>
You can get an id only if you already have a "pointer" to this netns.
_______________________________________________
Containers mailing list
Containers@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/containers
^ permalink raw reply
* Re: [RFC PATCH net-next v2 0/5] netns: allow to identify peer netns
From: Nicolas Dichtel @ 2014-09-24 16:31 UTC (permalink / raw)
To: Cong Wang
Cc: netdev, containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Andy Lutomirski, Stephen Hemminger, Eric W. Biederman,
linux-api-u79uwXL29TY76Z2rM5mHXA, Andrew Morton, David Miller
In-Reply-To: <CAHA+R7MVL=WpepRy8iz6iT6Kkq1RHG+b9TxJothP94ixyAj-3Q-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
Le 24/09/2014 18:15, Cong Wang a écrit :
> On Wed, Sep 24, 2014 at 9:01 AM, Cong Wang <cwang@twopensource.com> wrote:
>>
>> And clearly you missed my question above: how do you get netns id
>> without sharing /var/run/netns/ ?
>
> OK, I found it:
>
>> Ids are stored in the parent user namespace. These ids are valid only inside
>> this user namespace. The user can retrieve these ids via a new netlink messages,
>> but only if peer netns are in the same user namespace.
>
> So your example is confusing, perhaps you need some other way to show the ID's
> instead of binding to ip netns output which is basically ls
> /var/run/netns/. We don't
> want an inner netns know anything outside, IOW, we don't share /var/run/netns/.
Hmm, not sure to understand you. My usecase shares /var/run/netns, because
there is only one user ns and one mount ns.
> I think in this case your ID's are still available, but aren't you
> providing a new way
> for the inner netns device to escape which we are trying to avoid?
It's why the ids depend on user ns. Only if user ns are the same we allow to
get an id for a peer netns.
_______________________________________________
Containers mailing list
Containers@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/containers
^ permalink raw reply
* Re: [PATCH 4/4] Adds ioctl interface support for ext4 project
From: Jan Kara @ 2014-09-24 16:43 UTC (permalink / raw)
To: Li Xi
Cc: linux-fsdevel-u79uwXL29TY76Z2rM5mHXA,
linux-ext4-u79uwXL29TY76Z2rM5mHXA,
linux-api-u79uwXL29TY76Z2rM5mHXA, tytso-3s7WtUTddSA,
adilger-m1MBpc4rdrD3fQ9qLvQP4Q, jack-AlSwsSmVLrQ,
viro-RmSDqhL/yNMiFSDQTTA3OLVCufUGDwFn, hch-wEGCiKHe2LqWVfeAwA7xHQ,
dmonakhov-GEFAQzZX7r8dnm+yROfE0A
In-Reply-To: <1411567470-31799-5-git-send-email-lixi-LfVdkaOWEx8@public.gmane.org>
On Wed 24-09-14 22:04:30, Li Xi wrote:
> This patch adds ioctl interface for setting/getting project of ext4.
One more thing...
> Signed-off-by: Li Xi <lixi-LfVdkaOWEx8@public.gmane.org>
> ---
> Documentation/filesystems/ext4.txt | 4 ++
> fs/ext4/ext4.h | 2 +
> fs/ext4/ioctl.c | 85 ++++++++++++++++++++++++++++++++++++
> 3 files changed, 91 insertions(+), 0 deletions(-)
>
> + case EXT4_IOC_SETPROJECT:
> + {
> + __u32 projid;
> + int err;
> + handle_t *handle;
> + kprojid_t kprojid;
> + struct ext4_iloc iloc;
> + struct ext4_inode *raw_inode;
> +
> + struct dquot *transfer_to[EXT4_MAXQUOTAS] = { };
> +
> + /* Make sure caller can change project. */
> + if (!capable(CAP_SYS_ADMIN))
> + return -EACCES;
I realized you forgot to check EXT4_FEATURE_RO_COMPAT_PROJECT here...
Honza
> +
> + if (get_user(projid, (__u32 __user *) arg))
> + return -EFAULT;
> +
> + kprojid = make_kprojid(&init_user_ns, (projid_t)projid);
> +
> + if (projid_eq(kprojid, EXT4_I(inode)->i_projid))
> + return 0;
> +
> + err = mnt_want_write_file(filp);
> + if (err)
> + return err;
> +
> + err = -EPERM;
> + mutex_lock(&inode->i_mutex);
> + /* Is it quota file? Do not allow user to mess with it */
> + if (IS_NOQUOTA(inode))
> + goto project_out;
> +
> + dquot_initialize(inode);
> +
> + handle = ext4_journal_start(inode, EXT4_HT_QUOTA,
> + EXT4_QUOTA_INIT_BLOCKS(sb) +
> + EXT4_QUOTA_DEL_BLOCKS(sb) + 3);
> + if (IS_ERR(handle)) {
> + err = PTR_ERR(handle);
> + goto project_out;
> + }
> +
> + err = ext4_reserve_inode_write(handle, inode, &iloc);
> + if (err)
> + goto project_stop;
> +
> + raw_inode = ext4_raw_inode(&iloc);
> + if ((EXT4_INODE_SIZE(inode->i_sb) <=
> + EXT4_GOOD_OLD_INODE_SIZE) ||
> + (!EXT4_FITS_IN_INODE(raw_inode, ei, i_projid))) {
> + err = -EFBIG;
> + goto project_stop;
> + }
> +
> + transfer_to[PRJQUOTA] = dqget(sb, make_kqid_projid(kprojid));
> + if (!transfer_to[PRJQUOTA])
> + goto project_set;
> +
> + err = __dquot_transfer(inode, transfer_to);
> + dqput(transfer_to[PRJQUOTA]);
> + if (err)
> + goto project_stop;
> +
> +project_set:
> + EXT4_I(inode)->i_projid = kprojid;
> + inode->i_ctime = ext4_current_time(inode);
> + err = ext4_mark_iloc_dirty(handle, inode, &iloc);
> +project_stop:
> + ext4_journal_stop(handle);
> +project_out:
> + mutex_unlock(&inode->i_mutex);
> + mnt_drop_write_file(filp);
> + return err;
> + }
> default:
> return -ENOTTY;
> }
> --
> 1.7.1
>
--
Jan Kara <jack-AlSwsSmVLrQ@public.gmane.org>
SUSE Labs, CR
^ permalink raw reply
* Re: [RFC PATCH net-next v2 0/5] netns: allow to identify peer netns
From: Cong Wang @ 2014-09-24 16:45 UTC (permalink / raw)
To: Nicolas Dichtel
Cc: netdev, containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Andy Lutomirski, Stephen Hemminger, Eric W. Biederman,
linux-api-u79uwXL29TY76Z2rM5mHXA, Andrew Morton, David Miller
In-Reply-To: <5422F0F4.6000709-pdR9zngts4EAvxtiuMwx3w@public.gmane.org>
On Wed, Sep 24, 2014 at 9:27 AM, Nicolas Dichtel
<nicolas.dichtel-pdR9zngts4EAvxtiuMwx3w@public.gmane.org> wrote:
> Now informations got with 'ip link' are wrong and incomplete:
> - the link dev is now tunl0 instead of eth0, because we only got an
> ifindex
> from the kernel without any netns informations.
This is not new, macvlan has the same problem. This is why I said
it is mostly a display problem, maybe just mark the ifindex as -1 or
something when it is not in this netns. At least I don't expect the inner
netns know anything outside, and I don't think I am the only one using
netns in this way.
> - the encapsulation addresses are not part of this netns but the user
> doesn't
> known that (still because netns info is missing). These IPv4 addresses
> may
> exist into this netns.
I don't remember your x-netns code, but we have two choices:
1) Lookup the route of the netns which it is in
If the address is not available in this netns, it will fail, this is expected
since tunnel device is not a pure L2 device. Or maybe just fail
early when we move it.
2) Lookup the route of the netns where it was created
Transparent for upper layer, but as you said, the outer address is not
available in this netns therefore hard to display. Just hiding this information
doesn't seem wrong to me.
> - it's not possible to create the same netdevice with these infos.
>
This is expected, because after all you are already in a different netns.
^ permalink raw reply
* Re: [RFC PATCH net-next v2 0/5] netns: allow to identify peer netns
From: Cong Wang @ 2014-09-24 16:48 UTC (permalink / raw)
To: Nicolas Dichtel
Cc: netdev, containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Andy Lutomirski, Stephen Hemminger, Eric W. Biederman,
linux-api-u79uwXL29TY76Z2rM5mHXA, Andrew Morton, David Miller
In-Reply-To: <5422F1F7.8010308-pdR9zngts4EAvxtiuMwx3w@public.gmane.org>
On Wed, Sep 24, 2014 at 9:31 AM, Nicolas Dichtel
<nicolas.dichtel-pdR9zngts4EAvxtiuMwx3w@public.gmane.org> wrote:
>> I think in this case your ID's are still available, but aren't you
>> providing a new way
>> for the inner netns device to escape which we are trying to avoid?
>
> It's why the ids depend on user ns. Only if user ns are the same we allow to
> get an id for a peer netns.
Too late, userns is relatively new, relying on it breaks our existing
assumption.
^ permalink raw reply
* Re: [PATCH 4/4] Adds ioctl interface support for ext4 project
From: Jan Kara @ 2014-09-24 17:01 UTC (permalink / raw)
To: Christoph Hellwig
Cc: Jan Kara, Li Xi, linux-fsdevel, linux-ext4, linux-api, tytso,
adilger, viro, dmonakhov, xfs
In-Reply-To: <20140924162634.GA16886@infradead.org>
On Wed 24-09-14 09:26:34, Christoph Hellwig wrote:
> On Wed, Sep 24, 2014 at 06:25:07PM +0200, Jan Kara wrote:
> > On Wed 24-09-14 22:04:30, Li Xi wrote:
> > > This patch adds ioctl interface for setting/getting project of ext4.
> > The patch looks good to me. I was just wondering whether it won't be
> > useful to add an ioctl() which isn't ext4 specific. We could just extend
> > ->setattr() to allow setting of project ID (most filesystems would just
> > return -EOPNOTSUPP but ext4 and xfs could do the right thing) and then call
> > ->setattr from the generic ioctl. That way userspace won't have to care
> > about filesystem type when setting project ID... What do others think?
>
> Absolutely. In general I also wonder why this patch doesn't implement
> the full XFS API. Maybe there is a reason it was considered and
> rejected, but it would be helpful to document why.
Do you mean full get/setfsxattr API? That basically contains project ID,
flags (those that are currently get/set with FS_IOC_GETFLAGS/SETFLAGS), and
extent size hint right? That seems workable and it would also make setting
of PROJINHERIT flag fs agnostic. Only we would have to create some generic
flags namespace and merge into that ext4 flags and have a translation
function for the old ext4 flags. Also I'm afraid we may quickly run out of
32 available flags in xflags so we'd need to extend that. But all this
seems to be doable.
Honza
--
Jan Kara <jack@suse.cz>
SUSE Labs, CR
^ permalink raw reply
* Re: [RFC PATCH net-next v2 0/5] netns: allow to identify peer netns
From: Andy Lutomirski @ 2014-09-24 17:05 UTC (permalink / raw)
To: Nicolas Dichtel
Cc: Network Development, Linux Containers,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Linux API,
David S. Miller, Eric W. Biederman, Stephen Hemminger,
Andrew Morton
In-Reply-To: <54228F8B.2030804-pdR9zngts4EAvxtiuMwx3w@public.gmane.org>
On Wed, Sep 24, 2014 at 2:31 AM, Nicolas Dichtel
<nicolas.dichtel-pdR9zngts4EAvxtiuMwx3w@public.gmane.org> wrote:
> Le 23/09/2014 21:26, Andy Lutomirski a écrit :
>
>> On Tue, Sep 23, 2014 at 6:20 AM, Nicolas Dichtel
>> <nicolas.dichtel-pdR9zngts4EAvxtiuMwx3w@public.gmane.org> wrote:
>>>
>>> The goal of this serie is to be able to multicast netlink messages with
>>> an
>>> attribute that identify a peer netns.
>>> This is needed by the userland to interpret some informations contained
>>> in
>>> netlink messages (like IFLA_LINK value, but also some other attributes in
>>> case
>>> of x-netns netdevice (see also
>>> http://thread.gmane.org/gmane.linux.network/315933/focus=316064 and
>>> http://thread.gmane.org/gmane.linux.kernel.containers/28301/focus=4239)).
>>>
>>> Ids are stored in the parent user namespace. These ids are valid only
>>> inside
>>> this user namespace. The user can retrieve these ids via a new netlink
>>> messages,
>>> but only if peer netns are in the same user namespace.
>>
>>
>> What about the parent / ancestors of the owning userns? Can processes
>> in those usernses see any form of netns id?
>
> With this serie no. I'm not sure if ancestors really needs to be able to
> get these ids. What is your opinion?
I might be missing some consideration here, but I would hope that ip
link would work correctly if I have a veth interface shared with a
netns that's in a child userns.
--Andy
--
Andy Lutomirski
AMA Capital Management, LLC
^ permalink raw reply
* Re: [PATCH 1/4] Adds general codes to enforces project quota limits
From: Jan Kara @ 2014-09-24 17:10 UTC (permalink / raw)
To: Christoph Hellwig
Cc: Li Xi, linux-fsdevel-u79uwXL29TY76Z2rM5mHXA,
linux-ext4-u79uwXL29TY76Z2rM5mHXA,
linux-api-u79uwXL29TY76Z2rM5mHXA, tytso-3s7WtUTddSA,
adilger-m1MBpc4rdrD3fQ9qLvQP4Q, jack-AlSwsSmVLrQ,
viro-RmSDqhL/yNMiFSDQTTA3OLVCufUGDwFn,
dmonakhov-GEFAQzZX7r8dnm+yROfE0A
In-Reply-To: <20140924161417.GA1978-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org>
On Wed 24-09-14 09:14:17, Christoph Hellwig wrote:
> > -#define MAXQUOTAS 2
> > +#define MAXQUOTAS 3
>
> This bloats every single inode in the system. I think this would be a
> good opportunity to move i_dquot into the per-filesystem structures
> and provide a little get_dquots methods to get at them from the
> quota code.
Yeah, I'm aware of that but I decided I won't bother Li Xi with that
since it's independent issue and 8 bytes aren't that terrible. Personally
I somewhat prefer what I did in
http://www.spinics.net/lists/linux-fsdevel/msg74927.html
where we don't introduce additional method but rather a table with field
offsets in superblock. If people agree with this, I can cook up a patch for
quota relatively quickly.
Honza
--
Jan Kara <jack-AlSwsSmVLrQ@public.gmane.org>
SUSE Labs, CR
^ permalink raw reply
* Re: [PATCH 2/4] Adds project ID support for ext4
From: Jan Kara @ 2014-09-24 17:11 UTC (permalink / raw)
To: Li Xi
Cc: linux-fsdevel, linux-ext4, linux-api, tytso, adilger, jack, viro,
hch, dmonakhov
In-Reply-To: <1411567470-31799-3-git-send-email-lixi@ddn.com>
On Wed 24-09-14 22:04:28, Li Xi wrote:
> This patch adds a new internal field of ext4 inode to save project
> identifier. Also a new flag EXT4_INODE_PROJINHERIT is added for
> inheriting project ID from parent directory.
Nice. Just a few smaller things below.
Firstly, I think your patch forgets to add a check to EXT4_IOC_SETFLAGS
ioctl so that EXT4_INODE_PROJINHERIT flag can be set only when
EXT4_FEATURE_RO_COMPAT_PROJECT is enabled.
> Signed-off-by: Li Xi <lixi@ddn.com>
> ---
> fs/ext4/ext4.h | 21 +++++++++++++++++----
> fs/ext4/ialloc.c | 6 ++++++
> fs/ext4/inode.c | 29 ++++++++++++++++++++++++++++-
> fs/ext4/namei.c | 10 ++++++++++
> fs/ext4/super.c | 1 +
> 5 files changed, 62 insertions(+), 5 deletions(-)
>
...
> diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
> index 5b87fc3..b6b7284 100644
> --- a/fs/ext4/ialloc.c
> +++ b/fs/ext4/ialloc.c
> @@ -756,6 +756,12 @@ struct inode *__ext4_new_inode(handle_t *handle, struct inode *dir,
> inode->i_gid = dir->i_gid;
> } else
> inode_init_owner(inode, dir, mode);
> + if (ext4_test_inode_flag(dir, EXT4_INODE_PROJINHERIT)) {
> + ei->i_projid = EXT4_I(dir)->i_projid;
> + } else {
> + ei->i_projid =
> + make_kprojid(&init_user_ns, EXT4_DEF_PROJID);
> + }
> dquot_initialize(inode);
>
> if (!goal)
> diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
> index d5dd7d4..8c641eb 100644
> --- a/fs/ext4/inode.c
> +++ b/fs/ext4/inode.c
> @@ -3874,6 +3874,24 @@ static inline void ext4_iget_extra_inode(struct inode *inode,
> EXT4_I(inode)->i_inline_off = 0;
> }
>
> +static projid_t ext4_inode_projid_get(struct inode *inode, struct ext4_inode *raw,
> + struct ext4_inode_info *ei)
> +{
> + return (projid_t)le32_to_cpu(raw->i_projid);
> +}
> +
> +static void ext4_inode_projid_set(struct inode *inode, struct ext4_inode *raw,
> + struct ext4_inode_info *ei, projid_t projid)
> +{
> + raw->i_projid = cpu_to_le32(projid);
> +}
I don't see a need for these two wrappers. IMHO they just obfuscate the
code. Please just write the code directly in appropriate place.
> @@ -4204,7 +4228,8 @@ static int ext4_do_update_inode(handle_t *handle,
> EXT4_INODE_SET_XTIME(i_atime, inode, raw_inode);
> EXT4_EINODE_SET_XTIME(i_crtime, ei, raw_inode);
>
> - if (ext4_inode_blocks_set(handle, raw_inode, ei)) {
> + err = ext4_inode_blocks_set(handle, raw_inode, ei);
> + if (err) {
> spin_unlock(&ei->i_raw_lock);
> goto out_brelse;
> }
This is an unrelated fix, right? Please send as a separate patch. Thanks!
> @@ -4255,6 +4280,8 @@ static int ext4_do_update_inode(handle_t *handle,
>
> ext4_inode_csum_set(inode, raw_inode, ei);
>
> + ext4_inode_projid_set(inode, raw_inode, ei, i_projid);
> +
This is wrong - you have to set project ID before a checksum is computed.
Honza
--
Jan Kara <jack@suse.cz>
SUSE Labs, CR
^ permalink raw reply
* Re: [PATCH 1/4] Adds general codes to enforces project quota limits
From: Christoph Hellwig @ 2014-09-24 17:13 UTC (permalink / raw)
To: Jan Kara
Cc: Christoph Hellwig, Li Xi, linux-fsdevel-u79uwXL29TY76Z2rM5mHXA,
linux-ext4-u79uwXL29TY76Z2rM5mHXA,
linux-api-u79uwXL29TY76Z2rM5mHXA, tytso-3s7WtUTddSA,
adilger-m1MBpc4rdrD3fQ9qLvQP4Q,
viro-RmSDqhL/yNMiFSDQTTA3OLVCufUGDwFn,
dmonakhov-GEFAQzZX7r8dnm+yROfE0A
In-Reply-To: <20140924171020.GF27000-+0h/O2h83AeN3ZZ/Hiejyg@public.gmane.org>
On Wed, Sep 24, 2014 at 07:10:20PM +0200, Jan Kara wrote:
> Yeah, I'm aware of that but I decided I won't bother Li Xi with that
> since it's independent issue and 8 bytes aren't that terrible.
For the inode of which we have so many instances it is. Especially when
only one filesystem can make use of this new field.
> Personally
> I somewhat prefer what I did in
> http://www.spinics.net/lists/linux-fsdevel/msg74927.html
> where we don't introduce additional method but rather a table with field
> offsets in superblock. If people agree with this, I can cook up a patch for
> quota relatively quickly.
There's an even better way. We stopped calling the dquot_* functions
from generic code, so now the filesystem can simply pass in a pointer
to the dquot array.
^ permalink raw reply
* Re: [PATCH v2 2/3] tools: adding clean target to user selftest
From: Shuah Khan @ 2014-09-24 17:20 UTC (permalink / raw)
To: Peter Feiner
Cc: linux-api, linux-kernel, David Herrmann, Greg Thelen,
Hugh Dickens, Andrew Morton, Shuah Khan
In-Reply-To: <1411502120-28219-3-git-send-email-pfeiner@google.com>
On 09/23/2014 01:55 PM, Peter Feiner wrote:
> Gets rid of this error when running 'make clean' in the selftests
> directory:
>
> make[1]: Entering directory `.../tools/testing/selftests/user'
> make[1]: *** No rule to make target `clean'. Stop.
>
Signed-off is missing. Please make sure patches have
your Signed-off
-- Shuah
--
Shuah Khan
Sr. Linux Kernel Developer
Samsung Research America (Silicon Valley)
shuahkh@osg.samsung.com | (970) 217-8978
^ permalink raw reply
* Re: [PATCH 3/4] Adds project quota support for ext4
From: Jan Kara @ 2014-09-24 17:31 UTC (permalink / raw)
To: Li Xi
Cc: linux-fsdevel-u79uwXL29TY76Z2rM5mHXA,
linux-ext4-u79uwXL29TY76Z2rM5mHXA,
linux-api-u79uwXL29TY76Z2rM5mHXA, tytso-3s7WtUTddSA,
adilger-m1MBpc4rdrD3fQ9qLvQP4Q, jack-AlSwsSmVLrQ,
viro-RmSDqhL/yNMiFSDQTTA3OLVCufUGDwFn, hch-wEGCiKHe2LqWVfeAwA7xHQ,
dmonakhov-GEFAQzZX7r8dnm+yROfE0A
In-Reply-To: <1411567470-31799-4-git-send-email-lixi-LfVdkaOWEx8@public.gmane.org>
On Wed 24-09-14 22:04:29, Li Xi wrote:
> This patch adds mount options for enabling/disabling project quota
> accounting and enforcement. A new specific inode is also used for
> project quota accounting.
The patch looks mostly fine. A few smaller things below.
...
> @@ -1433,6 +1437,8 @@ static const struct mount_opts {
> MOPT_SET | MOPT_Q},
> {Opt_grpquota, EXT4_MOUNT_QUOTA | EXT4_MOUNT_GRPQUOTA,
> MOPT_SET | MOPT_Q},
> + {Opt_prjquota, EXT4_MOUNT_QUOTA | EXT4_MOUNT_PRJQUOTA,
> + MOPT_SET | MOPT_Q},
> {Opt_noquota, (EXT4_MOUNT_QUOTA | EXT4_MOUNT_USRQUOTA |
> EXT4_MOUNT_GRPQUOTA), MOPT_CLEAR | MOPT_Q},
I think you missed to add EXT4_MOUNT_PRJQUOTA to Opt_noquota...
...
> @@ -2833,6 +2855,13 @@ static int ext4_feature_set_ok(struct super_block *sb, int readonly)
> "without CONFIG_QUOTA");
> return 0;
> }
> + if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_PROJECT) &&
> + !readonly) {
> + ext4_msg(sb, KERN_ERR,
> + "Filesystem with project quota feature cannot be"
> + "mounted RDWR without CONFIG_QUOTA");
> + return 0;
> + }
Hum, I don't think this is right. EXT4_FEATURE_RO_COMPAT_PROJECT is about
maintaining project IDs not about quota. So it seems perfectly OK to have
EXT4_FEATURE_RO_COMPAT_PROJECT without CONFIG_QUOTA.
...
> @@ -5060,6 +5129,8 @@ static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf)
> ext4_fsblk_t overhead = 0, resv_blocks;
> u64 fsid;
> s64 bfree;
> + struct inode *inode = dentry->d_inode;
> + int err = 0;
> resv_blocks = EXT4_C2B(sbi, atomic64_read(&sbi->s_resv_clusters));
>
> if (!test_opt(sb, MINIX_DF))
> @@ -5084,7 +5155,18 @@ static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf)
> buf->f_fsid.val[0] = fsid & 0xFFFFFFFFUL;
> buf->f_fsid.val[1] = (fsid >> 32) & 0xFFFFFFFFUL;
>
> - return 0;
> + if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_PROJECT) &&
> + ext4_test_inode_flag(inode, EXT4_INODE_PROJINHERIT) &&
> + sb_has_quota_usage_enabled(sb, PRJQUOTA) &&
> + sb_has_quota_limits_enabled(sb, PRJQUOTA)) {
If limits are enabled, then usage is enabled. So the check for usage
enabled is superfluous...
> + err = ext4_statfs_project(sb, EXT4_I(inode)->i_projid, buf);
> + if (err) {
> + ext4_warning(sb, "Cannot get quota of project %u\n",
> + from_kprojid(&init_user_ns,
> + EXT4_I(inode)->i_projid));
> + }
The warning seems actually bogus - it can happen that project quota
enforcement gets turned off after we check sb_has_quota_limits_enabled()
but before calling dget() in ext4_statfs_project(). So I would just treat
error from ext4_statfs_project() as project quota being disabled...
Honza
--
Jan Kara <jack-AlSwsSmVLrQ@public.gmane.org>
SUSE Labs, CR
^ permalink raw reply
* Re: [PATCH 1/4] Adds general codes to enforces project quota limits
From: Jan Kara @ 2014-09-24 17:37 UTC (permalink / raw)
To: Christoph Hellwig
Cc: Jan Kara, Li Xi, linux-fsdevel-u79uwXL29TY76Z2rM5mHXA,
linux-ext4-u79uwXL29TY76Z2rM5mHXA,
linux-api-u79uwXL29TY76Z2rM5mHXA, tytso-3s7WtUTddSA,
adilger-m1MBpc4rdrD3fQ9qLvQP4Q,
viro-RmSDqhL/yNMiFSDQTTA3OLVCufUGDwFn,
dmonakhov-GEFAQzZX7r8dnm+yROfE0A
In-Reply-To: <20140924171306.GA23874-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org>
On Wed 24-09-14 10:13:06, Christoph Hellwig wrote:
> On Wed, Sep 24, 2014 at 07:10:20PM +0200, Jan Kara wrote:
> > Yeah, I'm aware of that but I decided I won't bother Li Xi with that
> > since it's independent issue and 8 bytes aren't that terrible.
>
> For the inode of which we have so many instances it is. Especially when
> only one filesystem can make use of this new field.
>
> > Personally
> > I somewhat prefer what I did in
> > http://www.spinics.net/lists/linux-fsdevel/msg74927.html
> > where we don't introduce additional method but rather a table with field
> > offsets in superblock. If people agree with this, I can cook up a patch for
> > quota relatively quickly.
>
> There's an even better way. We stopped calling the dquot_* functions
> from generic code, so now the filesystem can simply pass in a pointer
> to the dquot array.
I thought that as well before I tried that :) There are situations like
when turning quotas on & off where we iterate over all inodes and need to
get dquots from the inode... I don't see how to handle those situations
without fs callback / table of offsets.
Honza
--
Jan Kara <jack-AlSwsSmVLrQ@public.gmane.org>
SUSE Labs, CR
^ 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