113496ba1Ssnj/***************************************************************************
213496ba1Ssnj
313496ba1Ssnj Copyright 2000 Intel Corporation.  All Rights Reserved.
413496ba1Ssnj
513496ba1Ssnj Permission is hereby granted, free of charge, to any person obtaining a
613496ba1Ssnj copy of this software and associated documentation files (the
713496ba1Ssnj "Software"), to deal in the Software without restriction, including
813496ba1Ssnj without limitation the rights to use, copy, modify, merge, publish,
913496ba1Ssnj distribute, sub license, and/or sell copies of the Software, and to
1013496ba1Ssnj permit persons to whom the Software is furnished to do so, subject to
1113496ba1Ssnj the following conditions:
1213496ba1Ssnj
1313496ba1Ssnj The above copyright notice and this permission notice (including the
1413496ba1Ssnj next paragraph) shall be included in all copies or substantial portions
1513496ba1Ssnj of the Software.
1613496ba1Ssnj
1713496ba1Ssnj THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
1813496ba1Ssnj OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
1913496ba1Ssnj MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
2013496ba1Ssnj IN NO EVENT SHALL INTEL, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
2113496ba1Ssnj DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
2213496ba1Ssnj OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
2313496ba1Ssnj THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2413496ba1Ssnj
2513496ba1Ssnj **************************************************************************/
2613496ba1Ssnj
2713496ba1Ssnj/*
2813496ba1Ssnj * i830_video.c: i830/i845 Xv driver.
2913496ba1Ssnj *
3013496ba1Ssnj * Copyright © 2002 by Alan Hourihane and David Dawes
3113496ba1Ssnj *
3213496ba1Ssnj * Authors:
3313496ba1Ssnj *	Alan Hourihane <alanh@tungstengraphics.com>
3413496ba1Ssnj *	David Dawes <dawes@xfree86.org>
3513496ba1Ssnj *
3613496ba1Ssnj * Derived from i810 Xv driver:
3713496ba1Ssnj *
3813496ba1Ssnj * Authors of i810 code:
3913496ba1Ssnj * 	Jonathan Bian <jonathan.bian@intel.com>
4013496ba1Ssnj *      Offscreen Images:
4113496ba1Ssnj *        Matt Sottek <matthew.j.sottek@intel.com>
4213496ba1Ssnj */
4313496ba1Ssnj
4413496ba1Ssnj#ifdef HAVE_CONFIG_H
4513496ba1Ssnj#include "config.h"
4613496ba1Ssnj#endif
4713496ba1Ssnj
4813496ba1Ssnj#include <inttypes.h>
4913496ba1Ssnj#include <math.h>
5013496ba1Ssnj#include <string.h>
5113496ba1Ssnj#include <assert.h>
5213496ba1Ssnj#include <errno.h>
5313496ba1Ssnj
5413496ba1Ssnj#include "xorg-server.h"
5513496ba1Ssnj#include "xf86.h"
5613496ba1Ssnj#include "xf86_OSproc.h"
5713496ba1Ssnj#include "compiler.h"
5813496ba1Ssnj#include "xf86Pci.h"
5913496ba1Ssnj#include "xf86fbman.h"
6013496ba1Ssnj#include "xf86drm.h"
6113496ba1Ssnj#include "regionstr.h"
6213496ba1Ssnj#include "randrstr.h"
6313496ba1Ssnj#include "windowstr.h"
6413496ba1Ssnj#include "damage.h"
6513496ba1Ssnj#include "intel.h"
6613496ba1Ssnj#include "intel_uxa.h"
6713496ba1Ssnj#include "i830_reg.h"
6813496ba1Ssnj#include "xf86xv.h"
6913496ba1Ssnj#include <X11/extensions/Xv.h>
7013496ba1Ssnj#include "dixstruct.h"
7113496ba1Ssnj#include "fourcc.h"
7213496ba1Ssnj
7313496ba1Ssnj#ifdef INTEL_XVMC
7413496ba1Ssnj#define _INTEL_XVMC_SERVER_
7513496ba1Ssnj#include "intel_xvmc.h"
7613496ba1Ssnj#endif
7713496ba1Ssnj
7813496ba1Ssnj/* overlay debugging printf function */
7913496ba1Ssnj#if 0
8013496ba1Ssnj#define UXA_VIDEO_DEBUG ErrorF
8113496ba1Ssnj#else
8213496ba1Ssnj#define UXA_VIDEO_DEBUG if (0) ErrorF
8313496ba1Ssnj#endif
8413496ba1Ssnj
8513496ba1Ssnjstatic int intel_uxa_video_put_image_textured(ScrnInfoPtr, short, short, short, short, short, short,
8613496ba1Ssnj			short, short, int, unsigned char *, short, short,
8713496ba1Ssnj			Bool, RegionPtr, pointer, DrawablePtr);
8813496ba1Ssnj
8913496ba1Ssnjstatic int
9013496ba1Ssnjintel_uxa_video_set_port_attribute(ScrnInfoPtr scrn,
9113496ba1Ssnj			     Atom attribute, INT32 value, pointer data)
9213496ba1Ssnj{
9313496ba1Ssnj	intel_adaptor_private *adaptor_priv = (intel_adaptor_private *) data;
9413496ba1Ssnj
9513496ba1Ssnj	if (attribute == intel_xv_Brightness) {
9613496ba1Ssnj		if ((value < -128) || (value > 127))
9713496ba1Ssnj			return BadValue;
9813496ba1Ssnj		adaptor_priv->brightness = value;
9913496ba1Ssnj		return Success;
10013496ba1Ssnj	} else if (attribute == intel_xv_Contrast) {
10113496ba1Ssnj		if ((value < 0) || (value > 255))
10213496ba1Ssnj			return BadValue;
10313496ba1Ssnj		adaptor_priv->contrast = value;
10413496ba1Ssnj		return Success;
10513496ba1Ssnj	} else if (attribute == intel_xv_SyncToVblank) {
10613496ba1Ssnj		if ((value < -1) || (value > 1))
10713496ba1Ssnj			return BadValue;
10813496ba1Ssnj		adaptor_priv->SyncToVblank = value;
10913496ba1Ssnj		return Success;
11013496ba1Ssnj	} else {
11113496ba1Ssnj		return BadMatch;
11213496ba1Ssnj	}
11313496ba1Ssnj}
11413496ba1Ssnj
11513496ba1Ssnj
11613496ba1Ssnjstatic int xvmc_passthrough(int id)
11713496ba1Ssnj{
11813496ba1Ssnj#ifdef INTEL_XVMC
11913496ba1Ssnj	return id == FOURCC_XVMC;
12013496ba1Ssnj#else
12113496ba1Ssnj	return 0;
12213496ba1Ssnj#endif
12313496ba1Ssnj}
12413496ba1Ssnj
12513496ba1Ssnj
12613496ba1Ssnjstatic void
12713496ba1Ssnjintel_wait_for_scanline(ScrnInfoPtr scrn, PixmapPtr pixmap,
12813496ba1Ssnj			xf86CrtcPtr crtc, RegionPtr clipBoxes)
12913496ba1Ssnj{
13013496ba1Ssnj	intel_screen_private *intel = intel_get_screen_private(scrn);
13113496ba1Ssnj	pixman_box16_t box, crtc_box;
13213496ba1Ssnj	int pipe, event;
13313496ba1Ssnj	Bool full_height;
13413496ba1Ssnj	int y1, y2;
13513496ba1Ssnj
13613496ba1Ssnj	pipe = -1;
13713496ba1Ssnj	if (scrn->vtSema && pixmap_is_scanout(pixmap))
13813496ba1Ssnj		pipe = intel_crtc_to_pipe(crtc);
13913496ba1Ssnj	if (pipe < 0)
14013496ba1Ssnj		return;
14113496ba1Ssnj
14213496ba1Ssnj	box = *REGION_EXTENTS(unused, clipBoxes);
14313496ba1Ssnj
14413496ba1Ssnj	if (crtc->transform_in_use)
14513496ba1Ssnj		pixman_f_transform_bounds(&crtc->f_framebuffer_to_crtc, &box);
14613496ba1Ssnj
14713496ba1Ssnj	/* We could presume the clip was correctly computed... */
14813496ba1Ssnj	intel_crtc_box(crtc, &crtc_box);
14913496ba1Ssnj	intel_box_intersect(&box, &crtc_box, &box);
15013496ba1Ssnj
15113496ba1Ssnj	/*
15213496ba1Ssnj	 * Make sure we don't wait for a scanline that will
15313496ba1Ssnj	 * never occur
15413496ba1Ssnj	 */
15513496ba1Ssnj	y1 = (crtc_box.y1 <= box.y1) ? box.y1 - crtc_box.y1 : 0;
15613496ba1Ssnj	y2 = (box.y2 <= crtc_box.y2) ?
15713496ba1Ssnj		box.y2 - crtc_box.y1 : crtc_box.y2 - crtc_box.y1;
15813496ba1Ssnj	if (y2 <= y1)
15913496ba1Ssnj		return;
16013496ba1Ssnj
16113496ba1Ssnj	full_height = FALSE;
16213496ba1Ssnj	if (y1 == 0 && y2 == (crtc_box.y2 - crtc_box.y1))
16313496ba1Ssnj		full_height = TRUE;
16413496ba1Ssnj
16513496ba1Ssnj	/*
16613496ba1Ssnj	 * Pre-965 doesn't have SVBLANK, so we need a bit
16713496ba1Ssnj	 * of extra time for the blitter to start up and
16813496ba1Ssnj	 * do its job for a full height blit
16913496ba1Ssnj	 */
17013496ba1Ssnj	if (full_height && INTEL_INFO(intel)->gen < 040)
17113496ba1Ssnj		y2 -= 2;
17213496ba1Ssnj
17313496ba1Ssnj	if (pipe == 0) {
17413496ba1Ssnj		pipe = MI_LOAD_SCAN_LINES_DISPLAY_PIPEA;
17513496ba1Ssnj		event = MI_WAIT_FOR_PIPEA_SCAN_LINE_WINDOW;
17613496ba1Ssnj		if (full_height && INTEL_INFO(intel)->gen >= 040)
17713496ba1Ssnj			event = MI_WAIT_FOR_PIPEA_SVBLANK;
17813496ba1Ssnj	} else {
17913496ba1Ssnj		pipe = MI_LOAD_SCAN_LINES_DISPLAY_PIPEB;
18013496ba1Ssnj		event = MI_WAIT_FOR_PIPEB_SCAN_LINE_WINDOW;
18113496ba1Ssnj		if (full_height && INTEL_INFO(intel)->gen >= 040)
18213496ba1Ssnj			event = MI_WAIT_FOR_PIPEB_SVBLANK;
18313496ba1Ssnj	}
18413496ba1Ssnj
18513496ba1Ssnj	if (crtc->mode.Flags & V_INTERLACE) {
18613496ba1Ssnj		/* DSL count field lines */
18713496ba1Ssnj		y1 /= 2;
18813496ba1Ssnj		y2 /= 2;
18913496ba1Ssnj	}
19013496ba1Ssnj
19113496ba1Ssnj	BEGIN_BATCH(5);
19213496ba1Ssnj	/* The documentation says that the LOAD_SCAN_LINES command
19313496ba1Ssnj	 * always comes in pairs. Don't ask me why. */
19413496ba1Ssnj	OUT_BATCH(MI_LOAD_SCAN_LINES_INCL | pipe);
19513496ba1Ssnj	OUT_BATCH((y1 << 16) | (y2-1));
19613496ba1Ssnj	OUT_BATCH(MI_LOAD_SCAN_LINES_INCL | pipe);
19713496ba1Ssnj	OUT_BATCH((y1 << 16) | (y2-1));
19813496ba1Ssnj	OUT_BATCH(MI_WAIT_FOR_EVENT | event);
19913496ba1Ssnj	ADVANCE_BATCH();
20013496ba1Ssnj}
20113496ba1Ssnj
20213496ba1Ssnj/*
20313496ba1Ssnj * The source rectangle of the video is defined by (src_x, src_y, src_w, src_h).
20413496ba1Ssnj * The dest rectangle of the video is defined by (drw_x, drw_y, drw_w, drw_h).
20513496ba1Ssnj * id is a fourcc code for the format of the video.
20613496ba1Ssnj * buf is the pointer to the source data in system memory.
20713496ba1Ssnj * width and height are the w/h of the source data.
20813496ba1Ssnj * If "sync" is TRUE, then we must be finished with *buf at the point of return
20913496ba1Ssnj * (which we always are).
21013496ba1Ssnj * clipBoxes is the clipping region in screen space.
21113496ba1Ssnj * data is a pointer to our port private.
21213496ba1Ssnj * drawable is some Drawable, which might not be the screen in the case of
21313496ba1Ssnj * compositing.  It's a new argument to the function in the 1.1 server.
21413496ba1Ssnj */
21513496ba1Ssnjstatic int
21613496ba1Ssnjintel_uxa_video_put_image_textured(ScrnInfoPtr scrn,
21713496ba1Ssnj                                   short src_x, short src_y,
21813496ba1Ssnj                                   short drw_x, short drw_y,
21913496ba1Ssnj                                   short src_w, short src_h,
22013496ba1Ssnj                                   short drw_w, short drw_h,
22113496ba1Ssnj                                   int id, unsigned char *buf,
22213496ba1Ssnj                                   short width, short height,
22313496ba1Ssnj                                   Bool sync, RegionPtr clipBoxes, pointer data,
22413496ba1Ssnj                                   DrawablePtr drawable)
22513496ba1Ssnj{
22613496ba1Ssnj	intel_screen_private *intel = intel_get_screen_private(scrn);
22713496ba1Ssnj	intel_adaptor_private *adaptor_priv = (intel_adaptor_private *) data;
22813496ba1Ssnj	PixmapPtr pixmap = get_drawable_pixmap(drawable);
22913496ba1Ssnj	int dstPitch, dstPitch2;
23013496ba1Ssnj	BoxRec dstBox;
23113496ba1Ssnj	xf86CrtcPtr crtc;
23213496ba1Ssnj	int top, left, npixels, nlines;
23313496ba1Ssnj
23413496ba1Ssnj	if (!intel_uxa_pixmap_is_offscreen(pixmap))
23513496ba1Ssnj		return BadAlloc;
23613496ba1Ssnj
23713496ba1Ssnj#if 0
23813496ba1Ssnj	ErrorF("I830PutImage: src: (%d,%d)(%d,%d), dst: (%d,%d)(%d,%d)\n"
23913496ba1Ssnj	       "width %d, height %d\n", src_x, src_y, src_w, src_h, drw_x,
24013496ba1Ssnj	       drw_y, drw_w, drw_h, width, height);
24113496ba1Ssnj#endif
24213496ba1Ssnj
24313496ba1Ssnj	if (!intel_clip_video_helper(scrn,
24413496ba1Ssnj				    adaptor_priv,
24513496ba1Ssnj				    &crtc,
24613496ba1Ssnj				    &dstBox,
24713496ba1Ssnj				    src_x, src_y, drw_x, drw_y,
24813496ba1Ssnj				    src_w, src_h, drw_w, drw_h,
24913496ba1Ssnj				    id,
25013496ba1Ssnj				    &top, &left, &npixels, &nlines, clipBoxes,
25113496ba1Ssnj				    width, height))
25213496ba1Ssnj		return Success;
25313496ba1Ssnj
25413496ba1Ssnj	if (xvmc_passthrough(id)) {
25513496ba1Ssnj		uint32_t *gem_handle = (uint32_t *)buf;
25613496ba1Ssnj		int size;
25713496ba1Ssnj
25813496ba1Ssnj		intel_setup_dst_params(scrn, adaptor_priv, width, height,
25913496ba1Ssnj				&dstPitch, &dstPitch2, &size, id);
26013496ba1Ssnj
26113496ba1Ssnj		if (IS_I915G(intel) || IS_I915GM(intel)) {
26213496ba1Ssnj			/* XXX: i915 is not support and needs some
26313496ba1Ssnj			 * serious care.  grep for KMS in i915_hwmc.c */
26413496ba1Ssnj			return BadAlloc;
26513496ba1Ssnj		}
26613496ba1Ssnj
26713496ba1Ssnj		if (adaptor_priv->buf)
26813496ba1Ssnj			drm_intel_bo_unreference(adaptor_priv->buf);
26913496ba1Ssnj
27013496ba1Ssnj		adaptor_priv->buf =
27113496ba1Ssnj			drm_intel_bo_gem_create_from_name(intel->bufmgr,
27213496ba1Ssnj							  "xvmc surface",
27313496ba1Ssnj							  *gem_handle);
27413496ba1Ssnj		if (adaptor_priv->buf == NULL)
27513496ba1Ssnj			return BadAlloc;
27613496ba1Ssnj
27713496ba1Ssnj		adaptor_priv->reusable = FALSE;
27813496ba1Ssnj	} else {
27913496ba1Ssnj		if (!intel_video_copy_data(scrn, adaptor_priv, width, height,
28013496ba1Ssnj					  &dstPitch, &dstPitch2,
28113496ba1Ssnj					  top, left, npixels, nlines, id, buf))
28213496ba1Ssnj			return BadAlloc;
28313496ba1Ssnj	}
28413496ba1Ssnj
28513496ba1Ssnj	if (crtc && adaptor_priv->SyncToVblank != 0 && INTEL_INFO(intel)->gen < 060) {
28613496ba1Ssnj		intel_wait_for_scanline(scrn, pixmap, crtc, clipBoxes);
28713496ba1Ssnj	}
28813496ba1Ssnj
28913496ba1Ssnj	if (INTEL_INFO(intel)->gen >= 060) {
29013496ba1Ssnj		Gen6DisplayVideoTextured(scrn, adaptor_priv, id, clipBoxes,
29113496ba1Ssnj					 width, height, dstPitch, dstPitch2,
29213496ba1Ssnj					 src_w, src_h,
29313496ba1Ssnj					 drw_w, drw_h, pixmap);
29413496ba1Ssnj	} else if (INTEL_INFO(intel)->gen >= 040) {
29513496ba1Ssnj		I965DisplayVideoTextured(scrn, adaptor_priv, id, clipBoxes,
29613496ba1Ssnj					 width, height, dstPitch, dstPitch2,
29713496ba1Ssnj					 src_w, src_h,
29813496ba1Ssnj					 drw_w, drw_h, pixmap);
29913496ba1Ssnj	} else {
30013496ba1Ssnj		I915DisplayVideoTextured(scrn, adaptor_priv, id, clipBoxes,
30113496ba1Ssnj					 width, height, dstPitch, dstPitch2,
30213496ba1Ssnj					 src_w, src_h, drw_w, drw_h,
30313496ba1Ssnj					 pixmap);
30413496ba1Ssnj	}
30513496ba1Ssnj
30613496ba1Ssnj	intel_get_screen_private(scrn)->needs_flush = TRUE;
30713496ba1Ssnj	DamageDamageRegion(drawable, clipBoxes);
30813496ba1Ssnj
30913496ba1Ssnj	/* And make sure the WAIT_FOR_EVENT is queued before any
31013496ba1Ssnj	 * modesetting/dpms operations on the pipe.
31113496ba1Ssnj	 */
31213496ba1Ssnj	intel_batch_submit(scrn);
31313496ba1Ssnj
31413496ba1Ssnj	return Success;
31513496ba1Ssnj}
31613496ba1Ssnj
31713496ba1SsnjXF86VideoAdaptorPtr intel_uxa_video_setup_image_textured(ScreenPtr screen)
31813496ba1Ssnj{
31913496ba1Ssnj	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
32013496ba1Ssnj	intel_screen_private *intel = intel_get_screen_private(scrn);
32113496ba1Ssnj	XF86VideoAdaptorPtr adapt;
32213496ba1Ssnj	intel_adaptor_private *adaptor_privs;
32313496ba1Ssnj	DevUnion *devUnions;
32413496ba1Ssnj	int nports = 16, i;
32513496ba1Ssnj
32613496ba1Ssnj	UXA_VIDEO_DEBUG("intel_video_overlay_setup_image\n");
32713496ba1Ssnj
32813496ba1Ssnj	adapt = calloc(1, sizeof(XF86VideoAdaptorRec));
32913496ba1Ssnj	adaptor_privs = calloc(nports, sizeof(intel_adaptor_private));
33013496ba1Ssnj	devUnions = calloc(nports, sizeof(DevUnion));
33113496ba1Ssnj	if (adapt == NULL || adaptor_privs == NULL || devUnions == NULL) {
33213496ba1Ssnj		free(adapt);
33313496ba1Ssnj		free(adaptor_privs);
33413496ba1Ssnj		free(devUnions);
33513496ba1Ssnj		return NULL;
33613496ba1Ssnj	}
33713496ba1Ssnj
33813496ba1Ssnj	adapt->type = XvWindowMask | XvInputMask | XvImageMask;
33913496ba1Ssnj	adapt->flags = 0;
34013496ba1Ssnj	adapt->name = "Intel(R) Textured Video";
34113496ba1Ssnj	adapt->nEncodings = 1;
34213496ba1Ssnj	adapt->pEncodings = xnfalloc(sizeof(intel_xv_dummy_encoding));
34313496ba1Ssnj	memcpy(adapt->pEncodings, intel_xv_dummy_encoding, sizeof(intel_xv_dummy_encoding));
34413496ba1Ssnj	adapt->nFormats = NUM_FORMATS;
34513496ba1Ssnj	adapt->pFormats = intel_xv_formats;
34613496ba1Ssnj	adapt->nPorts = nports;
34713496ba1Ssnj	adapt->pPortPrivates = devUnions;
34813496ba1Ssnj	adapt->nAttributes = 0;
34913496ba1Ssnj	adapt->pAttributes = NULL;
35013496ba1Ssnj	if (IS_I915G(intel) || IS_I915GM(intel))
35113496ba1Ssnj		adapt->nImages = NUM_IMAGES - XVMC_IMAGE;
35213496ba1Ssnj	else
35313496ba1Ssnj		adapt->nImages = NUM_IMAGES;
35413496ba1Ssnj
35513496ba1Ssnj	adapt->pImages = intel_xv_images;
35613496ba1Ssnj	adapt->PutVideo = NULL;
35713496ba1Ssnj	adapt->PutStill = NULL;
35813496ba1Ssnj	adapt->GetVideo = NULL;
35913496ba1Ssnj	adapt->GetStill = NULL;
36013496ba1Ssnj	adapt->StopVideo = intel_video_stop_video;
36113496ba1Ssnj	adapt->SetPortAttribute = intel_uxa_video_set_port_attribute;
36213496ba1Ssnj	adapt->GetPortAttribute = intel_video_get_port_attribute;
36313496ba1Ssnj	adapt->QueryBestSize = intel_video_query_best_size;
36413496ba1Ssnj	adapt->PutImage = intel_uxa_video_put_image_textured;
36513496ba1Ssnj	adapt->QueryImageAttributes = intel_video_query_image_attributes;
36613496ba1Ssnj
36713496ba1Ssnj	for (i = 0; i < nports; i++) {
36813496ba1Ssnj		intel_adaptor_private *adaptor_priv = &adaptor_privs[i];
36913496ba1Ssnj
37013496ba1Ssnj		adaptor_priv->textured = TRUE;
37113496ba1Ssnj		adaptor_priv->videoStatus = 0;
37213496ba1Ssnj		adaptor_priv->buf = NULL;
37313496ba1Ssnj		adaptor_priv->old_buf[0] = NULL;
37413496ba1Ssnj		adaptor_priv->old_buf[1] = NULL;
37513496ba1Ssnj
37613496ba1Ssnj		adaptor_priv->rotation = RR_Rotate_0;
37713496ba1Ssnj		adaptor_priv->SyncToVblank = 1;
37813496ba1Ssnj
37913496ba1Ssnj		/* gotta uninit this someplace, XXX: shouldn't be necessary for textured */
38013496ba1Ssnj		REGION_NULL(screen, &adaptor_priv->clip);
38113496ba1Ssnj
38213496ba1Ssnj		adapt->pPortPrivates[i].ptr = (pointer) (adaptor_priv);
38313496ba1Ssnj	}
38413496ba1Ssnj
38513496ba1Ssnj	intel_xv_SyncToVblank = MAKE_ATOM("XV_SYNC_TO_VBLANK");
38613496ba1Ssnj
38713496ba1Ssnj	return adapt;
38813496ba1Ssnj}
389