diff options
author | Ben Skeggs <bskeggs@redhat.com> | 2010-11-23 19:52:43 -0500 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2010-12-21 02:18:39 -0500 |
commit | ddbaf79a8b047dcccf766d0518626cdc0f43d58e (patch) | |
tree | c506ab3e829b8c0e537dc93eb626483603e8b8ed | |
parent | 966a5b7daa15e152bc2e1f0407939cc721fb5995 (diff) |
drm/nvc0: implement fbcon acceleration
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
-rw-r--r-- | drivers/gpu/drm/nouveau/Makefile | 5 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_fbcon.c | 24 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_fbcon.h | 6 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvc0_fbcon.c | 271 |
4 files changed, 300 insertions, 6 deletions
diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index 141771beb8e6..e12c97fd8db8 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile | |||
@@ -22,9 +22,10 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \ | |||
22 | nv84_crypt.o \ | 22 | nv84_crypt.o \ |
23 | nv04_instmem.o nv50_instmem.o nvc0_instmem.o \ | 23 | nv04_instmem.o nv50_instmem.o nvc0_instmem.o \ |
24 | nv50_evo.o nv50_crtc.o nv50_dac.o nv50_sor.o \ | 24 | nv50_evo.o nv50_crtc.o nv50_dac.o nv50_sor.o \ |
25 | nv50_cursor.o nv50_display.o nv50_fbcon.o \ | 25 | nv50_cursor.o nv50_display.o \ |
26 | nv04_dac.o nv04_dfp.o nv04_tv.o nv17_tv.o nv17_tv_modes.o \ | 26 | nv04_dac.o nv04_dfp.o nv04_tv.o nv17_tv.o nv17_tv_modes.o \ |
27 | nv04_crtc.o nv04_display.o nv04_cursor.o nv04_fbcon.o \ | 27 | nv04_crtc.o nv04_display.o nv04_cursor.o \ |
28 | nv04_fbcon.o nv50_fbcon.o nvc0_fbcon.o \ | ||
28 | nv10_gpio.o nv50_gpio.o \ | 29 | nv10_gpio.o nv50_gpio.o \ |
29 | nv50_calc.o \ | 30 | nv50_calc.o \ |
30 | nv04_pm.o nv50_pm.o nva3_pm.o \ | 31 | nv04_pm.o nv50_pm.o nva3_pm.o \ |
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index ea861c915149..326eeda6ad7f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c | |||
@@ -68,6 +68,8 @@ nouveau_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect) | |||
68 | else | 68 | else |
69 | if (dev_priv->card_type < NV_C0) | 69 | if (dev_priv->card_type < NV_C0) |
70 | ret = nv50_fbcon_fillrect(info, rect); | 70 | ret = nv50_fbcon_fillrect(info, rect); |
71 | else | ||
72 | ret = nvc0_fbcon_fillrect(info, rect); | ||
71 | mutex_unlock(&dev_priv->channel->mutex); | 73 | mutex_unlock(&dev_priv->channel->mutex); |
72 | } | 74 | } |
73 | 75 | ||
@@ -98,6 +100,8 @@ nouveau_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *image) | |||
98 | else | 100 | else |
99 | if (dev_priv->card_type < NV_C0) | 101 | if (dev_priv->card_type < NV_C0) |
100 | ret = nv50_fbcon_copyarea(info, image); | 102 | ret = nv50_fbcon_copyarea(info, image); |
103 | else | ||
104 | ret = nvc0_fbcon_copyarea(info, image); | ||
101 | mutex_unlock(&dev_priv->channel->mutex); | 105 | mutex_unlock(&dev_priv->channel->mutex); |
102 | } | 106 | } |
103 | 107 | ||
@@ -128,6 +132,8 @@ nouveau_fbcon_imageblit(struct fb_info *info, const struct fb_image *image) | |||
128 | else | 132 | else |
129 | if (dev_priv->card_type < NV_C0) | 133 | if (dev_priv->card_type < NV_C0) |
130 | ret = nv50_fbcon_imageblit(info, image); | 134 | ret = nv50_fbcon_imageblit(info, image); |
135 | else | ||
136 | ret = nvc0_fbcon_imageblit(info, image); | ||
131 | mutex_unlock(&dev_priv->channel->mutex); | 137 | mutex_unlock(&dev_priv->channel->mutex); |
132 | } | 138 | } |
133 | 139 | ||
@@ -163,10 +169,18 @@ nouveau_fbcon_sync(struct fb_info *info) | |||
163 | return 0; | 169 | return 0; |
164 | } | 170 | } |
165 | 171 | ||
166 | BEGIN_RING(chan, 0, 0x0104, 1); | 172 | if (dev_priv->card_type >= NV_C0) { |
167 | OUT_RING(chan, 0); | 173 | BEGIN_NVC0(chan, 2, NvSub2D, 0x010c, 1); |
168 | BEGIN_RING(chan, 0, 0x0100, 1); | 174 | OUT_RING (chan, 0); |
169 | OUT_RING(chan, 0); | 175 | BEGIN_NVC0(chan, 2, NvSub2D, 0x0100, 1); |
176 | OUT_RING (chan, 0); | ||
177 | } else { | ||
178 | BEGIN_RING(chan, 0, 0x0104, 1); | ||
179 | OUT_RING (chan, 0); | ||
180 | BEGIN_RING(chan, 0, 0x0100, 1); | ||
181 | OUT_RING (chan, 0); | ||
182 | } | ||
183 | |||
170 | nouveau_bo_wr32(chan->notifier_bo, chan->m2mf_ntfy + 3, 0xffffffff); | 184 | nouveau_bo_wr32(chan->notifier_bo, chan->m2mf_ntfy + 3, 0xffffffff); |
171 | FIRE_RING(chan); | 185 | FIRE_RING(chan); |
172 | mutex_unlock(&chan->mutex); | 186 | mutex_unlock(&chan->mutex); |
@@ -374,6 +388,8 @@ nouveau_fbcon_create(struct nouveau_fbdev *nfbdev, | |||
374 | else | 388 | else |
375 | if (dev_priv->card_type < NV_C0) | 389 | if (dev_priv->card_type < NV_C0) |
376 | ret = nv50_fbcon_accel_init(info); | 390 | ret = nv50_fbcon_accel_init(info); |
391 | else | ||
392 | ret = nvc0_fbcon_accel_init(info); | ||
377 | 393 | ||
378 | if (ret == 0) | 394 | if (ret == 0) |
379 | info->fbops = &nouveau_fbcon_ops; | 395 | info->fbops = &nouveau_fbcon_ops; |
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.h b/drivers/gpu/drm/nouveau/nouveau_fbcon.h index 6b933f2c3a5b..b73c29f87fc3 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.h +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.h | |||
@@ -44,11 +44,17 @@ int nv04_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region); | |||
44 | int nv04_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect); | 44 | int nv04_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect); |
45 | int nv04_fbcon_imageblit(struct fb_info *info, const struct fb_image *image); | 45 | int nv04_fbcon_imageblit(struct fb_info *info, const struct fb_image *image); |
46 | int nv04_fbcon_accel_init(struct fb_info *info); | 46 | int nv04_fbcon_accel_init(struct fb_info *info); |
47 | |||
47 | int nv50_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect); | 48 | int nv50_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect); |
48 | int nv50_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region); | 49 | int nv50_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region); |
49 | int nv50_fbcon_imageblit(struct fb_info *info, const struct fb_image *image); | 50 | int nv50_fbcon_imageblit(struct fb_info *info, const struct fb_image *image); |
50 | int nv50_fbcon_accel_init(struct fb_info *info); | 51 | int nv50_fbcon_accel_init(struct fb_info *info); |
51 | 52 | ||
53 | int nvc0_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect); | ||
54 | int nvc0_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region); | ||
55 | int nvc0_fbcon_imageblit(struct fb_info *info, const struct fb_image *image); | ||
56 | int nvc0_fbcon_accel_init(struct fb_info *info); | ||
57 | |||
52 | void nouveau_fbcon_gpu_lockup(struct fb_info *info); | 58 | void nouveau_fbcon_gpu_lockup(struct fb_info *info); |
53 | 59 | ||
54 | int nouveau_fbcon_init(struct drm_device *dev); | 60 | int nouveau_fbcon_init(struct drm_device *dev); |
diff --git a/drivers/gpu/drm/nouveau/nvc0_fbcon.c b/drivers/gpu/drm/nouveau/nvc0_fbcon.c new file mode 100644 index 000000000000..cbb4a1ae20b1 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvc0_fbcon.c | |||
@@ -0,0 +1,271 @@ | |||
1 | /* | ||
2 | * Copyright 2010 Red Hat Inc. | ||
3 | * | ||
4 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
5 | * copy of this software and associated documentation files (the "Software"), | ||
6 | * to deal in the Software without restriction, including without limitation | ||
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
8 | * and/or sell copies of the Software, and to permit persons to whom the | ||
9 | * Software is furnished to do so, subject to the following conditions: | ||
10 | * | ||
11 | * The above copyright notice and this permission notice shall be included in | ||
12 | * all copies or substantial portions of the Software. | ||
13 | * | ||
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
17 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | ||
18 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||
19 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
20 | * OTHER DEALINGS IN THE SOFTWARE. | ||
21 | * | ||
22 | * Authors: Ben Skeggs | ||
23 | */ | ||
24 | |||
25 | #include "drmP.h" | ||
26 | #include "nouveau_drv.h" | ||
27 | #include "nouveau_dma.h" | ||
28 | #include "nouveau_ramht.h" | ||
29 | #include "nouveau_fbcon.h" | ||
30 | #include "nouveau_mm.h" | ||
31 | |||
32 | int | ||
33 | nvc0_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect) | ||
34 | { | ||
35 | struct nouveau_fbdev *nfbdev = info->par; | ||
36 | struct drm_device *dev = nfbdev->dev; | ||
37 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
38 | struct nouveau_channel *chan = dev_priv->channel; | ||
39 | int ret; | ||
40 | |||
41 | ret = RING_SPACE(chan, rect->rop == ROP_COPY ? 7 : 11); | ||
42 | if (ret) | ||
43 | return ret; | ||
44 | |||
45 | if (rect->rop != ROP_COPY) { | ||
46 | BEGIN_NVC0(chan, 2, NvSub2D, 0x02ac, 1); | ||
47 | OUT_RING (chan, 1); | ||
48 | } | ||
49 | BEGIN_NVC0(chan, 2, NvSub2D, 0x0588, 1); | ||
50 | if (info->fix.visual == FB_VISUAL_TRUECOLOR || | ||
51 | info->fix.visual == FB_VISUAL_DIRECTCOLOR) | ||
52 | OUT_RING (chan, ((uint32_t *)info->pseudo_palette)[rect->color]); | ||
53 | else | ||
54 | OUT_RING (chan, rect->color); | ||
55 | BEGIN_NVC0(chan, 2, NvSub2D, 0x0600, 4); | ||
56 | OUT_RING (chan, rect->dx); | ||
57 | OUT_RING (chan, rect->dy); | ||
58 | OUT_RING (chan, rect->dx + rect->width); | ||
59 | OUT_RING (chan, rect->dy + rect->height); | ||
60 | if (rect->rop != ROP_COPY) { | ||
61 | BEGIN_NVC0(chan, 2, NvSub2D, 0x02ac, 1); | ||
62 | OUT_RING (chan, 3); | ||
63 | } | ||
64 | FIRE_RING(chan); | ||
65 | return 0; | ||
66 | } | ||
67 | |||
68 | int | ||
69 | nvc0_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region) | ||
70 | { | ||
71 | struct nouveau_fbdev *nfbdev = info->par; | ||
72 | struct drm_device *dev = nfbdev->dev; | ||
73 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
74 | struct nouveau_channel *chan = dev_priv->channel; | ||
75 | int ret; | ||
76 | |||
77 | ret = RING_SPACE(chan, 12); | ||
78 | if (ret) | ||
79 | return ret; | ||
80 | |||
81 | BEGIN_NVC0(chan, 2, NvSub2D, 0x0110, 1); | ||
82 | OUT_RING (chan, 0); | ||
83 | BEGIN_NVC0(chan, 2, NvSub2D, 0x08b0, 4); | ||
84 | OUT_RING (chan, region->dx); | ||
85 | OUT_RING (chan, region->dy); | ||
86 | OUT_RING (chan, region->width); | ||
87 | OUT_RING (chan, region->height); | ||
88 | BEGIN_NVC0(chan, 2, NvSub2D, 0x08d0, 4); | ||
89 | OUT_RING (chan, 0); | ||
90 | OUT_RING (chan, region->sx); | ||
91 | OUT_RING (chan, 0); | ||
92 | OUT_RING (chan, region->sy); | ||
93 | FIRE_RING(chan); | ||
94 | return 0; | ||
95 | } | ||
96 | |||
97 | int | ||
98 | nvc0_fbcon_imageblit(struct fb_info *info, const struct fb_image *image) | ||
99 | { | ||
100 | struct nouveau_fbdev *nfbdev = info->par; | ||
101 | struct drm_device *dev = nfbdev->dev; | ||
102 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
103 | struct nouveau_channel *chan = dev_priv->channel; | ||
104 | uint32_t width, dwords, *data = (uint32_t *)image->data; | ||
105 | uint32_t mask = ~(~0 >> (32 - info->var.bits_per_pixel)); | ||
106 | uint32_t *palette = info->pseudo_palette; | ||
107 | int ret; | ||
108 | |||
109 | if (image->depth != 1) | ||
110 | return -ENODEV; | ||
111 | |||
112 | ret = RING_SPACE(chan, 11); | ||
113 | if (ret) | ||
114 | return ret; | ||
115 | |||
116 | width = ALIGN(image->width, 32); | ||
117 | dwords = (width * image->height) >> 5; | ||
118 | |||
119 | BEGIN_NVC0(chan, 2, NvSub2D, 0x0814, 2); | ||
120 | if (info->fix.visual == FB_VISUAL_TRUECOLOR || | ||
121 | info->fix.visual == FB_VISUAL_DIRECTCOLOR) { | ||
122 | OUT_RING (chan, palette[image->bg_color] | mask); | ||
123 | OUT_RING (chan, palette[image->fg_color] | mask); | ||
124 | } else { | ||
125 | OUT_RING (chan, image->bg_color); | ||
126 | OUT_RING (chan, image->fg_color); | ||
127 | } | ||
128 | BEGIN_NVC0(chan, 2, NvSub2D, 0x0838, 2); | ||
129 | OUT_RING (chan, image->width); | ||
130 | OUT_RING (chan, image->height); | ||
131 | BEGIN_NVC0(chan, 2, NvSub2D, 0x0850, 4); | ||
132 | OUT_RING (chan, 0); | ||
133 | OUT_RING (chan, image->dx); | ||
134 | OUT_RING (chan, 0); | ||
135 | OUT_RING (chan, image->dy); | ||
136 | |||
137 | while (dwords) { | ||
138 | int push = dwords > 2047 ? 2047 : dwords; | ||
139 | |||
140 | ret = RING_SPACE(chan, push + 1); | ||
141 | if (ret) | ||
142 | return ret; | ||
143 | |||
144 | dwords -= push; | ||
145 | |||
146 | BEGIN_NVC0(chan, 6, NvSub2D, 0x0860, push); | ||
147 | OUT_RINGp(chan, data, push); | ||
148 | data += push; | ||
149 | } | ||
150 | |||
151 | FIRE_RING(chan); | ||
152 | return 0; | ||
153 | } | ||
154 | |||
155 | int | ||
156 | nvc0_fbcon_accel_init(struct fb_info *info) | ||
157 | { | ||
158 | struct nouveau_fbdev *nfbdev = info->par; | ||
159 | struct drm_device *dev = nfbdev->dev; | ||
160 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
161 | struct nouveau_channel *chan = dev_priv->channel; | ||
162 | struct nouveau_bo *nvbo = nfbdev->nouveau_fb.nvbo; | ||
163 | int ret, format; | ||
164 | |||
165 | ret = nouveau_gpuobj_gr_new(chan, 0x902d, 0x902d); | ||
166 | if (ret) | ||
167 | return ret; | ||
168 | |||
169 | switch (info->var.bits_per_pixel) { | ||
170 | case 8: | ||
171 | format = 0xf3; | ||
172 | break; | ||
173 | case 15: | ||
174 | format = 0xf8; | ||
175 | break; | ||
176 | case 16: | ||
177 | format = 0xe8; | ||
178 | break; | ||
179 | case 32: | ||
180 | switch (info->var.transp.length) { | ||
181 | case 0: /* depth 24 */ | ||
182 | case 8: /* depth 32, just use 24.. */ | ||
183 | format = 0xe6; | ||
184 | break; | ||
185 | case 2: /* depth 30 */ | ||
186 | format = 0xd1; | ||
187 | break; | ||
188 | default: | ||
189 | return -EINVAL; | ||
190 | } | ||
191 | break; | ||
192 | default: | ||
193 | return -EINVAL; | ||
194 | } | ||
195 | |||
196 | ret = RING_SPACE(chan, 60); | ||
197 | if (ret) { | ||
198 | WARN_ON(1); | ||
199 | nouveau_fbcon_gpu_lockup(info); | ||
200 | return ret; | ||
201 | } | ||
202 | |||
203 | printk(KERN_ERR "fb vma 0x%010llx\n", nvbo->vma.offset); | ||
204 | |||
205 | BEGIN_NVC0(chan, 2, NvSub2D, 0x0000, 1); | ||
206 | OUT_RING (chan, 0x0000902d); | ||
207 | BEGIN_NVC0(chan, 2, NvSub2D, 0x0104, 2); | ||
208 | OUT_RING (chan, upper_32_bits(chan->notifier_bo->bo.offset)); | ||
209 | OUT_RING (chan, lower_32_bits(chan->notifier_bo->bo.offset)); | ||
210 | BEGIN_NVC0(chan, 2, NvSub2D, 0x0290, 1); | ||
211 | OUT_RING (chan, 0); | ||
212 | BEGIN_NVC0(chan, 2, NvSub2D, 0x0888, 1); | ||
213 | OUT_RING (chan, 1); | ||
214 | BEGIN_NVC0(chan, 2, NvSub2D, 0x02ac, 1); | ||
215 | OUT_RING (chan, 3); | ||
216 | BEGIN_NVC0(chan, 2, NvSub2D, 0x02a0, 1); | ||
217 | OUT_RING (chan, 0x55); | ||
218 | BEGIN_NVC0(chan, 2, NvSub2D, 0x08c0, 4); | ||
219 | OUT_RING (chan, 0); | ||
220 | OUT_RING (chan, 1); | ||
221 | OUT_RING (chan, 0); | ||
222 | OUT_RING (chan, 1); | ||
223 | BEGIN_NVC0(chan, 2, NvSub2D, 0x0580, 2); | ||
224 | OUT_RING (chan, 4); | ||
225 | OUT_RING (chan, format); | ||
226 | BEGIN_NVC0(chan, 2, NvSub2D, 0x02e8, 2); | ||
227 | OUT_RING (chan, 2); | ||
228 | OUT_RING (chan, 1); | ||
229 | |||
230 | BEGIN_NVC0(chan, 2, NvSub2D, 0x0804, 1); | ||
231 | OUT_RING (chan, format); | ||
232 | BEGIN_NVC0(chan, 2, NvSub2D, 0x0800, 1); | ||
233 | OUT_RING (chan, 1); | ||
234 | BEGIN_NVC0(chan, 2, NvSub2D, 0x0808, 3); | ||
235 | OUT_RING (chan, 0); | ||
236 | OUT_RING (chan, 0); | ||
237 | OUT_RING (chan, 1); | ||
238 | BEGIN_NVC0(chan, 2, NvSub2D, 0x081c, 1); | ||
239 | OUT_RING (chan, 1); | ||
240 | BEGIN_NVC0(chan, 2, NvSub2D, 0x0840, 4); | ||
241 | OUT_RING (chan, 0); | ||
242 | OUT_RING (chan, 1); | ||
243 | OUT_RING (chan, 0); | ||
244 | OUT_RING (chan, 1); | ||
245 | BEGIN_NVC0(chan, 2, NvSub2D, 0x0200, 10); | ||
246 | OUT_RING (chan, format); | ||
247 | OUT_RING (chan, 1); | ||
248 | OUT_RING (chan, 0); | ||
249 | OUT_RING (chan, 1); | ||
250 | OUT_RING (chan, 0); | ||
251 | OUT_RING (chan, info->fix.line_length); | ||
252 | OUT_RING (chan, info->var.xres_virtual); | ||
253 | OUT_RING (chan, info->var.yres_virtual); | ||
254 | OUT_RING (chan, upper_32_bits(nvbo->vma.offset)); | ||
255 | OUT_RING (chan, lower_32_bits(nvbo->vma.offset)); | ||
256 | BEGIN_NVC0(chan, 2, NvSub2D, 0x0230, 10); | ||
257 | OUT_RING (chan, format); | ||
258 | OUT_RING (chan, 1); | ||
259 | OUT_RING (chan, 0); | ||
260 | OUT_RING (chan, 1); | ||
261 | OUT_RING (chan, 0); | ||
262 | OUT_RING (chan, info->fix.line_length); | ||
263 | OUT_RING (chan, info->var.xres_virtual); | ||
264 | OUT_RING (chan, info->var.yres_virtual); | ||
265 | OUT_RING (chan, upper_32_bits(nvbo->vma.offset)); | ||
266 | OUT_RING (chan, lower_32_bits(nvbo->vma.offset)); | ||
267 | FIRE_RING (chan); | ||
268 | |||
269 | return 0; | ||
270 | } | ||
271 | |||