If your noise function scales to 4D, then you can feed x,y,z,w with the coordinates of two 2D circles. The resulting image will tile optimally on both x and y axis.
For example(very sloppy but for clarity) :
float textureSize = 1024.0f;
for(float i = 0.0f; i < textureSize; i += 1.0f){
for(float j = 0.0f; j < textureSize; j += 1.0f){
nx = cos((i / textureSize) * 2.0f * PI);
ny = cos((j / textureSize) * 2.0f * PI);
nz = sin((i / textureSize) * 2.0f * PI);
nw = sin((j / textureSize) * 2.0f * PI);
looped_noise = noise_4D( nx, ny, nz, nw, octaves, persistence, lacunarity, blah...);
noise_buffer[((int)i * (int)textureSize) + (int)j] = looped_noise;
}
}
If your function does not easily scale to 4D, try with any simplex noise implementation out there.