diff options
author | Dave Airlie <airlied@redhat.com> | 2015-09-04 13:07:18 +1000 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2015-09-04 13:07:18 +1000 |
commit | 55cdb314494b0372c2f0ba3a589aeb412a9626ba (patch) | |
tree | 58500c051db12c4dae76d03ec174c6487a7f02f9 /drivers/gpu | |
parent | 99495589aa4de7166af254bc497cdbe133fc24bb (diff) | |
parent | c3058579a2882bb4bb6bf1ab0fe65f5ed070e530 (diff) | |
download | linux-55cdb314494b0372c2f0ba3a589aeb412a9626ba.tar.bz2 |
Merge branch 'exynos-drm-next' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos into drm-next
This is a last pull request, which includes two g2d patches
I missed, and more cleanup series of Exynos drm driver.
The cleanup series makes Exynos drm driver more simple,
and removes unnecessary codes, and considers multiple plane format
of framebuffer. I hope this not to be late.
* 'exynos-drm-next' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos:
drm/exynos: remove buf_cnt from struct exynos_drm_fb
drm/exynos: remove exynos_drm_fb_get_buf_cnt()
drm/exynos: cleanup exynos_user_fb_create()
drm/exynos: update exynos_drm_framebuffer_init() for multiple buffers
drm/exynos: cleanup to get gem object for fb
drm/exynos: update fb_info via only one function
drm/exynos: cleanup exynos_drm_fbdev_update()
drm/exynos: s/exynos_gem_obj/obj in exynos_drm_fbdev.c
drm/exynos: remove exynos_drm_fb_set_buf_cnt()
drm/exynos: remove superfluous checks in g2d_check_reg_offset()
drm/exynos: fix size check in g2d_check_buf_desc_is_valid()
Diffstat (limited to 'drivers/gpu')
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_fb.c | 115 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_fb.h | 12 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_fbdev.c | 78 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_g2d.c | 59 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_plane.c | 2 |
5 files changed, 110 insertions, 156 deletions
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c index 59ebbe547290..084280859589 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fb.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c @@ -23,7 +23,6 @@ #include "exynos_drm_drv.h" #include "exynos_drm_fb.h" #include "exynos_drm_fbdev.h" -#include "exynos_drm_gem.h" #include "exynos_drm_iommu.h" #include "exynos_drm_crtc.h" @@ -33,12 +32,10 @@ * exynos specific framebuffer structure. * * @fb: drm framebuffer obejct. - * @buf_cnt: a buffer count to drm framebuffer. * @exynos_gem_obj: array of exynos specific gem object containing a gem object. */ struct exynos_drm_fb { struct drm_framebuffer fb; - unsigned int buf_cnt; struct exynos_drm_gem_obj *exynos_gem_obj[MAX_FB_BUFFER]; }; @@ -98,10 +95,6 @@ static int exynos_drm_fb_create_handle(struct drm_framebuffer *fb, { struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb); - /* This fb should have only one gem object. */ - if (WARN_ON(exynos_fb->buf_cnt != 1)) - return -EINVAL; - return drm_gem_handle_create(file_priv, &exynos_fb->exynos_gem_obj[0]->base, handle); } @@ -122,119 +115,77 @@ static struct drm_framebuffer_funcs exynos_drm_fb_funcs = { .dirty = exynos_drm_fb_dirty, }; -void exynos_drm_fb_set_buf_cnt(struct drm_framebuffer *fb, - unsigned int cnt) -{ - struct exynos_drm_fb *exynos_fb; - - exynos_fb = to_exynos_fb(fb); - - exynos_fb->buf_cnt = cnt; -} - -unsigned int exynos_drm_fb_get_buf_cnt(struct drm_framebuffer *fb) -{ - struct exynos_drm_fb *exynos_fb; - - exynos_fb = to_exynos_fb(fb); - - return exynos_fb->buf_cnt; -} - struct drm_framebuffer * exynos_drm_framebuffer_init(struct drm_device *dev, struct drm_mode_fb_cmd2 *mode_cmd, - struct drm_gem_object *obj) + struct exynos_drm_gem_obj **gem_obj, + int count) { struct exynos_drm_fb *exynos_fb; - struct exynos_drm_gem_obj *exynos_gem_obj; + int i; int ret; - exynos_gem_obj = to_exynos_gem_obj(obj); - - ret = check_fb_gem_memory_type(dev, exynos_gem_obj); - if (ret < 0) - return ERR_PTR(ret); - exynos_fb = kzalloc(sizeof(*exynos_fb), GFP_KERNEL); if (!exynos_fb) return ERR_PTR(-ENOMEM); + for (i = 0; i < count; i++) { + ret = check_fb_gem_memory_type(dev, gem_obj[i]); + if (ret < 0) + goto err; + + exynos_fb->exynos_gem_obj[i] = gem_obj[i]; + } + drm_helper_mode_fill_fb_struct(&exynos_fb->fb, mode_cmd); - exynos_fb->exynos_gem_obj[0] = exynos_gem_obj; ret = drm_framebuffer_init(dev, &exynos_fb->fb, &exynos_drm_fb_funcs); - if (ret) { - kfree(exynos_fb); + if (ret < 0) { DRM_ERROR("failed to initialize framebuffer\n"); - return ERR_PTR(ret); + goto err; } return &exynos_fb->fb; + +err: + kfree(exynos_fb); + return ERR_PTR(ret); } static struct drm_framebuffer * exynos_user_fb_create(struct drm_device *dev, struct drm_file *file_priv, struct drm_mode_fb_cmd2 *mode_cmd) { + struct exynos_drm_gem_obj *gem_objs[MAX_FB_BUFFER]; struct drm_gem_object *obj; - struct exynos_drm_gem_obj *exynos_gem_obj; - struct exynos_drm_fb *exynos_fb; - int i, ret; - - exynos_fb = kzalloc(sizeof(*exynos_fb), GFP_KERNEL); - if (!exynos_fb) - return ERR_PTR(-ENOMEM); - - obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[0]); - if (!obj) { - DRM_ERROR("failed to lookup gem object\n"); - ret = -ENOENT; - goto err_free; - } - - drm_helper_mode_fill_fb_struct(&exynos_fb->fb, mode_cmd); - exynos_fb->exynos_gem_obj[0] = to_exynos_gem_obj(obj); - exynos_fb->buf_cnt = drm_format_num_planes(mode_cmd->pixel_format); - - DRM_DEBUG_KMS("buf_cnt = %d\n", exynos_fb->buf_cnt); + struct drm_framebuffer *fb; + int i; + int ret; - for (i = 1; i < exynos_fb->buf_cnt; i++) { + for (i = 0; i < drm_format_num_planes(mode_cmd->pixel_format); i++) { obj = drm_gem_object_lookup(dev, file_priv, - mode_cmd->handles[i]); + mode_cmd->handles[i]); if (!obj) { DRM_ERROR("failed to lookup gem object\n"); ret = -ENOENT; - exynos_fb->buf_cnt = i; - goto err_unreference; + goto err; } - exynos_gem_obj = to_exynos_gem_obj(obj); - exynos_fb->exynos_gem_obj[i] = exynos_gem_obj; - - ret = check_fb_gem_memory_type(dev, exynos_gem_obj); - if (ret < 0) - goto err_unreference; + gem_objs[i] = to_exynos_gem_obj(obj); } - ret = drm_framebuffer_init(dev, &exynos_fb->fb, &exynos_drm_fb_funcs); - if (ret) { - DRM_ERROR("failed to init framebuffer.\n"); - goto err_unreference; + fb = exynos_drm_framebuffer_init(dev, mode_cmd, gem_objs, i); + if (IS_ERR(fb)) { + ret = PTR_ERR(fb); + goto err; } - return &exynos_fb->fb; + return fb; -err_unreference: - for (i = 0; i < exynos_fb->buf_cnt; i++) { - struct drm_gem_object *obj; +err: + while (i--) + drm_gem_object_unreference_unlocked(&gem_objs[i]->base); - obj = &exynos_fb->exynos_gem_obj[i]->base; - if (obj) - drm_gem_object_unreference_unlocked(obj); - } -err_free: - kfree(exynos_fb); return ERR_PTR(ret); } diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.h b/drivers/gpu/drm/exynos/exynos_drm_fb.h index 1c9e27c32cd1..85e4445b920e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fb.h +++ b/drivers/gpu/drm/exynos/exynos_drm_fb.h @@ -14,10 +14,13 @@ #ifndef _EXYNOS_DRM_FB_H_ #define _EXYNOS_DRM_FB_H +#include "exynos_drm_gem.h" + struct drm_framebuffer * exynos_drm_framebuffer_init(struct drm_device *dev, struct drm_mode_fb_cmd2 *mode_cmd, - struct drm_gem_object *obj); + struct exynos_drm_gem_obj **gem_obj, + int count); /* get gem object of a drm framebuffer */ struct exynos_drm_gem_obj *exynos_drm_fb_gem_obj(struct drm_framebuffer *fb, @@ -25,11 +28,4 @@ struct exynos_drm_gem_obj *exynos_drm_fb_gem_obj(struct drm_framebuffer *fb, void exynos_drm_mode_config_init(struct drm_device *dev); -/* set a buffer count to drm framebuffer. */ -void exynos_drm_fb_set_buf_cnt(struct drm_framebuffer *fb, - unsigned int cnt); - -/* get a buffer count to drm framebuffer. */ -unsigned int exynos_drm_fb_get_buf_cnt(struct drm_framebuffer *fb); - #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index 624595afbce0..a221f753ad9c 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -21,7 +21,6 @@ #include "exynos_drm_drv.h" #include "exynos_drm_fb.h" #include "exynos_drm_fbdev.h" -#include "exynos_drm_gem.h" #include "exynos_drm_iommu.h" #define MAX_CONNECTOR 4 @@ -32,7 +31,7 @@ struct exynos_drm_fbdev { struct drm_fb_helper drm_fb_helper; - struct exynos_drm_gem_obj *exynos_gem_obj; + struct exynos_drm_gem_obj *obj; }; static int exynos_drm_fb_mmap(struct fb_info *info, @@ -40,7 +39,7 @@ static int exynos_drm_fb_mmap(struct fb_info *info, { struct drm_fb_helper *helper = info->par; struct exynos_drm_fbdev *exynos_fbd = to_exynos_fbdev(helper); - struct exynos_drm_gem_obj *obj = exynos_fbd->exynos_gem_obj; + struct exynos_drm_gem_obj *obj = exynos_fbd->obj; unsigned long vm_size; int ret; @@ -75,37 +74,38 @@ static struct fb_ops exynos_drm_fb_ops = { }; static int exynos_drm_fbdev_update(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes, - struct drm_framebuffer *fb) + struct drm_fb_helper_surface_size *sizes, + struct exynos_drm_gem_obj *obj) { - struct fb_info *fbi = helper->fbdev; - struct exynos_drm_gem_obj *obj; + struct fb_info *fbi; + struct drm_framebuffer *fb = helper->fb; unsigned int size = fb->width * fb->height * (fb->bits_per_pixel >> 3); unsigned int nr_pages; unsigned long offset; + fbi = drm_fb_helper_alloc_fbi(helper); + if (IS_ERR(fbi)) { + DRM_ERROR("failed to allocate fb info.\n"); + return PTR_ERR(fbi); + } + + fbi->par = helper; + fbi->flags = FBINFO_FLAG_DEFAULT; + fbi->fbops = &exynos_drm_fb_ops; + drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth); drm_fb_helper_fill_var(fbi, helper, sizes->fb_width, sizes->fb_height); - /* RGB formats use only one buffer */ - obj = exynos_drm_fb_gem_obj(fb, 0); - if (!obj) { - DRM_DEBUG_KMS("gem object is null.\n"); - return -EFAULT; - } - nr_pages = obj->size >> PAGE_SHIFT; obj->kvaddr = (void __iomem *) vmap(obj->pages, nr_pages, VM_MAP, pgprot_writecombine(PAGE_KERNEL)); if (!obj->kvaddr) { DRM_ERROR("failed to map pages to kernel space.\n"); + drm_fb_helper_release_fbi(helper); return -EIO; } - /* buffer count to framebuffer always is 1 at booting time. */ - exynos_drm_fb_set_buf_cnt(fb, 1); - offset = fbi->var.xoffset * (fb->bits_per_pixel >> 3); offset += fbi->var.yoffset * fb->pitches[0]; @@ -120,9 +120,8 @@ static int exynos_drm_fbdev_create(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) { struct exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper); - struct exynos_drm_gem_obj *exynos_gem_obj; + struct exynos_drm_gem_obj *obj; struct drm_device *dev = helper->dev; - struct fb_info *fbi; struct drm_mode_fb_cmd2 mode_cmd = { 0 }; struct platform_device *pdev = dev->platformdev; unsigned long size; @@ -140,47 +139,34 @@ static int exynos_drm_fbdev_create(struct drm_fb_helper *helper, mutex_lock(&dev->struct_mutex); - fbi = drm_fb_helper_alloc_fbi(helper); - if (IS_ERR(fbi)) { - DRM_ERROR("failed to allocate fb info.\n"); - ret = PTR_ERR(fbi); - goto out; - } - size = mode_cmd.pitches[0] * mode_cmd.height; - exynos_gem_obj = exynos_drm_gem_create(dev, EXYNOS_BO_CONTIG, size); + obj = exynos_drm_gem_create(dev, EXYNOS_BO_CONTIG, size); /* * If physically contiguous memory allocation fails and if IOMMU is * supported then try to get buffer from non physically contiguous * memory area. */ - if (IS_ERR(exynos_gem_obj) && is_drm_iommu_supported(dev)) { + if (IS_ERR(obj) && is_drm_iommu_supported(dev)) { dev_warn(&pdev->dev, "contiguous FB allocation failed, falling back to non-contiguous\n"); - exynos_gem_obj = exynos_drm_gem_create(dev, EXYNOS_BO_NONCONTIG, - size); + obj = exynos_drm_gem_create(dev, EXYNOS_BO_NONCONTIG, size); } - if (IS_ERR(exynos_gem_obj)) { - ret = PTR_ERR(exynos_gem_obj); - goto err_release_fbi; + if (IS_ERR(obj)) { + ret = PTR_ERR(obj); + goto out; } - exynos_fbdev->exynos_gem_obj = exynos_gem_obj; + exynos_fbdev->obj = obj; - helper->fb = exynos_drm_framebuffer_init(dev, &mode_cmd, - &exynos_gem_obj->base); + helper->fb = exynos_drm_framebuffer_init(dev, &mode_cmd, &obj, 1); if (IS_ERR(helper->fb)) { DRM_ERROR("failed to create drm framebuffer.\n"); ret = PTR_ERR(helper->fb); goto err_destroy_gem; } - fbi->par = helper; - fbi->flags = FBINFO_FLAG_DEFAULT; - fbi->fbops = &exynos_drm_fb_ops; - - ret = exynos_drm_fbdev_update(helper, sizes, helper->fb); + ret = exynos_drm_fbdev_update(helper, sizes, obj); if (ret < 0) goto err_destroy_framebuffer; @@ -190,9 +176,7 @@ static int exynos_drm_fbdev_create(struct drm_fb_helper *helper, err_destroy_framebuffer: drm_framebuffer_cleanup(helper->fb); err_destroy_gem: - exynos_drm_gem_destroy(exynos_gem_obj); -err_release_fbi: - drm_fb_helper_release_fbi(helper); + exynos_drm_gem_destroy(obj); /* * if failed, all resources allocated above would be released by @@ -285,11 +269,11 @@ static void exynos_drm_fbdev_destroy(struct drm_device *dev, struct drm_fb_helper *fb_helper) { struct exynos_drm_fbdev *exynos_fbd = to_exynos_fbdev(fb_helper); - struct exynos_drm_gem_obj *exynos_gem_obj = exynos_fbd->exynos_gem_obj; + struct exynos_drm_gem_obj *obj = exynos_fbd->obj; struct drm_framebuffer *fb; - if (exynos_gem_obj->kvaddr) - vunmap(exynos_gem_obj->kvaddr); + if (obj->kvaddr) + vunmap(obj->kvaddr); /* release drm framebuffer and real buffer */ if (fb_helper->fb && fb_helper->fb->funcs) { diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c index ba008391a2fc..535b4ad6c4b1 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c @@ -48,11 +48,13 @@ /* registers for base address */ #define G2D_SRC_BASE_ADDR 0x0304 +#define G2D_SRC_STRIDE_REG 0x0308 #define G2D_SRC_COLOR_MODE 0x030C #define G2D_SRC_LEFT_TOP 0x0310 #define G2D_SRC_RIGHT_BOTTOM 0x0314 #define G2D_SRC_PLANE2_BASE_ADDR 0x0318 #define G2D_DST_BASE_ADDR 0x0404 +#define G2D_DST_STRIDE_REG 0x0408 #define G2D_DST_COLOR_MODE 0x040C #define G2D_DST_LEFT_TOP 0x0410 #define G2D_DST_RIGHT_BOTTOM 0x0414 @@ -148,6 +150,7 @@ struct g2d_cmdlist { * A structure of buffer description * * @format: color format + * @stride: buffer stride/pitch in bytes * @left_x: the x coordinates of left top corner * @top_y: the y coordinates of left top corner * @right_x: the x coordinates of right bottom corner @@ -156,6 +159,7 @@ struct g2d_cmdlist { */ struct g2d_buf_desc { unsigned int format; + unsigned int stride; unsigned int left_x; unsigned int top_y; unsigned int right_x; @@ -589,6 +593,7 @@ static enum g2d_reg_type g2d_get_reg_type(int reg_offset) switch (reg_offset) { case G2D_SRC_BASE_ADDR: + case G2D_SRC_STRIDE_REG: case G2D_SRC_COLOR_MODE: case G2D_SRC_LEFT_TOP: case G2D_SRC_RIGHT_BOTTOM: @@ -598,6 +603,7 @@ static enum g2d_reg_type g2d_get_reg_type(int reg_offset) reg_type = REG_TYPE_SRC_PLANE2; break; case G2D_DST_BASE_ADDR: + case G2D_DST_STRIDE_REG: case G2D_DST_COLOR_MODE: case G2D_DST_LEFT_TOP: case G2D_DST_RIGHT_BOTTOM: @@ -652,8 +658,8 @@ static bool g2d_check_buf_desc_is_valid(struct g2d_buf_desc *buf_desc, enum g2d_reg_type reg_type, unsigned long size) { - unsigned int width, height; - unsigned long area; + int width, height; + unsigned long bpp, last_pos; /* * check source and destination buffers only. @@ -662,22 +668,37 @@ static bool g2d_check_buf_desc_is_valid(struct g2d_buf_desc *buf_desc, if (reg_type != REG_TYPE_SRC && reg_type != REG_TYPE_DST) return true; - width = buf_desc->right_x - buf_desc->left_x; + /* This check also makes sure that right_x > left_x. */ + width = (int)buf_desc->right_x - (int)buf_desc->left_x; if (width < G2D_LEN_MIN || width > G2D_LEN_MAX) { - DRM_ERROR("width[%u] is out of range!\n", width); + DRM_ERROR("width[%d] is out of range!\n", width); return false; } - height = buf_desc->bottom_y - buf_desc->top_y; + /* This check also makes sure that bottom_y > top_y. */ + height = (int)buf_desc->bottom_y - (int)buf_desc->top_y; if (height < G2D_LEN_MIN || height > G2D_LEN_MAX) { - DRM_ERROR("height[%u] is out of range!\n", height); + DRM_ERROR("height[%d] is out of range!\n", height); return false; } - area = (unsigned long)width * (unsigned long)height * - g2d_get_buf_bpp(buf_desc->format); - if (area > size) { - DRM_ERROR("area[%lu] is out of range[%lu]!\n", area, size); + bpp = g2d_get_buf_bpp(buf_desc->format); + + /* Compute the position of the last byte that the engine accesses. */ + last_pos = ((unsigned long)buf_desc->bottom_y - 1) * + (unsigned long)buf_desc->stride + + (unsigned long)buf_desc->right_x * bpp - 1; + + /* + * Since right_x > left_x and bottom_y > top_y we already know + * that the first_pos < last_pos (first_pos being the position + * of the first byte the engine accesses), it just remains to + * check if last_pos is smaller then the buffer size. + */ + + if (last_pos >= size) { + DRM_ERROR("last engine access position [%lu] " + "is out of range [%lu]!\n", last_pos, size); return false; } @@ -973,8 +994,6 @@ static int g2d_check_reg_offset(struct device *dev, goto err; reg_type = g2d_get_reg_type(reg_offset); - if (reg_type == REG_TYPE_NONE) - goto err; /* check userptr buffer type. */ if ((cmdlist->data[index] & ~0x7fffffff) >> 31) { @@ -983,14 +1002,22 @@ static int g2d_check_reg_offset(struct device *dev, } else buf_info->types[reg_type] = BUF_TYPE_GEM; break; + case G2D_SRC_STRIDE_REG: + case G2D_DST_STRIDE_REG: + if (for_addr) + goto err; + + reg_type = g2d_get_reg_type(reg_offset); + + buf_desc = &buf_info->descs[reg_type]; + buf_desc->stride = cmdlist->data[index + 1]; + break; case G2D_SRC_COLOR_MODE: case G2D_DST_COLOR_MODE: if (for_addr) goto err; reg_type = g2d_get_reg_type(reg_offset); - if (reg_type == REG_TYPE_NONE) - goto err; buf_desc = &buf_info->descs[reg_type]; value = cmdlist->data[index + 1]; @@ -1003,8 +1030,6 @@ static int g2d_check_reg_offset(struct device *dev, goto err; reg_type = g2d_get_reg_type(reg_offset); - if (reg_type == REG_TYPE_NONE) - goto err; buf_desc = &buf_info->descs[reg_type]; value = cmdlist->data[index + 1]; @@ -1018,8 +1043,6 @@ static int g2d_check_reg_offset(struct device *dev, goto err; reg_type = g2d_get_reg_type(reg_offset); - if (reg_type == REG_TYPE_NONE) - goto err; buf_desc = &buf_info->descs[reg_type]; value = cmdlist->data[index + 1]; diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c index 865d6eb0c845..714822441467 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_plane.c +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c @@ -126,7 +126,7 @@ static int exynos_plane_atomic_check(struct drm_plane *plane, if (!state->fb) return 0; - nr = exynos_drm_fb_get_buf_cnt(state->fb); + nr = drm_format_num_planes(state->fb->pixel_format); for (i = 0; i < nr; i++) { struct exynos_drm_gem_obj *obj = exynos_drm_fb_gem_obj(state->fb, i); |